Example #1
0
        /// <summary>
        /// Do a build configured for UWP Applications to the specified path, returns the error from <see cref="BuildPlayer(UwpBuildInfo)"/>
        /// </summary>
        /// <param name="buildDirectory"></param>
        /// <param name="showDialog"></param>
        /// <param name="buildAppx"></param>
        /// <returns>True, if build was successful.</returns>
        public static async Task <bool> BuildPlayer(string buildDirectory, bool showDialog = true, bool buildAppx = false)
        {
            if (UnityPlayerBuildTools.CheckBuildScenes() == false)
            {
                return(false);
            }

            var buildInfo = new UwpBuildInfo
            {
                OutputDirectory = buildDirectory,
                Scenes          = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(scene => scene.path),
                BuildAppx       = buildAppx,
                AppIconPath     = UwpBuildDeployPreferences._3DAppIconPath,

                // Configure a post build action that will compile the generated solution
                PostBuildAction = PostBuildAction
            };

            async void PostBuildAction(IBuildInfo innerBuildInfo, BuildReport buildReport)
            {
                if (buildReport.summary.result != BuildResult.Succeeded)
                {
                    EditorUtility.DisplayDialog($"{PlayerSettings.productName} WindowsStoreApp Build {buildReport.summary.result}!", "See console for details", "OK");
                }
                else
                {
                    if (showDialog &&
                        !EditorUtility.DisplayDialog(PlayerSettings.productName, "Build Complete", "OK", "Build AppX"))
                    {
                        var _buildInfo = innerBuildInfo as UwpBuildInfo;
                        Debug.Assert(_buildInfo != null);
                        EditorAssemblyReloadManager.LockReloadAssemblies = true;
                        await UwpAppxBuildTools.BuildAppxAsync(_buildInfo);

                        EditorAssemblyReloadManager.LockReloadAssemblies = false;
                    }
                }
            }

            return(await BuildPlayer(buildInfo));
        }
Example #2
0
        /// <summary>
        /// Build the Uwp Player.
        /// </summary>
        /// <param name="buildInfo"></param>
        public static async Task <bool> BuildPlayer(UwpBuildInfo buildInfo)
        {
            #region Gather Build Data

            if (buildInfo.IsCommandLine)
            {
                ParseBuildCommandLine(ref buildInfo);
            }

            #endregion Gather Build Data

            BuildReport buildReport = UnityPlayerBuildTools.BuildUnityPlayer(buildInfo);

            bool success = buildReport != null && buildReport.summary.result == BuildResult.Succeeded;

            if (success && buildInfo.BuildAppx)
            {
                success &= await UwpAppxBuildTools.BuildAppxAsync(buildInfo);
            }

            return(success);
        }
