public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        if (SC.StageTargetConfigurations.Count != 1)
        {
            throw new AutomationException(ExitCode.Error_OnlyOneTargetConfigurationSupported, "XXX is currently only able to package one target configuration at a time, but StageTargetConfigurations contained {0} configurations", SC.StageTargetConfigurations.Count);
        }

        string BatchPath = GetBatchPath(Params, SC);

        File.WriteAllText(BatchPath, string.Format("notepad {0}", Params.GetProjectExeForPlatform(UnrealTargetPlatform.XXX).ToString()));

        PrintRunTime();
    }
Example #2
0
    // --------------------------------------------------------------------------------
    // ArchiveCommand.Automation.cs

    public override void GetFilesToArchive(ProjectParams Params, DeploymentContext SC)
    {
        if (SC.StageTargetConfigurations.Count != 1)
        {
            throw new AutomationException("iOS is currently only able to package one target configuration at a time, but StageTargetConfigurations contained {0} configurations", SC.StageTargetConfigurations.Count);
        }

        // copy to archive directory
        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", "HTML5");

        string UE4GameBasename      = Path.GetFileNameWithoutExtension(Params.GetProjectExeForPlatform(UnrealTargetPlatform.HTML5).ToString());
        string ProjectBasename      = Params.ShortProjectName;
        string ProjectConfiguration = Params.ClientConfigsToBuild[0].ToString();

        if (ProjectConfiguration != "Development")
        {
            UE4GameBasename += "-HTML5-" + ProjectConfiguration;
            ProjectBasename += "-HTML5-" + ProjectConfiguration;
        }

        SC.ArchiveFiles(PackagePath, UE4GameBasename + ".wasm");                        // MAIN game code
        SC.ArchiveFiles(PackagePath, UE4GameBasename + ".js");                          // MAIN js file (emscripten)
        SC.ArchiveFiles(PackagePath, UE4GameBasename + ".js.symbols");                  // symbols file
        SC.ArchiveFiles(PackagePath, "Utility.js");                                     // utilities
        SC.ArchiveFiles(PackagePath, ProjectBasename + ".data");                        // DATA file
        SC.ArchiveFiles(PackagePath, ProjectBasename + ".data.js");                     // DATA file js driver (emscripten)
        SC.ArchiveFiles(PackagePath, ProjectBasename + ".UE4.js");                      // UE4 js file
        SC.ArchiveFiles(PackagePath, ProjectBasename + ".css");                         // UE4 css file
        SC.ArchiveFiles(PackagePath, ProjectBasename + ".html");                        // landing page.

        // Archive HTML5 Server and a Readme.
        SC.ArchiveFiles(CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/"), "HTML5LaunchHelper.exe");
        SC.ArchiveFiles(CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5/"), "Readme.txt");
        SC.ArchiveFiles(PackagePath, "RunMacHTML5LaunchHelper.command");
        SC.ArchiveFiles(PackagePath, ".htaccess");

        if (Compressed)
        {
            SC.ArchiveFiles(PackagePath, UE4GameBasename + ".wasmgz");
            SC.ArchiveFiles(PackagePath, UE4GameBasename + ".jsgz");
            SC.ArchiveFiles(PackagePath, UE4GameBasename + ".js.symbolsgz");
            SC.ArchiveFiles(PackagePath, "Utility.jsgz");
            SC.ArchiveFiles(PackagePath, ProjectBasename + ".datagz");
            SC.ArchiveFiles(PackagePath, ProjectBasename + ".data.jsgz");
            SC.ArchiveFiles(PackagePath, ProjectBasename + ".UE4.jsgz");
            SC.ArchiveFiles(PackagePath, ProjectBasename + ".cssgz");
        }
        else
        {
            // nuke old compressed files to prevent using stale files
            File.Delete(UE4GameBasename + ".wasmgz");
            File.Delete(UE4GameBasename + ".jsgz");
            File.Delete(UE4GameBasename + ".js.symbolsgz");
            File.Delete("Utility.jsgz");
            File.Delete(ProjectBasename + ".datagz");
            File.Delete(ProjectBasename + ".data.jsgz");
            File.Delete(ProjectBasename + ".UE4.jsgz");
            File.Delete(ProjectBasename + ".cssgz");
        }

        if (HTMLPakAutomation.CanCreateMapPaks(Params))
        {
            // find all paks.
            string[] Files = Directory.GetFiles(Path.Combine(PackagePath, Params.ShortProjectName), "*", SearchOption.AllDirectories);
            foreach (string PakFile in Files)
            {
                SC.ArchivedFiles.Add(PakFile, Path.GetFileName(PakFile));
            }
        }

        UploadToS3(SC, ProjectBasename + ".html");
    }
Example #3
0
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        LogInformation("Package {0}", Params.RawProjectPath);

        LogInformation("Setting Emscripten SDK for packaging..");
        HTML5SDKInfo.SetupEmscriptenTemp();
        HTML5SDKInfo.SetUpEmscriptenConfigFile();

        // ----------------------------------------
        // ini configurations
        ConfigHierarchy ConfigCache = UnrealBuildTool.ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(Params.RawProjectPath), UnrealTargetPlatform.HTML5);

        // Debug and Development builds are not compressed to:
        // - speed up iteration times
        // - ensure (IndexedDB) data are not cached/used
        // Shipping builds "can be":
        // - compressed
        // - (IndexedDB) cached
        string ProjectConfiguration = Params.ClientConfigsToBuild[0].ToString();

        if (ProjectConfiguration == "Shipping")
        {
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "Compressed", out Compressed);
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableIndexedDB", out enableIndexedDB);
        }
        LogInformation("HTML5Platform.Automation: Compressed = " + Compressed);
        LogInformation("HTML5Platform.Automation: EnableIndexedDB = " + enableIndexedDB);

        // ----------------------------------------
        // package directory
        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }
        string _ProjectNameExtra  = ProjectConfiguration != "Development" ? "-HTML5-" + ProjectConfiguration : "";
        string _ProjectFullpath   = Params.GetProjectExeForPlatform(UnrealTargetPlatform.HTML5).ToString();
        string _ProjectFilename   = Path.GetFileNameWithoutExtension(_ProjectFullpath) + _ProjectNameExtra;
        string SrcUE4GameBasename = Path.Combine(Path.GetDirectoryName(_ProjectFullpath), _ProjectFilename);
        string UE4GameBasename    = Path.Combine(PackagePath, _ProjectFilename);
        string ProjectBasename    = Path.Combine(PackagePath, Params.ShortProjectName + _ProjectNameExtra);

        // ----------------------------------------
        // packaging
        if (HTMLPakAutomation.CanCreateMapPaks(Params))
        {
            HTMLPakAutomation PakAutomation = new HTMLPakAutomation(Params, SC);

            // Create Necessary Paks.
            PakAutomation.CreateEnginePak();
            PakAutomation.CreateGamePak();
            PakAutomation.CreateContentDirectoryPak();

            // Create Emscripten Package from Necessary Paks. - This will be the VFS.
            PakAutomation.CreateEmscriptenDataPackage(PackagePath, ProjectBasename + ".data");

            // Create All Map Paks which  will be downloaded on the fly.
            PakAutomation.CreateMapPak();

            // Create Delta Paks if setup.
            List <string> Paks = new List <string>();
            ConfigCache.GetArray("/Script/HTML5PlatformEditor.HTML5TargetSettings", "LevelTransitions", out Paks);

            if (Paks != null)
            {
                foreach (var Pak in Paks)
                {
                    var    Matched = Regex.Matches(Pak, "\"[^\"]+\"", RegexOptions.IgnoreCase);
                    string MapFrom = Path.GetFileNameWithoutExtension(Matched[0].ToString().Replace("\"", ""));
                    string MapTo   = Path.GetFileNameWithoutExtension(Matched[1].ToString().Replace("\"", ""));
                    PakAutomation.CreateDeltaMapPaks(MapFrom, MapTo);
                }
            }
        }
        else
        {
            // we need to operate in the root
            string PythonPath     = HTML5SDKInfo.Python();
            string EmPackagerPath = HTML5SDKInfo.EmscriptenPackager();

            using (new ScopedEnvVar("EM_CONFIG", HTML5SDKInfo.DOT_EMSCRIPTEN))
            {
                using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
                {
                    string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\" --no-heap-copy", EmPackagerPath, ProjectBasename + ".data");
                    RunAndLog(CmdEnv, PythonPath, CmdLine);
                }
            }
        }


        // ----------------------------------------
        // copy to package directory

        // ensure the ue4game binary exists, if applicable
        if (!FileExists_NoExceptions(SrcUE4GameBasename + ".js"))
        {
            LogInformation("Failed to find game application " + SrcUE4GameBasename + ".js");
            throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not find application {0}. You may need to build the UE4 project with your target configuration and platform.", SrcUE4GameBasename + ".js");
        }

        if (!Params.IsCodeBasedProject)
        {
            // template project - need to copy over UE4Game.*
            File.Copy(SrcUE4GameBasename + ".wasm", UE4GameBasename + ".wasm", true);
            File.Copy(SrcUE4GameBasename + ".js", UE4GameBasename + ".js", true);
            File.Copy(SrcUE4GameBasename + ".js.symbols", UE4GameBasename + ".js.symbols", true);

            File.SetAttributes(UE4GameBasename + ".wasm", FileAttributes.Normal);
            File.SetAttributes(UE4GameBasename + ".js", FileAttributes.Normal);
            File.SetAttributes(UE4GameBasename + ".js.symbols", FileAttributes.Normal);
        }
        // else, c++ projects will compile "to" PackagePath

        // note: ( ProjectBasename + ".data" ) already created above (!HTMLPakAutomation.CanCreateMapPaks())


        // ----------------------------------------
        // generate HTML files to the package directory

        // custom HTML, JS (if any), and CSS (if any) template files
        string LocalBuildPath = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5");
        string BuildPath      = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Build", "HTML5");
        string TemplateFile   = CombinePaths(BuildPath, "project_template.html");

        if (!File.Exists(TemplateFile))
        {
            // fall back to default UE4 template files
            BuildPath    = LocalBuildPath;
            TemplateFile = CombinePaths(BuildPath, "project_template.html");
        }
        GenerateFileFromTemplate(TemplateFile, ProjectBasename + ".html", Params, ConfigCache);

        TemplateFile = CombinePaths(BuildPath, "project_template.js");
        if (File.Exists(TemplateFile))
        {
            GenerateFileFromTemplate(TemplateFile, ProjectBasename + ".UE4.js", Params, ConfigCache);
        }

        TemplateFile = CombinePaths(BuildPath, "project_template.css");
        if (File.Exists(TemplateFile))
        {
            GenerateFileFromTemplate(TemplateFile, ProjectBasename + ".css", Params, ConfigCache);
        }


        // ----------------------------------------
        // (development) support files
        string MacBashTemplateFile = CombinePaths(LocalBuildPath, "RunMacHTML5LaunchHelper_template.command");
        string MacBashOutputFile   = Path.Combine(PackagePath, "RunMacHTML5LaunchHelper.command");
        string MonoPath            = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/BatchFiles/Mac/SetupMono.sh");

        GenerateMacCommandFromTemplate(MacBashTemplateFile, MacBashOutputFile, MonoPath);
        // ........................................
        string htaccesspath = Path.Combine(PackagePath, ".htaccess");

        if (File.Exists(htaccesspath))
        {
            FileAttributes attributes = File.GetAttributes(htaccesspath);
            if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            {
                attributes &= ~FileAttributes.ReadOnly;
                File.SetAttributes(htaccesspath, attributes);
            }
        }
        File.Copy(CombinePaths(LocalBuildPath, "htaccess_template.txt"), htaccesspath, true);


        // ----------------------------------------
        // final copies

        // Gather utlity .js files and combine into one file
        string DestinationFile = PackagePath + "/Utility.js";

        File.Delete(DestinationFile);
        // spelling this out - one file at a time (i.e. don't slurp in project_template.js)
        File.AppendAllText(DestinationFile, File.ReadAllText(CombinePaths(LocalBuildPath, "json2.js")));
        File.AppendAllText(DestinationFile, File.ReadAllText(CombinePaths(LocalBuildPath, "jstorage.js")));
        File.AppendAllText(DestinationFile, File.ReadAllText(CombinePaths(LocalBuildPath, "moz_binarystring.js")));

        if (Compressed)
        {
            LogInformation("Build configuration is " + ProjectConfiguration + ", so (gzip) compressing files for web servers.");

            // Compress all files. These are independent tasks which can be threaded.
            List <Task> CompressionTasks = new List <Task>();

            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(UE4GameBasename + ".wasm", UE4GameBasename + ".wasmgz")));                            // main game code
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(UE4GameBasename + ".js", UE4GameBasename + ".jsgz")));                                // main js (emscripten)
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(UE4GameBasename + ".js.symbols", UE4GameBasename + ".js.symbolsgz")));                // symbols fil.
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(PackagePath + "/Utility.js", PackagePath + "/Utility.jsgz")));                        // Utility
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".data", ProjectBasename + ".datagz")));                            // DATA file
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".data.js", ProjectBasename + ".data.jsgz")));                      // DATA file .js driver (emscripten)
            if (File.Exists(ProjectBasename + ".UE4.js"))
            {
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".UE4.js", ProjectBasename + ".UE4.jsgz")));                            // UE4 js
            }
            if (File.Exists(ProjectBasename + ".css"))
            {
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".css", ProjectBasename + ".cssgz")));                                          // UE4 css
            }
            Task.WaitAll(CompressionTasks.ToArray());
        }
        else
        {
            LogInformation("Build configuration is " + ProjectConfiguration + ", so not compressing. Build Shipping configuration to compress files to save space.");

            // nuke old compressed files to prevent using stale files
            File.Delete(UE4GameBasename + ".wasmgz");
            File.Delete(UE4GameBasename + ".jsgz");
            File.Delete(UE4GameBasename + ".js.symbolsgz");
            File.Delete(PackagePath + "/Utility.jsgz");
            File.Delete(ProjectBasename + ".datagz");
            File.Delete(ProjectBasename + ".data.jsgz");
            File.Delete(ProjectBasename + ".UE4.jsgz");
            File.Delete(ProjectBasename + ".cssgz");
        }

        File.Copy(CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/HTML5LaunchHelper.exe"), CombinePaths(PackagePath, "HTML5LaunchHelper.exe"), true);
//		Task.WaitAll(CompressionTasks);
        PrintRunTime();
    }
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        LogInformation("Package {0}", Params.RawProjectPath);

        LogInformation("Setting Emscripten SDK for packaging..");
        HTML5SDKInfo.SetupEmscriptenTemp();
        HTML5SDKInfo.SetUpEmscriptenConfigFile();

        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }

        // ini configurations
        var  ConfigCache  = UnrealBuildTool.ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(Params.RawProjectPath), UnrealTargetPlatform.HTML5);
        bool targetWebGL1 = false;         // inverted checked - this will be going away soon...

        if (ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "TargetWebGL1", out targetWebGL1))
        {
            targetWebGL2 = !targetWebGL1;
        }

        // Debug and Development builds are not uncompressed to:
        // - speed up iteration times
        // - ensure (IndexedDB) data are not cached/used
        // Shipping builds "can be":
        // - compressed
        // - (IndexedDB) cached
        if (Params.ClientConfigsToBuild[0].ToString() == "Shipping")
        {
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "Compressed", out Compressed);
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableIndexedDB", out enableIndexedDB);
        }
        LogInformation("HTML5Platform.Automation: TargetWebGL2 = " + targetWebGL2);
        LogInformation("HTML5Platform.Automation: Compressed = " + Compressed);
        LogInformation("HTML5Platform.Automation: EnableIndexedDB = " + enableIndexedDB);

        string FinalDataLocation = Path.Combine(PackagePath, Params.ShortProjectName) + ".data";

        if (HTMLPakAutomation.CanCreateMapPaks(Params))
        {
            HTMLPakAutomation PakAutomation = new HTMLPakAutomation(Params, SC);

            // Create Necessary Paks.
            PakAutomation.CreateEnginePak();
            PakAutomation.CreateGamePak();
            PakAutomation.CreateContentDirectoryPak();

            // Create Emscripten Package from Necessary Paks. - This will be the VFS.
            PakAutomation.CreateEmscriptenDataPackage(PackagePath, FinalDataLocation);

            // Create All Map Paks which  will be downloaded on the fly.
            PakAutomation.CreateMapPak();

            // Create Delta Paks if setup.
            List <string> Paks = new List <string>();
            ConfigCache.GetArray("/Script/HTML5PlatformEditor.HTML5TargetSettings", "LevelTransitions", out Paks);

            if (Paks != null)
            {
                foreach (var Pak in Paks)
                {
                    var    Matched = Regex.Matches(Pak, "\"[^\"]+\"", RegexOptions.IgnoreCase);
                    string MapFrom = Path.GetFileNameWithoutExtension(Matched[0].ToString().Replace("\"", ""));
                    string MapTo   = Path.GetFileNameWithoutExtension(Matched[1].ToString().Replace("\"", ""));
                    PakAutomation.CreateDeltaMapPaks(MapFrom, MapTo);
                }
            }
        }
        else
        {
            // we need to operate in the root
            string PythonPath   = HTML5SDKInfo.Python();
            string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

            using (new ScopedEnvVar("EM_CONFIG", HTML5SDKInfo.DOT_EMSCRIPTEN))
            {
                using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
                {
                    string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\" --no-heap-copy", PackagerPath, FinalDataLocation);
                    RunAndLog(CmdEnv, PythonPath, CmdLine);
                }
            }
        }

        // copy the "Executable" to the package directory
        string ProjectGameExeFilename = Params.GetProjectExeForPlatform(UnrealTargetPlatform.HTML5).ToString();
        string GameBasename           = Path.GetFileNameWithoutExtension(ProjectGameExeFilename);

        if (Params.ClientConfigsToBuild[0].ToString() != "Development")
        {
            GameBasename += "-HTML5-" + Params.ClientConfigsToBuild[0].ToString();
        }
        // no extension
        string GameBasepath            = Path.GetDirectoryName(ProjectGameExeFilename);
        string FullGameBasePath        = Path.Combine(GameBasepath, GameBasename);
        string FullPackageGameBasePath = Path.Combine(PackagePath, GameBasename);

        // with extension
        string GameExe                = GameBasename + ".js";
        string FullGameExePath        = Path.Combine(GameBasepath, GameExe);
        string FullPackageGameExePath = Path.Combine(PackagePath, GameExe);

        // ensure the ue4game binary exists, if applicable
        if (!FileExists_NoExceptions(FullGameExePath))
        {
            LogInformation("Failed to find game application " + FullGameExePath);
            throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not find application {0}. You may need to build the UE4 project with your target configuration and platform.", FullGameExePath);
        }

        if (FullGameExePath != FullPackageGameExePath)         // TODO: remove this check
        {
            File.Copy(FullGameExePath + ".symbols", FullPackageGameExePath + ".symbols", true);
            File.Copy(FullGameBasePath + ".wasm", FullPackageGameBasePath + ".wasm", true);
            File.Copy(FullGameExePath, FullPackageGameExePath, true);
        }

        File.SetAttributes(FullPackageGameExePath + ".symbols", FileAttributes.Normal);
        File.SetAttributes(FullPackageGameBasePath + ".wasm", FileAttributes.Normal);
        File.SetAttributes(FullPackageGameExePath, FileAttributes.Normal);

        // put the HTML file to the package directory
        string TemplateFile = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5/GameX.html.template");
        string OutputFile   = Path.Combine(PackagePath,
                                           (Params.ClientConfigsToBuild[0].ToString() != "Development" ?
                                            (Params.ShortProjectName + "-HTML5-" + Params.ClientConfigsToBuild[0].ToString()) :
                                            Params.ShortProjectName)) + ".html";

        string CanvasScaleMode;

        ConfigCache.GetString("/Script/HTML5PlatformEditor.HTML5TargetSettings", "CanvasScalingMode", out CanvasScaleMode);
        GenerateFileFromTemplate(TemplateFile,
                                 OutputFile,
                                 Params.ShortProjectName,
                                 Params.ClientConfigsToBuild[0].ToString(),
//				Params.StageCommandline,
                                 !Params.IsCodeBasedProject,
                                 HTML5SDKInfo.HeapSize(ConfigCache, Params.ClientConfigsToBuild[0].ToString()),
                                 CanvasScaleMode
                                 );

        string MacBashTemplateFile = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5/RunMacHTML5LaunchHelper.command.template");
        string MacBashOutputFile   = Path.Combine(PackagePath, "RunMacHTML5LaunchHelper.command");
        string MonoPath            = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/BatchFiles/Mac/SetupMono.sh");

        GenerateMacCommandFromTemplate(MacBashTemplateFile, MacBashOutputFile, MonoPath);

        string htaccessTemplate = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5/htaccess.template");
        string htaccesspath     = Path.Combine(PackagePath, ".htaccess");

        if (File.Exists(htaccesspath))
        {
            FileAttributes attributes = File.GetAttributes(htaccesspath);
            if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            {
                attributes &= ~FileAttributes.ReadOnly;
                File.SetAttributes(htaccesspath, attributes);
            }
        }
        File.Copy(htaccessTemplate, htaccesspath, true);

        string JSDir  = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5");
        string OutDir = PackagePath;

        // Gather utlity .js files and combine into one file
        string[] UtilityJavaScriptFiles = Directory.GetFiles(JSDir, "*.js");

        string DestinationFile = OutDir + "/Utility.js";

        File.Delete(DestinationFile);
        foreach (var UtilityFile in UtilityJavaScriptFiles)
        {
            string Data = File.ReadAllText(UtilityFile);
            File.AppendAllText(DestinationFile, Data);
        }

        if (Compressed)
        {
            LogInformation("Build configuration is " + Params.ClientConfigsToBuild[0].ToString() + ", so (gzip) compressing files for web servers.");

            // Compress all files. These are independent tasks which can be threaded.
            List <Task> CompressionTasks = new List <Task>();

            // data file
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FinalDataLocation, FinalDataLocation + "gz")));

            // data file .js driver.
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FinalDataLocation + ".js", FinalDataLocation + ".jsgz")));

            // main game code
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameBasePath + ".wasm", FullPackageGameBasePath + ".wasmgz")));
            // main js.
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameExePath, FullPackageGameExePath + "gz")));

            // symbols file.
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameExePath + ".symbols", FullPackageGameExePath + ".symbolsgz")));

            // Utility
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(OutDir + "/Utility.js", OutDir + "/Utility.jsgz")));

            Task.WaitAll(CompressionTasks.ToArray());
        }
        else
        {
            LogInformation("Build configuration is " + Params.ClientConfigsToBuild[0].ToString() + ", so not compressing. Build Shipping configuration to compress files to save space.");

            // nuke old compressed files to prevent using stale files
            File.Delete(FinalDataLocation + "gz");
            File.Delete(FinalDataLocation + ".jsgz");
            File.Delete(FullPackageGameExePath + "gz");
            File.Delete(FullPackageGameBasePath + ".wasmgz");
            File.Delete(FullPackageGameExePath + ".symbolsgz");
            File.Delete(OutDir + "/Utility.jsgz");
        }

        File.Copy(CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/HTML5LaunchHelper.exe"), CombinePaths(OutDir, "HTML5LaunchHelper.exe"), true);
//		Task.WaitAll(CompressionTasks);
        PrintRunTime();
    }