protected virtual string IncludeBuildVersionInPackageVersion(string VersionNumber)
        {
            BuildVersion BuildVersionForPackage;

            if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out BuildVersionForPackage) && BuildVersionForPackage.Changelist != 0)
            {
                // Break apart the version number into individual elements
                string[] SplitVersionString = VersionNumber.Split('.');
                VersionNumber = string.Format("{0}.{1}.{2}.{3}",
                                              SplitVersionString[0],
                                              SplitVersionString[1],
                                              BuildVersionForPackage.Changelist / 10000,
                                              BuildVersionForPackage.Changelist % 10000);
            }

            return(VersionNumber);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Finds all the preprocessor definitions that need to be set for the current engine.
        /// </summary>
        /// <returns>List of preprocessor definitions that should be set</returns>
        public static List <string> GetPreprocessorDefinitions()
        {
            List <string> PreprocessorDefines = new List <string>();

            PreprocessorDefines.Add("WITH_FORWARDED_MODULE_RULES_CTOR");
            PreprocessorDefines.Add("WITH_FORWARDED_TARGET_RULES_CTOR");

            // Define macros for the UE4 version, starting with 4.17
            BuildVersion Version;

            if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version))
            {
                for (int MinorVersion = 17; MinorVersion <= Version.MinorVersion; MinorVersion++)
                {
                    PreprocessorDefines.Add(String.Format("UE_4_{0}_OR_LATER", MinorVersion));
                }
            }
            return(PreprocessorDefines);
        }
