// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using EpicGames.Core; namespace UnrealBuildTool { /// /// Stores the version (or a unique build ID) for the modules for a target in a certain folder. /// /// This allows the runtime to identify which modules are used for which files, and which version they're at. This prevents stale binaries from being /// loaded by the runtime when making a local unversioned build, and allows faster incremental builds than compiling the build changelist into every /// module when making versioned builds. /// [Serializable] public class ModuleManifest { /// /// Unique build id for the modules described in the manifest. /// public string BuildId; /// /// Map of module name to file name. /// public Dictionary ModuleNameToFileName = new Dictionary(); /// /// Constructs the module map with the given changelist /// /// The unique build id public ModuleManifest(string InBuildId) { BuildId = InBuildId; } /// /// Merge another manifest into this one /// /// The manifest to merge in public void Include(ModuleManifest Other) { foreach (KeyValuePair Pair in Other.ModuleNameToFileName) { if (!ModuleNameToFileName.ContainsKey(Pair.Key)) { ModuleNameToFileName.Add(Pair.Key, Pair.Value); } } } /// /// Gets the standard path for an manifest /// /// The modular app name being built /// The target configuration /// The target platform /// The architecture of the target platform /// /// Filename for the app receipt public static string GetStandardFileName(string AppName, UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration, UnrealArchitectures BuildArchitectures, bool bIsGameDirectory) { string BaseName = AppName; if (Configuration != UnrealTargetConfiguration.Development && !(Configuration == UnrealTargetConfiguration.DebugGame && !bIsGameDirectory)) { BaseName += String.Format("-{0}-{1}", Platform.ToString(), Configuration.ToString()); } if (UnrealArchitectureConfig.ForPlatform(Platform).RequiresArchitectureFilenames(BuildArchitectures)) { BaseName += BuildArchitectures.ToString(); } return String.Format("{0}.modules", BaseName); } /// /// Read an app receipt from disk /// /// Filename to read from /// The receipt that was read public static ModuleManifest Read(FileReference FileName) { JsonObject Object = JsonObject.Read(FileName); ModuleManifest Receipt = new ModuleManifest(Object.GetStringField("BuildId")); JsonObject Modules = Object.GetObjectField("Modules"); foreach (string ModuleName in Modules.KeyNames) { Receipt.ModuleNameToFileName.Add(ModuleName, Modules.GetStringField(ModuleName)); } return Receipt; } /// /// Tries to read a receipt from disk. /// /// The filename that was read /// If successful, the receipt that was read. Null otherwise. /// True if the file was read succesfully. public static bool TryRead(FileReference FileName, [NotNullWhen(true)] out ModuleManifest? Result) { if (!FileReference.Exists(FileName)) { Result = null; return false; } try { Result = Read(FileName); return true; } catch (Exception) { Result = null; return false; } } /// /// Write the receipt to disk. /// /// The file to write to public void Write(FileReference FileName) { DirectoryReference.CreateDirectory(FileName.Directory); using (StreamWriter Writer = new StreamWriter(FileName.FullName)) { Write(Writer); } } /// /// Write the receipt to disk. /// /// The writer to output to public void Write(TextWriter Writer) { using (JsonWriter OutputWriter = new JsonWriter(Writer, true)) { OutputWriter.WriteObjectStart(); OutputWriter.WriteValue("BuildId", BuildId); OutputWriter.WriteObjectStart("Modules"); foreach (KeyValuePair ModuleNameToFileNamePair in ModuleNameToFileName.OrderBy(x => x.Key)) { OutputWriter.WriteValue(ModuleNameToFileNamePair.Key, ModuleNameToFileNamePair.Value); } OutputWriter.WriteObjectEnd(); OutputWriter.WriteObjectEnd(); } } } }