/// <summary> /// Writes a XGE task file containing the specified actions to the specified file path. /// </summary> static void WriteTaskFile(List <Action> InActions, string TaskFilePath, bool bProgressMarkup, bool bXGEExport) { Dictionary <string, string> ExportEnv = new Dictionary <string, string>(); List <Action> Actions = InActions; if (bXGEExport) { IDictionary CurrentEnvironment = Environment.GetEnvironmentVariables(); foreach (System.Collections.DictionaryEntry Pair in CurrentEnvironment) { if (!UnrealBuildTool.InitialEnvironment.Contains(Pair.Key) || (string)(UnrealBuildTool.InitialEnvironment[Pair.Key]) != (string)(Pair.Value)) { ExportEnv.Add((string)(Pair.Key), (string)(Pair.Value)); } } int NumSortErrors = 0; for (int ActionIndex = 0; ActionIndex < InActions.Count; ActionIndex++) { Action Action = InActions[ActionIndex]; foreach (FileItem Item in Action.PrerequisiteItems) { if (Item.ProducingAction != null && InActions.Contains(Item.ProducingAction)) { int DepIndex = InActions.IndexOf(Item.ProducingAction); if (DepIndex > ActionIndex) { //Console.WriteLine("Action is not topologically sorted."); //Console.WriteLine(" {0} {1}", Action.CommandPath, Action.CommandArguments); //Console.WriteLine("Dependency"); //Console.WriteLine(" {0} {1}", Item.ProducingAction.CommandPath, Item.ProducingAction.CommandArguments); NumSortErrors++; } } } } if (NumSortErrors > 0) { //Console.WriteLine("The UBT action graph was not sorted. Sorting actions...."); Actions = new List <Action>(); HashSet <int> UsedActions = new HashSet <int>(); for (int ActionIndex = 0; ActionIndex < InActions.Count; ActionIndex++) { if (UsedActions.Contains(ActionIndex)) { continue; } Action Action = InActions[ActionIndex]; foreach (FileItem Item in Action.PrerequisiteItems) { if (Item.ProducingAction != null && InActions.Contains(Item.ProducingAction)) { int DepIndex = InActions.IndexOf(Item.ProducingAction); if (UsedActions.Contains(DepIndex)) { continue; } Actions.Add(Item.ProducingAction); UsedActions.Add(DepIndex); } } Actions.Add(Action); UsedActions.Add(ActionIndex); } for (int ActionIndex = 0; ActionIndex < Actions.Count; ActionIndex++) { Action Action = Actions[ActionIndex]; foreach (FileItem Item in Action.PrerequisiteItems) { if (Item.ProducingAction != null && Actions.Contains(Item.ProducingAction)) { int DepIndex = Actions.IndexOf(Item.ProducingAction); if (DepIndex > ActionIndex) { Console.WriteLine("Action is not topologically sorted."); Console.WriteLine(" {0} {1}", Action.CommandPath, Action.CommandArguments); Console.WriteLine("Dependency"); Console.WriteLine(" {0} {1}", Item.ProducingAction.CommandPath, Item.ProducingAction.CommandArguments); throw new BuildException("Cyclical Dependency in action graph."); } } } } } } XmlDocument XGETaskDocument = new XmlDocument(); // <BuildSet FormatVersion="1">...</BuildSet> XmlElement BuildSetElement = XGETaskDocument.CreateElement("BuildSet"); XGETaskDocument.AppendChild(BuildSetElement); BuildSetElement.SetAttribute("FormatVersion", "1"); // <Environments>...</Environments> XmlElement EnvironmentsElement = XGETaskDocument.CreateElement("Environments"); BuildSetElement.AppendChild(EnvironmentsElement); // <Environment Name="Default">...</CompileEnvironment> XmlElement EnvironmentElement = XGETaskDocument.CreateElement("Environment"); EnvironmentsElement.AppendChild(EnvironmentElement); EnvironmentElement.SetAttribute("Name", "Default"); // <Tools>...</Tools> XmlElement ToolsElement = XGETaskDocument.CreateElement("Tools"); EnvironmentElement.AppendChild(ToolsElement); if (ExportEnv.Count > 0) { // <Variables>...</Variables> XmlElement VariablesElement = XGETaskDocument.CreateElement("Variables"); EnvironmentElement.AppendChild(VariablesElement); foreach (KeyValuePair <string, string> Pair in ExportEnv) { // <Variable>...</Variable> XmlElement VariableElement = XGETaskDocument.CreateElement("Variable"); VariablesElement.AppendChild(VariableElement); VariableElement.SetAttribute("Name", Pair.Key); VariableElement.SetAttribute("Value", Pair.Value); } } for (int ActionIndex = 0; ActionIndex < Actions.Count; ActionIndex++) { Action Action = Actions[ActionIndex]; // <Tool ... /> XmlElement ToolElement = XGETaskDocument.CreateElement("Tool"); ToolsElement.AppendChild(ToolElement); ToolElement.SetAttribute("Name", string.Format("Tool{0}", ActionIndex)); ToolElement.SetAttribute("AllowRemote", Action.bCanExecuteRemotely.ToString()); // The XGE documentation says that 'AllowIntercept' must be set to 'true' for all tools where 'AllowRemote' is enabled ToolElement.SetAttribute("AllowIntercept", Action.bCanExecuteRemotely.ToString()); string OutputPrefix = ""; if (bProgressMarkup) { OutputPrefix += ProgressMarkupPrefix; } if (Action.bShouldOutputStatusDescription) { OutputPrefix += Action.StatusDescription; } if (OutputPrefix.Length > 0) { ToolElement.SetAttribute("OutputPrefix", OutputPrefix); } // When running on Windows, differentiate between .exe and batch files. // Those (.bat, .cmd) need to be run via cmd /c or shellexecute, // the latter which we can't use because we want to redirect input/output bool bLaunchViaCmdExe = (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64) && (!Path.GetExtension(Action.CommandPath).ToLower().EndsWith(".exe")); string CommandPath = ""; string CommandArguments = ""; if (bLaunchViaCmdExe) { CommandPath = "cmd.exe"; CommandArguments = string.Format ( "/c \"\"{0}\" {1}\"", (Action.CommandPath), (Action.CommandArguments) ); } else { CommandPath = Action.CommandPath; CommandArguments = Action.CommandArguments; } ToolElement.SetAttribute("Params", CommandArguments); ToolElement.SetAttribute("Path", CommandPath); ToolElement.SetAttribute("SkipIfProjectFailed", "true"); if (Action.bIsGCCCompiler) { ToolElement.SetAttribute("AutoReserveMemory", "*.gch"); } else { ToolElement.SetAttribute("AutoReserveMemory", "*.pch"); } ToolElement.SetAttribute( "OutputFileMasks", string.Join( ",", Action.ProducedItems.ConvertAll <string>( delegate(FileItem ProducedItem) { return(ProducedItem.ToString()); } ).ToArray() ) ); } // <Project Name="Default" Env="Default">...</Project> XmlElement ProjectElement = XGETaskDocument.CreateElement("Project"); BuildSetElement.AppendChild(ProjectElement); ProjectElement.SetAttribute("Name", "Default"); ProjectElement.SetAttribute("Env", "Default"); for (int ActionIndex = 0; ActionIndex < Actions.Count; ActionIndex++) { Action Action = Actions[ActionIndex]; // <Task ... /> XmlElement TaskElement = XGETaskDocument.CreateElement("Task"); ProjectElement.AppendChild(TaskElement); TaskElement.SetAttribute("SourceFile", ""); if (!Action.bShouldOutputStatusDescription) { // If we were configured to not output a status description, then we'll instead // set 'caption' text for this task, so that the XGE coordinator has something // to display within the progress bars. For tasks that are outputting a // description, XGE automatically displays that text in the progress bar, so we // only need to do this for tasks that output their own progress. TaskElement.SetAttribute("Caption", Action.StatusDescription); } TaskElement.SetAttribute("Name", string.Format("Action{0}", ActionIndex)); TaskElement.SetAttribute("Tool", string.Format("Tool{0}", ActionIndex)); TaskElement.SetAttribute("WorkingDir", Action.WorkingDirectory); TaskElement.SetAttribute("SkipIfProjectFailed", "true"); // Create a semi-colon separated list of the other tasks this task depends on the results of. List <string> DependencyNames = new List <string>(); foreach (FileItem Item in Action.PrerequisiteItems) { if (Item.ProducingAction != null && Actions.Contains(Item.ProducingAction)) { DependencyNames.Add(string.Format("Action{0}", Actions.IndexOf(Item.ProducingAction))); } } if (DependencyNames.Count > 0) { TaskElement.SetAttribute("DependsOn", string.Join(";", DependencyNames.ToArray())); } } // Write the XGE task XML to a temporary file. using (FileStream OutputFileStream = new FileStream(TaskFilePath, FileMode.Create, FileAccess.Write)) { XGETaskDocument.Save(OutputFileStream); } }
/// <summary> /// Executes a list of actions. /// </summary> public static void ExecuteActions(BuildConfiguration BuildConfiguration, List <Action> ActionsToExecute, List <TargetDescriptor> TargetDescriptors = null) { Log.TraceInformation("Execute actions num:" + ActionsToExecute.Count + " target descriptors num:" + TargetDescriptors.Count); if (ActionsToExecute.Count == 0) { Log.TraceInformation("Target is up to date"); } else { // Figure out which executor to use ActionExecutor Executor; if (BuildConfiguration.bAllowHybridExecutor && HybridExecutor.IsAvailable()) { Executor = new HybridExecutor(); } else if (BuildConfiguration.bAllowXGE && XGE.IsAvailable()) { Executor = new XGE(); } else if (BuildConfiguration.bAllowDistcc) { Executor = new Distcc(); } else if (BuildConfiguration.bAllowSNDBS && SNDBS.IsAvailable()) { Executor = new SNDBS(); } else if (BuildConfiguration.bAllowParallelExecutor && ParallelExecutor.IsAvailable()) { Executor = new ParallelExecutor(); } else { Executor = new LocalExecutor(); } //if (BuildConfiguration.bAllowFastBuild && FastBuild_v1.IsAvailable()) //{ // Executor = new FastBuild_v1(Executor, TargetDescriptors); //} if (BuildConfiguration.bAllowFastBuild && FastBuild_v2.IsAvailable()) { Executor = new FastBuild_v2(Executor, TargetDescriptors); } // Execute the build Stopwatch Timer = Stopwatch.StartNew(); if (!Executor.ExecuteActions(ActionsToExecute, BuildConfiguration.bLogDetailedActionStats)) { throw new CompilationResultException(CompilationResult.OtherCompilationError); } Log.TraceInformation("Total time in {0} executor: {1:0.00} seconds", Executor.Name, Timer.Elapsed.TotalSeconds); // Reset the file info for all the produced items foreach (Action BuildAction in ActionsToExecute) { foreach (FileItem ProducedItem in BuildAction.ProducedItems) { ProducedItem.ResetCachedInfo(); } } // Verify the link outputs were created (seems to happen with Win64 compiles) foreach (Action BuildAction in ActionsToExecute) { if (BuildAction.ActionType == ActionType.Link) { foreach (FileItem Item in BuildAction.ProducedItems) { if (!Item.Exists) { throw new BuildException("Failed to produce item: {0}", Item.AbsolutePath); } } } } } }