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