// Copyright Epic Games, Inc. All Rights Reserved. using EpicGames.BuildGraph; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using EpicGames.Core; using UnrealBuildBase; using UnrealBuildTool; using Microsoft.Extensions.Logging; using static AutomationTool.CommandUtils; namespace AutomationTool.Tasks { /// /// Parameters for the Tag Receipt task. /// public class SanitizeReceiptTaskParameters { /// /// Set of receipt files (*.target) to read, including wildcards and tag names, separated by semicolons. /// [TaskParameter(ValidationType = TaskParameterValidationType.FileSpec)] public string Files; /// /// Path to the Engine folder, used to expand $(EngineDir) properties in receipt files. Defaults to the Engine directory for the current workspace. /// [TaskParameter(Optional = true)] public DirectoryReference EngineDir; } /// /// Task that tags build products and/or runtime dependencies by reading from *.target files. /// [TaskElement("SanitizeReceipt", typeof(SanitizeReceiptTaskParameters))] class SanitizeReceiptTask : BgTaskImpl { /// /// Parameters to this task /// SanitizeReceiptTaskParameters Parameters; /// /// Constructor /// /// Parameters to select which files to search public SanitizeReceiptTask(SanitizeReceiptTaskParameters 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 async Task ExecuteAsync(JobContext Job, HashSet BuildProducts, Dictionary> TagNameToFileSet) { // Set the Engine directory DirectoryReference EngineDir = Parameters.EngineDir ?? Unreal.EngineDirectory; // Resolve the input list IEnumerable TargetFiles = ResolveFilespec(Unreal.RootDirectory, Parameters.Files, TagNameToFileSet); await ExecuteAsync(TargetFiles, EngineDir); } public static Task ExecuteAsync(IEnumerable TargetFiles, DirectoryReference EngineDir) { EngineDir ??= Unreal.EngineDirectory; foreach(FileReference TargetFile in TargetFiles) { // check all files are .target files if (TargetFile.GetExtension() != ".target") { throw new AutomationException("Invalid file passed to TagReceipt task ({0})", TargetFile.FullName); } // Print the name of the file being scanned Logger.LogInformation("Sanitizing {TargetFile}", TargetFile); using(new LogIndentScope(" ")) { // Read the receipt TargetReceipt Receipt; if (!TargetReceipt.TryRead(TargetFile, EngineDir, out Receipt)) { Logger.LogWarning("Unable to load file using TagReceipt task ({Arg0})", TargetFile.FullName); continue; } // Remove any build products that don't exist List NewBuildProducts = new List(Receipt.BuildProducts.Count); foreach(BuildProduct BuildProduct in Receipt.BuildProducts) { if(FileReference.Exists(BuildProduct.Path)) { NewBuildProducts.Add(BuildProduct); } else { Logger.LogInformation("Removing build product: {File}", BuildProduct.Path); } } Receipt.BuildProducts = NewBuildProducts; // Remove any runtime dependencies that don't exist RuntimeDependencyList NewRuntimeDependencies = new RuntimeDependencyList(); foreach(RuntimeDependency RuntimeDependency in Receipt.RuntimeDependencies) { if(FileReference.Exists(RuntimeDependency.Path)) { NewRuntimeDependencies.Add(RuntimeDependency); } else { Logger.LogInformation("Removing runtime dependency: {File}", RuntimeDependency.Path); } } Receipt.RuntimeDependencies = NewRuntimeDependencies; // Save the new receipt Receipt.Write(TargetFile, EngineDir); } } 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 required by this task /// /// The tag names which are required by this task public override IEnumerable FindConsumedTagNames() { return FindTagNamesFromFilespec(Parameters.Files); } /// /// Find all the referenced tags from tasks in this task /// /// The tag names which are produced/modified by this task public override IEnumerable FindProducedTagNames() { return new string[0]; } } /// /// Extension methods /// public static class SanitizeReceiptExtensions { /// /// Sanitize the given receipt files, removing any files that don't exist in the current workspace /// public static async Task SanitizeReceiptsAsync(this FileSet TargetFiles, DirectoryReference EngineDir = null) { await SanitizeReceiptTask.ExecuteAsync(TargetFiles, EngineDir); } } }