Example #3
0
        private static void ParseBuildCommandLine(ref UwpBuildInfo buildInfo)
        {
            IBuildInfo iBuildInfo = buildInfo;

            UnityPlayerBuildTools.ParseBuildCommandLine(ref iBuildInfo);

            string[] arguments = Environment.GetCommandLineArgs();

            for (int i = 0; i < arguments.Length; ++i)
            {
                switch (arguments[i])
                {
                case "-buildAppx":
                    buildInfo.BuildAppx = true;
                    break;

                case "-rebuildAppx":
                    buildInfo.RebuildAppx = true;
                    break;
                }
            }
        }
        /// <summary>
        /// Build the UWP appx bundle for this project.  Requires that <see cref="UwpPlayerBuildTools.BuildPlayer(string,bool,bool)"/> has already be run or a user has
        /// previously built the Unity Player with the WSA Player as the Build Target.
        /// </summary>
        /// <param name="buildInfo"></param>
        /// <returns>True, if the appx build was successful.</returns>
        public static async Task <bool> BuildAppxAsync(UwpBuildInfo buildInfo)
        {
            if (!EditorAssemblyReloadManager.LockReloadAssemblies)
            {
                Debug.LogError("Lock Reload assemblies before attempting to build appx!");
                return(false);
            }

            if (IsBuilding)
            {
                Debug.LogWarning("Build already in progress!");
                return(false);
            }

            if (Application.isBatchMode)
            {
                // We don't need stack traces on all our logs. Makes things a lot easier to read.
                Application.SetStackTraceLogType(LogType.Log, StackTraceLogType.None);
            }

            Debug.Log("Starting Unity Appx Build...");

            IsBuilding = true;
            string slnFilename = Path.Combine(buildInfo.OutputDirectory, $"{PlayerSettings.productName}.sln");

            if (!File.Exists(slnFilename))
            {
                Debug.LogError("Unable to find Solution to build from!");
                return(IsBuilding = false);
            }

            // Get and validate the msBuild path...
            var msBuildPath = await FindMsBuildPathAsync();

            if (!File.Exists(msBuildPath))
            {
                Debug.LogError($"MSBuild.exe is missing or invalid!\n{msBuildPath}");
                return(IsBuilding = false);
            }

            // Ensure that the generated .appx version increments by modifying Package.appxmanifest
            if (!UpdateAppxManifest(buildInfo))
            {
                Debug.LogError("Failed to increment package version!");
                return(IsBuilding = false);
            }

            string storagePath         = Path.GetFullPath(Path.Combine(Path.Combine(Application.dataPath, ".."), buildInfo.OutputDirectory));
            string solutionProjectPath = Path.GetFullPath(Path.Combine(storagePath, $@"{PlayerSettings.productName}.sln"));

            // Now do the actual appx build
            var processResult = await new Process().StartProcessAsync(
                msBuildPath,
                $"\"{solutionProjectPath}\" /t:{(buildInfo.RebuildAppx ? "Rebuild" : "Build")} /p:Configuration={buildInfo.Configuration} /p:Platform={buildInfo.BuildPlatform} /verbosity:m",
                !Application.isBatchMode);

            switch (processResult.ExitCode)
            {
            case 0:
                Debug.Log("Appx Build Successful!");

                if (Application.isBatchMode)
                {
                    Debug.Log(string.Join("\n", processResult.Output));
                }
                break;

            case -1073741510:
                Debug.LogWarning("The build was terminated either by user's keyboard input CTRL+C or CTRL+Break or closing command prompt window.");
                break;

            default:
            {
                if (processResult.ExitCode != 0)
                {
                    Debug.LogError($"{PlayerSettings.productName} appx build Failed! (ErrorCode: {processResult.ExitCode})");

                    if (Application.isBatchMode)
                    {
                        Debug.LogError(string.Join("\n", processResult.Errors));
                    }
                }

                break;
            }
            }

            AssetDatabase.SaveAssets();

            IsBuilding = false;
            return(processResult.ExitCode == 0);
        }
        private static bool UpdateAppxManifest(UwpBuildInfo buildInfo)
        {
            // Find the manifest, assume the one we want is the first one
            string[] manifests = Directory.GetFiles(BuildDeployPreferences.AbsoluteBuildDirectory, "Package.appxmanifest", SearchOption.AllDirectories);

            if (manifests.Length == 0)
            {
                Debug.LogError($"Unable to find Package.appxmanifest file for build (in path - {BuildDeployPreferences.AbsoluteBuildDirectory})");
                return(false);
            }

            if (manifests.Length > 1)
            {
                Debug.LogWarning("Found more than one appxmanifest in the target build folder!");
            }

            const string uap5 = "uap5";

            var rootNode     = XElement.Load(manifests[0]);
            var identityNode = rootNode.Element(rootNode.GetDefaultNamespace() + "Identity");

            if (identityNode == null)
            {
                Debug.LogError($"Package.appxmanifest for build (in path - {BuildDeployPreferences.AbsoluteBuildDirectory}) is missing an <Identity /> node");
                return(false);
            }

            var dependencies = rootNode.Element(rootNode.GetDefaultNamespace() + "Dependencies");

            if (dependencies == null)
            {
                Debug.LogError($"Package.appxmanifest for build (in path - {BuildDeployPreferences.AbsoluteBuildDirectory}) is missing <Dependencies /> node.");
                return(false);
            }

            UpdateDependenciesElement(dependencies, rootNode.GetDefaultNamespace());

            // Setup the 3d app icon.
            if (!string.IsNullOrWhiteSpace(UwpBuildDeployPreferences._3DAppIconPath))
            {
                // Add the uap5 namespace if it doesn't exist.
                if (rootNode.GetNamespaceOfPrefix(uap5) == null)
                {
                    rootNode.Add(new XAttribute(XNamespace.Xmlns + uap5, Uap5NameSpace));
                }

                var ignorable = rootNode.Attribute(XName.Get("IgnorableNamespaces"));

                if (ignorable != null)
                {
                    if (!ignorable.Value.Contains(uap5))
                    {
                        ignorable.Value = $"{ignorable.Value} {uap5}";
                    }
                }

                // find mixed reality model
                var modelContainer = rootNode.Descendants(Uap5NameSpace + "MixedRealityModel").ToArray();

                if (modelContainer.Any())
                {
                    var element = modelContainer.First();
                    var path    = element.Attribute(XName.Get("Path"));

                    if (path != null)
                    {
                        path.Value = buildInfo.AppIconPath;
                    }
                    else
                    {
                        element.Add(new XAttribute("Path", buildInfo.AppIconPath));
                    }
                }
                else
                {
                    var modelElement = new XElement(Uap5NameSpace + "MixedRealityModel");
                    var defaultTile  = rootNode.Descendants(UapNameSpace + "DefaultTile").First();
                    defaultTile.Add(modelElement);
                    modelElement.Add(new XAttribute("Path", buildInfo.AppIconPath));
                }
            }

            // We use XName.Get instead of string -> XName implicit conversion because
            // when we pass in the string "Version", the program doesn't find the attribute.
            // Best guess as to why this happens is that implicit string conversion doesn't set the namespace to empty
            var versionAttr = identityNode.Attribute(XName.Get("Version"));

            if (versionAttr == null)
            {
                Debug.LogError($"Package.appxmanifest for build (in path - {BuildDeployPreferences.AbsoluteBuildDirectory}) is missing a Version attribute in the <Identity /> node.");
                return(false);
            }

            // Assume package version always has a '.' between each number.
            // According to https://msdn.microsoft.com/en-us/library/windows/apps/br211441.aspx
            // Package versions are always of the form Major.Minor.Build.Revision.
            // Note: Revision number reserved for Windows Store, and a value other than 0 will fail WACK.
            var version    = PlayerSettings.WSA.packageVersion;
            var newVersion = new Version(version.Major, version.Minor, buildInfo.AutoIncrement ? version.Build + 1 : version.Build, version.Revision);

            PlayerSettings.WSA.packageVersion = newVersion;
            versionAttr.Value = newVersion.ToString();
            rootNode.Save(manifests[0]);
            return(true);
        }