Exemplo n.º 1
0
        /// <summary>
        /// Read a receipt from disk.
        /// </summary>
        /// <param name="Location">Filename to read from</param>
        /// <param name="EngineDir">Engine directory for expanded variables</param>
        public static TargetReceipt Read(FileReference Location, DirectoryReference EngineDir)
        {
            JsonObject RawObject = JsonObject.Read(Location);

            // Read the initial fields
            string                    TargetName    = RawObject.GetStringField("TargetName");
            TargetType                TargetType    = RawObject.GetEnumField <TargetType>("TargetType");
            UnrealTargetPlatform      Platform      = UnrealTargetPlatform.Parse(RawObject.GetStringField("Platform"));
            UnrealTargetConfiguration Configuration = RawObject.GetEnumField <UnrealTargetConfiguration>("Configuration");

            // Try to read the build version
            BuildVersion Version;

            if (!BuildVersion.TryParse(RawObject.GetObjectField("Version"), out Version))
            {
                throw new JsonParseException("Invalid 'Version' field");
            }

            // Read the project path
            FileReference ProjectFile;

            string RelativeProjectFile;

            if (RawObject.TryGetStringField("Project", out RelativeProjectFile))
            {
                ProjectFile = FileReference.Combine(Location.Directory, RelativeProjectFile);
            }
            else
            {
                ProjectFile = null;
            }

            // Create the receipt
            TargetReceipt Receipt = new TargetReceipt(ProjectFile, TargetName, TargetType, Platform, Configuration, Version);

            // Get the project directory
            DirectoryReference ProjectDir = Receipt.ProjectDir;

            // Read the launch executable
            string Launch;

            if (RawObject.TryGetStringField("Launch", out Launch))
            {
                Receipt.Launch = ExpandPathVariables(Launch, EngineDir, ProjectDir);
            }

            // Read the build products
            JsonObject[] BuildProductObjects;
            if (RawObject.TryGetObjectArrayField("BuildProducts", out BuildProductObjects))
            {
                foreach (JsonObject BuildProductObject in BuildProductObjects)
                {
                    string           Path;
                    BuildProductType Type;
                    if (BuildProductObject.TryGetStringField("Path", out Path) && BuildProductObject.TryGetEnumField("Type", out Type))
                    {
                        FileReference File = ExpandPathVariables(Path, EngineDir, ProjectDir);

                        string Module;
                        BuildProductObject.TryGetStringField("Module", out Module);

                        Receipt.AddBuildProduct(File, Type);
                    }
                }
            }

            // Read the runtime dependencies
            JsonObject[] RuntimeDependencyObjects;
            if (RawObject.TryGetObjectArrayField("RuntimeDependencies", out RuntimeDependencyObjects))
            {
                foreach (JsonObject RuntimeDependencyObject in RuntimeDependencyObjects)
                {
                    string Path;
                    if (RuntimeDependencyObject.TryGetStringField("Path", out Path))
                    {
                        FileReference File = ExpandPathVariables(Path, EngineDir, ProjectDir);

                        StagedFileType Type;
                        if (!RuntimeDependencyObject.TryGetEnumField("Type", out Type))
                        {
                            // Previous format included an optional IgnoreIfMissing flag, which was only used for debug files. We can explicitly reference them as DebugNonUFS files now.
                            bool bIgnoreIfMissing;
                            if (RuntimeDependencyObject.TryGetBoolField("IgnoreIfMissing", out bIgnoreIfMissing))
                            {
                                bIgnoreIfMissing = false;
                            }
                            Type = bIgnoreIfMissing? StagedFileType.DebugNonUFS : StagedFileType.NonUFS;
                        }

                        Receipt.RuntimeDependencies.Add(File, Type);
                    }
                }
            }

            // Read the additional properties
            JsonObject[] AdditionalPropertyObjects;
            if (RawObject.TryGetObjectArrayField("AdditionalProperties", out AdditionalPropertyObjects))
            {
                foreach (JsonObject AdditionalPropertyObject in AdditionalPropertyObjects)
                {
                    string Name;
                    if (AdditionalPropertyObject.TryGetStringField("Name", out Name))
                    {
                        string Value;
                        if (AdditionalPropertyObject.TryGetStringField("Value", out Value))
                        {
                            Receipt.AdditionalProperties.Add(new ReceiptProperty(Name, Value));
                        }
                    }
                }
            }

            return(Receipt);
        }
Exemplo n.º 2
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="Inner">The writable build version instance</param>
 public ReadOnlyBuildVersion(BuildVersion Inner)
 {
     this.Inner = Inner;
 }
Exemplo n.º 3
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="InProjectFile">Path to the project file for this target</param>
 /// <param name="InTargetName">The name of the target being compiled</param>
 /// <param name="InTargetType">The type of target</param>
 /// <param name="InPlatform">Platform for the target being compiled</param>
 /// <param name="InConfiguration">Configuration of the target being compiled</param>
 /// <param name="InVersion">Version information for the target</param>
 public TargetReceipt(FileReference InProjectFile, string InTargetName, TargetType InTargetType, UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration, BuildVersion InVersion)
 {
     ProjectFile   = InProjectFile;
     ProjectDir    = DirectoryReference.FromFile(InProjectFile);
     TargetName    = InTargetName;
     Platform      = InPlatform;
     Configuration = InConfiguration;
     TargetType    = InTargetType;
     Version       = InVersion;
 }
Exemplo n.º 4
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);
        }
        /// <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.º 6
