private static string GetAppPlatformOutputName(AppBuildInfo buildInfo)
        {
            if (buildInfo.BuildTargetDevice == AppBuildInfo.BuildTargetDevices.None)
            {
                var platformName = "";
                var appPlatform  = MapAppTargetPlatform(buildInfo.BuildTarget);

                switch (appPlatform)
                {
                case AppPackPlatform.WindowsStandalone:
                    platformName = "emulator";
                    break;

                case AppPackPlatform.Android:
                    platformName = "android";
                    break;
                }

                return(platformName.ToLower());
            }
            else
            {
                return(buildInfo.BuildTargetDevice.ToString().ToLower());
            }
        }
        public ActionResult AppBuildInfo()
        {
            var result = new AppBuildInfo
            {
                Bitness    = 64,
                Boost      = "1.72.0",
                Libtorrent = "1.2.5.0",
                Openssl    = "1.1.1f",
                Qt         = "5.13.2",
                Zlib       = "1.2.11"
            };

            return(Ok(result));
        }
        /// <summary>
        /// Builds a Liminal App.
        /// </summary>
        /// <param name="buildInfo">The build information.</param>
        public static void Build(AppBuildInfo buildInfo)
        {
            if (buildInfo == null)
            {
                throw new ArgumentNullException("buildInfo");
            }

            var assetBundles = AssetDatabase.GetAllAssetBundleNames();

            foreach (var bundle in assetBundles)
            {
                // boolean true forces the asset bundles to be deleted even if they're in use.
                AssetDatabase.RemoveAssetBundleName(bundle, true);
            }

            AssetImporter.GetAtPath(buildInfo.Scene.path).SetAssetBundleNameAndVariant("appscene", "");

            // Get and verify the target platform is supported
            var appPlatform = MapAppTargetPlatform(buildInfo.BuildTarget);

            if (appPlatform == AppPackPlatform.Unknown)
            {
                throw new Exception(string.Format("The supplied buildTarget is currently unsupported: {0}", buildInfo.BuildTarget));
            }

            // Read the app manifest (this will throw if there are errors)
            var appManifest = ReadAppManifest();

            if (appManifest.Id == 0)
            {
                throw new Exception("Application Id is zero");
            }

            UpdateProgressBar("Building Limapp", "Checking Scene", 0.1F);

            // Find the ExperienceApp
            var app = UnityEngine.Object.FindObjectOfType <ExperienceApp>();

            VerifyAppSceneSetup(app);

            Debug.LogFormat("[Liminal.Build] Building app {0}, for platform {1}", appManifest.Id, appPlatform);

            // Clear out existing data fields on the experience
            ClearAppData(app);

            var asmName          = "App" + appManifest.Id.ToString().PadLeft(AppManifest.MaxIdLength, '0');
            var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildInfo.BuildTarget);

            try
            {
                var sw = System.Diagnostics.Stopwatch.StartNew();

                // Ensure the output directory is ready
                var outputPath = GetOutputPath();
                Directory.CreateDirectory(outputPath);

                // Generate final application assembly
                // This will be packed into an AssetBundle that will be loaded into the master app
                // NOTE: .bytes extension is used so that unity won't try to load the DLL
                var asmPath      = GetAppAssemblyPath() + ".bytes";
                var asmBuilder   = new AppAssemblyBuilder();
                var asmBuildInfo = new AppAssemblyBuilder.AssemblyBuildInfo()
                {
                    Name             = asmName,
                    BuildTarget      = buildInfo.BuildTarget,
                    BuildTargetGroup = buildTargetGroup,
                    Version          = appManifest.Version
                };
                asmBuilder.Build(asmBuildInfo, asmPath);

                // Build asset lookup for the current scene
                Debug.Log("[Liminal.Build] Building asset lookup...");
                var assetLookupBuilder = new AssetLookupBuilder();
                var assetLookup        = assetLookupBuilder.Build(buildInfo.Scene);

                // Serialize scene data
                // This will write any data from classes/structs in the app marked with [Serializable], so that we
                // can deserialize them when loaded into the master app (Unity won't do this with loaded assemblies...)
                Debug.Log("[Liminal.Build] Serializing scene...");
                var serializer = new AppSerializer(new AssemblyDataProvider(asmName), assetLookup);
                var jsonPath   = Path.Combine(outputPath, AppDataName);
                var jsonData   = serializer.Serialize(buildInfo.Scene, jsonPath);

                UpdateProgressBar("Building Limapp", "Serializing App Data", 0.2F);
                // Assign serialized app data and lookup to the ExperienceApp object
                SetAppData(app, jsonData, assetLookup);

                // Build asset bundles
                UpdateProgressBar("Building Limapp", "Building scene AssetBundle", 0.4F);
                Debug.Log("[Liminal.Build] Building scene AssetBundle...");
                BuildPipeline.BuildAssetBundles(outputPath,
                                                BuildAssetBundleOptions.UncompressedAssetBundle | BuildAssetBundleOptions.ForceRebuildAssetBundle,
                                                buildInfo.BuildTarget);

                // Run post-processor on the asset bundle
                var sceneBundlePath = Path.Combine(outputPath, "appscene");
                var sceneBundleProc = new SceneBundleProcessor(BuildConsts.ProjectAssemblyName, asmName);
                sceneBundleProc.Process(sceneBundlePath);

                // Pack to AppPack (.limapp)
                // This will also compress the app (using LZMA)
                UpdateProgressBar("Building Limapp", "Packing App", 0.7F);
                Debug.Log("[Liminal.Build] Packing app...");

                var platformName = GetAppPlatformOutputName(buildInfo);
                var extension    = GetFileExtension(buildInfo.CompressionType);
                var appFilename  = string.Format("app_{0}_{1}_v{2}.{3}", appManifest.Id, platformName,
                                                 appManifest.Version, extension);
                var appPackPath = Path.Combine(outputPath, appFilename);
                var appPack     = new AppPack()
                {
                    TargetPlatform  = appPlatform,
                    ApplicationId   = appManifest.Id,
                    Assemblies      = BuildPackAssemblyRawBytesList(asmPath, buildTargetGroup, buildInfo.BuildTarget),
                    SceneBundle     = File.ReadAllBytes(Path.Combine(outputPath, "appscene")),
                    CompressionType = buildInfo.CompressionType,
                };

                // Pack the AppPack into a compressed file
                UpdateProgressBar("Building Limapp", "Compressing App", 0.8F);

                new AppPacker()
                .PackAsync(appPack, appPackPath)
                .Wait();

                UpdateProgressBar("Building Limapp", "Cleaning up", 0.9F);
                Debug.Log("[Liminal.Build] Cleaning up...");

                // Clean up
                // Delete temporary files
                foreach (var file in Directory.GetFiles(outputPath))
                {
                    var ext = Path.GetExtension(file).ToLower();
                    if (ext != ".limapp" && ext != ".ulimapp")
                    {
                        TryDeleteFile(file);
                    }
                }

                AssetDatabase.Refresh();

                var appPath = Path.GetFullPath(appPackPath);
                var appFile = new FileInfo(appPath);

                EditorUtility.ClearProgressBar();
                sw.Stop();
                Debug.LogFormat("[Liminal.Build] Build completed successfully in {0:0.00}s. Size: {1:0.00}mb, Output: {2}", sw.Elapsed.TotalSeconds, BytesToMb(appFile.Length), appPath);
            }
            catch (Exception ex)
            {
                Debug.LogError("[Liminal.Build] Build failed.");
                Debug.LogException(ex);

                EditorUtility.ClearProgressBar();
            }
            finally
            {
                // Ensure everything is always cleaned up...
                ClearAppData(app);
                AssetLookupBuilder.DestroyExisting(buildInfo.Scene);

                GUIUtility.ExitGUI();
            }
        }