public override List <UEBuildModule> FindGameModules() { var GameModules = new List <UEBuildModule>(); foreach (var ModuleName in ModuleNames) { UEBuildModule Module = Target.GetModuleByName(ModuleName); if (!Utils.IsFileUnderDirectory(Module.ModuleDirectory, BuildConfiguration.RelativeEnginePath)) { GameModules.Add(Module); } } return(GameModules); }
/// <summary> /// Collects all header files included in a CPPFile /// </summary> /// <param name="CPPFile"></param> /// <param name="Result"></param> /// <param name="FileToRead"></param> /// <param name="FileContents"></param> /// <param name="InstalledFolder"></param> /// <param name="StartIndex"></param> /// <param name="EndIndex"></param> private static void CollectHeaders(FileItem CPPFile, List <DependencyInclude> Result, string FileToRead, string FileContents, string InstalledFolder, int StartIndex, int EndIndex) { Match M = CPPHeaderRegex.Match(FileContents, StartIndex, EndIndex - StartIndex); CaptureCollection CC = M.Groups["HeaderFile"].Captures; Result.Capacity = Math.Max(Result.Count + CC.Count, Result.Capacity); foreach (Capture C in CC) { string HeaderValue = C.Value; if (HeaderValue.IndexOfAny(Path.GetInvalidPathChars()) != -1) { throw new BuildException("In {0}: An #include statement contains invalid characters. You might be missing a double-quote character. (\"{1}\")", FileToRead, C.Value); } //@TODO: The intermediate exclusion is to work around autogenerated absolute paths in Module.SomeGame.cpp style files bool bIsIntermediateOrThirdParty = FileToRead.Contains("Intermediate") || FileToRead.Contains("ThirdParty"); bool bCheckForBackwardSlashes = FileToRead.StartsWith(InstalledFolder); if (UnrealBuildTool.HasUProjectFile()) { bCheckForBackwardSlashes |= Utils.IsFileUnderDirectory(FileToRead, UnrealBuildTool.GetUProjectPath()); } if (bCheckForBackwardSlashes && !bIsIntermediateOrThirdParty) { if (HeaderValue.IndexOf('\\', 0) >= 0) { throw new BuildException("In {0}: #include \"{1}\" contains backslashes ('\\'), please use forward slashes ('/') instead.", FileToRead, C.Value); } } HeaderValue = Utils.CleanDirectorySeparators(HeaderValue); Result.Add(new DependencyInclude(HeaderValue)); } // also look for #import in objective C files string Ext = Path.GetExtension(CPPFile.AbsolutePath).ToUpperInvariant(); if (Ext == ".MM" || Ext == ".M") { M = MMHeaderRegex.Match(FileContents, StartIndex, EndIndex - StartIndex); CC = M.Groups["HeaderFile"].Captures; Result.Capacity += CC.Count; foreach (Capture C in CC) { Result.Add(new DependencyInclude(C.Value)); } } }
/** Updates the intermediate include directory timestamps of all the passed in UObject modules */ private static void UpdateDirectoryTimestamps(List <UHTModuleInfo> UObjectModules) { foreach (var Module in UObjectModules) { string GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var GeneratedCodeDirectoryInfo = new DirectoryInfo(GeneratedCodeDirectory); try { if (GeneratedCodeDirectoryInfo.Exists) { // Don't write anything to the engine directory if we're running an installed build if (UnrealBuildTool.IsEngineInstalled() && Utils.IsFileUnderDirectory(Module.ModuleDirectory, BuildConfiguration.RelativeEnginePath)) { continue; } // Touch the include directory since we have technically 'generated' the headers // However, the headers might not be touched at all since that would cause the compiler to recompile everything // We can't alter the directory timestamp directly, because this may throw exceptions when the directory is // open in visual studio or windows explorer, so instead we create a blank file that will change the timestamp for us string TimestampFile = GeneratedCodeDirectoryInfo.FullName + Path.DirectorySeparatorChar + @"Timestamp"; if (!GeneratedCodeDirectoryInfo.Exists) { GeneratedCodeDirectoryInfo.Create(); } // Save all of the UObject files to a timestamp file. We'll load these on the next run to see if any new // files with UObject classes were deleted, so that we'll know to run UHT even if the timestamps of all // of the other source files were unchanged { var AllUObjectFiles = new List <string>(); AllUObjectFiles.AddRange(Module.PublicUObjectClassesHeaders.ConvertAll(Item => Item.AbsolutePath)); AllUObjectFiles.AddRange(Module.PublicUObjectHeaders.ConvertAll(Item => Item.AbsolutePath)); AllUObjectFiles.AddRange(Module.PrivateUObjectHeaders.ConvertAll(Item => Item.AbsolutePath)); ResponseFile.Create(TimestampFile, AllUObjectFiles); } } } catch (Exception Exception) { throw new BuildException(Exception, "Couldn't touch header directories: " + Exception.Message); } } }
/** Updates the intermediate include directory timestamps of all the passed in UObject modules */ private static void UpdateDirectoryTimestamps(List <UHTModuleInfo> UObjectModules) { foreach (var Module in UObjectModules) { string GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var GeneratedCodeDirectoryInfo = new DirectoryInfo(GeneratedCodeDirectory); try { if (GeneratedCodeDirectoryInfo.Exists) { // Don't write anything to the engine directory if we're running an installed build if (UnrealBuildTool.IsEngineInstalled() && Utils.IsFileUnderDirectory(Module.ModuleDirectory, BuildConfiguration.RelativeEnginePath)) { continue; } // Touch the include directory since we have technically 'generated' the headers // However, the headers might not be touched at all since that would cause the compiler to recompile everything // We can't alter the directory timestamp directly, because this may throw exceptions when the directory is // open in visual studio or windows explorer, so instead we create a blank file that will change the timestamp for us string TimestampFile = GeneratedCodeDirectoryInfo.FullName + Path.DirectorySeparatorChar + @"Timestamp"; if (!GeneratedCodeDirectoryInfo.Exists) { GeneratedCodeDirectoryInfo.Create(); } if (File.Exists(TimestampFile)) { File.Delete(TimestampFile); } using (File.Create(TimestampFile)) { } } } catch (Exception Exception) { throw new BuildException(Exception, "Couldn't touch header directories: " + Exception.Message); } } }
/// <summary> /// Get the name of the response file for the current linker environment and output file /// </summary> /// <param name="LinkEnvironment"></param> /// <param name="OutputFile"></param> /// <returns></returns> public static string GetResponseFileName(LinkEnvironment LinkEnvironment, FileItem OutputFile) { // Construct a relative path for the intermediate response file string ResponseFileName = Path.Combine(LinkEnvironment.Config.IntermediateDirectory, Path.GetFileName(OutputFile.AbsolutePath) + ".response"); if (UnrealBuildTool.HasUProjectFile()) { // If this is the uproject being built, redirect the intermediate if (Utils.IsFileUnderDirectory(OutputFile.AbsolutePath, UnrealBuildTool.GetUProjectPath())) { ResponseFileName = Path.Combine( UnrealBuildTool.GetUProjectPath(), BuildConfiguration.PlatformIntermediateFolder, Path.GetFileNameWithoutExtension(UnrealBuildTool.GetUProjectFile()), LinkEnvironment.Config.Target.Configuration.ToString(), Path.GetFileName(OutputFile.AbsolutePath) + ".response"); } } // Convert the relative path to an absolute path ResponseFileName = Path.GetFullPath(ResponseFileName); return(ResponseFileName); }
public UHTManifest(UEBuildTarget Target, string InRootLocalPath, string InRootBuildPath, IEnumerable <UHTModuleInfo> ModuleInfo) { IsGameTarget = TargetRules.IsGameType(Target.TargetType); RootLocalPath = InRootLocalPath; RootBuildPath = InRootBuildPath; TargetName = Target.GetTargetName(); Modules = ModuleInfo.Select(Info => new Module { Name = Info.ModuleName, ModuleType = Info.ModuleType, BaseDirectory = Info.ModuleDirectory, IncludeBase = Info.ModuleDirectory, OutputDirectory = Path.GetDirectoryName(Info.GeneratedCPPFilenameBase), ClassesHeaders = Info.PublicUObjectClassesHeaders.Select((Header) => Header.AbsolutePath).ToList(), PublicHeaders = Info.PublicUObjectHeaders.Select((Header) => Header.AbsolutePath).ToList(), PrivateHeaders = Info.PrivateUObjectHeaders.Select((Header) => Header.AbsolutePath).ToList(), PCH = Info.PCH, GeneratedCPPFilenameBase = Info.GeneratedCPPFilenameBase, SaveExportedHeaders = !UnrealBuildTool.IsEngineInstalled() || !Utils.IsFileUnderDirectory(Info.ModuleDirectory, BuildConfiguration.RelativeEnginePath), UHTGeneratedCodeVersion = Info.GeneratedCodeVersion, }).ToList(); }
/** * Checks the class header files and determines if generated UObject code files are out of date in comparison. * @param UObjectModules Modules that we generate headers for * * @return True if the code files are out of date * */ private static bool AreGeneratedCodeFilesOutOfDate(List <UHTModuleInfo> UObjectModules, DateTime HeaderToolTimestamp) { // Get CoreUObject.generated.cpp timestamp. If the source files are older than the CoreUObject generated code, we'll // need to regenerate code for the module DateTime?CoreGeneratedTimestamp = null; { // Find the CoreUObject module foreach (var Module in UObjectModules) { if (Module.ModuleName.Equals("CoreUObject", StringComparison.InvariantCultureIgnoreCase)) { CoreGeneratedTimestamp = GetCoreGeneratedTimestamp(Module.ModuleName, Path.GetDirectoryName(Module.GeneratedCPPFilenameBase)); break; } } if (CoreGeneratedTimestamp == null) { throw new BuildException("Could not find CoreUObject in list of all UObjectModules"); } } foreach (var Module in UObjectModules) { // If the engine is installed, skip skip checking timestamps for modules that are under the engine directory if (UnrealBuildTool.IsEngineInstalled() && Utils.IsFileUnderDirectory(Module.ModuleDirectory, BuildConfiguration.RelativeEnginePath)) { continue; } // Make sure we have an existing folder for generated code. If not, then we definitely need to generate code! var GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var TestDirectory = (FileSystemInfo) new DirectoryInfo(GeneratedCodeDirectory); if (!TestDirectory.Exists) { // Generated code directory is missing entirely! Log.TraceVerbose("UnrealHeaderTool needs to run because no generated code directory was found for module {0}", Module.ModuleName); return(true); } // Grab our special "Timestamp" file that we saved after the last set of headers were generated. This file // actually contains the list of source files which contained UObjects, so that we can compare to see if any // UObject source files were deleted (or no longer contain UObjects), which means we need to run UHT even // if no other source files were outdated string TimestampFile = Path.Combine(GeneratedCodeDirectory, @"Timestamp"); var SavedTimestampFileInfo = (FileSystemInfo) new FileInfo(TimestampFile); if (!SavedTimestampFileInfo.Exists) { // Timestamp file was missing (possibly deleted/cleaned), so headers are out of date Log.TraceVerbose("UnrealHeaderTool needs to run because UHT Timestamp file did not exist for module {0}", Module.ModuleName); return(true); } // Make sure the last UHT run completed after UnrealHeaderTool.exe was compiled last, and after the CoreUObject headers were touched last. var SavedTimestamp = SavedTimestampFileInfo.LastWriteTime; if (HeaderToolTimestamp > SavedTimestamp || CoreGeneratedTimestamp > SavedTimestamp) { // Generated code is older than UnrealHeaderTool.exe or CoreUObject headers. Out of date! Log.TraceVerbose("UnrealHeaderTool needs to run because UnrealHeaderTool.exe or CoreUObject headers are newer than SavedTimestamp for module {0}", Module.ModuleName); return(true); } // Iterate over our UObjects headers and figure out if any of them have changed var AllUObjectHeaders = new List <FileItem>(); AllUObjectHeaders.AddRange(Module.PublicUObjectClassesHeaders); AllUObjectHeaders.AddRange(Module.PublicUObjectHeaders); AllUObjectHeaders.AddRange(Module.PrivateUObjectHeaders); // Load up the old timestamp file and check to see if anything has changed { var UObjectFilesFromPreviousRun = File.ReadAllLines(TimestampFile); if (AllUObjectHeaders.Count != UObjectFilesFromPreviousRun.Length) { Log.TraceVerbose("UnrealHeaderTool needs to run because there are a different number of UObject source files in module {0}", Module.ModuleName); return(true); } for (int FileIndex = 0; FileIndex < AllUObjectHeaders.Count; ++FileIndex) { if (!UObjectFilesFromPreviousRun[FileIndex].Equals(AllUObjectHeaders[FileIndex].AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { Log.TraceVerbose("UnrealHeaderTool needs to run because the set of UObject source files in module {0} has changed", Module.ModuleName); return(true); } } } foreach (var HeaderFile in AllUObjectHeaders) { var HeaderFileTimestamp = HeaderFile.Info.LastWriteTime; // Has the source header changed since we last generated headers successfully? if (HeaderFileTimestamp > SavedTimestamp) { Log.TraceVerbose("UnrealHeaderTool needs to run because SavedTimestamp is older than HeaderFileTimestamp ({0}) for module {1}", HeaderFile.AbsolutePath, Module.ModuleName); return(true); } // When we're running in assembler mode, outdatedness cannot be inferred by checking the directory timestamp // of the source headers. We don't care if source files were added or removed in this mode, because we're only // able to process the known UObject headers that are in the Makefile. If UObject header files are added/removed, // we expect the user to re-run GenerateProjectFiles which will force UBTMakefile outdatedness. // @todo ubtmake: Possibly, we should never be doing this check these days. if (UnrealBuildTool.IsGatheringBuild || !UnrealBuildTool.IsAssemblingBuild) { // Also check the timestamp on the directory the source file is in. If the directory timestamp has // changed, new source files may have been added or deleted. We don't know whether the new/deleted // files were actually UObject headers, but because we don't know all of the files we processed // in the previous run, we need to assume our generated code is out of date if the directory timestamp // is newer. var HeaderDirectoryTimestamp = new DirectoryInfo(Path.GetDirectoryName(HeaderFile.AbsolutePath)).LastWriteTime; if (HeaderDirectoryTimestamp > SavedTimestamp) { Log.TraceVerbose("UnrealHeaderTool needs to run because the directory containing an existing header ({0}) has changed, and headers may have been added to or deleted from module {1}", HeaderFile.AbsolutePath, Module.ModuleName); return(true); } } } } return(false); }
/** * Checks the class header files and determines if generated UObject code files are out of date in comparison. * @param UObjectModules Modules that we generate headers for * * @return True if the code files are out of date * */ private static bool AreGeneratedCodeFilesOutOfDate(List <UHTModuleInfo> UObjectModules) { bool bIsOutOfDate = false; // Get UnrealHeaderTool timestamp. If it's newer than generated headers, they need to be rebuilt too. var HeaderToolTimestamp = CheckIfUnrealHeaderToolIsUpToDate(); // Get CoreUObject.generated.cpp timestamp. If the source files are older than the CoreUObject generated code, we'll // need to regenerate code for the module DateTime?CoreGeneratedTimestamp = null; { // Find the CoreUObject module foreach (var Module in UObjectModules) { if (Module.ModuleName.Equals("CoreUObject", StringComparison.InvariantCultureIgnoreCase)) { CoreGeneratedTimestamp = GetCoreGeneratedTimestamp(Module.ModuleName, Path.GetDirectoryName(Module.GeneratedCPPFilenameBase)); break; } } if (CoreGeneratedTimestamp == null) { throw new BuildException("Could not find CoreUObject in list of all UObjectModules"); } } foreach (var Module in UObjectModules) { // If the engine is installed, skip skip checking timestamps for modules that are under the engine directory if (UnrealBuildTool.IsEngineInstalled() && Utils.IsFileUnderDirectory(Module.ModuleDirectory, BuildConfiguration.RelativeEnginePath)) { continue; } // Make sure we have an existing folder for generated code. If not, then we definitely need to generate code! var GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var TestDirectory = (FileSystemInfo) new DirectoryInfo(GeneratedCodeDirectory); if (TestDirectory.Exists) { // Grab our special "Timestamp" file that we saved after the last set of headers were generated string TimestampFile = Path.Combine(GeneratedCodeDirectory, @"Timestamp"); var SavedTimestampFileInfo = (FileSystemInfo) new FileInfo(TimestampFile); if (SavedTimestampFileInfo.Exists) { // Make sure the last UHT run completed after UnrealHeaderTool.exe was compiled last, and after the CoreUObject headers were touched last. var SavedTimestamp = SavedTimestampFileInfo.LastWriteTime; if (SavedTimestamp.CompareTo(HeaderToolTimestamp) > 0 && SavedTimestamp.CompareTo(CoreGeneratedTimestamp) > 0) { // Iterate over our UObjects headers and figure out if any of them have changed var AllUObjectHeaders = new List <FileItem>(); AllUObjectHeaders.AddRange(Module.PublicUObjectClassesHeaders); AllUObjectHeaders.AddRange(Module.PublicUObjectHeaders); AllUObjectHeaders.AddRange(Module.PrivateUObjectHeaders); foreach (var HeaderFile in AllUObjectHeaders) { var HeaderFileTimestamp = HeaderFile.Info.LastWriteTime; // Has the source header changed since we last generated headers successfully? if (SavedTimestamp.CompareTo(HeaderFileTimestamp) < 0) { bIsOutOfDate = true; break; } // When we're running in assembler mode, outdatedness cannot be inferred by checking the directory timestamp // of the source headers. We don't care if source files were added or removed in this mode, because we're only // able to process the known UObject headers that are in the Makefile. If UObject header files are added/removed, // we expect the user to re-run GenerateProjectFiles which will force UBTMakefile outdatedness. // @todo ubtmake: Possibly, we should never be doing this check these days. if (UnrealBuildTool.IsGatheringBuild || !UnrealBuildTool.IsAssemblingBuild) { // Also check the timestamp on the directory the source file is in. If the directory timestamp has // changed, new source files may have been added or deleted. We don't know whether the new/deleted // files were actually UObject headers, but because we don't know all of the files we processed // in the previous run, we need to assume our generated code is out of date if the directory timestamp // is newer. var HeaderDirectoryTimestamp = new DirectoryInfo(Path.GetDirectoryName(HeaderFile.AbsolutePath)).LastWriteTime; if (SavedTimestamp.CompareTo(HeaderDirectoryTimestamp) < 0) { bIsOutOfDate = true; break; } } } } else { // Generated code is older UnrealHeaderTool.exe or CoreUObject headers. Out of date! bIsOutOfDate = true; } } else { // Timestamp file was missing (possibly deleted/cleaned), so headers are out of date bIsOutOfDate = true; } } else { // Generated code directory is missing entirely! bIsOutOfDate = true; } // If even one module is out of date, we're done! UHT does them all in one fell swoop.; if (bIsOutOfDate) { break; } } return(bIsOutOfDate); }
/** * Checks the class header files and determines if generated UObject code files are out of date in comparison. * @param UObjectModules Modules that we generate headers for * * @return True if the code files are out of date * */ private static bool AreGeneratedCodeFilesOutOfDate(List <UHTModuleInfo> UObjectModules) { bool bIsOutOfDate = false; // Get UnrealHeaderTool timestamp. If it's newer than generated headers, they need to be rebuilt too. var HeaderToolTimestamp = CheckIfUnrealHeaderToolIsUpToDate(); // Get CoreUObject.generated.cpp timestamp. If the source files are older than the CoreUObject generated code, we'll // need to regenerate code for the module DateTime?CoreGeneratedTimestamp = null; { // Find the CoreUObject module foreach (var Module in UObjectModules) { if (Module.ModuleName.Equals("CoreUObject", StringComparison.InvariantCultureIgnoreCase)) { CoreGeneratedTimestamp = GetCoreGeneratedTimestamp(Module.ModuleName, Path.GetDirectoryName(Module.GeneratedCPPFilenameBase)); break; } } if (CoreGeneratedTimestamp == null) { throw new BuildException("Could not find CoreUObject in list of all UObjectModules"); } } foreach (var Module in UObjectModules) { // In Rocket, we skip checking timestamps for modules that don't exist within the project's directory if (UnrealBuildTool.RunningRocket()) { // @todo Rocket: This could be done in a better way I'm sure if (!Utils.IsFileUnderDirectory(Module.ModuleDirectory, UnrealBuildTool.GetUProjectPath())) { // Engine or engine plugin module - Rocket does not regenerate them so don't compare their timestamps continue; } } // Make sure we have an existing folder for generated code. If not, then we definitely need to generate code! var GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var TestDirectory = (FileSystemInfo) new DirectoryInfo(GeneratedCodeDirectory); if (TestDirectory.Exists) { // Grab our special "Timestamp" file that we saved after the last set of headers were generated. This file // actually contains the list of source files which contained UObjects, so that we can compare to see if any // UObject source files were deleted (or no longer contain UObjects), which means we need to run UHT even // if no other source files were outdated string TimestampFile = Path.Combine(GeneratedCodeDirectory, @"Timestamp"); var SavedTimestampFileInfo = (FileSystemInfo) new FileInfo(TimestampFile); if (SavedTimestampFileInfo.Exists) { // Make sure the last UHT run completed after UnrealHeaderTool.exe was compiled last, and after the CoreUObject headers were touched last. var SavedTimestamp = SavedTimestampFileInfo.LastWriteTime; if (SavedTimestamp.CompareTo(HeaderToolTimestamp) > 0 && SavedTimestamp.CompareTo(CoreGeneratedTimestamp) > 0) { // Iterate over our UObjects headers and figure out if any of them have changed var AllUObjectHeaders = new List <FileItem>(); AllUObjectHeaders.AddRange(Module.PublicUObjectClassesHeaders); AllUObjectHeaders.AddRange(Module.PublicUObjectHeaders); AllUObjectHeaders.AddRange(Module.PrivateUObjectHeaders); // Load up the old timestamp file and check to see if anything has changed { var UObjectFilesFromPreviousRun = File.ReadAllLines(TimestampFile); if (AllUObjectHeaders.Count == UObjectFilesFromPreviousRun.Length) { for (int FileIndex = 0; FileIndex < AllUObjectHeaders.Count; ++FileIndex) { if (!UObjectFilesFromPreviousRun[FileIndex].Equals(AllUObjectHeaders[FileIndex].AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { bIsOutOfDate = true; Log.TraceVerbose("UnrealHeaderTool needs to run because the set of UObject source files in module {0} has changed", Module.ModuleName); break; } } } else { bIsOutOfDate = true; Log.TraceVerbose("UnrealHeaderTool needs to run because there are a different number of UObject source files in module {0}", Module.ModuleName); } } foreach (var HeaderFile in AllUObjectHeaders) { var HeaderFileTimestamp = HeaderFile.Info.LastWriteTime; // Has the source header changed since we last generated headers successfully? if (SavedTimestamp.CompareTo(HeaderFileTimestamp) < 0) { Log.TraceVerbose("UnrealHeaderTool needs to run because SavedTimestamp is older than HeaderFileTimestamp (" + HeaderFile.AbsolutePath + ") for module {0}", Module.ModuleName); bIsOutOfDate = true; break; } } } else { // Generated code is older UnrealHeaderTool.exe or CoreUObject headers. Out of date! Log.TraceVerbose("UnrealHeaderTool needs to run because UnrealHeaderTool.exe or CoreUObject headers are newer than SavedTimestamp for module {0}", Module.ModuleName); bIsOutOfDate = true; } } else { // Timestamp file was missing (possibly deleted/cleaned), so headers are out of date Log.TraceVerbose("UnrealHeaderTool needs to run because UHT Timestamp file did not exist for module {0}", Module.ModuleName); bIsOutOfDate = true; } } else { // Generated code directory is missing entirely! Log.TraceVerbose("UnrealHeaderTool needs to run because no generated code directory was found for module {0}", Module.ModuleName); bIsOutOfDate = true; } // If even one module is out of date, we're done! UHT does them all in one fell swoop.; if (bIsOutOfDate) { break; } } return(bIsOutOfDate); }