0
        /// <summary>
        /// Read a receipt from disk.
        /// </summary>
        /// <param name="FileName">Filename to read from</param>
        public static TargetReceipt Read(string FileName)
        {
            JsonObject RawObject = JsonObject.Read(FileName);

            // Read the initial fields
            string TargetName = RawObject.GetStringField("TargetName");
            UnrealTargetPlatform      Platform      = RawObject.GetEnumField <UnrealTargetPlatform>("Platform");
            UnrealTargetConfiguration Configuration = RawObject.GetEnumField <UnrealTargetConfiguration>("Configuration");
            string BuildId = RawObject.GetStringField("BuildId");

            // Try to read the build version
            BuildVersion Version;

            if (!BuildVersion.TryParse(RawObject.GetObjectField("Version"), out Version))
            {
                throw new JsonParseException("Invalid 'Version' field");
            }

            // Create the receipt
            TargetReceipt Receipt = new TargetReceipt(TargetName, Platform, Configuration, BuildId, Version);

            // Read the build products
            JsonObject[] BuildProductObjects;
            if (RawObject.TryGetObjectArrayField("BuildProducts", out BuildProductObjects))
            {
                foreach (JsonObject BuildProductObject in BuildProductObjects)
                {
                    string           Path;
                    BuildProductType Type;
                    if (BuildProductObject.TryGetStringField("Path", out Path) && BuildProductObject.TryGetEnumField("Type", out Type))
                    {
                        string Module;
                        BuildProductObject.TryGetStringField("Module", out Module);

                        BuildProduct NewBuildProduct = Receipt.AddBuildProduct(Path, Type);

                        bool IsPrecompiled;
                        if (BuildProductObject.TryGetBoolField("IsPrecompiled", out IsPrecompiled))
                        {
                            NewBuildProduct.IsPrecompiled = IsPrecompiled;
                        }
                    }
                }
            }

            // Read the runtime dependencies
            JsonObject[] RuntimeDependencyObjects;
            if (RawObject.TryGetObjectArrayField("RuntimeDependencies", out RuntimeDependencyObjects))
            {
                foreach (JsonObject RuntimeDependencyObject in RuntimeDependencyObjects)
                {
                    string Path;
                    if (RuntimeDependencyObject.TryGetStringField("Path", out Path))
                    {
                        StagedFileType Type;
                        if (!RuntimeDependencyObject.TryGetEnumField("Type", out Type))
                        {
                            // Previous format included an optional IgnoreIfMissing flag, which was only used for debug files. We can explicitly reference them as DebugNonUFS files now.
                            bool bIgnoreIfMissing;
                            if (RuntimeDependencyObject.TryGetBoolField("IgnoreIfMissing", out bIgnoreIfMissing))
                            {
                                bIgnoreIfMissing = false;
                            }
                            Type = bIgnoreIfMissing? StagedFileType.DebugNonUFS : StagedFileType.NonUFS;
                        }
                        Receipt.RuntimeDependencies.Add(Path, Type);
                    }
                }
            }

            // Read the additional properties
            JsonObject[] AdditionalPropertyObjects;
            if (RawObject.TryGetObjectArrayField("AdditionalProperties", out AdditionalPropertyObjects))
            {
                foreach (JsonObject AdditionalPropertyObject in AdditionalPropertyObjects)
                {
                    string Name;
                    if (AdditionalPropertyObject.TryGetStringField("Name", out Name))
                    {
                        string Value;
                        if (AdditionalPropertyObject.TryGetStringField("Value", out Value))
                        {
                            Receipt.AdditionalProperties.Add(new ReceiptProperty(Name, Value));
                        }
                    }
                }
            }

            // Read the precompiled dependencies
            string[] PrecompiledBuildDependencies;
            if (RawObject.TryGetStringArrayField("PrecompiledBuildDependencies", out PrecompiledBuildDependencies))
            {
                Receipt.PrecompiledBuildDependencies.UnionWith(PrecompiledBuildDependencies);
            }

            // Read the precompiled dependencies
            string[] PrecompiledRuntimeDependencies;
            if (RawObject.TryGetStringArrayField("PrecompiledRuntimeDependencies", out PrecompiledRuntimeDependencies))
            {
                Receipt.PrecompiledRuntimeDependencies.UnionWith(PrecompiledRuntimeDependencies);
            }

            return(Receipt);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Parse a list of target descriptors from the command line
        /// </summary>
        /// <param name="Arguments">Command-line arguments</param>
        /// <param name="ProjectFile">The project file, if already set. May be updated if not.</param>
        /// <returns>List of target descriptors</returns>
        public static List <TargetDescriptor> ParseCommandLine(string[] Arguments, ref FileReference ProjectFile)
        {
            UnrealTargetPlatform      Platform      = UnrealTargetPlatform.Unknown;
            UnrealTargetConfiguration Configuration = UnrealTargetConfiguration.Unknown;
            List <string>             TargetNames   = new List <string>();
            List <TargetType>         TargetTypes   = new List <TargetType>();
            string            Architecture          = null;
            List <OnlyModule> OnlyModules           = new List <OnlyModule>();
            FileReference     ForeignPlugin         = null;
            string            ForceReceiptFileName  = null;

            // Settings for creating/using static libraries for the engine
            for (int ArgumentIndex = 0; ArgumentIndex < Arguments.Length; ArgumentIndex++)
            {
                string Argument = Arguments[ArgumentIndex];
                if (!Argument.StartsWith("-"))
                {
                    UnrealTargetPlatform ParsedPlatform;
                    if (Enum.TryParse(Argument, true, out ParsedPlatform) && ParsedPlatform != UnrealTargetPlatform.Unknown)
                    {
                        if (Platform != UnrealTargetPlatform.Unknown)
                        {
                            throw new BuildException("Multiple platforms specified on command line (first {0}, then {1})", Platform, ParsedPlatform);
                        }
                        Platform = ParsedPlatform;
                        continue;
                    }

                    UnrealTargetConfiguration ParsedConfiguration;
                    if (Enum.TryParse(Argument, true, out ParsedConfiguration) && ParsedConfiguration != UnrealTargetConfiguration.Unknown)
                    {
                        if (Configuration != UnrealTargetConfiguration.Unknown)
                        {
                            throw new BuildException("Multiple configurations specified on command line (first {0}, then {1})", Configuration, ParsedConfiguration);
                        }
                        Configuration = ParsedConfiguration;
                        continue;
                    }

                    // Make sure the target name is valid. It may be the path to a project file.
                    if (Argument.IndexOfAny(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, '.' }) == -1)
                    {
                        TargetNames.Add(Argument);
                    }
                }
                else
                {
                    string Value;
                    if (ParseArgumentValue(Argument, "-TargetType=", out Value))
                    {
                        TargetType Type;
                        if (!Enum.TryParse(Value, true, out Type))
                        {
                            throw new BuildException("Invalid target type: '{0}'", Value);
                        }
                        TargetTypes.Add(Type);
                    }
                    else if (ParseArgumentValue(Argument, "-Module=", out Value))
                    {
                        OnlyModules.Add(new OnlyModule(Value));
                    }
                    else if (ParseArgumentValue(Argument, "-ModuleWithSuffix=", out Value))
                    {
                        int SuffixIdx = Value.LastIndexOf(',');
                        if (SuffixIdx == -1)
                        {
                            throw new BuildException("Missing suffix argument from -ModuleWithSuffix=Name,Suffix");
                        }
                        OnlyModules.Add(new OnlyModule(Value.Substring(0, SuffixIdx), Value.Substring(SuffixIdx + 1)));
                    }
                    else if (ParseArgumentValue(Argument, "-Plugin=", out Value))
                    {
                        if (ForeignPlugin != null)
                        {
                            throw new BuildException("Only one foreign plugin to compile may be specified per invocation");
                        }
                        ForeignPlugin = new FileReference(Value);
                    }
                    else if (ParseArgumentValue(Argument, "-Receipt=", out Value))
                    {
                        ForceReceiptFileName = Value;
                    }
                    else
                    {
                        switch (Arguments[ArgumentIndex].ToUpperInvariant())
                        {
                        case "-MODULE":
                            throw new BuildException("'-Module <Name>' syntax is no longer supported on the command line. Use '-Module=<Name>' instead.");

                        case "-MODULEWITHSUFFIX":
                            throw new BuildException("'-ModuleWithSuffix <Name> <Suffix>' syntax is no longer supported on the command line. Use '-Module=<Name>,<Suffix>' instead.");

                        case "-PLUGIN":
                            throw new BuildException("'-Plugin <Path>' syntax is no longer supported on the command line. Use '-Plugin=<Path>' instead.");

                        case "-RECEIPT":
                            throw new BuildException("'-Receipt <Path>' syntax is no longer supported on the command line. Use '-Receipt=<Path>' instead.");
                        }
                    }
                }
            }

            if (Platform == UnrealTargetPlatform.Unknown)
            {
                throw new BuildException("Couldn't find platform name.");
            }
            if (Configuration == UnrealTargetConfiguration.Unknown)
            {
                throw new BuildException("Couldn't determine configuration name.");
            }

            if (Architecture == null)
            {
                Architecture = UEBuildPlatform.GetBuildPlatform(Platform).GetDefaultArchitecture(ProjectFile);
            }

            // Create all the target descriptors for targets specified by type
            foreach (TargetType Type in TargetTypes)
            {
                if (ProjectFile == null)
                {
                    throw new BuildException("-TargetType=... requires a project file to be specified");
                }
                else
                {
                    TargetNames.Add(RulesCompiler.CreateProjectRulesAssembly(ProjectFile).GetTargetNameByType(Type, Platform, Configuration, Architecture, ProjectFile, new ReadOnlyBuildVersion(BuildVersion.ReadDefault())));
                }
            }

            // Create all the target descriptor
            List <TargetDescriptor> Targets = new List <TargetDescriptor>();

            foreach (string TargetName in TargetNames)
            {
                // If a project file was not specified see if we can find one
                if (ProjectFile == null && UProjectInfo.TryGetProjectForTarget(TargetName, out ProjectFile))
                {
                    Log.TraceVerbose("Found project file for {0} - {1}", TargetName, ProjectFile);
                }

                TargetDescriptor Target = new TargetDescriptor(ProjectFile, TargetName, Platform, Configuration, Architecture);
                Target.OnlyModules          = OnlyModules;
                Target.ForeignPlugin        = ForeignPlugin;
                Target.ForceReceiptFileName = ForceReceiptFileName;
                Targets.Add(Target);
            }

            // Make sure we could parse something
            if (Targets.Count == 0)
            {
                throw new BuildException("No target name was specified on the command-line.");
            }
            return(Targets);
        }
