コード例 #1
0
        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);
        }
コード例 #2
0
        /// <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));
                }
            }
        }
コード例 #3
0
        /** 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);
                }
            }
        }
コード例 #4
0
        /** 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);
                }
            }
        }
コード例 #5
0
        /// <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);
        }
コード例 #6
0
        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();
        }
コード例 #7
0
        /**
         * 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);
        }
コード例 #8
0
        /**
         * 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);
        }
コード例 #9
0
        /**
         * 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);
        }