// The rebuild lighting process
        #region RebuildLightMaps Command

        public override void ExecuteBuild()
        {
            Log("********** REBUILD LIGHT MAPS COMMAND STARTED **********");

            try
            {
                var Params = new ProjectParams
                             (
                    Command: this,
                    // Shared
                    RawProjectPath: ProjectPath
                             );

                // Sync and build our targets required for the commandlet to run correctly.
                P4.Sync(String.Format("{0}/...#head", P4Env.BuildRootP4));

                BuildNecessaryTargets();
                CheckOutMaps(Params);
                RunRebuildLightmapsCommandlet(Params);
                SubmitRebuiltMaps();
            }
            catch (Exception ProcessEx)
            {
                Log("********** REBUILD LIGHT MAPS COMMAND FAILED **********");
                HandleFailure(ProcessEx.Message);
                throw ProcessEx;
            }

            // The processes steps have completed successfully.
            HandleSuccess();

            Log("********** REBUILD LIGHT MAPS COMMAND COMPLETED **********");
        }
 private void SafeSync(string SyncCmdLine)
 {
     try
     {
         P4.Sync(SyncCmdLine);
     }
     catch (Exception Ex)
     {
         LogError("Unable to sync {0}", SyncCmdLine);
         LogError(Ex.Message);
     }
 }
        /// <summary>
        /// Syncs to given label.
        /// </summary>
        /// <param name="BranchPath">Current branch path.</param>
        /// <param name="LabelName">Label name to sync.</param>
        /// <param name="bArtistSync">Perform artist sync? (binaries to label, content to latest)</param>
        /// <param name="bPreview">Perform preview sync? (p4 sync -n)</param>
        private void SyncToLabel(string BranchPath, string LabelName, bool bArtistSync = true, bool bPreview = false)
        {
            var           ProgramRevisionSpec = "@" + LabelName;
            List <string> SyncSteps;

            if (bArtistSync)
            {
                // Get latest CL number to sync cause @head can change during
                // different syncs and it could create integrity problems in
                // workspace.
                var ContentRevisionSpec = "@" + P4.GetLatestCLNumber().ToString();

                var GameName = ParseGameNameFromLabel(LabelName);

                var ArtistSyncRulesPath = string.Format("{0}/{1}/Build/ArtistSyncRules.xml",
                                                        BranchPath, string.IsNullOrWhiteSpace(GameName) ? "Samples" : GameName);

                var SyncRules = string.Join("\n", P4.P4Print(ArtistSyncRulesPath + "#head"));

                if (string.IsNullOrWhiteSpace(SyncRules))
                {
                    throw new AutomationException("The path {0} is not valid or file is empty.", ArtistSyncRulesPath);
                }

                SyncSteps = GenerateSyncSteps(SyncRules, ContentRevisionSpec, ProgramRevisionSpec);
            }
            else
            {
                SyncSteps = new List <string>();
                SyncSteps.Add("/..." + ProgramRevisionSpec);                 // all files to label
            }

            foreach (var SyncStep in SyncSteps)
            {
                P4.Sync((bPreview ? "-n " : "") + BranchPath + SyncStep);
            }
        }
    public override void ExecuteBuild()
    {
        int WorkingCL = P4.CreateChange(P4Env.Client, String.Format("TestP4_StrandCheckout, head={0}", P4Env.Changelist));

        LogInformation("Build from {0}    Working in {1}", P4Env.Changelist, WorkingCL);

        List <string> Sign = new List <string>();

        Sign.Add(CombinePaths(CmdEnv.LocalRoot, @"\Engine\Binaries\DotNET\AgentInterface.dll"));

        LogInformation("Signing and adding {0} build products to changelist {1}...", Sign.Count, WorkingCL);

        CodeSign.SignMultipleIfEXEOrDLL(this, Sign);
        foreach (var File in Sign)
        {
            P4.Sync("-f -k " + File + "#head");             // sync the file without overwriting local one
            if (!FileExists(File))
            {
                throw new AutomationException("BUILD FAILED {0} was a build product but no longer exists", File);
            }

            P4.ReconcileNoDeletes(WorkingCL, File);
        }
    }
