// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnrealBuildTool; using AutomationTool; using System.Xml; using Tools.DotNETCommon; namespace AutomationTool { /// /// Parameters for a compile task /// public class CompileTaskParameters { /// /// The target to compile /// [TaskParameter] public string Target; /// /// The configuration to compile /// [TaskParameter] public UnrealTargetConfiguration Configuration; /// /// The platform to compile for /// [TaskParameter] public UnrealTargetPlatform Platform; /// /// Additional arguments for UnrealBuildTool /// [TaskParameter(Optional = true)] public string Arguments; /// /// Whether to allow using XGE for compilation /// [TaskParameter(Optional = true)] public bool AllowXGE = true; /// /// Whether to allow using the parallel executor for this compile /// [TaskParameter(Optional = true)] public bool AllowParallelExecutor = true; /// /// Whether to allow cleaning this target. If unspecified, targets are cleaned if the -Clean argument passed on the command line. /// [TaskParameter(Optional = true)] public bool? Clean = null; /// /// Tag to be applied to build products of this task /// [TaskParameter(Optional = true, ValidationType = TaskParameterValidationType.TagList)] public string Tag; } /// /// Executor for compile tasks, which can compile multiple tasks simultaneously /// public class CompileTaskExecutor : ITaskExecutor { /// /// List of targets to compile. As well as the target specifically added for this task, additional compile tasks may be merged with it. /// List Targets = new List(); /// /// Mapping of receipt filename to its corresponding tag name /// Dictionary TargetToTagName = new Dictionary(); /// /// Whether to allow using XGE for this job /// bool bAllowXGE = true; /// /// Whether to allow using the parallel executor for this job /// bool bAllowParallelExecutor = true; /// /// Constructor /// /// Initial task to execute public CompileTaskExecutor(CompileTask Task) { Add(Task); } /// /// Adds another task to this executor /// /// Task to add /// True if the task could be added, false otherwise public bool Add(CustomTask Task) { CompileTask CompileTask = Task as CompileTask; if(CompileTask == null) { return false; } if(Targets.Count > 0) { if (bAllowXGE != CompileTask.Parameters.AllowXGE) { return false; } if (!bAllowParallelExecutor || !CompileTask.Parameters.AllowParallelExecutor) { return false; } } CompileTaskParameters Parameters = CompileTask.Parameters; bAllowXGE &= Parameters.AllowXGE; bAllowParallelExecutor &= Parameters.AllowParallelExecutor; UE4Build.BuildTarget Target = new UE4Build.BuildTarget { TargetName = Parameters.Target, Platform = Parameters.Platform, Config = Parameters.Configuration, UBTArgs = "-nobuilduht " + (Parameters.Arguments ?? ""), Clean = Parameters.Clean }; if(!String.IsNullOrEmpty(Parameters.Tag)) { TargetToTagName.Add(Target, Parameters.Tag); } Targets.Add(Target); return true; } /// /// Execute all the tasks added to this executor. /// /// Information about the current job /// Set of build products produced by this node. /// Mapping from tag names to the set of files they include /// Whether the task succeeded or not. Exiting with an exception will be caught and treated as a failure. public void Execute(JobContext Job, HashSet BuildProducts, Dictionary> TagNameToFileSet) { // Create the agenda UE4Build.BuildAgenda Agenda = new UE4Build.BuildAgenda(); Agenda.Targets.AddRange(Targets); // Build everything Dictionary TargetToManifest = new Dictionary(); UE4Build Builder = new UE4Build(Job.OwnerCommand); bool bCanUseParallelExecutor = (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64 && bAllowParallelExecutor); // parallel executor is only available on Windows as of 2016-09-22 Builder.Build(Agenda, InDeleteBuildProducts: null, InUpdateVersionFiles: false, InForceNoXGE: !bAllowXGE, InUseParallelExecutor: bCanUseParallelExecutor, InTargetToManifest: TargetToManifest); UE4Build.CheckBuildProducts(Builder.BuildProductFiles); // Tag all the outputs foreach(KeyValuePair TargetTagName in TargetToTagName) { BuildManifest Manifest; if(!TargetToManifest.TryGetValue(TargetTagName.Key, out Manifest)) { throw new AutomationException("Missing manifest for target {0} {1} {2}", TargetTagName.Key.TargetName, TargetTagName.Key.Platform, TargetTagName.Key.Config); } foreach(string TagName in CustomTask.SplitDelimitedList(TargetTagName.Value)) { HashSet FileSet = CustomTask.FindOrAddTagSet(TagNameToFileSet, TagName); FileSet.UnionWith(Manifest.BuildProducts.Select(x => new FileReference(x))); } } // Add everything to the list of build products BuildProducts.UnionWith(Builder.BuildProductFiles.Select(x => new FileReference(x))); } } /// /// Compiles a target with UnrealBuildTool. /// [TaskElement("Compile", typeof(CompileTaskParameters))] public class CompileTask : CustomTask { /// /// Parameters for this task /// public CompileTaskParameters Parameters; /// /// Construct a compile task /// /// Parameters for this task public CompileTask(CompileTaskParameters Parameters) { this.Parameters = Parameters; } /// /// Execute the task. /// /// Information about the current job /// Set of build products produced by this node. /// Mapping from tag names to the set of files they include public override void Execute(JobContext Job, HashSet BuildProducts, Dictionary> TagNameToFileSet) { GetExecutor().Execute(Job, BuildProducts, TagNameToFileSet); } /// /// /// /// public override ITaskExecutor GetExecutor() { return new CompileTaskExecutor(this); } /// /// Output this task out to an XML writer. /// public override void Write(XmlWriter Writer) { Write(Writer, Parameters); } /// /// Find all the tags which are used as inputs to this task /// /// The tag names which are read by this task public override IEnumerable FindConsumedTagNames() { yield break; } /// /// Find all the tags which are modified by this task /// /// The tag names which are modified by this task public override IEnumerable FindProducedTagNames() { return FindTagNamesFromList(Parameters.Tag); } } }