// 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; namespace UnrealBuildTool { [Serializable] public class BuildProperty { [XmlAttribute] public string Name; [XmlAttribute] public string Value; private BuildProperty() { } public BuildProperty(string InName, string InValue) { Name = InName; Value = InValue; } } public enum BuildProductType { Executable, DynamicLibrary, StaticLibrary, ImportLibrary, SymbolFile, RequiredResource, } [Serializable] public class BuildProduct { [XmlAttribute] public string Path; [XmlAttribute] public BuildProductType Type; [XmlAttribute, DefaultValue(false)] 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 { [XmlAttribute] public string Path; [XmlAttribute, DefaultValue(null)] 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 BuildReceipt { [XmlArrayItem("Property")] public List Properties = new List(); [XmlArrayItem("BuildProduct")] public List BuildProducts = new List(); [XmlArrayItem("RuntimeDependency")] public List RuntimeDependencies = new List(); // if packaging in a mode where some files aren't required, set this to false public bool bRequireDependenciesToExist = true; /// /// Default constructor /// public BuildReceipt() { } /// /// Copy constructor /// /// Receipt to copy from public BuildReceipt(BuildReceipt Other) { foreach(BuildProduct OtherBuildProduct in Other.BuildProducts) { BuildProducts.Add(new BuildProduct(OtherBuildProduct)); } foreach(RuntimeDependency OtherRuntimeDependency in Other.RuntimeDependencies) { RuntimeDependencies.Add(new RuntimeDependency(OtherRuntimeDependency)); } } /// /// Sets a property with the given name /// /// Name of the property; case sensitive. /// Value for the property public void SetProperty(string Name, string Value) { BuildProperty Property = Properties.FirstOrDefault(x => x.Name == Name); if(Property == null) { Properties.Add(new BuildProperty(Name, Value)); } else { Property.Value = Value; } } /// /// Gets the value associated with a property /// /// Name of the property; case sensitive. /// Default value for the property if it's not found public string GetProperty(string Name, string DefaultValue) { BuildProperty Property = Properties.FirstOrDefault(x => x.Name == Name); if(Property == null) { return DefaultValue; } else { return Property.Value; } } /// /// 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(BuildReceipt 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(string EngineDir, string ProjectDir) { ExpandPathVariables(EngineDir, ProjectDir, new Dictionary()); } /// /// Control whether the dependencies are required while staging them /// /// public void SetDependenciesToBeRequired(bool InDependenciesAreRequired) { bRequireDependenciesToExist = InDependenciesAreRequired; } /// /// 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(string EngineDir, string ProjectDir, IDictionary OtherVariables) { // Build a dictionary containing the standard variable expansions Dictionary Variables = new Dictionary(OtherVariables); Variables["EngineDir"] = Path.GetFullPath(EngineDir).TrimEnd(Path.DirectorySeparatorChar); Variables["ProjectDir"] = Path.GetFullPath(ProjectDir).TrimEnd(Path.DirectorySeparatorChar); // 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 standard $(EngineDir) and $(ProjectDir) variables into any path strings, so it can be used on different machines. /// /// The engine directory. Relative paths are ok. /// The project directory. Relative paths are ok. public void InsertStandardPathVariables(string EngineDir, string ProjectDir) { string EnginePrefix = Path.GetFullPath(EngineDir).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; string ProjectPrefix = Path.GetFullPath(ProjectDir).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; foreach(BuildProduct BuildProduct in BuildProducts) { BuildProduct.Path = InsertStandardPathVariablesToString(BuildProduct.Path, EnginePrefix, ProjectPrefix); } foreach(RuntimeDependency RuntimeDependency in RuntimeDependencies) { RuntimeDependency.Path = InsertStandardPathVariablesToString(RuntimeDependency.Path, EnginePrefix, ProjectPrefix); if(RuntimeDependency.StagePath != null) { RuntimeDependency.StagePath = InsertStandardPathVariablesToString(RuntimeDependency.StagePath, EnginePrefix, ProjectPrefix); } } } /// /// 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 static string InsertStandardPathVariablesToString(string InputPath, string EnginePrefix, string ProjectPrefix) { string Result = InputPath; if(!InputPath.StartsWith("$(")) { string FullInputPath = Path.GetFullPath(InputPath); if(FullInputPath.StartsWith(EnginePrefix)) { Result = "$(EngineDir)" + FullInputPath.Substring(EnginePrefix.Length - 1); } else if(FullInputPath.StartsWith(ProjectPrefix)) { Result = "$(ProjectDir)" + FullInputPath.Substring(ProjectPrefix.Length - 1); } } return Result; } /// /// 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) { return Path.Combine(BaseDir, "Build", "Receipts", String.Format("{0}-{1}-{2}{3}.target.xml", TargetName, Platform.ToString(), Configuration.ToString(), BuildArchitecture)); } static XmlSerializer Serializer = XmlSerializer.FromTypes(new Type[]{ typeof(BuildReceipt) })[0]; /// /// Read a receipt from disk. /// /// Filename to read from public static BuildReceipt Read(string FileName) { using(StreamReader Reader = new StreamReader(FileName)) { return (BuildReceipt)Serializer.Deserialize(Reader); } } /// /// Write the receipt to disk. /// /// Output filename public void Write(string FileName) { using(StreamWriter Writer = new StreamWriter(FileName)) { Serializer.Serialize(Writer, this); } } } }