Exemplo n.º 8
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.º 9
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="InTargetName">The name of the target being compiled</param>
 /// <param name="InPlatform">Platform for the target being compiled</param>
 /// <param name="InConfiguration">Configuration of the target being compiled</param>
 public TargetReceipt(string InTargetName, UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration, string InBuildId, BuildVersion InVersion)
 {
     TargetName    = InTargetName;
     Platform      = InPlatform;
     Configuration = InConfiguration;
     BuildId       = InBuildId;
     Version       = InVersion;
 }
Exemplo n.º 10
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);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Execute the command, having obtained the appropriate mutex
        /// </summary>
        /// <param name="Arguments">Command line arguments</param>
        /// <returns>Exit code</returns>
        private int ExecuteInternal(CommandLineArguments Arguments)
        {
            // Read the target info
            WriteMetadataTargetInfo TargetInfo = BinaryFormatterUtils.Load <WriteMetadataTargetInfo>(Arguments.GetFileReference("-Input="));
            bool bNoManifestChanges            = Arguments.HasOption("-NoManifestChanges");
            int  VersionNumber = Arguments.GetInteger("-Version=");

            Arguments.CheckAllArgumentsUsed();

            // Make sure the version number is correct
            if (VersionNumber != CurrentVersionNumber)
            {
                throw new BuildException("Version number to WriteMetadataMode is incorrect (expected {0}, got {1})", CurrentVersionNumber, VersionNumber);
            }

            // Check if we need to set a build id
            TargetReceipt Receipt = TargetInfo.Receipt;

            if (String.IsNullOrEmpty(Receipt.Version.BuildId))
            {
                // Check if there's an existing version file. If it exists, try to merge in any manifests that are valid (and reuse the existing build id)
                BuildVersion PreviousVersion;
                if (TargetInfo.VersionFile != null && BuildVersion.TryRead(TargetInfo.VersionFile, out PreviousVersion))
                {
                    // Check if we can reuse the existing manifests. This prevents unnecessary builds when switching between projects.
                    Dictionary <FileReference, ModuleManifest> PreviousFileToManifest = new Dictionary <FileReference, ModuleManifest>();
                    if (TryRecyclingManifests(PreviousVersion.BuildId, TargetInfo.FileToManifest.Keys, PreviousFileToManifest))
                    {
                        // Merge files from the existing manifests with the new ones
                        foreach (KeyValuePair <FileReference, ModuleManifest> Pair in PreviousFileToManifest)
                        {
                            ModuleManifest TargetManifest = TargetInfo.FileToManifest[Pair.Key];
                            MergeManifests(Pair.Value, TargetManifest);
                        }

                        // Update the build id to use the current one
                        Receipt.Version.BuildId = PreviousVersion.BuildId;
                    }
                }

                // If the build id is still not set, generate a new one from a GUID
                if (String.IsNullOrEmpty(Receipt.Version.BuildId))
                {
                    Receipt.Version.BuildId = Guid.NewGuid().ToString();
                }
            }
            else
            {
                // Read all the manifests and merge them into the new ones, if they have the same build id
                foreach (KeyValuePair <FileReference, ModuleManifest> Pair in TargetInfo.FileToManifest)
                {
                    ModuleManifest SourceManifest;
                    if (TryReadManifest(Pair.Key, out SourceManifest) && SourceManifest.BuildId == Receipt.Version.BuildId)
                    {
                        MergeManifests(SourceManifest, Pair.Value);
                    }
                }
            }

            // Update the build id in all the manifests, and write them out
            foreach (KeyValuePair <FileReference, ModuleManifest> Pair in TargetInfo.FileToManifest)
            {
                FileReference ManifestFile = Pair.Key;
                if (!UnrealBuildTool.IsFileInstalled(ManifestFile))
                {
                    ModuleManifest Manifest = Pair.Value;
                    Manifest.BuildId = Receipt.Version.BuildId;

                    if (!FileReference.Exists(ManifestFile))
                    {
                        // If the file doesn't already exist, just write it out
                        DirectoryReference.CreateDirectory(ManifestFile.Directory);
                        Manifest.Write(ManifestFile);
                    }
                    else
                    {
                        // Otherwise write it to a buffer first
                        string OutputText;
                        using (StringWriter Writer = new StringWriter())
                        {
                            Manifest.Write(Writer);
                            OutputText = Writer.ToString();
                        }

                        // And only write it to disk if it's been modified. Note that if a manifest is out of date, we should have generated a new build id causing the contents to differ.
                        string CurrentText = FileReference.ReadAllText(ManifestFile);
                        if (CurrentText != OutputText)
                        {
                            if (bNoManifestChanges)
                            {
                                Log.TraceError("Build modifies {0}. This is not permitted. Before:\n    {1}\nAfter:\n    {2}", ManifestFile, CurrentText.Replace("\n", "\n    "), OutputText.Replace("\n", "\n    "));
                            }
                            else
                            {
                                FileReference.WriteAllText(ManifestFile, OutputText);
                            }
                        }
                    }
                }
            }

            // Write out the version file, if it's changed. Since this file is next to the executable, it may be used by multiple targets, and we should avoid modifying it unless necessary.
            if (TargetInfo.VersionFile != null && !UnrealBuildTool.IsFileInstalled(TargetInfo.VersionFile))
            {
                DirectoryReference.CreateDirectory(TargetInfo.VersionFile.Directory);

                StringWriter Writer = new StringWriter();
                Receipt.Version.Write(Writer);

                string Text = Writer.ToString();
                if (!FileReference.Exists(TargetInfo.VersionFile) || File.ReadAllText(TargetInfo.VersionFile.FullName) != Text)
                {
                    File.WriteAllText(TargetInfo.VersionFile.FullName, Text);
                }
            }

            // Write out the receipt
            if (!UnrealBuildTool.IsFileInstalled(TargetInfo.ReceiptFile))
            {
                DirectoryReference.CreateDirectory(TargetInfo.ReceiptFile.Directory);
                Receipt.Write(TargetInfo.ReceiptFile);
            }

            return(0);
        }
        /// <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);
                        }
                    }
                }
            }
        }