Exemplo n.º 3
0
        protected XElement GetIdentity(out string IdentityName)
        {
            // Read the PackageName from config
            var PackageName = Regex.Replace(GetConfigString("PackageName", "ProjectName", "DefaultUE4Project"), "[^-.A-Za-z0-9]", "");

            if (string.IsNullOrWhiteSpace(PackageName))
            {
                Log.TraceError("Invalid package name {0}. Package names must only contain letters, numbers, dash, and period and must be at least one character long.", PackageName);
                Log.TraceError("Consider using the setting [{0}]:PackageName to provide a specific value.", IniSection_PlatformTargetSettings);
            }

            var PublisherName = GetConfigString("PublisherName", "CompanyDistinguishedName", "CN=NoPublisher");
            var VersionNumber = GetConfigString("PackageVersion", "ProjectVersion", "1.0.0.0");

            VersionNumber = ValidatePackageVersion(VersionNumber);

            // If specified in the project settings attempt to retrieve the current build number and increment the version number by that amount, accounting for overflows
            bool bIncludeEngineVersionInPackageVersion;

            if (EngineIni.GetBool(IniSection_PlatformTargetSettings, "bIncludeEngineVersionInPackageVersion", out bIncludeEngineVersionInPackageVersion) && bIncludeEngineVersionInPackageVersion)
            {
                BuildVersion BuildVersionForPackage;
                if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out BuildVersionForPackage) && BuildVersionForPackage.Changelist != 0)
                {
                    // Break apart the version number into individual elements
                    string[] SplitVersionString = VersionNumber.Split('.');
                    VersionNumber = string.Format("{0}.{1}.{2}.{3}",
                                                  SplitVersionString[0],
                                                  SplitVersionString[1],
                                                  BuildVersionForPackage.Changelist / 10000,
                                                  BuildVersionForPackage.Changelist % 10000);
                }
            }

            IdentityName = PackageName;

            return(new XElement(GetName("Identity", Schema2010NS),
                                new XAttribute("Name", PackageName),
                                new XAttribute("Publisher", PublisherName),
                                new XAttribute("Version", VersionNumber)));
        }
        /// <summary>
        /// Loads a UBTMakefile from disk
        /// </summary>
        /// <param name="MakefilePath">Path to the makefile to load</param>
        /// <param name="ProjectFile">Path to the project file</param>
        /// <param name="ReasonNotLoaded">If the function returns null, this string will contain the reason why</param>
        /// <param name="WorkingSet">Interface to query which source files are in the working set</param>
        /// <returns>The loaded makefile, or null if it failed for some reason.  On failure, the 'ReasonNotLoaded' variable will contain information about why</returns>
        public static UBTMakefile LoadUBTMakefile(FileReference MakefilePath, FileReference ProjectFile, ISourceFileWorkingSet WorkingSet, out string ReasonNotLoaded)
        {
            // Check the directory timestamp on the project files directory.  If the user has generated project files more
            // recently than the UBTMakefile, then we need to consider the file to be out of date
            FileInfo UBTMakefileInfo = new FileInfo(MakefilePath.FullName);

            if (!UBTMakefileInfo.Exists)
            {
                // UBTMakefile doesn't even exist, so we won't bother loading it
                ReasonNotLoaded = "no existing makefile";
                return(null);
            }

            // Check the build version
            FileInfo BuildVersionFileInfo = new FileInfo(BuildVersion.GetDefaultFileName().FullName);

            if (BuildVersionFileInfo.Exists && UBTMakefileInfo.LastWriteTime.CompareTo(BuildVersionFileInfo.LastWriteTime) < 0)
            {
                Log.TraceVerbose("Existing makefile is older than Build.version, ignoring it");
                ReasonNotLoaded = "Build.version is newer";
                return(null);
            }

            // @todo ubtmake: This will only work if the directory timestamp actually changes with every single GPF.  Force delete existing files before creating new ones?  Eh... really we probably just want to delete + create a file in that folder
            //			-> UPDATE: Seems to work OK right now though on Windows platform, maybe due to GUID changes
            // @todo ubtmake: Some platforms may not save any files into this folder.  We should delete + generate a "touch" file to force the directory timestamp to be updated (or just check the timestamp file itself.  We could put it ANYWHERE, actually)

            // Installed Build doesn't need to check engine projects for outdatedness
            if (!UnrealBuildTool.IsEngineInstalled())
            {
                if (DirectoryReference.Exists(ProjectFileGenerator.IntermediateProjectFilesPath))
                {
                    DateTime EngineProjectFilesLastUpdateTime = new FileInfo(ProjectFileGenerator.ProjectTimestampFile).LastWriteTime;
                    if (UBTMakefileInfo.LastWriteTime.CompareTo(EngineProjectFilesLastUpdateTime) < 0)
                    {
                        // Engine project files are newer than UBTMakefile
                        Log.TraceVerbose("Existing makefile is older than generated engine project files, ignoring it");
                        ReasonNotLoaded = "project files are newer";
                        return(null);
                    }
                }
            }

            // Check the game project directory too
            if (ProjectFile != null)
            {
                string   ProjectFilename = ProjectFile.FullName;
                FileInfo ProjectFileInfo = new FileInfo(ProjectFilename);
                if (!ProjectFileInfo.Exists || UBTMakefileInfo.LastWriteTime.CompareTo(ProjectFileInfo.LastWriteTime) < 0)
                {
                    // .uproject file is newer than UBTMakefile
                    Log.TraceVerbose("Makefile is older than .uproject file, ignoring it");
                    ReasonNotLoaded = ".uproject file is newer";
                    return(null);
                }

                DirectoryReference MasterProjectRelativePath        = ProjectFile.Directory;
                string             GameIntermediateProjectFilesPath = Path.Combine(MasterProjectRelativePath.FullName, "Intermediate", "ProjectFiles");
                if (Directory.Exists(GameIntermediateProjectFilesPath))
                {
                    DateTime GameProjectFilesLastUpdateTime = new DirectoryInfo(GameIntermediateProjectFilesPath).LastWriteTime;
                    if (UBTMakefileInfo.LastWriteTime.CompareTo(GameProjectFilesLastUpdateTime) < 0)
                    {
                        // Game project files are newer than UBTMakefile
                        Log.TraceVerbose("Makefile is older than generated game project files, ignoring it");
                        ReasonNotLoaded = "game project files are newer";
                        return(null);
                    }
                }
            }

            // Check to see if UnrealBuildTool.exe was compiled more recently than the UBTMakefile
            DateTime UnrealBuildToolTimestamp = new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime;

            if (UBTMakefileInfo.LastWriteTime.CompareTo(UnrealBuildToolTimestamp) < 0)
            {
                // UnrealBuildTool.exe was compiled more recently than the UBTMakefile
                Log.TraceVerbose("Makefile is older than UnrealBuildTool.exe, ignoring it");
                ReasonNotLoaded = "UnrealBuildTool.exe is newer";
                return(null);
            }

            // Check to see if any BuildConfiguration files have changed since the last build
            List <XmlConfig.InputFile> InputFiles = XmlConfig.FindInputFiles();

            foreach (XmlConfig.InputFile InputFile in InputFiles)
            {
                FileInfo InputFileInfo = new FileInfo(InputFile.Location.FullName);
                if (InputFileInfo.LastWriteTime > UBTMakefileInfo.LastWriteTime)
                {
                    Log.TraceVerbose("Makefile is older than BuildConfiguration.xml, ignoring it");
                    ReasonNotLoaded = "BuildConfiguration.xml is newer";
                    return(null);
                }
            }

            UBTMakefile LoadedUBTMakefile = null;

            try
            {
                DateTime LoadUBTMakefileStartTime = DateTime.UtcNow;

                using (FileStream Stream = new FileStream(UBTMakefileInfo.FullName, FileMode.Open, FileAccess.Read))
                {
                    BinaryFormatter Formatter = new BinaryFormatter();
                    LoadedUBTMakefile = Formatter.Deserialize(Stream) as UBTMakefile;
                }

                if (UnrealBuildTool.bPrintPerformanceInfo)
                {
                    double LoadUBTMakefileTime = (DateTime.UtcNow - LoadUBTMakefileStartTime).TotalSeconds;
                    Log.TraceInformation("LoadUBTMakefile took " + LoadUBTMakefileTime + "s");
                }
            }
            catch (Exception Ex)
            {
                Log.TraceWarning("Failed to read makefile: {0}", Ex.Message);
                ReasonNotLoaded = "couldn't read existing makefile";
                return(null);
            }

            if (!LoadedUBTMakefile.IsValidMakefile())
            {
                Log.TraceWarning("Loaded makefile appears to have invalid contents, ignoring it ({0})", UBTMakefileInfo.FullName);
                ReasonNotLoaded = "existing makefile appears to be invalid";
                return(null);
            }

            // Check if any of the target's Build.cs files are newer than the makefile
            foreach (UEBuildTarget Target in LoadedUBTMakefile.Targets)
            {
                string TargetCsFilename = Target.TargetRulesFile.FullName;
                if (TargetCsFilename != null)
                {
                    FileInfo TargetCsFile        = new FileInfo(TargetCsFilename);
                    bool     bTargetCsFileExists = TargetCsFile.Exists;
                    if (!bTargetCsFileExists || TargetCsFile.LastWriteTime > UBTMakefileInfo.LastWriteTime)
                    {
                        Log.TraceVerbose("{0} has been {1} since makefile was built, ignoring it ({2})", TargetCsFilename, bTargetCsFileExists ? "changed" : "deleted", UBTMakefileInfo.FullName);
                        ReasonNotLoaded = string.Format("changes to target files");
                        return(null);
                    }
                }

                IEnumerable <string> BuildCsFilenames = Target.GetAllModuleBuildCsFilenames();
                foreach (string BuildCsFilename in BuildCsFilenames)
                {
                    if (BuildCsFilename != null)
                    {
                        FileInfo BuildCsFile        = new FileInfo(BuildCsFilename);
                        bool     bBuildCsFileExists = BuildCsFile.Exists;
                        if (!bBuildCsFileExists || BuildCsFile.LastWriteTime > UBTMakefileInfo.LastWriteTime)
                        {
                            Log.TraceVerbose("{0} has been {1} since makefile was built, ignoring it ({2})", BuildCsFilename, bBuildCsFileExists ? "changed" : "deleted", UBTMakefileInfo.FullName);
                            ReasonNotLoaded = string.Format("changes to module files");
                            return(null);
                        }
                    }
                }

                foreach (FlatModuleCsDataType FlatCsModuleData in Target.FlatModuleCsData.Values)
                {
                    if (FlatCsModuleData.BuildCsFilename != null && FlatCsModuleData.ExternalDependencies.Count > 0)
                    {
                        string BaseDir = Path.GetDirectoryName(FlatCsModuleData.BuildCsFilename);
                        foreach (string ExternalDependency in FlatCsModuleData.ExternalDependencies)
                        {
                            FileInfo DependencyFile        = new FileInfo(Path.Combine(BaseDir, ExternalDependency));
                            bool     bDependencyFileExists = DependencyFile.Exists;
                            if (!bDependencyFileExists || DependencyFile.LastWriteTime > UBTMakefileInfo.LastWriteTime)
                            {
                                Log.TraceVerbose("{0} has been {1} since makefile was built, ignoring it ({2})", DependencyFile.FullName, bDependencyFileExists ? "changed" : "deleted", UBTMakefileInfo.FullName);
                                ReasonNotLoaded = string.Format("changes to external dependency");
                                return(null);
                            }
                        }
                    }
                }
            }

            // 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.
            foreach (UEBuildTarget Target in LoadedUBTMakefile.Targets)
            {
                // Get all H files in processed modules newer than the makefile itself
                HashSet <string> HFilesNewerThanMakefile =
                    new HashSet <string>(
                        Target.FlatModuleCsData
                        .SelectMany(x => x.Value.ModuleSourceFolder != null ? Directory.EnumerateFiles(x.Value.ModuleSourceFolder.FullName, "*.h", SearchOption.AllDirectories) : Enumerable.Empty <string>())
                        .Where(y => Directory.GetLastWriteTimeUtc(y) > UBTMakefileInfo.LastWriteTimeUtc)
                        .OrderBy(z => z).Distinct()
                        );

                // Get all H files in all modules processed in the last makefile build
                HashSet <string> AllUHTHeaders = new HashSet <string>(Target.FlatModuleCsData.Select(x => x.Value).SelectMany(x => x.UHTHeaderNames));

                // 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 (string FileName in AllUHTHeaders)
                {
                    if (!File.Exists(FileName))
                    {
                        Log.TraceVerbose("File processed by UHT was deleted ({0}); invalidating makefile", FileName);
                        ReasonNotLoaded = string.Format("UHT file was deleted");
                        return(null);
                    }
                }

                // 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
                foreach (string Filename in HFilesNewerThanMakefile)
                {
                    bool bContainsUHTData = CPPHeaders.DoesFileContainUObjects(Filename);
                    bool bWasProcessed    = AllUHTHeaders.Contains(Filename);
                    if (bContainsUHTData != bWasProcessed)
                    {
                        Log.TraceVerbose("{0} {1} contain UHT types and now {2} , ignoring it ({3})", Filename, bWasProcessed ? "used to" : "didn't", bWasProcessed ? "doesn't" : "does", UBTMakefileInfo.FullName);
                        ReasonNotLoaded = string.Format("new files with reflected types");
                        return(null);
                    }
                }
            }

            // 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.)
            if (LoadedUBTMakefile.bUseAdaptiveUnityBuild)
            {
                // Check if any source files in the working set no longer belong in it
                foreach (FileItem SourceFile in LoadedUBTMakefile.SourceFileWorkingSet)
                {
                    if (!WorkingSet.Contains(SourceFile.Location) && File.GetLastWriteTimeUtc(SourceFile.AbsolutePath) > UBTMakefileInfo.LastWriteTimeUtc)
                    {
                        Log.TraceVerbose("{0} was part of source working set and now is not; invalidating makefile ({1})", SourceFile.AbsolutePath, UBTMakefileInfo.FullName);
                        ReasonNotLoaded = string.Format("working set of source files changed");
                        return(null);
                    }
                }

                // Check if any source files that are eligible for being in the working set have been modified
                foreach (FileItem SourceFile in LoadedUBTMakefile.CandidateSourceFilesForWorkingSet)
                {
                    if (WorkingSet.Contains(SourceFile.Location) && File.GetLastWriteTimeUtc(SourceFile.AbsolutePath) > UBTMakefileInfo.LastWriteTimeUtc)
                    {
                        Log.TraceVerbose("{0} was part of source working set and now is not; invalidating makefile ({1})", SourceFile.AbsolutePath, UBTMakefileInfo.FullName);
                        ReasonNotLoaded = string.Format("working set of source files changed");
                        return(null);
                    }
                }
            }

            ReasonNotLoaded = null;
            return(LoadedUBTMakefile);
        }
