// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml; using EpicGames.Core; using Microsoft.Extensions.Logging; using UnrealBuildBase; namespace AutomationTool.Tasks { /// /// Parameters for a copy task /// public class DeleteTaskParameters { /// /// List of file specifications separated by semicolons (for example, *.cpp;Engine/.../*.bat), or the name of a tag set /// [TaskParameter(Optional = true, ValidationType = TaskParameterValidationType.FileSpec)] public string Files { get; set; } /// /// List of directory names /// [TaskParameter(Optional = true)] public string Directories { get; set; } /// /// Whether to delete empty directories after deleting the files. Defaults to true. /// [TaskParameter(Optional = true)] public bool DeleteEmptyDirectories { get; set; } = true; /// /// Whether or not to use verbose logging. /// [TaskParameter(Optional = true)] public bool Verbose { get; set; } = false; } /// /// Delete a set of files. /// [TaskElement("Delete", typeof(DeleteTaskParameters))] public class DeleteTask : BgTaskImpl { readonly DeleteTaskParameters _parameters; /// /// Constructor /// /// Parameters for this task public DeleteTask(DeleteTaskParameters 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 Task ExecuteAsync(JobContext job, HashSet buildProducts, Dictionary> tagNameToFileSet) { if (_parameters.Files != null) { // Find all the referenced files and delete them HashSet files = ResolveFilespec(Unreal.RootDirectory, _parameters.Files, tagNameToFileSet); foreach (FileReference file in files) { if (_parameters.Verbose) { Logger.LogInformation("Deleting {File}", file.FullName); } if (!InternalUtils.SafeDeleteFile(file.FullName)) { Logger.LogWarning("Couldn't delete file {Arg0}", file.FullName); } } // Try to delete all the parent directories. Keep track of the directories we've already deleted to avoid hitting the disk. if (_parameters.DeleteEmptyDirectories) { // Find all the directories that we're touching HashSet parentDirectories = new HashSet(); foreach (FileReference file in files) { parentDirectories.Add(file.Directory); } // Recurse back up from each of those directories to the root folder foreach (DirectoryReference parentDirectory in parentDirectories) { for (DirectoryReference currentDirectory = parentDirectory; currentDirectory != Unreal.RootDirectory; currentDirectory = currentDirectory.ParentDirectory) { if (!TryDeleteEmptyDirectory(currentDirectory)) { break; } } } } } if (_parameters.Directories != null) { foreach (string directory in _parameters.Directories.Split(';')) { if (!String.IsNullOrEmpty(directory)) { if (_parameters.Verbose) { Logger.LogInformation("Deleting {Directory}", directory); } DirectoryReference fullDir = new DirectoryReference(directory); if (DirectoryReference.Exists(fullDir)) { FileUtils.ForceDeleteDirectory(fullDir); } } } } return Task.CompletedTask; } /// /// Deletes a directory, if it's empty /// /// The directory to check /// True if the directory was deleted, false if not static bool TryDeleteEmptyDirectory(DirectoryReference candidateDirectory) { // Make sure the directory exists if (!DirectoryReference.Exists(candidateDirectory)) { return false; } // Check if there are any files in it. If there are, don't bother trying to delete it. if (Directory.EnumerateFiles(candidateDirectory.FullName).Any() || Directory.EnumerateDirectories(candidateDirectory.FullName).Any()) { return false; } // Try to delete the directory. try { Directory.Delete(candidateDirectory.FullName); return true; } catch (Exception ex) { Logger.LogWarning("Couldn't delete directory {Arg0} ({Arg1})", candidateDirectory.FullName, ex.Message); return false; } } /// /// 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() { return FindTagNamesFromFilespec(_parameters.Files); } /// /// Find all the tags which are modified by this task /// /// The tag names which are modified by this task public override IEnumerable FindProducedTagNames() { yield break; } } }