Exemplo n.º 13
0
 /// <summary>
 /// Try to read a version file from disk
 /// </summary>
 /// <param name="Version">The version information</param>
 /// <returns>True if the version was read sucessfully, false otherwise</returns>
 public static bool TryRead(out BuildVersion Version)
 {
     return(TryRead(GetDefaultFileName(), out Version));
 }
Exemplo n.º 14
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="ProjectFile"></param>
        /// <param name="Executable"></param>
        /// <param name="StageDirectory"></param>
        /// <param name="PlatformType"></param>
        public static void GenerateAssetCatalog(FileReference ProjectFile, string Executable, string StageDirectory, UnrealTargetPlatform PlatformType)
        {
            // Initialize the toolchain.
            IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(PlatformType)).ReadProjectSettings(null);
            IOSToolChain       ToolChain       = new IOSToolChain(ProjectFile, ProjectSettings);

            // Determine whether the user has modified icons that require a remote Mac to build.
            CppPlatform Platform         = PlatformType == UnrealTargetPlatform.IOS ? CppPlatform.IOS : CppPlatform.TVOS;
            bool        bUserImagesExist = false;

            ToolChain.GenerateAssetCatalog(Platform, ref bUserImagesExist);

            // Don't attempt to do anything remotely if the user is using the default UE4 images.
            if (!bUserImagesExist)
            {
                return;
            }

            // Also don't attempt to use a remote Mac if packaging for TVOS on PC.
            if (Platform == CppPlatform.TVOS && BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
            {
                return;
            }

            // Save off the current bUseRPCUtil setting to restore at the end of this function.
            // At this time, iPhonePackager needs to be called with bUseRPCUtil == true.
            bool bSaveUseRPCUtil = RemoteToolChain.bUseRPCUtil;

            // Initialize the remote calling environment, taking into account the user's SSH setting.
            ToolChain.SetUpGlobalEnvironment(false);

            // Build the asset catalog ActionGraph.
            ActionGraph     ActionGraph = new ActionGraph();
            List <FileItem> OutputFiles = new List <FileItem>();

            ToolChain.CompileAssetCatalog(FileItem.GetItemByPath(Executable), Platform, ActionGraph, OutputFiles);

            ActionGraph.FinalizeActionGraph();

            // I'm not sure how to derive the UE4Game and Development arguments programmatically.
            string[] Arguments = new string[] { "UE4Game", (PlatformType == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"), "Development", "-UniqueBuildEnvironment" };

            // Perform all of the setup necessary to actually execute the ActionGraph instance.
            ReadOnlyBuildVersion Version        = new ReadOnlyBuildVersion(BuildVersion.ReadDefault());
            List <string[]>      TargetSettings = new List <string[]>();

            TargetSettings.Add(Arguments);
            var Targets = new List <UEBuildTarget>();
            Dictionary <UEBuildTarget, CPPHeaders> TargetToHeaders = new Dictionary <UEBuildTarget, CPPHeaders>();
            List <TargetDescriptor> TargetDescs = new List <TargetDescriptor>();

            foreach (string[] TargetSetting in TargetSettings)
            {
                TargetDescs.AddRange(TargetDescriptor.ParseCommandLine(TargetSetting, ref ProjectFile));
            }
            foreach (TargetDescriptor TargetDesc in TargetDescs)
            {
                UEBuildTarget Target = UEBuildTarget.CreateTarget(TargetDesc, Arguments, false, Version);
                if (Target == null)
                {
                    continue;
                }
                Targets.Add(Target);
                TargetToHeaders.Add(Target, null);
            }

            bool bIsRemoteCompile = BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac;

            // Create the build configuration object, and read the settings
            BuildConfiguration BuildConfiguration = new BuildConfiguration();

            XmlConfig.ApplyTo(BuildConfiguration);
            CommandLine.ParseArguments(Arguments, BuildConfiguration);
            BuildConfiguration.bUseUBTMakefiles = false;

            Action[] PrerequisiteActions;
            {
                HashSet <Action> PrerequisiteActionsSet = new HashSet <Action>();
                foreach (FileItem OutputFile in OutputFiles)
                {
                    ActionGraph.GatherPrerequisiteActions(OutputFile, ref PrerequisiteActionsSet);
                }
                PrerequisiteActions = PrerequisiteActionsSet.ToArray();
            }

            // Copy any asset catalog files to the remote Mac, if necessary.
            foreach (UEBuildTarget Target in Targets)
            {
                UEBuildPlatform.GetBuildPlatform(Target.Platform).PreBuildSync();
            }

            // Begin execution of the ActionGraph.
            Dictionary <UEBuildTarget, List <FileItem> > TargetToOutdatedPrerequisitesMap;
            List <Action> ActionsToExecute = ActionGraph.GetActionsToExecute(BuildConfiguration, PrerequisiteActions, Targets, TargetToHeaders, true, true, out TargetToOutdatedPrerequisitesMap);
            string        ExecutorName     = "Unknown";
            bool          bSuccess         = ActionGraph.ExecuteActions(BuildConfiguration, ActionsToExecute, bIsRemoteCompile, out ExecutorName, "", EHotReload.Disabled);

            if (bSuccess)
            {
                if (bIsRemoteCompile)
                {
                    // Copy the remotely built AssetCatalog directory locally.
                    foreach (FileItem OutputFile in OutputFiles)
                    {
                        string   RemoteDirectory = System.IO.Path.GetDirectoryName(OutputFile.AbsolutePath).Replace("\\", "/");
                        FileItem LocalExecutable = ToolChain.RemoteToLocalFileItem(FileItem.GetItemByPath(Executable));
                        string   LocalDirectory  = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(LocalExecutable.AbsolutePath), "AssetCatalog");
                        LocalDirectory = StageDirectory;
                        RPCUtilHelper.CopyDirectory(RemoteDirectory, LocalDirectory, RPCUtilHelper.ECopyOptions.DoNotReplace);
                    }
                }
                else
                {
                    // Copy the built AssetCatalog directory to the StageDirectory.
                    foreach (FileItem OutputFile in OutputFiles)
                    {
                        string SourceDirectory = System.IO.Path.GetDirectoryName(OutputFile.AbsolutePath).Replace("\\", "/");
                        System.IO.DirectoryInfo SourceDirectoryInfo = new System.IO.DirectoryInfo(SourceDirectory);
                        if (!System.IO.Directory.Exists(StageDirectory))
                        {
                            System.IO.Directory.CreateDirectory(StageDirectory);
                        }
                        System.IO.FileInfo[] SourceFiles = SourceDirectoryInfo.GetFiles();
                        foreach (System.IO.FileInfo SourceFile in SourceFiles)
                        {
                            string DestinationPath = System.IO.Path.Combine(StageDirectory, SourceFile.Name);
                            SourceFile.CopyTo(DestinationPath, true);
                        }
                    }
                }
            }

            // Restore the former bUseRPCUtil setting.
            RemoteToolChain.bUseRPCUtil = bSaveUseRPCUtil;
        }
Exemplo n.º 15
0
        /// <summary>
        /// Build a target remotely
        /// </summary>
        /// <param name="TargetDesc">Descriptor for the target to build</param>
        /// <param name="RemoteLogFile">Path to store the remote log file</param>
        /// <returns>True if the build succeeded, false otherwise</returns>
        public bool Build(TargetDescriptor TargetDesc, FileReference RemoteLogFile)
        {
            // Get the directory for working files
            DirectoryReference BaseDir = DirectoryReference.FromFile(TargetDesc.ProjectFile) ?? UnrealBuildTool.EngineDirectory;
            DirectoryReference TempDir = DirectoryReference.Combine(BaseDir, "Intermediate", "Remote", TargetDesc.Name, TargetDesc.Platform.ToString(), TargetDesc.Configuration.ToString());

            DirectoryReference.CreateDirectory(TempDir);

            bool bLogIsMapped = false;

            foreach (RemoteMapping Mapping in Mappings)
            {
                if (RemoteLogFile.Directory.FullName.Equals(Mapping.LocalDirectory.FullName, StringComparison.InvariantCultureIgnoreCase))
                {
                    bLogIsMapped = true;
                    break;
                }
            }
            if (!bLogIsMapped)
            {
                Mappings.Add(new RemoteMapping(RemoteLogFile.Directory, GetRemotePath(RemoteLogFile.Directory)));
            }

            // Compile the rules assembly
            RulesAssembly RulesAssembly = RulesCompiler.CreateTargetRulesAssembly(TargetDesc.ProjectFile, TargetDesc.Name, false, false, TargetDesc.ForeignPlugin);

            // Create the target rules
            TargetRules Rules = RulesAssembly.CreateTargetRules(TargetDesc.Name, TargetDesc.Platform, TargetDesc.Configuration, TargetDesc.Architecture, TargetDesc.ProjectFile, new ReadOnlyBuildVersion(BuildVersion.ReadDefault()), new string[0]);

            // Check if we need to enable a nativized plugin, and compile the assembly for that if we do
            FileReference NativizedPluginFile = Rules.GetNativizedPlugin();

            if (NativizedPluginFile != null)
            {
                RulesAssembly = RulesCompiler.CreatePluginRulesAssembly(NativizedPluginFile, false, RulesAssembly, false);
            }

            // Path to the local manifest file. This has to be translated from the remote format after the build is complete.
            List <FileReference> LocalManifestFiles = new List <FileReference>();

            // Path to the remote manifest file
            FileReference RemoteManifestFile = FileReference.Combine(TempDir, "Manifest.xml");

            // Prepare the arguments we will pass to the remote build
            List <string> RemoteArguments = new List <string>();

            RemoteArguments.Add(TargetDesc.Name);
            RemoteArguments.Add(TargetDesc.Platform.ToString());
            RemoteArguments.Add(TargetDesc.Configuration.ToString());
            RemoteArguments.Add("-SkipRulesCompile");                                                      // Use the rules assembly built locally
            RemoteArguments.Add(String.Format("-XmlConfigCache={0}", GetRemotePath(XmlConfig.CacheFile))); // Use the XML config cache built locally, since the remote won't have it
            RemoteArguments.Add(String.Format("-Log={0}", GetRemotePath(RemoteLogFile)));
            RemoteArguments.Add(String.Format("-Manifest={0}", GetRemotePath(RemoteManifestFile)));

            if (TargetDesc.ProjectFile != null)
            {
                RemoteArguments.Add(String.Format("-Project={0}", GetRemotePath(TargetDesc.ProjectFile)));
            }

            foreach (string LocalArgument in TargetDesc.AdditionalArguments)
            {
                int EqualsIdx = LocalArgument.IndexOf('=');
                if (EqualsIdx == -1)
                {
                    RemoteArguments.Add(LocalArgument);
                    continue;
                }

                string Key   = LocalArgument.Substring(0, EqualsIdx);
                string Value = LocalArgument.Substring(EqualsIdx + 1);

                if (Key.Equals("-Log", StringComparison.InvariantCultureIgnoreCase))
                {
                    // We are already writing to the local log file. The remote will produce a different log (RemoteLogFile)
                    continue;
                }
                if (Key.Equals("-Manifest", StringComparison.InvariantCultureIgnoreCase))
                {
                    LocalManifestFiles.Add(new FileReference(Value));
                    continue;
                }

                string RemoteArgument = LocalArgument;
                foreach (RemoteMapping Mapping in Mappings)
                {
                    if (Value.StartsWith(Mapping.LocalDirectory.FullName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        RemoteArgument = String.Format("{0}={1}", Key, GetRemotePath(Value));
                        break;
                    }
                }
                RemoteArguments.Add(RemoteArgument);
            }

            // Handle any per-platform setup that is required
            if (TargetDesc.Platform == UnrealTargetPlatform.IOS || TargetDesc.Platform == UnrealTargetPlatform.TVOS)
            {
                // Always generate a .stub
                RemoteArguments.Add("-CreateStub");

                // Cannot use makefiles, since we need PostBuildSync() to generate the IPA (and that requires a TargetRules instance)
                RemoteArguments.Add("-NoUBTMakefiles");

                // Get the provisioning data for this project
                IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(TargetDesc.Platform)).ReadProvisioningData(TargetDesc.ProjectFile);
                if (ProvisioningData == null || ProvisioningData.MobileProvisionFile == null)
                {
                    throw new BuildException("Unable to find mobile provision for {0}. See log for more information.", TargetDesc.Name);
                }

                // Create a local copy of the provision
                FileReference MobileProvisionFile = FileReference.Combine(TempDir, ProvisioningData.MobileProvisionFile.GetFileName());
                if (FileReference.Exists(MobileProvisionFile))
                {
                    FileReference.SetAttributes(MobileProvisionFile, FileAttributes.Normal);
                }
                FileReference.Copy(ProvisioningData.MobileProvisionFile, MobileProvisionFile, true);
                Log.TraceInformation("[Remote] Uploading {0}", MobileProvisionFile);
                UploadFile(MobileProvisionFile);

                // Extract the certificate for the project. Try to avoid calling IPP if we already have it.
                FileReference CertificateFile = FileReference.Combine(TempDir, "Certificate.p12");

                FileReference CertificateInfoFile     = FileReference.Combine(TempDir, "Certificate.txt");
                string        CertificateInfoContents = String.Format("{0}\n{1}", ProvisioningData.MobileProvisionFile, FileReference.GetLastWriteTimeUtc(ProvisioningData.MobileProvisionFile).Ticks);

                if (!FileReference.Exists(CertificateFile) || !FileReference.Exists(CertificateInfoFile) || FileReference.ReadAllText(CertificateInfoFile) != CertificateInfoContents)
                {
                    Log.TraceInformation("[Remote] Exporting certificate for {0}...", ProvisioningData.MobileProvisionFile);

                    StringBuilder Arguments = new StringBuilder("ExportCertificate");
                    if (TargetDesc.ProjectFile == null)
                    {
                        Arguments.AppendFormat(" \"{0}\"", UnrealBuildTool.EngineSourceDirectory);
                    }
                    else
                    {
                        Arguments.AppendFormat(" \"{0}\"", TargetDesc.ProjectFile.Directory);
                    }
                    Arguments.AppendFormat(" -provisionfile \"{0}\"", ProvisioningData.MobileProvisionFile);
                    Arguments.AppendFormat(" -outputcertificate \"{0}\"", CertificateFile);
                    if (TargetDesc.Platform == UnrealTargetPlatform.TVOS)
                    {
                        Arguments.Append(" -tvos");
                    }

                    ProcessStartInfo StartInfo = new ProcessStartInfo();
                    StartInfo.FileName  = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "DotNET", "IOS", "IPhonePackager.exe").FullName;
                    StartInfo.Arguments = Arguments.ToString();
                    if (Utils.RunLocalProcessAndLogOutput(StartInfo) != 0)
                    {
                        throw new BuildException("IphonePackager failed.");
                    }

                    FileReference.WriteAllText(CertificateInfoFile, CertificateInfoContents);
                }

                // Upload the certificate to the remote
                Log.TraceInformation("[Remote] Uploading {0}", CertificateFile);
                UploadFile(CertificateFile);

                // Tell the remote UBT instance to use them
                RemoteArguments.Add(String.Format("-ImportProvision={0}", GetRemotePath(MobileProvisionFile)));
                RemoteArguments.Add(String.Format("-ImportCertificate={0}", GetRemotePath(CertificateFile)));
                RemoteArguments.Add(String.Format("-ImportCertificatePassword=A"));
            }

            // Upload the workspace files
            UploadWorkspace(TempDir);

            // Fixup permissions on any shell scripts
            Execute(RemoteBaseDir, String.Format("chmod +x {0}/Build/BatchFiles/Mac/*.sh", EscapeShellArgument(GetRemotePath(UnrealBuildTool.EngineDirectory))));

            // Execute the compile
            Log.TraceInformation("[Remote] Executing build");

            StringBuilder BuildCommandLine = new StringBuilder("Engine/Build/BatchFiles/Mac/Build.sh");

            foreach (string RemoteArgument in RemoteArguments)
            {
                BuildCommandLine.AppendFormat(" {0}", EscapeShellArgument(RemoteArgument));
            }

            int Result = Execute(GetRemotePath(UnrealBuildTool.RootDirectory), BuildCommandLine.ToString());

            if (Result != 0)
            {
                if (RemoteLogFile != null)
                {
                    Log.TraceInformation("[Remote] Downloading {0}", RemoteLogFile);
                    DownloadFile(RemoteLogFile);
                }
                return(false);
            }

            // Download the manifest
            Log.TraceInformation("[Remote] Downloading {0}", RemoteManifestFile);
            DownloadFile(RemoteManifestFile);

            // Convert the manifest to local form
            BuildManifest Manifest = Utils.ReadClass <BuildManifest>(RemoteManifestFile.FullName);

            for (int Idx = 0; Idx < Manifest.BuildProducts.Count; Idx++)
            {
                Manifest.BuildProducts[Idx] = GetLocalPath(Manifest.BuildProducts[Idx]).FullName;
            }

            // Download the files from the remote
            if (TargetDesc.AdditionalArguments.Any(x => x.Equals("-GenerateManifest", StringComparison.InvariantCultureIgnoreCase)))
            {
                LocalManifestFiles.Add(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "Build", "Manifest.xml"));
            }
            else
            {
                Log.TraceInformation("[Remote] Downloading build products");

                List <FileReference> FilesToDownload = new List <FileReference>();
                FilesToDownload.Add(RemoteLogFile);
                FilesToDownload.AddRange(Manifest.BuildProducts.Select(x => new FileReference(x)));
                DownloadFiles(FilesToDownload);
            }

            // Write out all the local manifests
            foreach (FileReference LocalManifestFile in LocalManifestFiles)
            {
                Log.TraceInformation("[Remote] Writing {0}", LocalManifestFile);
                Utils.WriteClass <BuildManifest>(Manifest, LocalManifestFile.FullName, "");
            }
            return(true);
        }
