// 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);
}
}
}
}