// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. using AutomationTool; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using Tools.DotNETCommon; using UnrealBuildTool; namespace Win.Automation { /// /// Parameters for a task that purges data from a symbol store after a given age /// public class AgeStoreTaskParameters { /// /// The target platform to age symbols for. /// [TaskParameter] public UnrealTargetPlatform Platform; /// /// The symbol server directory. /// [TaskParameter] public string StoreDir; /// /// Number of days worth of symbols to keep. /// [TaskParameter] public int Days; /// /// A substring to match in directory file names before deleting symbols. This allows the "age store" task /// to avoid deleting symbols from other builds in the case where multiple builds share the same symbol server. /// Specific use of the filter value is determined by the symbol server structure defined by the platform tool chain. /// [TaskParameter(Optional = true)] public string Filter; } /// /// Task which strips symbols from a set of files. This task is named after the AGESTORE utility that comes with the Microsoft debugger tools SDK, but is actually a separate implementation. The main /// difference is that it uses the last modified time rather than last access time to determine which files to delete. /// [TaskElement("AgeStore", typeof(AgeStoreTaskParameters))] public class AgeStoreTask : CustomTask { /// /// Parameters for this task /// AgeStoreTaskParameters Parameters; /// /// Construct a spawn task /// /// Parameters for the task public AgeStoreTask(AgeStoreTaskParameters InParameters) { Parameters = InParameters; } private static void TryDelete(DirectoryInfo Directory) { try { Directory.Delete(true); CommandUtils.LogInformation("Removed '{0}'", Directory.FullName); } catch { CommandUtils.LogWarning("Couldn't delete '{0}' - skipping", Directory.FullName); } } private void RecurseDirectory(DateTime ExpireTimeUtc, DirectoryInfo CurrentDirectory, string[] DirectoryStructure, int Level, string Filter) { // Do a file search at the last level. if (Level == DirectoryStructure.Length) { // If all files are out of date, delete the directory... if (CurrentDirectory.EnumerateFiles().All(x => x.LastWriteTimeUtc < ExpireTimeUtc)) TryDelete(CurrentDirectory); } else { string[] Patterns = DirectoryStructure[Level].Split(';'); foreach (var Pattern in Patterns) { string ReplacedPattern = string.Format(Pattern, Filter); foreach (var ChildDirectory in CurrentDirectory.GetDirectories(ReplacedPattern, SearchOption.TopDirectoryOnly)) { RecurseDirectory(ExpireTimeUtc, ChildDirectory, DirectoryStructure, Level + 1, Filter); } } // Delete this directory if it is empty, and it is not the root directory. if (Level > 0 && !CurrentDirectory.EnumerateFileSystemInfos().Any()) TryDelete(CurrentDirectory); } } /// /// 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) { // Get the list of symbol file name patterns from the platform. Platform TargetPlatform = Platform.GetPlatform(Parameters.Platform); string[] DirectoryStructure = TargetPlatform.SymbolServerDirectoryStructure; if (DirectoryStructure == null) { throw new AutomationException("Platform does not specify the symbol server structure. Cannot age the symbol server."); } string Filter = string.IsNullOrWhiteSpace(Parameters.Filter) ? string.Empty : Parameters.Filter.Trim(); // Get the time at which to expire files DateTime ExpireTimeUtc = DateTime.UtcNow - TimeSpan.FromDays(Parameters.Days); CommandUtils.LogInformation("Expiring all files before {0}...", ExpireTimeUtc); // Scan the store directory and delete old symbol files DirectoryReference SymbolServerDirectory = ResolveDirectory(Parameters.StoreDir); LockFile.OptionallyTakeLock(TargetPlatform.SymbolServerRequiresLock, SymbolServerDirectory, TimeSpan.FromMinutes(15), () => { RecurseDirectory(ExpireTimeUtc, new DirectoryInfo(SymbolServerDirectory.FullName), DirectoryStructure, 0, Filter); }); } /// /// 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() { yield break; } } }