Exemplo n.º 16
0
        /// <summary>
        /// Read a receipt from disk.
        /// </summary>
        /// <param name="FileName">Filename to read from</param>
        public static TargetReceipt Read(string FileName)
        {
            JsonObject RawObject = JsonObject.Read(FileName);

            // Read the initial fields
            string TargetName = RawObject.GetStringField("TargetName");
            UnrealTargetPlatform      Platform      = RawObject.GetEnumField <UnrealTargetPlatform>("Platform");
            UnrealTargetConfiguration Configuration = RawObject.GetEnumField <UnrealTargetConfiguration>("Configuration");
            string BuildId = RawObject.GetStringField("BuildId");

            // Try to read the build version
            BuildVersion Version;

            if (!BuildVersion.TryParse(RawObject.GetObjectField("Version"), out Version))
            {
                throw new JsonParseException("Invalid 'Version' field");
            }

            // Create the receipt
            TargetReceipt Receipt = new TargetReceipt(TargetName, Platform, Configuration, BuildId, Version);

            // Read the build products
            JsonObject[] BuildProductObjects;
            if (RawObject.TryGetObjectArrayField("BuildProducts", out BuildProductObjects))
            {
                foreach (JsonObject BuildProductObject in BuildProductObjects)
                {
                    string           Path;
                    BuildProductType Type;
                    if (BuildProductObject.TryGetStringField("Path", out Path) && BuildProductObject.TryGetEnumField("Type", out Type))
                    {
                        string Module;
                        BuildProductObject.TryGetStringField("Module", out Module);

                        BuildProduct NewBuildProduct = Receipt.AddBuildProduct(Path, Type);

                        bool IsPrecompiled;
                        if (BuildProductObject.TryGetBoolField("IsPrecompiled", out IsPrecompiled))
                        {
                            NewBuildProduct.IsPrecompiled = IsPrecompiled;
                        }
                    }
                }
            }

            // Read the runtime dependencies
            JsonObject[] RuntimeDependencyObjects;
            if (RawObject.TryGetObjectArrayField("RuntimeDependencies", out RuntimeDependencyObjects))
            {
                foreach (JsonObject RuntimeDependencyObject in RuntimeDependencyObjects)
                {
                    string Path;
                    if (RuntimeDependencyObject.TryGetStringField("Path", out Path))
                    {
                        string StagePath;
                        if (!RuntimeDependencyObject.TryGetStringField("StagePath", out StagePath))
                        {
                            StagePath = null;
                        }
                        bool bIgnoreIfMissing;
                        if (!RuntimeDependencyObject.TryGetBoolField("IgnoreIfMissing", out bIgnoreIfMissing))
                        {
                            bIgnoreIfMissing = false;
                        }
                        Receipt.AddRuntimeDependency(Path, StagePath, bIgnoreIfMissing);
                    }
                }
            }

            // Read the additional properties
            JsonObject[] AdditionalPropertyObjects;
            if (RawObject.TryGetObjectArrayField("AdditionalProperties", out AdditionalPropertyObjects))
            {
                foreach (JsonObject AdditionalPropertyObject in AdditionalPropertyObjects)
                {
                    string Name;
                    if (AdditionalPropertyObject.TryGetStringField("Name", out Name))
                    {
                        string Value;
                        if (AdditionalPropertyObject.TryGetStringField("Value", out Value))
                        {
                            Receipt.AdditionalProperties.Add(new ReceiptProperty(Name, Value));
                        }
                    }
                }
            }

            return(Receipt);
        }