// Copyright Epic Games, Inc. All Rights Reserved. using AutomationTool; using EpicGames.BuildGraph; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using EpicGames.Core; using OpenTracing; using OpenTracing.Util; using UnrealBuildTool; namespace AutomationTool.Tasks { /// /// Parameters for a task that runs the cooker /// public class CookTaskParameters { /// /// Project file to be cooked. /// [TaskParameter] public string Project; /// /// The cook platform to target (for example, Windows). /// [TaskParameter] public string Platform; /// /// List of maps to be cooked, separated by '+' characters. /// [TaskParameter(Optional = true)] public string Maps; /// /// Additional arguments to be passed to the cooker. /// [TaskParameter(Optional = true)] public bool Versioned = false; /// /// Additional arguments to be passed to the cooker. /// [TaskParameter(Optional = true)] public string Arguments = ""; /// /// Optional path to what editor executable to run for cooking. /// [TaskParameter(Optional = true)] public string EditorExe = ""; /// /// Whether to tag the output from the cook. Since cooks produce a lot of files, it can be detrimental to spend time tagging them if we don't need them in a dependent node. /// [TaskParameter(Optional = true)] public bool TagOutput = true; /// /// Tag to be applied to build products of this task. /// [TaskParameter(Optional = true, ValidationType = TaskParameterValidationType.TagList)] public string Tag; } /// /// Cook a selection of maps for a certain platform /// [TaskElement("Cook", typeof(CookTaskParameters))] public class CookTask : BgTaskImpl { /// /// Parameters for the task /// CookTaskParameters Parameters; /// /// Constructor. /// /// Parameters for this task public CookTask(CookTaskParameters InParameters) { Parameters = InParameters; } /// /// 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 Task ExecuteAsync(JobContext Job, HashSet BuildProducts, Dictionary> TagNameToFileSet) { // Figure out the project that this target belongs to FileReference ProjectFile = null; if(Parameters.Project != null) { ProjectFile = new FileReference(Parameters.Project); if(!FileReference.Exists(ProjectFile)) { throw new AutomationException("Missing project file - {0}", ProjectFile.FullName); } } // Execute the cooker using (IScope Scope = GlobalTracer.Instance.BuildSpan("Cook").StartActive()) { Scope.Span.SetTag("project", ProjectFile == null ? "UE4" : ProjectFile.GetFileNameWithoutExtension()); Scope.Span.SetTag("platform", Parameters.Platform); string[] Maps = (Parameters.Maps == null)? null : Parameters.Maps.Split(new char[]{ '+' }); string Arguments = (Parameters.Versioned ? "" : "-Unversioned ") + "-LogCmds=\"LogSavePackage Warning\" " + Parameters.Arguments; string EditorExe = (string.IsNullOrWhiteSpace(Parameters.EditorExe) ? "UnrealEditor-Cmd.exe" : Parameters.EditorExe); CommandUtils.CookCommandlet(ProjectFile, EditorExe, Maps, null, null, null, Parameters.Platform, Arguments); } // Find all the cooked files List CookedFiles = new List(); if (Parameters.TagOutput) { foreach (string Platform in Parameters.Platform.Split('+')) { DirectoryReference PlatformCookedDirectory = DirectoryReference.Combine(ProjectFile.Directory, "Saved", "Cooked", Platform); if (!DirectoryReference.Exists(PlatformCookedDirectory)) { throw new AutomationException("Cook output directory not found ({0})", PlatformCookedDirectory.FullName); } List PlatformCookedFiles = DirectoryReference.EnumerateFiles(PlatformCookedDirectory, "*", System.IO.SearchOption.AllDirectories).ToList(); if (PlatformCookedFiles.Count == 0) { throw new AutomationException("Cooking did not produce any files in {0}", PlatformCookedDirectory.FullName); } CookedFiles.AddRange(PlatformCookedFiles); DirectoryReference PackagingFilesDirectory = DirectoryReference.Combine(ProjectFile.Directory, "Saved", "TmpPackaging", Platform); if (DirectoryReference.Exists(PackagingFilesDirectory)) { List PackagingFiles = DirectoryReference.EnumerateFiles(PackagingFilesDirectory, "*", System.IO.SearchOption.AllDirectories).ToList(); CookedFiles.AddRange(PackagingFiles); } } } // Apply the optional tag to the build products foreach(string TagName in FindTagNamesFromList(Parameters.Tag)) { FindOrAddTagSet(TagNameToFileSet, TagName).UnionWith(CookedFiles); } // Add them to the set of build products BuildProducts.UnionWith(CookedFiles); return Task.CompletedTask; } /// /// 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); } } }