/// <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="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, ISourceFileWorkingSet WorkingSet, out string ReasonNotLoaded) { using (Timeline.ScopeEvent("TargetMakefile.IsValidForSourceFiles()")) { // 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); } } } // Check if any of the additional dependencies has changed foreach (FileItem AdditionalDependency in Makefile.AdditionalDependencies) { if (!AdditionalDependency.Exists) { Log.TraceLog("{0} has been deleted since makefile was built.", AdditionalDependency.Location); ReasonNotLoaded = string.Format("{0} deleted", AdditionalDependency.Location.GetFileName()); return(false); } if (AdditionalDependency.LastWriteTimeUtc > Makefile.CreateTimeUtc) { Log.TraceLog("{0} has been modified since makefile was built.", AdditionalDependency.Location); ReasonNotLoaded = string.Format("{0} modified", AdditionalDependency.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); } } // We do a check to see if any modules' headers have changed which have // acquired or lost UHT types. If so, which should be rare, // we'll just invalidate the entire makefile and force it to be rebuilt. // Get all H files in processed modules newer than the makefile itself HashSet <FileItem> HFilesNewerThanMakefile = new HashSet <FileItem>(); foreach (UHTModuleHeaderInfo ModuleHeaderInfo in Makefile.UObjectModuleHeaders) { foreach (FileItem HeaderFile in ModuleHeaderInfo.SourceFolder.EnumerateFiles()) { if (HeaderFile.HasExtension(".h") && HeaderFile.LastWriteTimeUtc > Makefile.CreateTimeUtc) { HFilesNewerThanMakefile.Add(HeaderFile); } } } // Get all H files in all modules processed in the last makefile build HashSet <FileItem> AllUHTHeaders = new HashSet <FileItem>(Makefile.UObjectModuleHeaders.SelectMany(x => x.HeaderFiles)); // Check whether any headers have been deleted. If they have, we need to regenerate the makefile since the module might now be empty. If we don't, // and the file has been moved to a different module, we may include stale generated headers. foreach (FileItem HeaderFile in AllUHTHeaders) { if (!HeaderFile.Exists) { Log.TraceLog("File processed by UHT was deleted ({0}); invalidating makefile", HeaderFile); ReasonNotLoaded = string.Format("UHT file was deleted"); return(false); } } // Makefile is invalid if: // * There are any newer files which contain no UHT data, but were previously in the makefile // * There are any newer files contain data which needs processing by UHT, but weren't not previously in the makefile SourceFileMetadataCache MetadataCache = SourceFileMetadataCache.CreateHierarchy(ProjectFile); foreach (FileItem HeaderFile in HFilesNewerThanMakefile) { bool bContainsUHTData = MetadataCache.ContainsReflectionMarkup(HeaderFile); bool bWasProcessed = AllUHTHeaders.Contains(HeaderFile); if (bContainsUHTData != bWasProcessed) { Log.TraceLog("{0} {1} contain UHT types and now {2} , ignoring it", HeaderFile, bWasProcessed ? "used to" : "didn't", bWasProcessed ? "doesn't" : "does"); ReasonNotLoaded = string.Format("new files with reflected types"); 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); }
/// <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); }