Exemplo n.º 5
0
        public static void WriteDocumentation(Type RulesType, FileReference OutputFile)
        {
            // Get the path to the XML documentation
            FileReference InputDocumentationFile = new FileReference(Assembly.GetExecutingAssembly().Location).ChangeExtension(".xml");

            if (!FileReference.Exists(InputDocumentationFile))
            {
                throw new BuildException("Generated assembly documentation not found at {0}.", InputDocumentationFile);
            }

            // Get the current engine version for versioning the page
            BuildVersion Version;

            if (!BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version))
            {
                throw new BuildException("Unable to read the current build version");
            }

            // Read the documentation
            XmlDocument InputDocumentation = new XmlDocument();

            InputDocumentation.Load(InputDocumentationFile.FullName);

            // Filter the properties into read-only and read/write lists
            List <FieldInfo> ReadOnlyFields  = new List <FieldInfo>();
            List <FieldInfo> ReadWriteFields = new List <FieldInfo>();

            foreach (FieldInfo Field in RulesType.GetFields(BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.Public))
            {
                if (!Field.FieldType.IsClass || !Field.FieldType.Name.EndsWith("TargetRules"))
                {
                    if (Field.IsInitOnly)
                    {
                        ReadOnlyFields.Add(Field);
                    }
                    else
                    {
                        ReadWriteFields.Add(Field);
                    }
                }
            }

            // Make sure the output file is writable
            FileReference.MakeWriteable(OutputFile);

            // Generate the UDN documentation file
            using (StreamWriter Writer = new StreamWriter(OutputFile.FullName))
            {
                Writer.WriteLine("Availability: NoPublish");
                Writer.WriteLine("Title: Build Configuration Properties Page");
                Writer.WriteLine("Crumbs:");
                Writer.WriteLine("Description: This is a procedurally generated markdown page.");
                Writer.WriteLine("Version: {0}.{1}", Version.MajorVersion, Version.MinorVersion);
                Writer.WriteLine("");
                if (ReadOnlyFields.Count > 0)
                {
                    Writer.WriteLine("### Read-Only Properties");
                    Writer.WriteLine();
                    foreach (FieldInfo Field in ReadOnlyFields)
                    {
                        OutputField(InputDocumentation, Field, Writer);
                    }
                    Writer.WriteLine();
                }
                if (ReadWriteFields.Count > 0)
                {
                    Writer.WriteLine("### Read/Write Properties");
                    foreach (FieldInfo Field in ReadWriteFields)
                    {
                        OutputField(InputDocumentation, Field, Writer);
                    }
                    Writer.WriteLine("");
                }
            }

            // Success!
            Log.TraceInformation("Written documentation to {0}.", OutputFile);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Loads a makefile  from disk
        /// </summary>
        /// <param name="MakefilePath">Path to the makefile to load</param>
        /// <param name="ProjectFile">Path to the project file</param>
        /// <param name="Platform">Platform for this makefile</param>
        /// <param name="Arguments">Command line arguments for this target</param>
        /// <param name="ReasonNotLoaded">If the function returns null, this string will contain the reason why</param>
        /// <returns>The loaded makefile, or null if it failed for some reason.  On failure, the 'ReasonNotLoaded' variable will contain information about why</returns>
        public static TargetMakefile Load(FileReference MakefilePath, FileReference ProjectFile, UnrealTargetPlatform Platform, string[] Arguments, out string ReasonNotLoaded)
        {
            using (Timeline.ScopeEvent("Checking dependent timestamps"))
            {
                // Check the directory timestamp on the project files directory.  If the user has generated project files more recently than the makefile, then we need to consider the file to be out of date
                FileInfo MakefileInfo = new FileInfo(MakefilePath.FullName);
                if (!MakefileInfo.Exists)
                {
                    // Makefile doesn't even exist, so we won't bother loading it
                    ReasonNotLoaded = "no existing makefile";
                    return(null);
                }

                // Check the build version
                FileInfo BuildVersionFileInfo = new FileInfo(BuildVersion.GetDefaultFileName().FullName);
                if (BuildVersionFileInfo.Exists && MakefileInfo.LastWriteTime.CompareTo(BuildVersionFileInfo.LastWriteTime) < 0)
                {
                    Log.TraceLog("Existing makefile is older than Build.version, ignoring it");
                    ReasonNotLoaded = "Build.version is newer";
                    return(null);
                }

                // @todo ubtmake: This will only work if the directory timestamp actually changes with every single GPF.  Force delete existing files before creating new ones?  Eh... really we probably just want to delete + create a file in that folder
                //			-> UPDATE: Seems to work OK right now though on Windows platform, maybe due to GUID changes
                // @todo ubtmake: Some platforms may not save any files into this folder.  We should delete + generate a "touch" file to force the directory timestamp to be updated (or just check the timestamp file itself.  We could put it ANYWHERE, actually)

                // Installed Build doesn't need to check engine projects for outdatedness
                if (!UnrealBuildTool.IsEngineInstalled())
                {
                    if (DirectoryReference.Exists(ProjectFileGenerator.IntermediateProjectFilesPath))
                    {
                        DateTime EngineProjectFilesLastUpdateTime = new FileInfo(ProjectFileGenerator.ProjectTimestampFile).LastWriteTime;
                        if (MakefileInfo.LastWriteTime.CompareTo(EngineProjectFilesLastUpdateTime) < 0)
                        {
                            // Engine project files are newer than makefile
                            Log.TraceLog("Existing makefile is older than generated engine project files, ignoring it");
                            ReasonNotLoaded = "project files are newer";
                            return(null);
                        }
                    }
                }

                // Check the game project directory too
                if (ProjectFile != null)
                {
                    string   ProjectFilename = ProjectFile.FullName;
                    FileInfo ProjectFileInfo = new FileInfo(ProjectFilename);
                    if (!ProjectFileInfo.Exists || MakefileInfo.LastWriteTime.CompareTo(ProjectFileInfo.LastWriteTime) < 0)
                    {
                        // .uproject file is newer than makefile
                        Log.TraceLog("Makefile is older than .uproject file, ignoring it");
                        ReasonNotLoaded = ".uproject file is newer";
                        return(null);
                    }

                    DirectoryReference MasterProjectRelativePath        = ProjectFile.Directory;
                    string             GameIntermediateProjectFilesPath = Path.Combine(MasterProjectRelativePath.FullName, "Intermediate", "ProjectFiles");
                    if (Directory.Exists(GameIntermediateProjectFilesPath))
                    {
                        DateTime GameProjectFilesLastUpdateTime = new DirectoryInfo(GameIntermediateProjectFilesPath).LastWriteTime;
                        if (MakefileInfo.LastWriteTime.CompareTo(GameProjectFilesLastUpdateTime) < 0)
                        {
                            // Game project files are newer than makefile
                            Log.TraceLog("Makefile is older than generated game project files, ignoring it");
                            ReasonNotLoaded = "game project files are newer";
                            return(null);
                        }
                    }
                }

                // Check to see if UnrealBuildTool.exe was compiled more recently than the makefile
                DateTime UnrealBuildToolTimestamp = new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime;
                if (MakefileInfo.LastWriteTime.CompareTo(UnrealBuildToolTimestamp) < 0)
                {
                    // UnrealBuildTool.exe was compiled more recently than the makefile
                    Log.TraceLog("Makefile is older than UnrealBuildTool.exe, ignoring it");
                    ReasonNotLoaded = "UnrealBuildTool.exe is newer";
                    return(null);
                }

                // Check to see if any BuildConfiguration files have changed since the last build
                List <XmlConfig.InputFile> InputFiles = XmlConfig.FindInputFiles();
                foreach (XmlConfig.InputFile InputFile in InputFiles)
                {
                    FileInfo InputFileInfo = new FileInfo(InputFile.Location.FullName);
                    if (InputFileInfo.LastWriteTime > MakefileInfo.LastWriteTime)
                    {
                        Log.TraceLog("Makefile is older than BuildConfiguration.xml, ignoring it");
                        ReasonNotLoaded = "BuildConfiguration.xml is newer";
                        return(null);
                    }
                }
            }

            TargetMakefile Makefile;

            using (Timeline.ScopeEvent("Loading makefile"))
            {
                try
                {
                    using (BinaryArchiveReader Reader = new BinaryArchiveReader(MakefilePath))
                    {
                        int Version = Reader.ReadInt();
                        if (Version != CurrentVersion)
                        {
                            ReasonNotLoaded = "makefile version does not match";
                            return(null);
                        }
                        Makefile = new TargetMakefile(Reader);
                    }
                }
                catch (Exception Ex)
                {
                    Log.TraceWarning("Failed to read makefile: {0}", Ex.Message);
                    Log.TraceLog("Exception: {0}", Ex.ToString());
                    ReasonNotLoaded = "couldn't read existing makefile";
                    return(null);
                }
            }

            using (Timeline.ScopeEvent("Checking makefile validity"))
            {
                // Check if the arguments are different
                if (!Enumerable.SequenceEqual(Makefile.AdditionalArguments, Arguments))
                {
                    ReasonNotLoaded = "command line arguments changed";
                    return(null);
                }

                // Check if ini files are newer. Ini files contain build settings too.
                DirectoryReference ProjectDirectory = DirectoryReference.FromFile(ProjectFile);
                foreach (ConfigHierarchyType IniType in (ConfigHierarchyType[])Enum.GetValues(typeof(ConfigHierarchyType)))
                {
                    foreach (FileReference IniFilename in ConfigHierarchy.EnumerateConfigFileLocations(IniType, ProjectDirectory, Platform))
                    {
                        FileInfo IniFileInfo = new FileInfo(IniFilename.FullName);
                        if (IniFileInfo.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                        {
                            // Ini files are newer than makefile
                            ReasonNotLoaded = "ini files are newer than makefile";
                            return(null);
                        }
                    }
                }

                // Get the current build metadata from the platform
                string CurrentExternalMetadata = UEBuildPlatform.GetBuildPlatform(Platform).GetExternalBuildMetadata(ProjectFile);
                if (String.Compare(CurrentExternalMetadata, Makefile.ExternalMetadata, StringComparison.Ordinal) != 0)
                {
                    Log.TraceLog("Old metadata:\n", Makefile.ExternalMetadata);
                    Log.TraceLog("New metadata:\n", CurrentExternalMetadata);
                    ReasonNotLoaded = "build metadata has changed";
                    return(null);
                }
            }

            // The makefile is ok
            ReasonNotLoaded = null;
            return(Makefile);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Generates documentation files for the available settings, by merging the XML documentation from the compiler.
        /// </summary>
        /// <param name="OutputFile">The documentation file to write</param>
        public static void WriteDocumentation(FileReference OutputFile)
        {
            // Find all the configurable types
            List <Type> ConfigTypes = FindConfigurableTypes();

            // Find all the configurable fields from the given types
            Dictionary <string, Dictionary <string, FieldInfo> > CategoryToFields = new Dictionary <string, Dictionary <string, FieldInfo> >();

            FindConfigurableFields(ConfigTypes, CategoryToFields);

            // Get the path to the XML documentation
            FileReference InputDocumentationFile = new FileReference(Assembly.GetExecutingAssembly().Location).ChangeExtension(".xml");

            if (!FileReference.Exists(InputDocumentationFile))
            {
                throw new BuildException("Generated assembly documentation not found at {0}.", InputDocumentationFile);
            }

            // Get the current engine version for versioning the page
            BuildVersion Version;

            if (!BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version))
            {
                throw new BuildException("Unable to read the current build version");
            }

            // Read the documentation
            XmlDocument InputDocumentation = new XmlDocument();

            InputDocumentation.Load(InputDocumentationFile.FullName);

            // Make sure we can write to the output file
            FileReference.MakeWriteable(OutputFile);

            // Generate the UDN documentation file
            using (StreamWriter Writer = new StreamWriter(OutputFile.FullName))
            {
                Writer.WriteLine("Availability: NoPublish");
                Writer.WriteLine("Title: Build Configuration Properties Page");
                Writer.WriteLine("Crumbs:");
                Writer.WriteLine("Description: This is a procedurally generated markdown page.");
                Writer.WriteLine("Version: {0}.{1}", Version.MajorVersion, Version.MinorVersion);
                Writer.WriteLine("");

                foreach (KeyValuePair <string, Dictionary <string, FieldInfo> > CategoryPair in CategoryToFields)
                {
                    string CategoryName = CategoryPair.Key;
                    Writer.WriteLine("### {0}", CategoryName);
                    Writer.WriteLine();

                    Dictionary <string, FieldInfo> Fields = CategoryPair.Value;
                    foreach (KeyValuePair <string, FieldInfo> FieldPair in Fields)
                    {
                        string FieldName = FieldPair.Key;

                        FieldInfo Field = FieldPair.Value;
                        XmlNode   Node  = InputDocumentation.SelectSingleNode(String.Format("//member[@name='F:{0}.{1}']/summary", Field.DeclaringType.FullName, Field.Name));
                        if (Node != null)
                        {
                            // Reflow the comments into paragraphs, assuming that each paragraph will be separated by a blank line
                            List <string> Lines = new List <string>(Node.InnerText.Trim().Split('\n').Select(x => x.Trim()));
                            for (int Idx = Lines.Count - 1; Idx > 0; Idx--)
                            {
                                if (Lines[Idx - 1].Length > 0 && !Lines[Idx].StartsWith("*") && !Lines[Idx].StartsWith("-"))
                                {
                                    Lines[Idx - 1] += " " + Lines[Idx];
                                    Lines.RemoveAt(Idx);
                                }
                            }

                            // Write the result to the .udn file
                            if (Lines.Count > 0)
                            {
                                Writer.WriteLine("$ {0} : {1}", FieldName, Lines[0]);
                                for (int Idx = 1; Idx < Lines.Count; Idx++)
                                {
                                    if (Lines[Idx].StartsWith("*") || Lines[Idx].StartsWith("-"))
                                    {
                                        Writer.WriteLine("        * {0}", Lines[Idx].Substring(1).TrimStart());
                                    }
                                    else
                                    {
                                        Writer.WriteLine("    * {0}", Lines[Idx]);
                                    }
                                }
                                Writer.WriteLine();
                            }
                        }
                    }
                }
            }

            // Success!
            Log.TraceInformation("Written documentation to {0}.", OutputFile);
        }
        /// <summary>
        /// Constructor. Compiles a rules assembly from the given source files.
        /// </summary>
        /// <param name="Plugins">All the plugins included in this assembly</param>
        /// <param name="ModuleFiles">List of module files to compile</param>
        /// <param name="TargetFiles">List of target files to compile</param>
        /// <param name="ModuleFileToPluginInfo">Mapping of module file to the plugin that contains it</param>
        /// <param name="AssemblyFileName">The output path for the compiled assembly</param>
        /// <param name="Parent">The parent rules assembly</param>
        public RulesAssembly(IReadOnlyList <PluginInfo> Plugins, List <FileReference> ModuleFiles, List <FileReference> TargetFiles, Dictionary <FileReference, PluginInfo> ModuleFileToPluginInfo, FileReference AssemblyFileName, RulesAssembly Parent)
        {
            this.Plugins = Plugins;
            this.ModuleFileToPluginInfo = ModuleFileToPluginInfo;
            this.Parent = Parent;

            // Find all the source files
            List <FileReference> AssemblySourceFiles = new List <FileReference>();

            AssemblySourceFiles.AddRange(ModuleFiles);
            AssemblySourceFiles.AddRange(TargetFiles);

            // Compile the assembly
            if (AssemblySourceFiles.Count > 0)
            {
                List <string> PreprocessorDefines = new List <string>();
                PreprocessorDefines.Add("WITH_FORWARDED_MODULE_RULES_CTOR");
                PreprocessorDefines.Add("WITH_FORWARDED_TARGET_RULES_CTOR");

                // Define macros for the UE4 version, starting with 4.17
                BuildVersion Version;
                if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version))
                {
                    for (int MinorVersion = 17; MinorVersion <= Version.MinorVersion; MinorVersion++)
                    {
                        PreprocessorDefines.Add(String.Format("UE_4_{0}_OR_LATER", MinorVersion));
                    }
                }

                CompiledAssembly = DynamicCompilation.CompileAndLoadAssembly(AssemblyFileName, AssemblySourceFiles, PreprocessorDefines: PreprocessorDefines);
            }

            // Setup the module map
            foreach (FileReference ModuleFile in ModuleFiles)
            {
                string ModuleName = ModuleFile.GetFileNameWithoutAnyExtensions();
                if (!ModuleNameToModuleFile.ContainsKey(ModuleName))
                {
                    ModuleNameToModuleFile.Add(ModuleName, ModuleFile);
                }
            }

            // Setup the target map
            foreach (FileReference TargetFile in TargetFiles)
            {
                string TargetName = TargetFile.GetFileNameWithoutAnyExtensions();
                if (!TargetNameToTargetFile.ContainsKey(TargetName))
                {
                    TargetNameToTargetFile.Add(TargetName, TargetFile);
                }
            }

            // Write any deprecation warnings for methods overriden from a base with the [ObsoleteOverride] attribute. Unlike the [Obsolete] attribute, this ensures the message
            // is given because the method is implemented, not because it's called.
            if (CompiledAssembly != null)
            {
                foreach (Type CompiledType in CompiledAssembly.GetTypes())
                {
                    foreach (MethodInfo Method in CompiledType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
                    {
                        ObsoleteOverrideAttribute Attribute = Method.GetCustomAttribute <ObsoleteOverrideAttribute>(true);
                        if (Attribute != null)
                        {
                            FileReference Location;
                            if (!TryGetFileNameFromType(CompiledType, out Location))
                            {
                                Location = new FileReference(CompiledAssembly.Location);
                            }
                            Log.TraceWarning("{0}: warning: {1}", Location, Attribute.Message);
                        }
                    }
                    if (CompiledType.BaseType == typeof(ModuleRules))
                    {
                        ConstructorInfo Constructor = CompiledType.GetConstructor(new Type[] { typeof(TargetInfo) });
                        if (Constructor != null)
                        {
                            FileReference Location;
                            if (!TryGetFileNameFromType(CompiledType, out Location))
                            {
                                Location = new FileReference(CompiledAssembly.Location);
                            }
                            Log.TraceWarning("{0}: warning: Module constructors should take a ReadOnlyTargetRules argument (rather than a TargetInfo argument) and pass it to the base class constructor from 4.15 onwards. Please update the method signature.", Location);
                        }
                    }
                }
            }
        }