// Copyright Epic Games, Inc. All Rights Reserved. using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Xml; using EpicGames.Core; using Microsoft.Extensions.Logging; using UnrealBuildBase; using UnrealBuildTool; 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 { get; set; } /// /// 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 { get; set; } } /// /// Task that tags build products and/or runtime dependencies by reading from *.target files. /// [TaskElement("SanitizeReceipt", typeof(SanitizeReceiptTaskParameters))] class SanitizeReceiptTask : BgTaskImpl { readonly SanitizeReceiptTaskParameters _parameters; /// /// Constructor /// /// Parameters to select which files to search public SanitizeReceiptTask(SanitizeReceiptTaskParameters parameters) { _parameters = parameters; } /// /// ExecuteAsync 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 Execute(targetFiles, engineDir); } public static Task Execute(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 Enumerable.Empty(); } } /// /// 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.Execute(targetFiles, engineDir); } } }