// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Diagnostics; using System.Linq; using System.Xml.Serialization; using System.ComponentModel; using System.Runtime.Serialization; namespace UnrealBuildTool { public enum BuildProductType { Executable, DynamicLibrary, StaticLibrary, ImportLibrary, SymbolFile, RequiredResource, } [Serializable] public class BuildProduct { public string Path; public BuildProductType Type; public bool IsPrecompiled; private BuildProduct() { } public BuildProduct(string InPath, BuildProductType InType) { Path = InPath; Type = InType; } public BuildProduct(BuildProduct Other) { Path = Other.Path; Type = Other.Type; IsPrecompiled = Other.IsPrecompiled; } public override string ToString() { return Path; } } [Serializable] public class RuntimeDependency { /// /// The file that should be staged. Should use $(EngineDir) and $(ProjectDir) variables as a root, so that the target can be relocated to different machines. /// public string Path; /// /// The path that the file should be staged to, if different. Leave as null if the file should be staged to the same relative path. /// public string StagePath; private RuntimeDependency() { } public RuntimeDependency(string InPath) { Path = InPath; } public RuntimeDependency(string InPath, string InStagePath) { Path = InPath; StagePath = InStagePath; } public RuntimeDependency(RuntimeDependency InOther) { Path = InOther.Path; StagePath = InOther.StagePath; } public override string ToString() { return (StagePath == null)? Path : String.Format("{0} -> {1}", Path, StagePath); } } /// /// Stores a record of a built target, with all metadata that other tools may need to know about the build. /// [Serializable] public class TargetReceipt { /// /// The name of this target /// public string TargetName; /// /// Which platform the target is compiled for /// public UnrealTargetPlatform Platform; /// /// Which configuration this target is compiled in /// public UnrealTargetConfiguration Configuration; /// /// The unique ID for this build. /// public string BuildId; /// /// The changelist that this target was compiled with. /// public BuildVersion Version; /// /// The build products which are part of this target /// public List BuildProducts = new List(); /// /// All the runtime dependencies that this target relies on /// public List RuntimeDependencies = new List(); /// /// Default constructor /// public TargetReceipt() { } /// /// Constructor /// /// The name of the target being compiled /// Platform for the target being compiled /// Configuration of the target being compiled public TargetReceipt(string InTargetName, UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration, string InBuildId, BuildVersion InVersion) { TargetName = InTargetName; Platform = InPlatform; Configuration = InConfiguration; BuildId = InBuildId; Version = InVersion; } /// /// Copy constructor /// /// Receipt to copy from public TargetReceipt(TargetReceipt Other) { foreach(BuildProduct OtherBuildProduct in Other.BuildProducts) { BuildProducts.Add(new BuildProduct(OtherBuildProduct)); } foreach(RuntimeDependency OtherRuntimeDependency in Other.RuntimeDependencies) { RuntimeDependencies.Add(new RuntimeDependency(OtherRuntimeDependency)); } } /// /// Adds a build product to the receipt. Does not check whether it already exists. /// /// Path to the build product. /// Type of build product. /// The BuildProduct object that was created public BuildProduct AddBuildProduct(string Path, BuildProductType Type) { BuildProduct NewBuildProduct = new BuildProduct(Path, Type); BuildProducts.Add(NewBuildProduct); return NewBuildProduct; } /// /// Constructs a runtime dependency object and adds it to the receipt. /// /// Source path for the dependency /// Location for the dependency in the staged build /// The RuntimeDependency object that was created public RuntimeDependency AddRuntimeDependency(string Path, string StagePath) { RuntimeDependency NewRuntimeDependency = new RuntimeDependency(Path, StagePath); RuntimeDependencies.Add(NewRuntimeDependency); return NewRuntimeDependency; } /// /// Merges another receipt to this one. /// /// Receipt which should be merged public void Merge(TargetReceipt Other) { foreach(BuildProduct OtherBuildProduct in Other.BuildProducts) { BuildProducts.Add(OtherBuildProduct); } foreach(RuntimeDependency OtherRuntimeDependency in Other.RuntimeDependencies) { if(!RuntimeDependencies.Any(x => x.Path == OtherRuntimeDependency.Path && x.StagePath == OtherRuntimeDependency.StagePath)) { RuntimeDependencies.Add(OtherRuntimeDependency); } } } /// /// Expand all the path variables in the manifest /// /// Value for the $(EngineDir) variable /// Value for the $(ProjectDir) variable public void ExpandPathVariables(DirectoryReference EngineDir, DirectoryReference ProjectDir) { ExpandPathVariables(EngineDir, ProjectDir, new Dictionary()); } /// /// Expand all the path variables in the manifest, including a list of supplied variable values. /// /// Value for the $(EngineDir) variable /// Value for the $(ProjectDir) variable public void ExpandPathVariables(DirectoryReference EngineDir, DirectoryReference ProjectDir, IDictionary OtherVariables) { // Build a dictionary containing the standard variable expansions Dictionary Variables = new Dictionary(OtherVariables); Variables["EngineDir"] = EngineDir.FullName; Variables["ProjectDir"] = ProjectDir.FullName; // Replace all the variables in the paths foreach(BuildProduct BuildProduct in BuildProducts) { BuildProduct.Path = Utils.ExpandVariables(BuildProduct.Path, Variables); } foreach(RuntimeDependency RuntimeDependency in RuntimeDependencies) { RuntimeDependency.Path = Utils.ExpandVariables(RuntimeDependency.Path, Variables); if(RuntimeDependency.StagePath != null) { RuntimeDependency.StagePath = Utils.ExpandVariables(RuntimeDependency.StagePath, Variables); } } } /// /// Inserts $(EngineDir) and $(ProjectDir) variables into a path string, so it can be used on different machines. /// /// Input path /// The engine directory. Relative paths are ok. /// The project directory. Relative paths are ok. /// New string with the base directory replaced, or the original string public static string InsertPathVariables(string InputPath, DirectoryReference EngineDir, DirectoryReference ProjectDir) { string Result = InputPath; if(InputPath != null && !InputPath.StartsWith("$(")) { Result = InsertPathVariables(new FileReference(InputPath), EngineDir, ProjectDir); } return Result; } /// /// Inserts variables to make a file relative to $(EngineDir) or $(ProjectDir) /// /// The file to insert variables into. /// Value of the $(EngineDir) variable. /// Value of the $(ProjectDir) variable. /// Converted path for the file. public static string InsertPathVariables(FileReference File, DirectoryReference EngineDir, DirectoryReference ProjectDir) { if(File.IsUnderDirectory(EngineDir)) { return "$(EngineDir)" + Path.DirectorySeparatorChar + File.MakeRelativeTo(EngineDir); } else if(File.IsUnderDirectory(ProjectDir)) { return "$(ProjectDir)" + Path.DirectorySeparatorChar + File.MakeRelativeTo(ProjectDir); } else { return File.FullName; } } /// /// Returns the standard path to the build receipt for a given target /// /// Base directory for the target being built; either the project directory or engine directory. /// The target being built /// The target configuration /// The target platform /// Path to the receipt for this target public static string GetDefaultPath(string BaseDir, string TargetName, UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration, string BuildArchitecture) { if(String.IsNullOrEmpty(BuildArchitecture) && Configuration == UnrealTargetConfiguration.Development) { return Path.Combine(BaseDir, "Binaries", Platform.ToString(), String.Format("{0}.target", TargetName)); } else { return Path.Combine(BaseDir, "Binaries", Platform.ToString(), String.Format("{0}-{1}-{2}{3}.target", TargetName, Platform.ToString(), Configuration.ToString(), BuildArchitecture)); } } /// /// Read a receipt from disk. /// /// Filename to read from public static TargetReceipt Read(string FileName) { JsonObject RawObject = JsonObject.Read(FileName); // Read the initial fields string TargetName = RawObject.GetStringField("TargetName"); UnrealTargetPlatform Platform = RawObject.GetEnumField("Platform"); UnrealTargetConfiguration Configuration = RawObject.GetEnumField("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; } Receipt.AddRuntimeDependency(Path, StagePath); } } } return Receipt; } /// /// Try to read a receipt from disk, failing gracefully if it can't be read. /// /// Filename to read from public static bool TryRead(string FileName, out TargetReceipt Receipt) { if(!File.Exists(FileName)) { Receipt = null; return false; } try { Receipt = Read(FileName); return true; } catch(Exception) { Receipt = null; return false; } } /// /// Write the receipt to disk. /// /// Output filename public void Write(string FileName) { using(JsonWriter Writer = new JsonWriter(FileName)) { Writer.WriteObjectStart(); Writer.WriteValue("TargetName", TargetName); Writer.WriteValue("Platform", Platform.ToString()); Writer.WriteValue("Configuration", Configuration.ToString()); Writer.WriteValue("BuildId", BuildId); Writer.WriteObjectStart("Version"); Version.Write(Writer); Writer.WriteObjectEnd(); Writer.WriteArrayStart("BuildProducts"); foreach(BuildProduct BuildProduct in BuildProducts) { Writer.WriteObjectStart(); Writer.WriteValue("Path", BuildProduct.Path); Writer.WriteValue("Type", BuildProduct.Type.ToString()); if(BuildProduct.IsPrecompiled) { Writer.WriteValue("IsPrecompiled", BuildProduct.IsPrecompiled); } Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); Writer.WriteArrayStart("RuntimeDependencies"); foreach(RuntimeDependency RuntimeDependency in RuntimeDependencies) { Writer.WriteObjectStart(); Writer.WriteValue("Path", RuntimeDependency.Path); if(RuntimeDependency.StagePath != null) { Writer.WriteValue("StagePath", RuntimeDependency.StagePath); } Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); Writer.WriteObjectEnd(); } } } }