Пример #5
0
    public override void ExecuteBuild()
    {
        var EditorExe = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor-Cmd.exe");

        if (P4Enabled)
        {
            Log("Sync necessary content to head revision");
            P4.Sync(P4Env.BuildRootP4 + "/Engine/Config/...");
            P4.Sync(P4Env.BuildRootP4 + "/Engine/Content/...");
            P4.Sync(P4Env.BuildRootP4 + "/Engine/Source/...");
            Log("Localize from label {0}", P4Env.LabelToSync);
        }

        OneSkyConfigData OneSkyConfig = OneSkyConfigHelper.Find("OneSkyConfig_EpicGames");
        var oneSkyService             = new OneSkyService(OneSkyConfig.ApiKey, OneSkyConfig.ApiSecret);
        var projectGroup = GetProjectGroup(oneSkyService, "Unreal Engine");

        // Create changelist for backed up POs from OneSky.
        if (P4Enabled)
        {
            OneSkyDownloadedPOChangeList = P4.CreateChange(P4Env.Client, "OneSky downloaded PO backup.");
        }

        // Export all text from OneSky
        ExportProjectToDirectory(oneSkyService, projectGroup, "Engine");
        ExportProjectToDirectory(oneSkyService, projectGroup, "Editor");
        ExportProjectToDirectory(oneSkyService, projectGroup, "EditorTutorials");
        ExportProjectToDirectory(oneSkyService, projectGroup, "PropertyNames");
        ExportProjectToDirectory(oneSkyService, projectGroup, "ToolTips");
        ExportProjectToDirectory(oneSkyService, projectGroup, "Category");
        ExportProjectToDirectory(oneSkyService, projectGroup, "Keywords");

        // Submit changelist for backed up POs from OneSky.
        if (P4Enabled)
        {
            int SubmittedChangeList;
            P4.Submit(OneSkyDownloadedPOChangeList, out SubmittedChangeList);
        }

        // Setup editor arguments for SCC.
        string EditorArguments = String.Empty;

        if (P4Enabled)
        {
            EditorArguments = String.Format("-SCCProvider={0} -P4Port={1} -P4User={2} -P4Client={3} -P4Passwd={4}", "Perforce", P4Env.P4Port, P4Env.User, P4Env.Client, P4.GetAuthenticationToken());
        }
        else
        {
            EditorArguments = String.Format("-SCCProvider={0}", "None");
        }

        // Setup commandlet arguments for SCC.
        string CommandletSCCArguments = String.Empty;

        if (P4Enabled)
        {
            CommandletSCCArguments += (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-EnableSCC";
        }
        if (!AllowSubmit)
        {
            CommandletSCCArguments += (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-DisableSCCSubmit";
        }

        // Setup commandlet arguments with configurations.
        var CommandletArgumentSets = new string[]
        {
            String.Format("-config={0}", @"./Config/Localization/Engine.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
            String.Format("-config={0}", @"./Config/Localization/Editor.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
            String.Format("-config={0}", @"./Config/Localization/EditorTutorials.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
            String.Format("-config={0}", @"./Config/Localization/PropertyNames.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
            String.Format("-config={0}", @"./Config/Localization/ToolTips.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
            String.Format("-config={0}", @"./Config/Localization/Category.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
            String.Format("-config={0}", @"./Config/Localization/Keywords.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
        };

        // Execute commandlet for each set of arguments.
        foreach (var CommandletArguments in CommandletArgumentSets)
        {
            Log("Localization for {0} {1}", EditorArguments, CommandletArguments);

            Log("Running UE4Editor to generate localization data");

            string Arguments = String.Format("-run=GatherText {0} {1}", EditorArguments, CommandletArguments);
            var    RunResult = Run(EditorExe, Arguments);

            if (RunResult.ExitCode != 0)
            {
                throw new AutomationException("Error while executing localization commandlet '{0}'", Arguments);
            }
        }

        // Upload all text to OneSky
        UploadDirectoryToProject(GetProject(oneSkyService, "Unreal Engine", "Engine"), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Content/Localization/Engine"), "*.po");
        UploadDirectoryToProject(GetProject(oneSkyService, "Unreal Engine", "Editor"), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Content/Localization/Editor"), "*.po");
        UploadDirectoryToProject(GetProject(oneSkyService, "Unreal Engine", "EditorTutorials"), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Content/Localization/EditorTutorials"), "*.po");
        UploadDirectoryToProject(GetProject(oneSkyService, "Unreal Engine", "PropertyNames"), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Content/Localization/PropertyNames"), "*.po");
        UploadDirectoryToProject(GetProject(oneSkyService, "Unreal Engine", "ToolTips"), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Content/Localization/ToolTips"), "*.po");
        UploadDirectoryToProject(GetProject(oneSkyService, "Unreal Engine", "Category"), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Content/Localization/Category"), "*.po");
        UploadDirectoryToProject(GetProject(oneSkyService, "Unreal Engine", "Keywords"), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Content/Localization/Keywords"), "*.po");

        // Localisation statistics estimator.
        if (P4Enabled)
        {
            // Available only for P4
            var EstimatorExePath   = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/DotNET/TranslatedWordsCountEstimator.exe");
            var StatisticsFilePath = @"\\epicgames.net\root\UE3\Localization\WordCounts\udn.csv";

            var Arguments = string.Format(
                "{0} {1} {2} {3} {4}",
                StatisticsFilePath,
                P4Env.P4Port,
                P4Env.User,
                P4Env.Client,
                Environment.GetEnvironmentVariable("P4PASSWD"));

            var RunResult = Run(EstimatorExePath, Arguments);

            if (RunResult.ExitCode != 0)
            {
                throw new AutomationException("Error while executing TranslatedWordsCountEstimator with arguments '{0}'", Arguments);
            }
        }
    }
    public override void ExecuteBuild()
    {
        if (ParseParam("BuildEditor"))
        {
            UE4Build.BuildAgenda Agenda = new UE4Build.BuildAgenda();
            Agenda.AddTarget("UE4Editor", HostPlatform.Current.HostEditorPlatform, UnrealTargetConfiguration.Development);
            Agenda.AddTarget("ShaderCompileWorker", HostPlatform.Current.HostEditorPlatform, UnrealTargetConfiguration.Development);

            UE4Build Builder = new UE4Build(this);
            Builder.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: true, InForceNoXGE: true);
        }

        var EditorExe = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor-Cmd.exe");

        if (P4Enabled)
        {
            Log("Sync necessary content to head revision");
            P4.Sync(P4Env.Branch + "/Engine/Config/...");
            P4.Sync(P4Env.Branch + "/Engine/Content/...");
            P4.Sync(P4Env.Branch + "/Engine/Source/...");

            P4.Sync(P4Env.Branch + "/Portal/Config/...");
            P4.Sync(P4Env.Branch + "/Portal/Content/...");
            P4.Sync(P4Env.Branch + "/Portal/Source/...");
        }

        OneSkyConfigData OneSkyConfig = OneSkyConfigHelper.Find("OneSkyConfig_EpicGames");
        var oneSkyService             = new OneSkyService(OneSkyConfig.ApiKey, OneSkyConfig.ApiSecret);

        // Export Launcher text from OneSky
        {
            var launcherGroup = GetLauncherGroup(oneSkyService);
            var appProject    = GetAppProject(oneSkyService);
            var appFile       = appProject.UploadedFiles.FirstOrDefault(f => f.Filename == "App.po");

            //Export
            if (appFile != null)
            {
                ExportFileToDirectory(appFile, new DirectoryInfo(CmdEnv.LocalRoot + "/Portal/Content/Localization/App"), launcherGroup.EnabledCultures);
            }
        }

        // Setup editor arguments for SCC.
        string EditorArguments = String.Empty;

        if (P4Enabled)
        {
            EditorArguments = String.Format("-SCCProvider={0} -P4Port={1} -P4User={2} -P4Client={3} -P4Passwd={4}", "Perforce", P4Env.ServerAndPort, P4Env.User, P4Env.Client, P4.GetAuthenticationToken());
        }
        else
        {
            EditorArguments = String.Format("-SCCProvider={0}", "None");
        }

        // Setup commandlet arguments for SCC.
        string CommandletSCCArguments = String.Empty;

        if (P4Enabled)
        {
            CommandletSCCArguments += "-EnableSCC";
        }
        if (!AllowSubmit)
        {
            CommandletSCCArguments += (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-DisableSCCSubmit";
        }

        // Setup commandlet arguments with configurations.
        var CommandletArgumentSets = new string[]
        {
            String.Format("-config={0}", @"../Portal/Config/Localization/App.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments)
        };

        // Execute commandlet for each set of arguments.
        foreach (var CommandletArguments in CommandletArgumentSets)
        {
            Log("Localization for {0} {1}", EditorArguments, CommandletArguments);

            Log("Running UE4Editor to generate Localization data");

            string Arguments = String.Format("-run=GatherText {0} {1}", EditorArguments, CommandletArguments);
            var    RunResult = Run(EditorExe, Arguments);

            if (RunResult.ExitCode != 0)
            {
                throw new AutomationException("Error while executing localization commandlet '{0}'", Arguments);
            }
        }

        // Upload Launcher text to OneSky
        UploadDirectoryToProject(GetAppProject(oneSkyService), new DirectoryInfo(CmdEnv.LocalRoot + "/Portal/Content/Localization/App"), "*.po");
    }
Пример #7
0
    private void ProcessLocalizationProjects(LocalizationBatch LocalizationBatch, int PendingChangeList, string UEProjectRoot, string UEProjectName, string LocalizationProviderName, List <string> LocalizationSteps, string AdditionalCommandletArguments)
    {
        var EditorExe                       = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor-Cmd.exe");
        var RootWorkingDirectory            = CombinePaths(UEProjectRoot, LocalizationBatch.UEProjectDirectory);
        var RootLocalizationTargetDirectory = CombinePaths(UEProjectRoot, LocalizationBatch.LocalizationTargetDirectory);

        // Try and find our localization provider
        LocalizationProvider LocProvider = null;

        {
            LocalizationProvider.LocalizationProviderArgs LocProviderArgs;
            LocProviderArgs.RootWorkingDirectory            = RootWorkingDirectory;
            LocProviderArgs.RootLocalizationTargetDirectory = RootLocalizationTargetDirectory;
            LocProviderArgs.RemoteFilenamePrefix            = LocalizationBatch.RemoteFilenamePrefix;
            LocProviderArgs.Command           = this;
            LocProviderArgs.PendingChangeList = PendingChangeList;
            LocProvider = LocalizationProvider.GetLocalizationProvider(LocalizationProviderName, LocProviderArgs);
        }

        // Make sure the Localization configs and content is up-to-date to ensure we don't get errors later on
        if (P4Enabled)
        {
            Log("Sync necessary content to head revision");
            P4.Sync(P4Env.BuildRootP4 + "/" + LocalizationBatch.LocalizationTargetDirectory + "/Config/Localization/...");
            P4.Sync(P4Env.BuildRootP4 + "/" + LocalizationBatch.LocalizationTargetDirectory + "/Content/Localization/...");
        }

        // Generate the info we need to gather for each project
        var ProjectInfos = new List <ProjectInfo>();

        foreach (var ProjectName in LocalizationBatch.LocalizationProjectNames)
        {
            ProjectInfos.Add(GenerateProjectInfo(RootLocalizationTargetDirectory, ProjectName, LocalizationSteps));
        }

        if (LocalizationSteps.Contains("Download") && LocProvider != null)
        {
            // Export all text from our localization provider
            foreach (var ProjectInfo in ProjectInfos)
            {
                LocProvider.DownloadProjectFromLocalizationProvider(ProjectInfo.ProjectName, ProjectInfo.ImportInfo);
            }
        }

        // Setup editor arguments for SCC.
        string EditorArguments = String.Empty;

        if (P4Enabled)
        {
            EditorArguments = String.Format("-SCCProvider={0} -P4Port={1} -P4User={2} -P4Client={3} -P4Passwd={4} -P4Changelist={5} -EnableSCC -DisableSCCSubmit", "Perforce", P4Env.P4Port, P4Env.User, P4Env.Client, P4.GetAuthenticationToken(), PendingChangeList);
        }
        else
        {
            EditorArguments = String.Format("-SCCProvider={0}", "None");
        }
        EditorArguments += " -Unattended";

        // Execute commandlet for each config in each project.
        bool bLocCommandletFailed = false;

        foreach (var ProjectInfo in ProjectInfos)
        {
            foreach (var LocalizationStep in ProjectInfo.LocalizationSteps)
            {
                if (!LocalizationSteps.Contains(LocalizationStep.Name))
                {
                    continue;
                }

                var CommandletArguments = String.Format("-config=\"{0}\"", LocalizationStep.LocalizationConfigFile);

                if (!String.IsNullOrEmpty(AdditionalCommandletArguments))
                {
                    CommandletArguments += " " + AdditionalCommandletArguments;
                }

                string Arguments = String.Format("{0} -run=GatherText {1} {2}", UEProjectName, EditorArguments, CommandletArguments);
                Log("Running localization commandlet: {0}", Arguments);
                var StartTime   = DateTime.UtcNow;
                var RunResult   = Run(EditorExe, Arguments, null, ERunOptions.Default | ERunOptions.NoLoggingOfRunCommand);               // Disable logging of the run command as it will print the exit code which GUBP can pick up as an error (we do that ourselves below)
                var RunDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
                Log("Localization commandlet finished in {0}s", RunDuration / 1000);

                if (RunResult.ExitCode != 0)
                {
                    LogWarning("The localization commandlet exited with code {0} which likely indicates a crash. It ran with the following arguments: '{1}'", RunResult.ExitCode, Arguments);
                    bLocCommandletFailed = true;
                    break;                     // We failed a step, so don't process any other steps in this config chain
                }
            }
        }

        if (LocalizationSteps.Contains("Upload") && LocProvider != null)
        {
            if (bLocCommandletFailed)
            {
                LogWarning("Skipping upload to the localization provider due to an earlier commandlet failure.");
            }
            else
            {
                // Upload all text to our localization provider
                foreach (var ProjectInfo in ProjectInfos)
                {
                    LocProvider.UploadProjectToLocalizationProvider(ProjectInfo.ProjectName, ProjectInfo.ExportInfo);
                }
            }
        }
    }
    public override void ExecuteBuild()
    {
        var EditorExe = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor-Cmd.exe");

        // Parse out the required command line arguments
        var UEProjectDirectory = ParseParamValue("UEProjectDirectory");

        if (UEProjectDirectory == null)
        {
            throw new AutomationException("Missing required command line argument: 'UEProjectDirectory'");
        }

        var UEProjectName = ParseParamValue("UEProjectName");

        if (UEProjectName == null)
        {
            UEProjectName = "";
        }

        var OneSkyConfigName = ParseParamValue("OneSkyConfigName");

        if (OneSkyConfigName == null)
        {
            throw new AutomationException("Missing required command line argument: 'OneSkyConfigName'");
        }

        var OneSkyProjectGroupName = ParseParamValue("OneSkyProjectGroupName");

        if (OneSkyProjectGroupName == null)
        {
            throw new AutomationException("Missing required command line argument: 'OneSkyProjectGroupName'");
        }

        var OneSkyProjectNames = new List <string>();
        {
            var OneSkyProjectNamesStr = ParseParamValue("OneSkyProjectNames");
            if (OneSkyProjectNamesStr == null)
            {
                throw new AutomationException("Missing required command line argument: 'OneSkyProjectNames'");
            }
            foreach (var ProjectName in OneSkyProjectNamesStr.Split(','))
            {
                OneSkyProjectNames.Add(ProjectName.Trim());
            }
        }

        var OneSkyBranchSuffix = ParseParamValue("OneSkyBranchSuffix");

        var RootWorkingDirectory = CombinePaths(CmdEnv.LocalRoot, UEProjectDirectory);

        // Make sure the Localization configs and content is up-to-date to ensure we don't get errors later on
        if (P4Enabled)
        {
            Log("Sync necessary content to head revision");
            P4.Sync(P4Env.BuildRootP4 + "/" + UEProjectDirectory + "/Config/Localization/...");
            P4.Sync(P4Env.BuildRootP4 + "/" + UEProjectDirectory + "/Content/Localization/...");
        }

        // Generate the info we need to gather for each project
        var ProjectInfos = new List <ProjectInfo>();

        foreach (var ProjectName in OneSkyProjectNames)
        {
            ProjectInfos.Add(GenerateProjectInfo(RootWorkingDirectory, ProjectName));
        }

        OneSkyConfigData OneSkyConfig = OneSkyConfigHelper.Find(OneSkyConfigName);
        var OneSkyService             = new OneSkyService(OneSkyConfig.ApiKey, OneSkyConfig.ApiSecret);
        var OneSkyProjectGroup        = GetOneSkyProjectGroup(OneSkyService, OneSkyProjectGroupName);

        // Create changelist for backed up POs from OneSky.
        if (P4Enabled)
        {
            OneSkyDownloadedPOChangeList = P4.CreateChange(P4Env.Client, "OneSky downloaded PO backup.");
        }

        // Export all text from OneSky
        foreach (var ProjectInfo in ProjectInfos)
        {
            ExportOneSkyProjectToDirectory(RootWorkingDirectory, OneSkyService, OneSkyProjectGroup, OneSkyBranchSuffix, ProjectInfo);
        }

        // Submit changelist for backed up POs from OneSky.
        if (P4Enabled)
        {
            int SubmittedChangeList;
            P4.Submit(OneSkyDownloadedPOChangeList, out SubmittedChangeList);
        }

        // Setup editor arguments for SCC.
        string EditorArguments = String.Empty;

        if (P4Enabled)
        {
            EditorArguments = String.Format("-SCCProvider={0} -P4Port={1} -P4User={2} -P4Client={3} -P4Passwd={4}", "Perforce", P4Env.P4Port, P4Env.User, P4Env.Client, P4.GetAuthenticationToken());
        }
        else
        {
            EditorArguments = String.Format("-SCCProvider={0}", "None");
        }

        // Setup commandlet arguments for SCC.
        string CommandletSCCArguments = String.Empty;

        if (P4Enabled)
        {
            CommandletSCCArguments += (String.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-EnableSCC";
        }
        if (!AllowSubmit)
        {
            CommandletSCCArguments += (String.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-DisableSCCSubmit";
        }

        // Execute commandlet for each config in each project.
        foreach (var ProjectInfo in ProjectInfos)
        {
            foreach (var LocalizationConfigFile in ProjectInfo.LocalizationConfigFiles)
            {
                var CommandletArguments = String.Format("-config={0}", LocalizationConfigFile) + (String.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments);

                Log("Localization for {0} {1}", EditorArguments, CommandletArguments);

                Log("Running UE4Editor to generate localization data");

                string Arguments = String.Format("{0} -run=GatherText {1} {2}", UEProjectName, EditorArguments, CommandletArguments);
                var    RunResult = Run(EditorExe, Arguments);

                if (RunResult.ExitCode != 0)
                {
                    Console.WriteLine("[ERROR] Error while executing localization commandlet '{0}'", Arguments);
                }
            }
        }

        // Upload all text to OneSky
        foreach (var ProjectInfo in ProjectInfos)
        {
            UploadProjectToOneSky(RootWorkingDirectory, OneSkyService, OneSkyProjectGroup, OneSkyBranchSuffix, ProjectInfo);
        }
    }
Пример #9
0
    public override void ExecuteBuild()
    {
        var EditorExe = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor-Cmd.exe");

        if (P4Enabled)
        {
            Log("Sync necessary content to head revision");
            P4.Sync(P4Env.BuildRootP4 + "/Engine/Config/...");
            P4.Sync(P4Env.BuildRootP4 + "/Engine/Content/...");
            P4.Sync(P4Env.BuildRootP4 + "/Engine/Source/...");

            P4.Sync(P4Env.BuildRootP4 + "/Engine/Programs/NoRedist/UnrealEngineLauncher/Config/...");
            P4.Sync(P4Env.BuildRootP4 + "/Engine/Programs/NoRedist/UnrealEngineLauncher/Content/...");
            //P4.Sync(P4Env.BuildRootP4 + "/Engine/Source/..."); <- takes care of syncing Launcher source already

            Log("Localize from label {0}", P4Env.LabelToSync);
        }

        OneSkyConfigData OneSkyConfig = OneSkyConfigHelper.Find("OneSkyConfig_EpicGames");
        var oneSkyService             = new OneSkyService(OneSkyConfig.ApiKey, OneSkyConfig.ApiSecret);

        // Export Launcher text from OneSky
        {
            var launcherGroup = GetLauncherGroup(oneSkyService);
            var appProject    = GetAppProject(oneSkyService);
            var appFile       = appProject.UploadedFiles.FirstOrDefault(f => f.Filename == "App.po");

            //Export
            if (appFile != null)
            {
                ExportFileToDirectory(appFile, new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Programs/NoRedist/UnrealEngineLauncher/Content/Localization/App"), launcherGroup.EnabledCultures);
            }
        }

        // Setup editor arguments for SCC.
        string EditorArguments = String.Empty;

        if (P4Enabled)
        {
            EditorArguments = String.Format("-SCCProvider={0} -P4Port={1} -P4User={2} -P4Client={3} -P4Passwd={4}", "Perforce", P4Env.P4Port, P4Env.User, P4Env.Client, P4.GetAuthenticationToken());
        }
        else
        {
            EditorArguments = String.Format("-SCCProvider={0}", "None");
        }

        // Setup commandlet arguments for SCC.
        string CommandletSCCArguments = String.Empty;

        if (P4Enabled)
        {
            CommandletSCCArguments += (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-EnableSCC";
        }
        if (!AllowSubmit)
        {
            CommandletSCCArguments += (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-DisableSCCSubmit";
        }

        // Setup commandlet arguments with configurations.
        var CommandletArgumentSets = new string[]
        {
            String.Format("-config={0}", @"./Programs/NoRedist/UnrealEngineLauncher/Config/Localization/App.ini") + (string.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments),
            String.Format("-config={0}", @"./Programs/NoRedist/UnrealEngineLauncher/Config/Localization/WordCount.ini"),
        };

        // Execute commandlet for each set of arguments.
        foreach (var CommandletArguments in CommandletArgumentSets)
        {
            Log("Localization for {0} {1}", EditorArguments, CommandletArguments);

            Log("Running UE4Editor to generate Localization data");

            string Arguments = String.Format("-run=GatherText {0} {1}", EditorArguments, CommandletArguments);
            var    RunResult = Run(EditorExe, Arguments);

            if (RunResult.ExitCode != 0)
            {
                throw new AutomationException("Error while executing localization commandlet '{0}'", Arguments);
            }
        }

        // Upload Launcher text to OneSky
        UploadDirectoryToProject(GetAppProject(oneSkyService), new DirectoryInfo(CmdEnv.LocalRoot + "/Engine/Programs/NoRedist/UnrealEngineLauncher/Content/Localization/App"), "*.po");
    }
    public override ExitCode Execute()
    {
        LogInformation("************************* SyncProject");

        // These are files that should always be synced because tools update them
        string[] ForceSyncFiles = new string[]
        {
            "Engine/Build/Build.version",
            "Engine/Source/Programs/DotNETCommon/MetaData.cs"
        };

        // Parse the project filename (as a local path)
        string ProjectArg = ParseParamValue("Project", null);

        // Parse the changelist to sync to. -1 will sync to head.
        int  CL          = ParseParamInt("CL", -1);
        int  Threads     = ParseParamInt("threads", 2);
        bool ForceSync   = ParseParam("force");
        bool PreviewOnly = ParseParam("preview");

        bool ProjectOnly = ParseParam("projectonly");

        int  Retries     = ParseParamInt("retries", 3);
        bool Unversioned = ParseParam("unversioned");

        bool BuildProject    = ParseParam("build");
        bool OpenProject     = ParseParam("open");
        bool GenerateProject = ParseParam("generate");

        string PathArg = ParseParamValue("paths", "");

        IEnumerable <string> ExplicitPaths = PathArg.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries).Select(S => S.Trim());

        if (CL == 0)
        {
            if (!ForceSync)
            {
                throw new AutomationException("If using 0 CL to remove files -force must also be specified");
            }

            // Cannot unsync the engine folder
            ProjectOnly = true;
        }
        else if (CL == -1)
        {
            // find most recent change
            List <P4Connection.ChangeRecord> Records;

            string Cmd = string.Format("-s submitted -m1 //{0}/...", P4Env.Client);
            if (!P4.Changes(out Records, Cmd, false))
            {
                throw new AutomationException("p4 changes failed. Cannot find most recent CL");
            }

            CL = Records.First().CL;
        }

        bool EngineOnly = string.IsNullOrEmpty(ProjectArg);

        // Will be the full local path to the project file once resolved
        FileReference ProjectFile = null;

        // Will be the full workspace path to the project in P4 (if a project was specified)
        string P4ProjectPath = null;

        // Will be the full workspace path to the engine in P4 (If projectonly wasn't specified);
        string P4EnginePath = null;

        // If we're syncing a project find where it is in P4
        if (!EngineOnly)
        {
            ProjectFile = ProjectUtils.FindProjectFileFromName(ProjectArg);

            if (ProjectFile != null)
            {
                // get the path in P4
                P4WhereRecord Record = P4.Where(ProjectFile.FullName, AllowSpew: false).FirstOrDefault(x => x.DepotFile != null && !x.bUnmap);
                P4ProjectPath = Record.ClientFile;
            }
            else
            {
                // if they provided a name and not a path then find the file (requires that it's synced).
                string RelativePath = ProjectArg;

                if (Path.GetExtension(RelativePath).Length == 0)
                {
                    RelativePath = Path.ChangeExtension(RelativePath, "uproject");
                }

                if (!RelativePath.Contains(Path.DirectorySeparatorChar) && !RelativePath.Contains(Path.AltDirectorySeparatorChar))
                {
                    RelativePath = CommandUtils.CombinePaths(PathSeparator.Slash, Path.GetFileNameWithoutExtension(RelativePath), RelativePath);
                }

                Log.TraceInformation("{0} not on disk. Searching P4 for {1}", ProjectArg, RelativePath);

                List <string> SearchPaths = new List <string>();
                SearchPaths.Add("");
                string ProjectDirsFile = Directory.EnumerateFiles(CommandUtils.CombinePaths(CmdEnv.LocalRoot), "*.uprojectdirs").FirstOrDefault();
                if (ProjectDirsFile != null)
                {
                    foreach (string FilePath in File.ReadAllLines(ProjectDirsFile))
                    {
                        string Trimmed = FilePath.Trim();
                        if (!Trimmed.StartsWith("./", StringComparison.OrdinalIgnoreCase) &&
                            !Trimmed.StartsWith(";", StringComparison.OrdinalIgnoreCase) &&
                            Trimmed.IndexOfAny(Path.GetInvalidPathChars()) < 0)
                        {
                            SearchPaths.Add(Trimmed);
                        }
                    }
                }

                // Get the root of the branch containing the selected file
                foreach (string SearchPath in SearchPaths)
                {
                    string P4Path = CommandUtils.CombinePaths(PathSeparator.Slash, P4Env.ClientRoot, SearchPath, RelativePath);

                    if (P4.FileExistsInDepot(P4Path))
                    {
                        P4WhereRecord Record = P4.Where(P4Path, AllowSpew: false).FirstOrDefault(x => x.DepotFile != null && !x.bUnmap);
                        // make sure to sync with //workspace/path as it cleans up files if the user has stream switched
                        P4ProjectPath = Record.ClientFile;
                        Log.TraceInformation("Found project at {0}", P4ProjectPath);
                        break;
                    }
                }
            }

            if (string.IsNullOrEmpty(P4ProjectPath))
            {
                throw new AutomationException("Could not find project file for {0} locally or in P4. Provide a full path or check the subdirectory is listed in UE4Games.uprojectdirs", ProjectArg);
            }

            Log.TraceVerbose("Resolved {0} to P4 Path {1}", ProjectArg, P4ProjectPath);
        }

        // Build the list of paths that need syncing
        List <string> SyncPaths = new List <string>();


        //
        if (ExplicitPaths.Any())
        {
            // Add all explicit paths as <root>/Path/...
            ExplicitPaths.ToList().ForEach(P => SyncPaths.Add(CommandUtils.CombinePaths(PathSeparator.Slash, P4Env.ClientRoot, P, "...")));
        }
        else
        {
            // See if the engine is in P4 too by checking the p4 location of a local file
            string        LocalEngineFile = CommandUtils.CombinePaths(CmdEnv.LocalRoot, "Engine", "Source", "UE4Editor.target.cs");
            P4WhereRecord EngineRecord    = P4.Where(LocalEngineFile, AllowSpew: false).FirstOrDefault(x => x.DepotFile != null && !x.bUnmap);

            if (!ProjectOnly && P4.FileExistsInDepot(EngineRecord.DepotFile))
            {
                // make sure to sync with //workspace/path as it cleans up files if the user has stream switched
                P4EnginePath = EngineRecord.ClientFile.Replace("Engine/Source/UE4Editor.target.cs", "");
                SyncPaths.Add(CommandUtils.CombinePaths(PathSeparator.Slash, P4EnginePath + "*"));
                SyncPaths.Add(CommandUtils.CombinePaths(PathSeparator.Slash, P4EnginePath, "Engine", "..."));
            }

            if (!string.IsNullOrEmpty(P4ProjectPath))
            {
                string P4ProjectDir = Regex.Replace(P4ProjectPath, @"[^/]+\.uproject", "...", RegexOptions.IgnoreCase);
                SyncPaths.Add(P4ProjectDir);
            }
        }

        // Force these files as they can be overwritten by tools
        if (!PreviewOnly && !string.IsNullOrEmpty(P4EnginePath) && !ProjectOnly)
        {
            IEnumerable <string> ForceSyncList = ForceSyncFiles.Select(F => CommandUtils.CombinePaths(PathSeparator.Slash, P4EnginePath, F));

            foreach (var F in ForceSyncList)
            {
                LogInformation("Force-updating {0}", F);

                string SyncCommand = string.Format("-f {0}@{1}", F, CL);

                // sync with retries
                P4.Sync(SyncCommand, true, false, Retries);
            }
        }

        // Sync all the things
        foreach (string SyncPath in SyncPaths)
        {
            string SyncCommand = "";

            if (Threads > 1)
            {
                SyncCommand = string.Format(" --parallel \"threads={0}\" {1}", Threads, SyncCommand);
            }

            if (ForceSync)
            {
                SyncCommand = SyncCommand + " -f";
            }

            SyncCommand += string.Format(" {0}@{1}", SyncPath, CL);

            if (!PreviewOnly)
            {
                // sync with retries
                P4.Sync(SyncCommand, true, false, Retries);
            }
            else
            {
                LogInformation("sync {0}", SyncCommand);
            }
        }

        // P4 utils don't return errors :(
        ExitCode ExitStatus = ExitCode.Success;

        // Sync is complete so do the actions
        if (!PreviewOnly && CL > 0)
        {
            // Argument to pass to the editor (could be null with no project).
            string ProjectArgForEditor = "";

            if (!string.IsNullOrEmpty(ProjectArg))
            {
                // If we synced the project from P4 we couldn't resolve this earlier
                if (ProjectFile == null)
                {
                    NativeProjects.ClearCache();
                    ProjectFile = ProjectUtils.FindProjectFileFromName(ProjectArg);
                }

                ProjectArgForEditor = ProjectFile.FullName;
            }

            if (GenerateProject)
            {
                Log.TraceVerbose("Generating project files for {0}", ProjectArgForEditor);

                if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64 ||
                    BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win32)
                {
                    CommandUtils.Run("GenerateProjectFiles.bat", ProjectArgForEditor);
                }
                else
                {
                    CommandUtils.Run("GenerateProjectFiles.sh", ProjectArgForEditor);
                }
            }

            UE4Build Build = new UE4Build(this);
            if (!Unversioned && !ProjectOnly)
            {
                Build.UpdateVersionFiles(ActuallyUpdateVersionFiles: true, ChangelistNumberOverride: CL, IsPromotedOverride: false);
            }

            // Build everything
            if (BuildProject && ExitStatus == ExitCode.Success)
            {
                Log.TraceVerbose("Building Editor for {0}", ProjectArgForEditor);

                BuildEditor BuildCmd = new BuildEditor();
                BuildCmd.Clean       = ParseParam("clean");
                BuildCmd.ProjectName = ProjectArgForEditor;
                ExitStatus           = BuildCmd.Execute();
            }

            if (OpenProject && ExitStatus == ExitCode.Success)
            {
                Log.TraceVerbose("Opening Editor for {0}", ProjectArgForEditor);

                OpenEditor OpenCmd = new OpenEditor();
                OpenCmd.ProjectName = ProjectArgForEditor;
                ExitStatus          = OpenCmd.Execute();
            }
        }

        return(ExitCode.Success);
    }
    public override void ExecuteBuild()
    {
        var UEProjectRoot = ParseParamValue("UEProjectRoot");

        if (UEProjectRoot == null)
        {
            UEProjectRoot = CmdEnv.LocalRoot;
        }

        var UEProjectDirectory = ParseParamValue("UEProjectDirectory");

        if (UEProjectDirectory == null)
        {
            throw new AutomationException("Missing required command line argument: 'UEProjectDirectory'");
        }

        var UEProjectName = ParseParamValue("UEProjectName");

        if (UEProjectName == null)
        {
            UEProjectName = "";
        }

        var LocalizationProjectNames = new List <string>();
        {
            var LocalizationProjectNamesStr = ParseParamValue("LocalizationProjectNames");
            if (LocalizationProjectNamesStr != null)
            {
                foreach (var ProjectName in LocalizationProjectNamesStr.Split(','))
                {
                    LocalizationProjectNames.Add(ProjectName.Trim());
                }
            }
        }

        var LocalizationProviderName = ParseParamValue("LocalizationProvider");

        if (LocalizationProviderName == null)
        {
            LocalizationProviderName = "";
        }

        var LocalizationStepNames = new List <string>();
        {
            var LocalizationStepNamesStr = ParseParamValue("LocalizationSteps");
            if (LocalizationStepNamesStr == null)
            {
                LocalizationStepNames.AddRange(new string[] { "Download", "Gather", "Import", "Export", "Compile", "GenerateReports", "Upload" });
            }
            else
            {
                foreach (var StepName in LocalizationStepNamesStr.Split(','))
                {
                    LocalizationStepNames.Add(StepName.Trim());
                }
            }
            LocalizationStepNames.Add("Monolithic");             // Always allow the monolithic scripts to run as we don't know which steps they do
        }

        var ShouldGatherPlugins = ParseParam("IncludePlugins");
        var IncludePlugins      = new List <string>();
        var ExcludePlugins      = new List <string>();

        if (ShouldGatherPlugins)
        {
            var IncludePluginsStr = ParseParamValue("IncludePlugins");
            if (IncludePluginsStr != null)
            {
                foreach (var PluginName in IncludePluginsStr.Split(','))
                {
                    IncludePlugins.Add(PluginName.Trim());
                }
            }

            var ExcludePluginsStr = ParseParamValue("ExcludePlugins");
            if (ExcludePluginsStr != null)
            {
                foreach (var PluginName in ExcludePluginsStr.Split(','))
                {
                    ExcludePlugins.Add(PluginName.Trim());
                }
            }
        }

        var ShouldGatherPlatforms = ParseParam("IncludePlatforms");

        var AdditionalCommandletArguments = ParseParamValue("AdditionalCommandletArguments");

        if (AdditionalCommandletArguments == null)
        {
            AdditionalCommandletArguments = "";
        }

        var EnableParallelGather = ParseParam("ParallelGather");

        var StartTime = DateTime.UtcNow;

        var LocalizationBatches = new List <LocalizationBatch>();

        // Add the static set of localization projects as a batch
        if (LocalizationProjectNames.Count > 0)
        {
            LocalizationBatches.Add(new LocalizationBatch(UEProjectDirectory, UEProjectDirectory, "", LocalizationProjectNames));
        }

        // Build up any additional batches needed for platforms
        if (ShouldGatherPlatforms)
        {
            var PlatformsRootDirectory = new DirectoryReference(CombinePaths(UEProjectRoot, UEProjectDirectory, "Platforms"));
            if (DirectoryReference.Exists(PlatformsRootDirectory))
            {
                foreach (DirectoryReference PlatformDirectory in DirectoryReference.EnumerateDirectories(PlatformsRootDirectory))
                {
                    // Find the localization targets defined for this platform
                    var PlatformTargetNames = GetLocalizationTargetsFromDirectory(new DirectoryReference(CombinePaths(PlatformDirectory.FullName, "Config", "Localization")));
                    if (PlatformTargetNames.Count > 0)
                    {
                        var RootRelativePluginPath = PlatformDirectory.MakeRelativeTo(new DirectoryReference(UEProjectRoot));
                        RootRelativePluginPath = RootRelativePluginPath.Replace('\\', '/');                         // Make sure we use / as these paths are used with P4

                        LocalizationBatches.Add(new LocalizationBatch(UEProjectDirectory, RootRelativePluginPath, "", PlatformTargetNames));
                    }
                }
            }
        }

        // Build up any additional batches needed for plugins
        if (ShouldGatherPlugins)
        {
            var PluginsRootDirectory = new DirectoryReference(CombinePaths(UEProjectRoot, UEProjectDirectory));
            IReadOnlyList <PluginInfo> AllPlugins = Plugins.ReadPluginsFromDirectory(PluginsRootDirectory, "Plugins", UEProjectName.Length == 0 ? PluginType.Engine : PluginType.Project);

            // Add a batch for each plugin that meets our criteria
            var AvailablePluginNames = new HashSet <string>();
            foreach (var PluginInfo in AllPlugins)
            {
                AvailablePluginNames.Add(PluginInfo.Name);

                bool ShouldIncludePlugin = (IncludePlugins.Count == 0 || IncludePlugins.Contains(PluginInfo.Name)) && !ExcludePlugins.Contains(PluginInfo.Name);
                if (ShouldIncludePlugin && PluginInfo.Descriptor.LocalizationTargets != null && PluginInfo.Descriptor.LocalizationTargets.Length > 0)
                {
                    var RootRelativePluginPath = PluginInfo.Directory.MakeRelativeTo(new DirectoryReference(UEProjectRoot));
                    RootRelativePluginPath = RootRelativePluginPath.Replace('\\', '/');                     // Make sure we use / as these paths are used with P4

                    var PluginTargetNames = new List <string>();
                    foreach (var LocalizationTarget in PluginInfo.Descriptor.LocalizationTargets)
                    {
                        PluginTargetNames.Add(LocalizationTarget.Name);
                    }

                    LocalizationBatches.Add(new LocalizationBatch(UEProjectDirectory, RootRelativePluginPath, PluginInfo.Name, PluginTargetNames));
                }
            }

            // If we had an explicit list of plugins to include, warn if any were missing
            foreach (string PluginName in IncludePlugins)
            {
                if (!AvailablePluginNames.Contains(PluginName))
                {
                    LogWarning("The plugin '{0}' specified by -IncludePlugins wasn't found and will be skipped.", PluginName);
                }
            }
        }

        // Create a single changelist to use for all changes
        int PendingChangeList = 0;

        if (P4Enabled)
        {
            var ChangeListCommitMessage = String.Format("Localization Automation using CL {0}", P4Env.Changelist);
            if (File.Exists(CombinePaths(CmdEnv.LocalRoot, @"Engine/Restricted/NotForLicensees/Build/EpicInternal.txt")))
            {
                ChangeListCommitMessage += "\n#okforgithub ignore";
            }

            PendingChangeList = P4.CreateChange(P4Env.Client, ChangeListCommitMessage);
        }

        // Prepare to process each localization batch
        var LocalizationTasks = new List <LocalizationTask>();

        foreach (var LocalizationBatch in LocalizationBatches)
        {
            var LocalizationTask = new LocalizationTask(LocalizationBatch, UEProjectRoot, LocalizationProviderName, PendingChangeList, this);
            LocalizationTasks.Add(LocalizationTask);

            // Make sure the Localization configs and content is up-to-date to ensure we don't get errors later on
            if (P4Enabled)
            {
                LogInformation("Sync necessary content to head revision");
                P4.Sync(P4Env.Branch + "/" + LocalizationTask.Batch.LocalizationTargetDirectory + "/Config/Localization/...");
                P4.Sync(P4Env.Branch + "/" + LocalizationTask.Batch.LocalizationTargetDirectory + "/Content/Localization/...");
            }

            // Generate the info we need to gather for each project
            foreach (var ProjectName in LocalizationTask.Batch.LocalizationProjectNames)
            {
                LocalizationTask.ProjectInfos.Add(GenerateProjectInfo(LocalizationTask.RootLocalizationTargetDirectory, ProjectName, LocalizationStepNames));
            }
        }

        // Hash the current PO files on disk so we can work out whether they actually change
        Dictionary <string, byte[]> InitalPOFileHashes = null;

        if (P4Enabled)
        {
            InitalPOFileHashes = GetPOFileHashes(LocalizationBatches, UEProjectRoot);
        }

        // Download the latest translations from our localization provider
        if (LocalizationStepNames.Contains("Download"))
        {
            foreach (var LocalizationTask in LocalizationTasks)
            {
                if (LocalizationTask.LocProvider != null)
                {
                    foreach (var ProjectInfo in LocalizationTask.ProjectInfos)
                    {
                        LocalizationTask.LocProvider.DownloadProjectFromLocalizationProvider(ProjectInfo.ProjectName, ProjectInfo.ImportInfo);
                    }
                }
            }
        }

        // Begin the gather command for each task
        // These can run in parallel when ParallelGather is enabled
        {
            var EditorExe = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor-Cmd.exe");

            // Set the common basic editor arguments
            var EditorArguments = P4Enabled
                                ? String.Format("-SCCProvider=Perforce -P4Port={0} -P4User={1} -P4Client={2} -P4Passwd={3} -P4Changelist={4} -EnableSCC -DisableSCCSubmit", P4Env.ServerAndPort, P4Env.User, P4Env.Client, P4.GetAuthenticationToken(), PendingChangeList)
                                : "-SCCProvider=None";
            if (IsBuildMachine)
            {
                EditorArguments += " -BuildMachine";
            }
            EditorArguments += " -Unattended -LogLocalizationConflicts";
            if (EnableParallelGather)
            {
                EditorArguments += " -multiprocess";
            }
            if (!String.IsNullOrEmpty(AdditionalCommandletArguments))
            {
                EditorArguments += " " + AdditionalCommandletArguments;
            }

            // Set the common process run options
            var CommandletRunOptions = ERunOptions.Default | ERunOptions.NoLoggingOfRunCommand;             // Disable logging of the run command as it will print the exit code which GUBP can pick up as an error (we do that ourselves later)
            if (EnableParallelGather)
            {
                CommandletRunOptions |= ERunOptions.NoWaitForExit;
            }

            foreach (var LocalizationTask in LocalizationTasks)
            {
                var ProjectArgument = String.IsNullOrEmpty(UEProjectName) ? "" : String.Format("\"{0}\"", Path.Combine(LocalizationTask.RootWorkingDirectory, String.Format("{0}.uproject", UEProjectName)));

                foreach (var ProjectInfo in LocalizationTask.ProjectInfos)
                {
                    var LocalizationConfigFiles = new List <string>();
                    foreach (var LocalizationStep in ProjectInfo.LocalizationSteps)
                    {
                        if (LocalizationStepNames.Contains(LocalizationStep.Name))
                        {
                            LocalizationConfigFiles.Add(LocalizationStep.LocalizationConfigFile);
                        }
                    }

                    if (LocalizationConfigFiles.Count > 0)
                    {
                        var Arguments = String.Format("{0} -run=GatherText -config=\"{1}\" {2}", ProjectArgument, String.Join(";", LocalizationConfigFiles), EditorArguments);
                        LogInformation("Running localization commandlet for '{0}': {1}", ProjectInfo.ProjectName, Arguments);
                        LocalizationTask.GatherProcessResults.Add(Run(EditorExe, Arguments, null, CommandletRunOptions));
                    }
                    else
                    {
                        LocalizationTask.GatherProcessResults.Add(null);
                    }
                }
            }
        }

        // Wait for each commandlet process to finish and report the result.
        // This runs even for non-parallel execution to log the exit state of the process.
        foreach (var LocalizationTask in LocalizationTasks)
        {
            for (int ProjectIndex = 0; ProjectIndex < LocalizationTask.ProjectInfos.Count; ++ProjectIndex)
            {
                var ProjectInfo = LocalizationTask.ProjectInfos[ProjectIndex];
                var RunResult   = LocalizationTask.GatherProcessResults[ProjectIndex];

                if (RunResult != null)
                {
                    RunResult.WaitForExit();
                    RunResult.OnProcessExited();
                    RunResult.DisposeProcess();

                    if (RunResult.ExitCode == 0)
                    {
                        LogInformation("The localization commandlet for '{0}' exited with code 0.", ProjectInfo.ProjectName);
                    }
                    else
                    {
                        LogWarning("The localization commandlet for '{0}' exited with code {1} which likely indicates a crash.", ProjectInfo.ProjectName, RunResult.ExitCode);
                    }
                }
            }
        }

        // Upload the latest sources to our localization provider
        if (LocalizationStepNames.Contains("Upload"))
        {
            foreach (var LocalizationTask in LocalizationTasks)
            {
                if (LocalizationTask.LocProvider != null)
                {
                    // Upload all text to our localization provider
                    for (int ProjectIndex = 0; ProjectIndex < LocalizationTask.ProjectInfos.Count; ++ProjectIndex)
                    {
                        var ProjectInfo = LocalizationTask.ProjectInfos[ProjectIndex];
                        var RunResult   = LocalizationTask.GatherProcessResults[ProjectIndex];

                        if (RunResult != null && RunResult.ExitCode == 0)
                        {
                            // Recalculate the split platform paths before doing the upload, as the export may have changed them
                            ProjectInfo.ExportInfo.CalculateSplitPlatformNames(LocalizationTask.RootLocalizationTargetDirectory);
                            LocalizationTask.LocProvider.UploadProjectToLocalizationProvider(ProjectInfo.ProjectName, ProjectInfo.ExportInfo);
                        }
                        else
                        {
                            LogWarning("Skipping upload to the localization provider for '{0}' due to an earlier commandlet failure.", ProjectInfo.ProjectName);
                        }
                    }
                }
            }
        }

        // Clean-up the changelist so it only contains the changed files, and then submit it (if we were asked to)
        if (P4Enabled)
        {
            // Revert any PO files that haven't changed aside from their header
            {
                var POFilesToRevert = new List <string>();

                var CurrentPOFileHashes = GetPOFileHashes(LocalizationBatches, UEProjectRoot);
                foreach (var CurrentPOFileHashPair in CurrentPOFileHashes)
                {
                    byte[] InitialPOFileHash;
                    if (InitalPOFileHashes.TryGetValue(CurrentPOFileHashPair.Key, out InitialPOFileHash) && InitialPOFileHash.SequenceEqual(CurrentPOFileHashPair.Value))
                    {
                        POFilesToRevert.Add(CurrentPOFileHashPair.Key);
                    }
                }

                if (POFilesToRevert.Count > 0)
                {
                    var P4RevertArgsFilename = CombinePaths(CmdEnv.LocalRoot, "Engine", "Intermediate", String.Format("LocalizationP4RevertArgs-{0}.txt", Guid.NewGuid().ToString()));

                    using (StreamWriter P4RevertArgsWriter = File.CreateText(P4RevertArgsFilename))
                    {
                        foreach (var POFileToRevert in POFilesToRevert)
                        {
                            P4RevertArgsWriter.WriteLine(POFileToRevert);
                        }
                    }

                    P4.LogP4(String.Format("-x{0} revert", P4RevertArgsFilename));
                    DeleteFile_NoExceptions(P4RevertArgsFilename);
                }
            }

            // Revert any other unchanged files
            P4.RevertUnchanged(PendingChangeList);

            // Submit that single changelist now
            if (AllowSubmit)
            {
                int SubmittedChangeList;
                P4.Submit(PendingChangeList, out SubmittedChangeList);
            }
        }

        var RunDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;

        LogInformation("Localize command finished in {0} seconds", RunDuration / 1000);
    }
    public override void ExecuteBuild()
    {
        var EditorExe = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor-Cmd.exe");

        // Parse out the required command line arguments
        var UEProjectDirectory = ParseParamValue("UEProjectDirectory");

        if (UEProjectDirectory == null)
        {
            throw new AutomationException("Missing required command line argument: 'UEProjectDirectory'");
        }

        var UEProjectName = ParseParamValue("UEProjectName");

        if (UEProjectName == null)
        {
            UEProjectName = "";
        }

        var LocalizationProjectNames = new List <string>();
        {
            var LocalizationProjectNamesStr = ParseParamValue("LocalizationProjectNames");
            if (LocalizationProjectNamesStr == null)
            {
                throw new AutomationException("Missing required command line argument: 'LocalizationProjectNames'");
            }
            foreach (var ProjectName in LocalizationProjectNamesStr.Split(','))
            {
                LocalizationProjectNames.Add(ProjectName.Trim());
            }
        }

        var LocalizationProviderName = ParseParamValue("LocalizationProvider");

        if (LocalizationProviderName == null)
        {
            LocalizationProviderName = "OneSky";
        }

        var AdditionalCommandletArguments = ParseParamValue("AdditionalCommandletArguments");

        if (AdditionalCommandletArguments == null)
        {
            AdditionalCommandletArguments = "";
        }

        var RootWorkingDirectory = CombinePaths(CmdEnv.LocalRoot, UEProjectDirectory);

        // Try and find our localization provider
        LocalizationProvider LocProvider = null;

        {
            LocalizationProvider.LocalizationProviderArgs LocProviderArgs;
            LocProviderArgs.RootWorkingDirectory = RootWorkingDirectory;
            LocProviderArgs.CommandUtils         = this;
            LocProvider = LocalizationProvider.GetLocalizationProvider(LocalizationProviderName, LocProviderArgs);
        }

        // Make sure the Localization configs and content is up-to-date to ensure we don't get errors later on
        if (P4Enabled)
        {
            Log("Sync necessary content to head revision");
            P4.Sync(P4Env.BuildRootP4 + "/" + UEProjectDirectory + "/Config/Localization/...");
            P4.Sync(P4Env.BuildRootP4 + "/" + UEProjectDirectory + "/Content/Localization/...");
        }

        // Generate the info we need to gather for each project
        var ProjectInfos = new List <ProjectInfo>();

        foreach (var ProjectName in LocalizationProjectNames)
        {
            ProjectInfos.Add(GenerateProjectInfo(RootWorkingDirectory, ProjectName));
        }

        // Export all text from our localization provider
        foreach (var ProjectInfo in ProjectInfos)
        {
            LocProvider.DownloadProjectFromLocalizationProvider(ProjectInfo.ProjectName, ProjectInfo.ImportInfo);
        }

        // Setup editor arguments for SCC.
        string EditorArguments = String.Empty;

        if (P4Enabled)
        {
            EditorArguments = String.Format("-SCCProvider={0} -P4Port={1} -P4User={2} -P4Client={3} -P4Passwd={4}", "Perforce", P4Env.P4Port, P4Env.User, P4Env.Client, P4.GetAuthenticationToken());
        }
        else
        {
            EditorArguments = String.Format("-SCCProvider={0}", "None");
        }

        // Setup commandlet arguments for SCC.
        string CommandletSCCArguments = String.Empty;

        if (P4Enabled)
        {
            CommandletSCCArguments += (String.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-EnableSCC";
        }
        if (!AllowSubmit)
        {
            CommandletSCCArguments += (String.IsNullOrEmpty(CommandletSCCArguments) ? "" : " ") + "-DisableSCCSubmit";
        }

        // Execute commandlet for each config in each project.
        bool bLocCommandletFailed = false;

        foreach (var ProjectInfo in ProjectInfos)
        {
            foreach (var LocalizationConfigFile in ProjectInfo.LocalizationConfigFiles)
            {
                var CommandletArguments = String.Format("-config={0}", LocalizationConfigFile) + (String.IsNullOrEmpty(CommandletSCCArguments) ? "" : " " + CommandletSCCArguments);

                if (!String.IsNullOrEmpty(AdditionalCommandletArguments))
                {
                    CommandletArguments += " " + AdditionalCommandletArguments;
                }

                string Arguments = String.Format("{0} -run=GatherText {1} {2}", UEProjectName, EditorArguments, CommandletArguments);
                Log("Running localization commandlet: {0}", Arguments);
                var StartTime   = DateTime.UtcNow;
                var RunResult   = Run(EditorExe, Arguments, null, ERunOptions.Default | ERunOptions.NoLoggingOfRunCommand);               // Disable logging of the run command as it will print the exit code which GUBP can pick up as an error (we do that ourselves below)
                var RunDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
                Log("Localization commandlet finished in {0}s", RunDuration / 1000);

                if (RunResult.ExitCode != 0)
                {
                    LogWarning("The localization commandlet exited with code {0} which likely indicates a crash. It ran with the following arguments: '{1}'", RunResult.ExitCode, Arguments);
                    bLocCommandletFailed = true;
                    break;                     // We failed a step, so don't process any other steps in this config chain
                }
            }
        }

        if (bLocCommandletFailed)
        {
            LogWarning("Skipping upload to the localization provider due to an earlier commandlet failure.");
        }
        else
        {
            // Upload all text to our localization provider
            foreach (var ProjectInfo in ProjectInfos)
            {
                LocProvider.UploadProjectToLocalizationProvider(ProjectInfo.ProjectName, ProjectInfo.ExportInfo);
            }
        }
    }