/// <summary> /// Scans an arbitrary directory tree /// </summary> /// <param name="Directory">Root of the directory tree</param> static void ScanDirectoryTree(DirectoryItem Directory) { foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { Enqueue(() => ScanDirectoryTree(SubDirectory)); } Directory.CacheFiles(); }
/// <summary> /// Scans a plugin parent directory, adding tasks for subdirectories /// </summary> /// <param name="Directory">The directory which may contain plugin directories</param> static void ScanPluginFolder(DirectoryItem Directory) { foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { if (SubDirectory.EnumerateFiles().Any(x => x.HasExtension(".uplugin"))) { Enqueue(() => ScanDirectoryTree(DirectoryItem.Combine(SubDirectory, "Source"))); } else { Enqueue(() => ScanPluginFolder(SubDirectory)); } } }
/// <summary> /// Finds all the source files under a directory that contain reflection markup /// </summary> /// <param name="Directory">The directory to search</param> /// <param name="MetadataCache">Cache of source file metadata</param> /// <param name="ExcludedFolderNames">Set of folder names to ignore when recursing the directory tree</param> /// <param name="FilesWithMarkup">Receives the set of files which contain reflection markup</param> /// <param name="Queue">Queue to add sub-tasks to</param> static void FindFilesWithMarkup(DirectoryItem Directory, SourceFileMetadataCache MetadataCache, ReadOnlyHashSet <string> ExcludedFolderNames, ConcurrentBag <FileItem> FilesWithMarkup, ThreadPoolWorkQueue Queue) { // Search through all the subfolders foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { if (!ExcludedFolderNames.Contains(SubDirectory.Name)) { Queue.Enqueue(() => FindFilesWithMarkup(SubDirectory, MetadataCache, ExcludedFolderNames, FilesWithMarkup, Queue)); } } // Check for all the headers in this folder foreach (FileItem File in Directory.EnumerateFiles()) { if (File.HasExtension(".h") && MetadataCache.ContainsReflectionMarkup(File)) { FilesWithMarkup.Add(File); } } }
/// <summary> /// Search through a directory tree for any rules files /// </summary> /// <param name="Directory">The root directory to search from</param> /// <param name="Cache">Receives all the discovered rules files</param> /// <param name="Queue">Queue for adding additional tasks to</param> private static void FindAllRulesFilesRecursively(DirectoryItem Directory, RulesFileCache Cache, ThreadPoolWorkQueue Queue) { // Scan all the files in this directory bool bSearchSubFolders = true; foreach (FileItem File in Directory.EnumerateFiles()) { if (File.HasExtension(".build.cs")) { lock (Cache.ModuleRules) { Cache.ModuleRules.Add(File.Location); } bSearchSubFolders = false; } else if (File.HasExtension(".target.cs")) { lock (Cache.TargetRules) { Cache.TargetRules.Add(File.Location); } } else if (File.HasExtension(".automation.csproj")) { lock (Cache.AutomationModules) { Cache.AutomationModules.Add(File.Location); } bSearchSubFolders = false; } } // If we didn't find anything to stop the search, search all the subdirectories too if (bSearchSubFolders) { foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { Queue.Enqueue(() => FindAllRulesFilesRecursively(SubDirectory, Cache, Queue)); } } }
/// <summary> /// Determines if a directory, or any subdirectory of it, contains new source files /// </summary> /// <param name="Directory">Directory to search through</param> /// <param name="ExcludedFolderNames">Set of directory names to exclude</param> /// <returns>True if the directory contains any source files</returns> static bool ContainsSourceFiles(DirectoryItem Directory, ReadOnlyHashSet <string> ExcludedFolderNames) { // Check this directory isn't ignored if (!ExcludedFolderNames.Contains(Directory.Name)) { // Check for any source files in this actual directory FileItem[] SourceFiles = UEBuildModuleCPP.GetSourceFiles(Directory); if (SourceFiles.Length > 0) { return(true); } // Check for any source files in a subdirectory foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { if (ContainsSourceFiles(SubDirectory, ExcludedFolderNames)) { return(true); } } } return(false); }
/// <summary> /// Find paths to all the plugins under a given parent directory (recursively) /// </summary> /// <param name="ParentDirectory">Parent directory to look in. Plugins will be found in any *subfolders* of this directory.</param> /// <param name="FileNames">List of filenames. Will have all the discovered .uplugin files appended to it.</param> /// <param name="Queue">Queue for tasks to be executed</param> static void EnumeratePluginsInternal(DirectoryItem ParentDirectory, List <FileReference> FileNames, ThreadPoolWorkQueue Queue) { foreach (DirectoryItem ChildDirectory in ParentDirectory.EnumerateDirectories()) { bool bSearchSubDirectories = true; foreach (FileItem PluginFile in ChildDirectory.EnumerateFiles()) { if (PluginFile.HasExtension(".uplugin")) { lock (FileNames) { FileNames.Add(PluginFile.Location); } bSearchSubDirectories = false; } } if (bSearchSubDirectories) { Queue.Enqueue(() => EnumeratePluginsInternal(ChildDirectory, FileNames, Queue)); } } }
/// <summary> /// Checks if the makefile is valid for the current set of source files. This is done separately to the Load() method to allow pre-build steps to modify source files. /// </summary> /// <param name="Makefile">The makefile that has been loaded</param> /// <param name="ProjectFile">Path to the project file</param> /// <param name="Platform">The platform being built</param> /// <param name="WorkingSet">The current working set of source files</param> /// <param name="ReasonNotLoaded">If the makefile is not valid, is set to a message describing why</param> /// <returns>True if the makefile is valid, false otherwise</returns> public static bool IsValidForSourceFiles(TargetMakefile Makefile, FileReference ProjectFile, UnrealTargetPlatform Platform, ISourceFileWorkingSet WorkingSet, out string ReasonNotLoaded) { using (Timeline.ScopeEvent("TargetMakefile.IsValidForSourceFiles()")) { // Get the list of excluded folder names for this platform ReadOnlyHashSet <string> ExcludedFolderNames = UEBuildPlatform.GetBuildPlatform(Platform).GetExcludedFolderNames(); // Check if any source files have been added or removed foreach (KeyValuePair <DirectoryItem, FileItem[]> Pair in Makefile.DirectoryToSourceFiles) { DirectoryItem InputDirectory = Pair.Key; if (!InputDirectory.Exists || InputDirectory.LastWriteTimeUtc > Makefile.CreateTimeUtc) { FileItem[] SourceFiles = UEBuildModuleCPP.GetSourceFiles(InputDirectory); if (SourceFiles.Length < Pair.Value.Length) { ReasonNotLoaded = "source file removed"; return(false); } else if (SourceFiles.Length > Pair.Value.Length) { ReasonNotLoaded = "source file added"; return(false); } else if (SourceFiles.Intersect(Pair.Value).Count() != SourceFiles.Length) { ReasonNotLoaded = "source file modified"; return(false); } foreach (DirectoryItem Directory in InputDirectory.EnumerateDirectories()) { if (!Makefile.DirectoryToSourceFiles.ContainsKey(Directory) && ContainsSourceFiles(Directory, ExcludedFolderNames)) { ReasonNotLoaded = "directory added"; return(false); } } } } // Check if any external dependencies have changed. These comparisons are done against the makefile creation time. foreach (FileItem ExternalDependency in Makefile.ExternalDependencies) { if (!ExternalDependency.Exists) { Log.TraceLog("{0} has been deleted since makefile was built.", ExternalDependency.Location); ReasonNotLoaded = string.Format("{0} deleted", ExternalDependency.Location.GetFileName()); return(false); } if (ExternalDependency.LastWriteTimeUtc > Makefile.CreateTimeUtc) { Log.TraceLog("{0} has been modified since makefile was built.", ExternalDependency.Location); ReasonNotLoaded = string.Format("{0} modified", ExternalDependency.Location.GetFileName()); return(false); } } // Check if any internal dependencies has changed. These comparisons are done against the makefile modified time. foreach (FileItem InternalDependency in Makefile.InternalDependencies) { if (!InternalDependency.Exists) { Log.TraceLog("{0} has been deleted since makefile was written.", InternalDependency.Location); ReasonNotLoaded = string.Format("{0} deleted", InternalDependency.Location.GetFileName()); return(false); } if (InternalDependency.LastWriteTimeUtc > Makefile.ModifiedTimeUtc) { Log.TraceLog("{0} has been modified since makefile was written.", InternalDependency.Location); ReasonNotLoaded = string.Format("{0} modified", InternalDependency.Location.GetFileName()); return(false); } } // Check that no new plugins have been added foreach (FileReference PluginFile in Plugins.EnumeratePlugins(ProjectFile)) { FileItem PluginFileItem = FileItem.GetItemByFileReference(PluginFile); if (!Makefile.PluginFiles.Contains(PluginFileItem)) { Log.TraceLog("{0} has been added", PluginFile.GetFileName()); ReasonNotLoaded = string.Format("{0} has been added", PluginFile.GetFileName()); return(false); } } // Load the metadata cache SourceFileMetadataCache MetadataCache = SourceFileMetadataCache.CreateHierarchy(ProjectFile); // Find the set of files that contain reflection markup ConcurrentBag <FileItem> NewFilesWithMarkupBag = new ConcurrentBag <FileItem>(); using (ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue()) { foreach (DirectoryItem SourceDirectory in Makefile.SourceDirectories) { Queue.Enqueue(() => FindFilesWithMarkup(SourceDirectory, MetadataCache, ExcludedFolderNames, NewFilesWithMarkupBag, Queue)); } } // Check whether the list has changed List <FileItem> PrevFilesWithMarkup = Makefile.UObjectModuleHeaders.Where(x => !x.bUsePrecompiled).SelectMany(x => x.HeaderFiles).ToList(); List <FileItem> NextFilesWithMarkup = NewFilesWithMarkupBag.ToList(); if (NextFilesWithMarkup.Count != PrevFilesWithMarkup.Count || NextFilesWithMarkup.Intersect(PrevFilesWithMarkup).Count() != PrevFilesWithMarkup.Count) { ReasonNotLoaded = "UHT files changed"; return(false); } // If adaptive unity build is enabled, do a check to see if there are any source files that became part of the // working set since the Makefile was created (or, source files were removed from the working set.) If anything // changed, then we'll force a new Makefile to be created so that we have fresh unity build blobs. We always // want to make sure that source files in the working set are excluded from those unity blobs (for fastest possible // iteration times.) // Check if any source files in the working set no longer belong in it foreach (FileItem SourceFile in Makefile.WorkingSet) { if (!WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc) { Log.TraceLog("{0} was part of source working set and now is not; invalidating makefile", SourceFile.AbsolutePath); ReasonNotLoaded = string.Format("working set of source files changed"); return(false); } } // Check if any source files that are eligible for being in the working set have been modified foreach (FileItem SourceFile in Makefile.CandidatesForWorkingSet) { if (WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc) { Log.TraceLog("{0} was part of source working set and now is not", SourceFile.AbsolutePath); ReasonNotLoaded = string.Format("working set of source files changed"); return(false); } } } ReasonNotLoaded = null; return(true); }