// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Logging; using UnrealBuildBase; namespace UnrealBuildTool { class JunkDeleter { /// /// Loads JunkManifest.txt file and removes all junk files/folders defined in it. /// public static void DeleteJunk(ILogger Logger) { List JunkManifest = LoadJunkManifest(); DeleteAllJunk(JunkManifest, Logger); } /// /// Loads JunkManifest.txt file. /// /// Junk manifest file contents. private static List LoadJunkManifest() { string ManifestPath = ".." + Path.DirectorySeparatorChar + "Build" + Path.DirectorySeparatorChar + "JunkManifest.txt"; List JunkManifest = new List(); if (File.Exists(ManifestPath)) { string MachineName = Unreal.MachineName; using (StreamReader reader = new StreamReader(ManifestPath)) { string CurrentToRootDir = ".." + Path.DirectorySeparatorChar + ".."; string? LineRead; while ((LineRead = reader.ReadLine()) != null) { string JunkEntry = LineRead.Trim(); if (String.IsNullOrEmpty(JunkEntry) == false) { string[] Tokens = JunkEntry.Split(":".ToCharArray()); bool bIsValidJunkLine = true; foreach (string Token in Tokens) { if (Token.StartsWith("Machine=", StringComparison.InvariantCultureIgnoreCase) == true) { string[] InnerTokens = Token.Split("=".ToCharArray()); // check if the machine name on the line matches the current machine name, if not, we don't apply this junk if (InnerTokens.Length == 2 && MachineName.StartsWith(InnerTokens[1]) == false) { // Not meant for this machine bIsValidJunkLine = false; } } else if (Token.StartsWith("Platform=", StringComparison.InvariantCultureIgnoreCase) == true) { string[] InnerTokens = Token.Split("=".ToCharArray()); // check if the machine name on the line matches the current machine name, if not, we don't apply this junk if (InnerTokens.Length == 2) { UnrealTargetPlatform ParsedPlatform; // if the platform is valid, then we want to keep the files, which means that we don't want to apply the junk line if (UnrealTargetPlatform.TryParse(InnerTokens[1], out ParsedPlatform)) { if (UEBuildPlatform.TryGetBuildPlatform(ParsedPlatform, out _)) { // this is a good platform, so don't delete any files! bIsValidJunkLine = false; } } } } } // All paths within the manifest are Unreal root directory relative. // UBT's working directory is Engine\Source so add "..\..\" to each of the entires. if (bIsValidJunkLine) { // the entry is always the last element in the token array (after the final :) string FixedPath = Path.Combine(CurrentToRootDir, Tokens[^1]); FixedPath = FixedPath.Replace('\\', Path.DirectorySeparatorChar); JunkManifest.Add(FixedPath); } } } } } return JunkManifest; } /// /// Goes through each entry from the junk manifest and deletes it. /// /// JunkManifest.txt entries. /// Logger for output private static void DeleteAllJunk(List JunkManifest, ILogger Logger) { foreach (string Junk in JunkManifest) { if (IsFile(Junk)) { string FileName = Path.GetFileName(Junk); if (FileName.Contains('*')) { // Wildcard search and delete string DirectoryToLookIn = Path.GetDirectoryName(Junk)!; if (Directory.Exists(DirectoryToLookIn)) { // Delete all files within the specified folder string[] FilesToDelete = Directory.GetFiles(DirectoryToLookIn, FileName, SearchOption.TopDirectoryOnly); foreach (string JunkFile in FilesToDelete) { DeleteFile(JunkFile, Logger); } // Delete all subdirectories with the specified folder string[] DirectoriesToDelete = Directory.GetDirectories(DirectoryToLookIn, FileName, SearchOption.TopDirectoryOnly); foreach (string JunkFolder in DirectoriesToDelete) { DeleteDirectory(JunkFolder, Logger); } } } else { // Delete single file DeleteFile(Junk, Logger); } } else if (Directory.Exists(Junk)) { // Delete the selected folder and all its contents DeleteDirectory(Junk, Logger); } } } private static bool IsFile(string PathToCheck) { string FileName = Path.GetFileName(PathToCheck); if (String.IsNullOrEmpty(FileName) == false) { if (FileName.Contains('*')) { // Assume wildcards are file because the path will be searched for files and directories anyway. return true; } else { return File.Exists(PathToCheck); } } else { return false; } } /// /// Deletes a directory recursively gracefully handling all exceptions. /// /// Path. /// Logger for output private static void DeleteDirectory(string DirectoryPath, ILogger Logger) { try { Logger.LogInformation("Deleting junk directory: \"{Dir}\".", DirectoryPath); Directory.Delete(DirectoryPath, true); } catch (Exception Ex) { // Ignore all exceptions Logger.LogInformation("Unable to delete junk directory: \"{Dir}\". Error: {Ex}", DirectoryPath, Ex.Message.TrimEnd()); } } /// /// Deletes a file gracefully handling all exceptions. /// /// Filename. /// Logger for output private static void DeleteFile(string Filename, ILogger Logger) { try { Logger.LogInformation("Deleting junk file: \"{File}\".", Filename); File.Delete(Filename); } catch (Exception Ex) { // Ingore all exceptions Logger.LogInformation("Unable to delete junk file: \"{File}\". Error: {Ex}", Filename, Ex.Message.TrimEnd()); } } } }