Files
UnrealEngineUWP/Engine/Source/Programs/AutomationTool/GUBP/LegacyNodes.cs
Ben Marsh b21f02450f Move the CIS counter update into its own function.
[CL 2606473 by Ben Marsh in Main branch]
2015-06-30 17:09:44 -04:00

3492 lines
135 KiB
C#

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using AutomationTool;
using UnrealBuildTool;
using System.Reflection;
using System.Xml;
using System.Linq;
partial class GUBP
{
public abstract class GUBPNode
{
public List<string> FullNamesOfDependencies = new List<string>();
public List<string> FullNamesOfPseudosependencies = new List<string>(); //these are really only used for sorting. We want the editor to fail before the monolithics. Think of it as "can't possibly be useful without".
public List<string> FullNamesOfDependedOn = new List<string>();
public List<string> BuildProducts = null;
public List<string> DependentPromotions = new List<string>();
public List<string> AllDependencyBuildProducts = null;
public List<string> AllDependencies = null;
public List<string> CompletedDependencies = new List<string>();
public string AgentSharingGroup = "";
public int ComputedDependentCISFrequencyQuantumShift = -1;
public virtual string GetFullName()
{
throw new AutomationException("Unimplemented GetFullName.");
}
public virtual string GetDisplayGroupName()
{
return GetFullName();
}
public virtual string GameNameIfAnyForTempStorage()
{
return "";
}
public virtual string RootIfAnyForTempStorage()
{
return "";
}
public virtual void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public virtual void PostLoadFromSharedTempStorage(GUBP bp)
{
}
public virtual void DoFakeBuild(GUBP bp) // this is used to more rapidly test a build system, it does nothing but save a record of success as a build product
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public virtual bool IsSticky()
{
return false;
}
public virtual bool SendSuccessEmail()
{
return false;
}
public virtual bool RunInEC()
{
return true;
}
public virtual bool IsTest()
{
return false;
}
public virtual bool IsAggregate()
{
return false;
}
public virtual bool IsPromotableAggregate()
{
return false;
}
public virtual bool IsSeparatePromotable()
{
return false;
}
public virtual string NodeHostPlatform()
{
return "";
}
public virtual int AgentMemoryRequirement(GUBP bp)
{
return 0;
}
public virtual int TimeoutInMinutes()
{
return 90;
}
/// <summary>
/// When triggered by CIS (instead of a person) this dictates how often this node runs.
/// The builder has a increasing index, specified with -TimeIndex=N
/// If N mod (1 lshift CISFrequencyQuantumShift()) is not 0, the node is skipped
/// </summary>
public virtual int CISFrequencyQuantumShift(GUBP bp)
{
return 0;
}
/// <summary>
/// As above the maximum of all ancestors and pseudoancestors
/// </summary>
public int DependentCISFrequencyQuantumShift()
{
if (ComputedDependentCISFrequencyQuantumShift < 0)
{
throw new AutomationException("Asked for frequency shift before it was computed.");
}
return ComputedDependentCISFrequencyQuantumShift;
}
public virtual float Priority()
{
return 100.0f;
}
public virtual bool TriggerNode()
{
return false;
}
public virtual void SetAsExplicitTrigger()
{
}
public virtual string ECAgentString()
{
return "";
}
public virtual string ECProcedureInfix()
{
return "";
}
public virtual string ECProcedure()
{
if (IsSticky() && AgentSharingGroup != "")
{
throw new AutomationException("Node {0} is both agent sharing and sitcky.", GetFullName());
}
return String.Format("GUBP{0}_UAT_Node{1}{2}", ECProcedureInfix(), IsSticky() ? "" : "_Parallel", AgentSharingGroup != "" ? "_AgentShare" : "");
}
public virtual string ECProcedureParams()
{
var Result = String.Format(", {{actualParameterName => 'Sticky', value => '{0}'}}", IsSticky() ? 1 : 0);
if (AgentSharingGroup != "")
{
Result += String.Format(", {{actualParameterName => 'AgentSharingGroup', value => '{0}'}}", AgentSharingGroup);
}
return Result;
}
public static string MergeSpaceStrings(params string[] EmailLists)
{
var Emails = new List<string>();
foreach (var EmailList in EmailLists)
{
if (!String.IsNullOrEmpty(EmailList))
{
List<string> Parts = new List<string>(EmailList.Split(' '));
foreach (var Email in Parts)
{
if (!string.IsNullOrEmpty(Email) && !Emails.Contains(Email))
{
Emails.Add(Email);
}
}
}
}
string Result = "";
foreach (var Email in Emails)
{
if (Result != "")
{
Result += " ";
}
Result += Email;
}
return Result;
}
public void SaveRecordOfSuccessAndAddToBuildProducts(string Contents = "Just a record of success.")
{
string RecordOfSuccess = CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Logs", GetFullName() + "_Success.log");
CommandUtils.CreateDirectory(Path.GetDirectoryName(RecordOfSuccess));
CommandUtils.WriteAllText(RecordOfSuccess, Contents);
AddBuildProduct(RecordOfSuccess);
}
public void AddDependency(string Node)
{
if (!FullNamesOfDependencies.Contains(Node))
{
FullNamesOfDependencies.Add(Node);
}
}
public void AddPseudodependency(string Node)
{
if (!FullNamesOfPseudosependencies.Contains(Node))
{
FullNamesOfPseudosependencies.Add(Node);
}
}
public void AddCompletedDependency(string Node)
{
if(!CompletedDependencies.Contains(Node))
{
CompletedDependencies.Add(Node);
}
}
public void RemovePseudodependency(string Node)
{
if (FullNamesOfPseudosependencies.Contains(Node))
{
FullNamesOfPseudosependencies.Remove(Node);
}
}
public void AddBuildProduct(string Filename)
{
if (!CommandUtils.FileExists_NoExceptions(true, Filename))
{
throw new AutomationException("Cannot add build product {0} because it does not exist.", Filename);
}
FileInfo Info = new FileInfo(Filename);
if (!BuildProducts.Contains(Info.FullName))
{
BuildProducts.Add(Info.FullName);
}
}
public void AddDependentBuildProduct(string Filename)
{
if (!CommandUtils.FileExists_NoExceptions(true, Filename))
{
throw new AutomationException("Cannot add build product {0} because it does not exist.", Filename);
}
FileInfo Info = new FileInfo(Filename);
if (!AllDependencyBuildProducts.Contains(Info.FullName))
{
AllDependencyBuildProducts.Add(Info.FullName);
}
}
public void AddAllDependent(string Node)
{
if (!AllDependencies.Contains(Node))
{
AllDependencies.Add(Node);
}
}
public void RemoveOveralppingBuildProducts()
{
foreach (var ToRemove in AllDependencyBuildProducts)
{
BuildProducts.Remove(ToRemove);
}
}
}
public class VersionFilesNode : GUBPNode
{
public static string StaticGetFullName()
{
return "VersionFiles";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override void DoBuild(GUBP bp)
{
var UE4Build = new UE4Build(bp);
BuildProducts = UE4Build.UpdateVersionFiles(ActuallyUpdateVersionFiles: CommandUtils.P4Enabled && CommandUtils.AllowSubmit);
}
public override bool IsSticky()
{
return true;
}
}
public class HostPlatformNode : GUBPNode
{
protected UnrealTargetPlatform HostPlatform;
public HostPlatformNode(UnrealTargetPlatform InHostPlatform)
{
HostPlatform = InHostPlatform;
}
public override string GetDisplayGroupName()
{
string Name = GetFullName();
string Suffix = GetHostPlatformSuffix();
return Name.EndsWith(Suffix)? Name.Substring(0, Name.Length - Suffix.Length) : Name;
}
public static string StaticGetHostPlatformSuffix(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InAgentPlatform = UnrealTargetPlatform.Unknown)
{
if (InHostPlatform == UnrealTargetPlatform.Mac)
{
if (InAgentPlatform == UnrealTargetPlatform.Win64)
{
return "_ForMac";
}
return "_OnMac";
}
else if (InHostPlatform == UnrealTargetPlatform.Linux)
{
return "_OnLinux";
}
return "";
}
public virtual UnrealTargetPlatform GetAgentPlatform()
{
return HostPlatform;
}
public override string ECProcedureInfix()
{
if (GetAgentPlatform() == UnrealTargetPlatform.Mac)
{
if (IsSticky())
{
throw new AutomationException("Node {0} is sticky, but Mac hosted. Sticky nodes must be PC hosted.", GetFullName());
}
return "_Mac";
}
if(GetAgentPlatform() == UnrealTargetPlatform.Linux)
{
if(IsSticky())
{
throw new AutomationException("Node {0} is sticky, but Linux hosted. Sticky nodes must be PC hosted.", GetFullName());
}
return "_Linux";
}
return "";
}
public virtual string GetHostPlatformSuffix()
{
return StaticGetHostPlatformSuffix(HostPlatform, GetAgentPlatform());
}
public UnrealTargetPlatform GetAltHostPlatform()
{
return GUBP.GetAltHostPlatform(HostPlatform);
}
public override int TimeoutInMinutes()
{
return base.TimeoutInMinutes() + ((HostPlatform == UnrealTargetPlatform.Mac) ? 30 : 0); // Mac is slower and more resource constrained
}
}
public class CompileNode : HostPlatformNode
{
bool bDependentOnCompileTools = true;
public CompileNode(UnrealTargetPlatform InHostPlatform, bool DependentOnCompileTools = true)
: base(InHostPlatform)
{
bDependentOnCompileTools = DependentOnCompileTools;
if (DependentOnCompileTools)
{
AddDependency(ToolsForCompileNode.StaticGetFullName(HostPlatform));
}
else
{
AddDependency(VersionFilesNode.StaticGetFullName());
}
}
public virtual UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
return null;
}
public virtual void PostBuild(GUBP bp, UE4Build UE4Build)
{
}
public virtual void PostBuildProducts(GUBP bp)
{
}
public virtual bool DeleteBuildProducts()
{
return false;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
var UE4Build = new UE4Build(bp);
UE4Build.BuildAgenda Agenda = GetAgenda(bp);
if (Agenda != null)
{
bool ReallyDeleteBuildProducts = DeleteBuildProducts() && !GUBP.bForceIncrementalCompile;
Agenda.DoRetries = false; // these would delete build products
bool UseParallelExecutor = bDependentOnCompileTools && (HostPlatform == UnrealTargetPlatform.Win64);
UE4Build.Build(Agenda, InDeleteBuildProducts: ReallyDeleteBuildProducts, InUpdateVersionFiles: false, InForceNoXGE: true, InForceUnity: true, InUseParallelExecutor: UseParallelExecutor);
var StartPostBuild = DateTime.Now.ToString();
PostBuild(bp, UE4Build);
var FinishPostBuild = DateTime.Now.ToString();
PrintCSVFile(String.Format("UAT,PostBuild,{0},{1}", StartPostBuild, FinishPostBuild));
UE4Build.CheckBuildProducts(UE4Build.BuildProductFiles);
foreach (var Product in UE4Build.BuildProductFiles)
{
AddBuildProduct(Product);
}
RemoveOveralppingBuildProducts();
if (bp.bSignBuildProducts)
{
// Sign everything we built
var StartSign = DateTime.Now.ToString();
CodeSign.SignMultipleIfEXEOrDLL(bp, BuildProducts);
var FinishSign = DateTime.Now.ToString();
PrintCSVFile(String.Format("UAT,SignBuildProducts,{0},{1}", StartSign, FinishSign));
}
PostBuildProducts(bp);
}
if (Agenda == null || (BuildProducts.Count == 0 && GUBP.bForceIncrementalCompile))
{
SaveRecordOfSuccessAndAddToBuildProducts("Nothing to actually compile");
}
}
public override int TimeoutInMinutes()
{
return base.TimeoutInMinutes() + ((HostPlatform == UnrealTargetPlatform.Mac) ? 30 : 0);
}
}
public class ToolsForCompileNode : CompileNode
{
int TimeIndex;
public ToolsForCompileNode(UnrealTargetPlatform InHostPlatform, int InTimeIndex)
: base(InHostPlatform, false)
{
TimeIndex = InTimeIndex;
if (InHostPlatform != UnrealTargetPlatform.Win64)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "ToolsForCompile" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override bool IsSticky()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return true;
}
return false;
}
public override string ECProcedure()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return String.Format("GUBP_UAT_Node_Parallel_AgentShare_Editor");
}
return base.ECProcedure();
}
public override bool DeleteBuildProducts()
{
return true;
}
public override int AgentMemoryRequirement(GUBP bp)
{
if (bp.ParseParam("Launcher") || TimeIndex != 0 && HostPlatform != UnrealTargetPlatform.Mac)
{
return base.AgentMemoryRequirement(bp);
}
return 32;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
if (HostPlatform == UnrealTargetPlatform.Win64 && !GUBP.bForceIncrementalCompile)
{
Agenda.DotNetProjects.AddRange(
new string[]
{
@"Engine\Source\Programs\DotNETCommon\DotNETUtilities\DotNETUtilities.csproj",
@"Engine\Source\Programs\RPCUtility\RPCUtility.csproj",
}
);
}
string AddArgs = "-CopyAppBundleBackToDevice";
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
if (HostPlatform == UnrealTargetPlatform.Win64)
{
Agenda.AddTargets(new string[] { "ParallelExecutor" }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
}
return Agenda;
}
public override void PostBuild(GUBP bp, UE4Build UE4Build)
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
UE4Build.AddUATFilesToBuildProducts();
UE4Build.AddUBTFilesToBuildProducts();
}
}
}
public class RootEditorNode : CompileNode
{
public RootEditorNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
if (InHostPlatform != UnrealTargetPlatform.Win64)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "RootEditor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override bool DeleteBuildProducts()
{
return true;
}
public override void DoBuild(GUBP bp)
{
base.DoBuild(bp);
if(!bp.BranchOptions.bNoInstalledEngine)
{
FileFilter Filter = new FileFilter();
Filter.Include("/Engine/Intermediate/Build/" + HostPlatform.ToString() + "/UE4Editor/Inc/...");
Filter.Include("/Engine/Plugins/.../Intermediate/Build/" + HostPlatform.ToString() + "/UE4Editor/Inc/...");
string ZipFileName = StaticGetArchivedHeadersPath(HostPlatform);
CommandUtils.ZipFiles(ZipFileName, CommandUtils.CmdEnv.LocalRoot, Filter);
BuildProducts.Add(ZipFileName);
}
}
public static string StaticGetArchivedHeadersPath(UnrealTargetPlatform HostPlatform)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Precompiled", "Headers-RootEditor" + StaticGetHostPlatformSuffix(HostPlatform) + ".zip");
}
public override bool IsSticky()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return true;
}
return false;
}
public override string ECProcedure()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return String.Format("GUBP_UAT_Node_Parallel_AgentShare_Editor");
}
return base.ECProcedure();
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht";
if(!bp.BranchOptions.bNoInstalledEngine)
{
AddArgs += " -precompile";
}
if (bp.bOrthogonalizeEditorPlatforms)
{
AddArgs += " -skipnonhostplatforms";
}
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
}
}
return Agenda;
}
void DeleteStaleDLLs(GUBP bp)
{
if (GUBP.bForceIncrementalCompile)
{
return;
}
var Targets = new List<string>{bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName};
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
{
Targets.Add(ProgramTarget.TargetName);
}
}
foreach (var Target in Targets)
{
var EnginePlatformBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Binaries", HostPlatform.ToString());
var Wildcard = Target + "-*";
Log("************Deleting stale editor DLLs, path {0} wildcard {1}", EnginePlatformBinaries, Wildcard);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePlatformBinaries))
{
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
var EnginePluginBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Plugins");
var HostSubstring = CommandUtils.CombinePaths("/", HostPlatform.ToString(), "/");
Log("************Deleting stale editor DLLs, path {0} wildcard {1} host {2}", EnginePluginBinaries, Wildcard, HostSubstring);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePluginBinaries))
{
if (DiskFile.IndexOf(HostSubstring, StringComparison.InvariantCultureIgnoreCase) < 0)
{
continue;
}
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
}
}
public override void PostLoadFromSharedTempStorage(GUBP bp)
{
DeleteStaleDLLs(bp);
}
public override void PostBuildProducts(GUBP bp)
{
DeleteStaleDLLs(bp);
}
}
public class RootEditorCrossCompileLinuxNode : CompileNode
{
public RootEditorCrossCompileLinuxNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddDependency(RootEditorNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AddDependency(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
}
public static string StaticGetFullName()
{
return "RootEditor_Linux";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht";
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
UnrealTargetPlatform.Linux, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(UnrealTargetPlatform.Linux))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, UnrealTargetPlatform.Linux, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
}
}
return Agenda;
}
void DeleteStaleDLLs(GUBP bp)
{
if (GUBP.bForceIncrementalCompile)
{
return;
}
var Targets = new List<string> { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName };
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(UnrealTargetPlatform.Linux))
{
Targets.Add(ProgramTarget.TargetName);
}
}
foreach (var Target in Targets)
{
var EnginePlatformBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Binaries", UnrealTargetPlatform.Linux.ToString());
var Wildcard = Target + "-*";
Log("************Deleting stale editor DLLs, path {0} wildcard {1}", EnginePlatformBinaries, Wildcard);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePlatformBinaries))
{
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
var EnginePluginBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Plugins");
var HostSubstring = CommandUtils.CombinePaths("/", UnrealTargetPlatform.Linux.ToString(), "/");
Log("************Deleting stale editor DLLs, path {0} wildcard {1} host {2}", EnginePluginBinaries, Wildcard, HostSubstring);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePluginBinaries))
{
if (DiskFile.IndexOf(HostSubstring, StringComparison.InvariantCultureIgnoreCase) < 0)
{
continue;
}
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
}
}
public override void PostLoadFromSharedTempStorage(GUBP bp)
{
DeleteStaleDLLs(bp);
}
public override void PostBuildProducts(GUBP bp)
{
DeleteStaleDLLs(bp);
}
}
public class ToolsNode : CompileNode
{
public ToolsNode(GUBP bp, UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
if(!bp.BranchOptions.bNoEditorDependenciesForTools)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "Tools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override float Priority()
{
return base.Priority() - 1;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
if (HostPlatform == UnrealTargetPlatform.Win64)
{
if (!GUBP.bForceIncrementalCompile)
{
Agenda.DotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\UnrealControls\UnrealControls.csproj"),
}
);
}
Agenda.DotNetSolutions.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\NetworkProfiler\NetworkProfiler.sln"),
}
);
if (!GUBP.bForceIncrementalCompile)
{
Agenda.SwarmProject = CombinePaths(@"Engine\Source\Programs\UnrealSwarm\UnrealSwarm.sln");
}
bool WithIOS = !bp.BranchOptions.PlatformsToRemove.Contains(UnrealTargetPlatform.IOS);
if ( WithIOS )
{
Agenda.IOSDotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\IOS\iPhonePackager\iPhonePackager.csproj"),
CombinePaths(@"Engine\Source\Programs\IOS\DeploymentServer\DeploymentServer.csproj"),
CombinePaths(@"Engine\Source\Programs\IOS\MobileDeviceInterface\MobileDeviceInterface.csproj"),
CombinePaths(@"Engine\Source\Programs\IOS\DeploymentInterface\DeploymentInterface.csproj"),
}
);
}
bool WithHTML5 = !bp.BranchOptions.PlatformsToRemove.Contains(UnrealTargetPlatform.HTML5);
if (WithHTML5)
{
Agenda.HTML5DotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\HTML5\HTML5LaunchHelper\HTML5LaunchHelper.csproj"),
}
);
}
}
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
bool CrossCompile;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && !bInternalOnly && !SeparateNode)
{
if (!bp.BranchOptions.ExcludeNodes.Contains(ProgramTarget.TargetName))
{
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
}
}
}
}
}
return Agenda;
}
}
public class ToolsCrossCompileNode : CompileNode
{
public ToolsCrossCompileNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddPseudodependency(RootEditorCrossCompileLinuxNode.StaticGetFullName());
AgentSharingGroup = "ToolsCrossCompileGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "LinuxTools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override float Priority()
{
return base.Priority() - 1;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
bool CrossCompile;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && !bInternalOnly && !SeparateNode && CrossCompile)
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, UnrealTargetPlatform.Linux, Config, InAddArgs: AddArgs);
}
}
}
return Agenda;
}
}
public class SingleToolsNode : CompileNode
{
SingleTargetProperties ProgramTarget;
public SingleToolsNode(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
ProgramTarget = InProgramTarget;
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
{
return "Tools_" + InProgramTarget.TargetName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, ProgramTarget);
}
public override float Priority()
{
return base.Priority() + 2;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
}
}
return Agenda;
}
}
public class InternalToolsNode : CompileNode
{
public InternalToolsNode(GUBP bp, UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
if(!bp.BranchOptions.bNoEditorDependenciesForTools)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "InternalTools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override float Priority()
{
return base.Priority() - 2;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 1;
return Result;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
bool bAnyAdded = false;
var Agenda = new UE4Build.BuildAgenda();
if (HostPlatform == UnrealTargetPlatform.Win64)
{
bAnyAdded = true;
Agenda.DotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportCommon\CrashReportCommon.csproj"),
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportReceiver\CrashReportReceiver.csproj"),
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportProcess\CrashReportProcess.csproj"),
CombinePaths(@"Engine\Source\Programs\CrashReporter\RegisterPII\RegisterPII.csproj"),
});
Agenda.DotNetSolutions.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\UnrealDocTool\UnrealDocTool\UnrealDocTool.sln"),
}
);
Agenda.ExtraDotNetFiles.AddRange(
new string[]
{
"Interop.IWshRuntimeLibrary",
"UnrealMarkdown",
"CommonUnrealMarkdown",
}
);
}
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
bool CrossCompile;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && bInternalOnly && !SeparateNode)
{
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
bAnyAdded = true;
}
}
}
}
if (bAnyAdded)
{
return Agenda;
}
return null;
}
}
public class SingleInternalToolsNode : CompileNode
{
SingleTargetProperties ProgramTarget;
public SingleInternalToolsNode(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
SetupSingleInternalToolsNode(InProgramTarget);
}
public SingleInternalToolsNode(GUBP bp, UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
// Don't add rooteditor dependency if it isn't in the graph
var bRootEditorNodeDoesExit = bp.HasNode(RootEditorNode.StaticGetFullName(HostPlatform));
SetupSingleInternalToolsNode(InProgramTarget, !bRootEditorNodeDoesExit && bp.BranchOptions.bNoEditorDependenciesForTools);
}
private void SetupSingleInternalToolsNode(SingleTargetProperties InProgramTarget, bool bSkipRootEditorPsuedoDependency = true)
{
ProgramTarget = InProgramTarget;
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
if (!bSkipRootEditorPsuedoDependency)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
{
return "InternalTools_" + InProgramTarget.TargetName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, ProgramTarget);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 1;
return Result;
}
public override float Priority()
{
return base.Priority() + 3;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
}
}
return Agenda;
}
}
public class EditorPlatformNode : CompileNode
{
UnrealTargetPlatform EditorPlatform;
public EditorPlatformNode(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform Plat)
: base(InHostPlatform)
{
if (InHostPlatform != UnrealTargetPlatform.Win64)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
EditorPlatform = Plat;
AddDependency(RootEditorNode.StaticGetFullName(InHostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform Plat)
{
return Plat.ToString() + "_EditorPlatform" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, EditorPlatform);
}
public override float Priority()
{
return base.Priority() + 1;
}
public override bool IsSticky()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return true;
}
return false;
}
public override string ECProcedure()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return String.Format("GUBP_UAT_Node_Parallel_AgentShare_Editor");
}
return base.ECProcedure();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp);
return Result;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
if (!bp.bOrthogonalizeEditorPlatforms)
{
throw new AutomationException("EditorPlatformNode node should not be used unless we are orthogonalizing editor platforms.");
}
var Agenda = new UE4Build.BuildAgenda();
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice -onlyplatformspecificfor=" + EditorPlatform.ToString());
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && ProgramTarget.Rules.GUBP_NeedsPlatformSpecificDLLs())
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice -onlyplatformspecificfor=" + EditorPlatform.ToString());
}
}
return Agenda;
}
}
public class EditorGameNode : CompileNode
{
List<BranchInfo.BranchUProject> GameProjects = new List<BranchInfo.BranchUProject>();
public EditorGameNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
: base(InHostPlatform)
{
if (InHostPlatform != UnrealTargetPlatform.Win64)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
GameProjects.Add(InGameProj);
AddDependency(RootEditorNode.StaticGetFullName(InHostPlatform));
}
public void AddProject(BranchInfo.BranchUProject InGameProj)
{
if(InGameProj.Options(HostPlatform).GroupName != GameProjects[0].Options(HostPlatform).GroupName)
{
throw new AutomationException("Attempt to merge projects with different group names");
}
GameProjects.Add(InGameProj);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
{
return (InGameProj.Options(InHostPlatform).GroupName ?? InGameProj.GameName) + "_EditorGame" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProjects[0]);
}
public override bool IsSticky()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return true;
}
return false;
}
public override string ECProcedure()
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
return String.Format("GUBP_UAT_Node_Parallel_AgentShare_Editor");
}
return base.ECProcedure();
}
public override string GameNameIfAnyForTempStorage()
{
return GameProjects[0].Options(HostPlatform).GroupName ?? GameProjects[0].GameName;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string Args = "-nobuilduht -skipactionhistory -skipnonhostplatforms -CopyAppBundleBackToDevice -forceheadergeneration";
if(!bp.BranchOptions.bNoInstalledEngine)
{
Args += " -precompile";
}
foreach(BranchInfo.BranchUProject GameProj in GameProjects)
{
Agenda.AddTargets(
new string[] { GameProj.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, GameProj.FilePath, InAddArgs: Args);
}
return Agenda;
}
}
public class MakeFeaturePacksNode : HostPlatformNode
{
List<BranchInfo.BranchUProject> Projects;
public MakeFeaturePacksNode(UnrealTargetPlatform InHostPlatform, IEnumerable<BranchInfo.BranchUProject> InProjects)
: base(InHostPlatform)
{
Projects = new List<BranchInfo.BranchUProject>(InProjects);
AddDependency(ToolsNode.StaticGetFullName(InHostPlatform)); // for UnrealPak
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string GetOutputFile(BranchInfo.BranchUProject Project)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "FeaturePacks", Path.GetFileNameWithoutExtension(Project.GameName) + ".upack");
}
public static bool IsFeaturePack(BranchInfo.BranchUProject InGameProj)
{
// No obvious way to store this in the project options; it's a property of non-code projects too.
if(InGameProj.GameName == "StarterContent" || InGameProj.GameName == "MobileStarterContent" || InGameProj.GameName.StartsWith("FP_"))
{
return true;
}
if(InGameProj.GameName.StartsWith("TP_"))
{
return CommandUtils.FileExists(CommandUtils.CombinePaths(CommandUtils.GetDirectoryName(InGameProj.FilePath), "contents.txt"));
}
return false;
}
public static UnrealTargetPlatform GetDefaultBuildPlatform(GUBP bp)
{
if(bp.HostPlatforms.Contains(UnrealTargetPlatform.Win64))
{
return UnrealTargetPlatform.Win64;
}
else if(bp.HostPlatforms.Contains(UnrealTargetPlatform.Mac))
{
return UnrealTargetPlatform.Mac;
}
else
{
if (UnrealBuildTool.BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux && bp.HostPlatforms[0] != UnrealTargetPlatform.Linux)
{
throw new AutomationException("Linux is not (yet?) able to cross-compile nodes for platform {0}, did you forget -NoPC / -NoMac?", bp.HostPlatforms[0]);
}
return bp.HostPlatforms[0];
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "MakeFeaturePacks" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 2;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
foreach(BranchInfo.BranchUProject Project in Projects)
{
string ContentsFileName = CommandUtils.CombinePaths(CommandUtils.GetDirectoryName(Project.FilePath), "contents.txt");
// Make sure we delete the output file. It may be read-only.
string OutputFileName = GetOutputFile(Project);
CommandUtils.DeleteFile(OutputFileName);
// Get the command line
string CmdLine = CommandUtils.MakePathSafeToUseWithCommandLine(OutputFileName) + " " + CommandUtils.MakePathSafeToUseWithCommandLine("-create=" + ContentsFileName);
if (GlobalCommandLine.Installed)
{
CmdLine += " -installed";
}
if (GlobalCommandLine.UTF8Output)
{
CmdLine += " -UTF8Output";
}
// Run UnrealPak
string UnrealPakExe;
if(HostPlatform == UnrealTargetPlatform.Win64)
{
UnrealPakExe = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/Win64/UnrealPak.exe");
}
else
{
throw new AutomationException("Unknown path to UnrealPak for host platform ({0})", HostPlatform);
}
RunAndLog(CmdEnv, UnrealPakExe, CmdLine, Options: ERunOptions.Default | ERunOptions.AllowSpew | ERunOptions.UTF8Output);
// Add the build products
BuildProducts.Add(OutputFileName);
}
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class GamePlatformMonolithicsNode : CompileNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform TargetPlatform;
bool WithXp;
bool Precompiled; // If true, just builds targets which generate static libraries for the -UsePrecompiled option to UBT. If false, just build those that don't.
public GamePlatformMonolithicsNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, bool InWithXp = false, bool InPrecompiled = false)
: base(InHostPlatform)
{
GameProj = InGameProj;
TargetPlatform = InTargetPlatform;
WithXp = InWithXp;
Precompiled = InPrecompiled;
if (TargetPlatform == UnrealTargetPlatform.PS4 || TargetPlatform == UnrealTargetPlatform.XboxOne)
{
// Required for PS4MapFileUtil/XboxOnePDBFileUtil
AddDependency(ToolsNode.StaticGetFullName(InHostPlatform));
}
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
if (!bp.BranchOptions.ExcludePlatformsForEditor.Contains(InHostPlatform))
{
AddPseudodependency(EditorGameNode.StaticGetFullName(InHostPlatform, GameProj));
}
if (bp.HasNode(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform)))
{
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
}
}
else
{
if (TargetPlatform != InHostPlatform && bp.HasNode(GamePlatformMonolithicsNode.StaticGetFullName(InHostPlatform, bp.Branch.BaseEngineProject, InHostPlatform, Precompiled: Precompiled)))
{
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(InHostPlatform, bp.Branch.BaseEngineProject, InHostPlatform, Precompiled: Precompiled));
}
}
if (InGameProj.Options(InHostPlatform).bTestWithShared) /// compiling templates is only for testing purposes, and we will group them to avoid saturating the farm
{
AddPseudodependency(WaitForTestShared.StaticGetFullName());
AgentSharingGroup = "TemplateMonolithics" + StaticGetHostPlatformSuffix(InHostPlatform);
}
}
public override string GetDisplayGroupName()
{
return GameProj.GameName + "_Monolithics" + (Precompiled? "_Precompiled" : "");
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, bool WithXp = false, bool Precompiled = false)
{
string Name = InGameProj.GameName;
if(WithXp)
{
Name += "_WinXP_Mono";
}
else
{
Name += "_" + InTargetPlatform + "_Mono";
}
if(Precompiled)
{
Name += "_Precompiled";
}
return Name + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, TargetPlatform, WithXp, Precompiled);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override int AgentMemoryRequirement(GUBP bp)
{
if (bp.BranchOptions.EnhanceAgentRequirements.Contains(StaticGetFullName(HostPlatform, GameProj, TargetPlatform, WithXp, Precompiled)))
{
return 64;
}
return base.AgentMemoryRequirement(bp);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp);
if(GameProj.GameName != bp.Branch.BaseEngineProject.GameName || !Precompiled)
{
Result += 3; //only every 80m
}
else if (TargetPlatform != HostPlatform)
{
Result += 2; //only every 40m
}
return Result;
}
public static bool HasPrecompiledTargets(BranchInfo.BranchUProject Project, UnrealTargetPlatform HostPlatform, UnrealTargetPlatform TargetPlatform)
{
foreach(TargetRules.TargetType Kind in BranchInfo.MonolithicKinds)
{
if (Project.Properties.Targets.ContainsKey(Kind))
{
SingleTargetProperties Target = Project.Properties.Targets[Kind];
if(Target.Rules.GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly(HostPlatform, TargetPlatform).Any())
{
return true;
}
}
}
return false;
}
public override float Priority()
{
float Result = base.Priority();
if(Precompiled)
{
Result += 1.0f;
}
return Result;
}
public override void DoBuild(GUBP bp)
{
base.DoBuild(bp);
if(Precompiled)
{
// Get a list of all the build dependencies
UE4Build.BuildAgenda Agenda = GetAgenda(bp);
string FileListPath = new UE4Build(bp).GenerateExternalFileList(Agenda);
UnrealBuildTool.ExternalFileList FileList = UnrealBuildTool.Utils.ReadClass<UnrealBuildTool.ExternalFileList>(FileListPath);
// Make all the paths relative to the root
string FilterPrefix = CommandUtils.CombinePaths(PathSeparator.Slash, CommandUtils.CmdEnv.LocalRoot).TrimEnd('/') + "/";
for(int Idx = 0; Idx < FileList.FileNames.Count; Idx++)
{
if(FileList.FileNames[Idx].StartsWith(FilterPrefix, StringComparison.InvariantCultureIgnoreCase))
{
FileList.FileNames[Idx] = FileList.FileNames[Idx].Substring(FilterPrefix.Length);
}
else
{
CommandUtils.LogError("Referenced external file is not under local root: {0}", FileList.FileNames[Idx]);
}
}
// Write the resulting file list out to disk
string OutputFileListPath = StaticGetBuildDependenciesPath(HostPlatform, GameProj, TargetPlatform);
UnrealBuildTool.Utils.WriteClass<UnrealBuildTool.ExternalFileList>(FileList, OutputFileListPath, "");
AddBuildProduct(OutputFileListPath);
// Archive all the headers
FileFilter Filter = new FileFilter();
Filter.Include("/Engine/Intermediate/Build/" + TargetPlatform.ToString() + "/UE4/Inc/...");
Filter.Include("/Engine/Plugins/.../Intermediate/Build/" + TargetPlatform.ToString() + "/UE4/Inc/...");
string ZipFileName = StaticGetArchivedHeadersPath(HostPlatform, GameProj, TargetPlatform);
CommandUtils.ZipFiles(ZipFileName, CommandUtils.CmdEnv.LocalRoot, Filter);
BuildProducts.Add(ZipFileName);
}
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
if (!bp.ActivePlatforms.Contains(TargetPlatform))
{
throw new AutomationException("{0} is not a supported platform for {1}", TargetPlatform.ToString(), GetFullName());
}
var Agenda = new UE4Build.BuildAgenda();
string Args = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
if(Precompiled)
{
Args += " -precompile";
// MSVC doesn't provide a way to strip symbols from static libraries - you have to use PDBs, but that causes random OOM
// exceptions with the /FS arg because mspdbsrv is 32-bit. Just disable compiler debug info manually for now.
if(TargetPlatform == UnrealTargetPlatform.Win32 || TargetPlatform == UnrealTargetPlatform.Win64)
{
Args += " -nodebuginfo";
}
}
if (WithXp)
{
Args += " -winxp";
}
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (GameProj.Properties.Targets.ContainsKey(Kind))
{
var Target = GameProj.Properties.Targets[Kind];
var AllowXp = Target.Rules.GUBP_BuildWindowsXPMonolithics();
if (!WithXp || (AllowXp && WithXp))
{
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
var AdditionalPlatforms = Target.Rules.GUBP_GetBuildOnlyPlatforms_MonolithicOnly(HostPlatform);
var AllPlatforms = Platforms.Union(AdditionalPlatforms);
if (AllPlatforms.Contains(TargetPlatform) && Target.Rules.SupportsPlatform(TargetPlatform))
{
List<UnrealTargetConfiguration> Configs;
if(Precompiled)
{
Configs = Target.Rules.GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly(HostPlatform, TargetPlatform);
}
else
{
Configs = Target.Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, TargetPlatform).Except(Target.Rules.GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly(HostPlatform, TargetPlatform)).ToList();
}
foreach (var Config in Configs)
{
if (GameProj.GameName == bp.Branch.BaseEngineProject.GameName)
{
Agenda.AddTargets(new string[] { Target.TargetName }, TargetPlatform, Config, InAddArgs: Args);
}
else
{
Agenda.AddTargets(new string[] { Target.TargetName }, TargetPlatform, Config, GameProj.FilePath, InAddArgs: Args);
}
}
}
}
}
}
return Agenda;
}
public static string StaticGetArchivedHeadersPath(UnrealTargetPlatform HostPlatform, BranchInfo.BranchUProject GameProj, UnrealTargetPlatform TargetPlatform)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Precompiled", "Headers-" + StaticGetFullName(HostPlatform, GameProj, TargetPlatform) + ".zip");
}
public static string StaticGetBuildDependenciesPath(UnrealTargetPlatform HostPlatform, BranchInfo.BranchUProject GameProj, UnrealTargetPlatform TargetPlatform)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Precompiled", "BuildDependencies-" + StaticGetFullName(HostPlatform, GameProj, TargetPlatform) + ".xml");
}
}
public class SuccessNode : GUBPNode
{
public SuccessNode()
{
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public override bool SendSuccessEmail()
{
return true;
}
}
public class GeneralSuccessNode : GUBP.SuccessNode
{
string MyName;
public GeneralSuccessNode(string InMyName)
{
MyName = InMyName;
}
public static string StaticGetFullName(string InMyName)
{
return InMyName + "_Success";
}
public override string GetFullName()
{
return StaticGetFullName(MyName);
}
}
public class AggregateNode : GUBPNode
{
public AggregateNode()
{
}
public override bool RunInEC()
{
return false;
}
public override bool IsAggregate()
{
return true;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
}
public override void DoFakeBuild(GUBP bp) // this is used to more rapidly test a build system, it does nothing but save a record of success as a build product
{
BuildProducts = new List<string>();
}
}
public class HostPlatformAggregateNode : AggregateNode
{
protected UnrealTargetPlatform HostPlatform;
public HostPlatformAggregateNode(UnrealTargetPlatform InHostPlatform)
{
HostPlatform = InHostPlatform;
}
public static string StaticGetHostPlatformSuffix(UnrealTargetPlatform InHostPlatform)
{
return HostPlatformNode.StaticGetHostPlatformSuffix(InHostPlatform);
}
public virtual string GetHostPlatformSuffix()
{
return StaticGetHostPlatformSuffix(HostPlatform);
}
public UnrealTargetPlatform GetAltHostPlatform()
{
return GUBP.GetAltHostPlatform(HostPlatform);
}
}
public class EditorAndToolsNode : HostPlatformAggregateNode
{
public EditorAndToolsNode(GUBP bp, UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
AddDependency(ToolsNode.StaticGetFullName(HostPlatform));
AddDependency(InternalToolsNode.StaticGetFullName(HostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "BaseEditorAndTools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
}
public class AggregatePromotableNode : AggregateNode
{
protected List<UnrealTargetPlatform> HostPlatforms;
string PromotionLabelPrefix;
public AggregatePromotableNode(List<UnrealTargetPlatform> InHostPlatforms, string InPromotionLabelPrefix)
{
HostPlatforms = InHostPlatforms;
PromotionLabelPrefix = InPromotionLabelPrefix;
}
public static string StaticGetFullName(string InPromotionLabelPrefix)
{
return InPromotionLabelPrefix + "_Promotable_Aggregate";
}
public override bool IsPromotableAggregate()
{
return true;
}
public override string GetFullName()
{
return StaticGetFullName(PromotionLabelPrefix);
}
}
public class GameAggregatePromotableNode : AggregatePromotableNode
{
BranchInfo.BranchUProject GameProj;
public GameAggregatePromotableNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms, BranchInfo.BranchUProject InGameProj, bool IsSeparate)
: base(InHostPlatforms, InGameProj.GameName)
{
GameProj = InGameProj;
foreach (var HostPlatform in HostPlatforms)
{
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
if(!bp.BranchOptions.PromotablesWithoutTools.Contains(GameProj.GameName))
{
AddDependency(ToolsNode.StaticGetFullName(HostPlatform));
AddDependency(InternalToolsNode.StaticGetFullName(HostPlatform));
}
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
}
// add all of the platforms I use
{
var Platforms = bp.GetMonolithicPlatformsForUProject(HostPlatform, InGameProj, false);
if (bp.bOrthogonalizeEditorPlatforms)
{
foreach (var Plat in Platforms)
{
AddDependency(EditorPlatformNode.StaticGetFullName(HostPlatform, Plat));
}
}
}
{
if (!GameProj.Options(HostPlatform).bPromoteEditorOnly)
{
var Platforms = bp.GetMonolithicPlatformsForUProject(HostPlatform, InGameProj, true);
foreach (var Plat in Platforms)
{
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, Plat));
if (Plat == UnrealTargetPlatform.Win32 && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Game))
{
if (GameProj.Properties.Targets[TargetRules.TargetType.Game].Rules.GUBP_BuildWindowsXPMonolithics())
{
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, Plat, true));
}
}
}
}
}
}
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj)
{
return AggregatePromotableNode.StaticGetFullName(InGameProj.GameName);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override bool IsSeparatePromotable()
{
bool IsSeparate = false;
foreach(UnrealTargetPlatform HostPlatform in HostPlatforms)
{
if(GameProj.Options(HostPlatform).bSeparateGamePromotion)
{
IsSeparate = true;
}
}
return IsSeparate;
}
}
public class SharedAggregatePromotableNode : AggregatePromotableNode
{
public SharedAggregatePromotableNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms)
: base(InHostPlatforms, "Shared")
{
foreach (var HostPlatform in HostPlatforms)
{
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
{
var Options = bp.Branch.BaseEngineProject.Options(HostPlatform);
if (Options.bIsPromotable && !Options.bSeparateGamePromotion)
{
AddDependency(GameAggregatePromotableNode.StaticGetFullName(bp.Branch.BaseEngineProject));
}
}
foreach (var CodeProj in bp.Branch.CodeProjects)
{
var Options = CodeProj.Options(HostPlatform);
if (!Options.bSeparateGamePromotion)
{
if (Options.bIsPromotable)
{
AddDependency(GameAggregatePromotableNode.StaticGetFullName(CodeProj));
}
else if (Options.bTestWithShared)
{
if (!Options.bIsNonCode)
{
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, CodeProj)); // if we are just testing, we will still include the editor stuff
}
}
}
}
if(HostPlatform == UnrealTargetPlatform.Win64 && bp.ActivePlatforms.Contains(UnrealTargetPlatform.Linux))
{
AddDependency(RootEditorCrossCompileLinuxNode.StaticGetFullName());
AddDependency(ToolsCrossCompileNode.StaticGetFullName(HostPlatform));
}
}
if(!bp.BranchOptions.bNoInstalledEngine)
{
AddDependency(MakeFeaturePacksNode.StaticGetFullName(MakeFeaturePacksNode.GetDefaultBuildPlatform(bp)));
}
}
public override bool IsSeparatePromotable()
{
return true;
}
public static string StaticGetFullName()
{
return AggregatePromotableNode.StaticGetFullName("Shared");
}
}
public class WaitForUserInput : GUBPNode
{
protected bool bTriggerWasTriggered;
public WaitForUserInput()
{
bTriggerWasTriggered = false;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public override bool TriggerNode()
{
return true;
}
public override void SetAsExplicitTrigger()
{
bTriggerWasTriggered = true;
}
public override bool IsSticky()
{
return bTriggerWasTriggered;
}
public virtual string GetTriggerStateName()
{
return "GenericTrigger";
}
public virtual string GetTriggerDescText()
{
return "GenericTrigger no description text available";
}
public virtual string GetTriggerActionText()
{
return "GenericTrigger no action text available";
}
public virtual bool TriggerRequiresRecursiveWorkflow()
{
return true;
}
public override string ECProcedure()
{
if (bTriggerWasTriggered)
{
return base.ECProcedure(); // after this user hits the trigger, we want to run this as an ordinary node
}
if (TriggerRequiresRecursiveWorkflow())
{
return String.Format("GUBP_UAT_Trigger"); //here we run a recursive workflow to wait for the trigger
}
return String.Format("GUBP_Hardcoded_Trigger"); //here we advance the state in the hardcoded workflow so folks can approve
}
public override string ECProcedureParams()
{
var Result = base.ECProcedureParams();
if (!bTriggerWasTriggered)
{
Result += String.Format(", {{actualParameterName => 'TriggerState', value => '{0}'}}, {{actualParameterName => 'ActionText', value =>\"{1}\"}}, {{actualParameterName => 'DescText', value =>\"{2}\"}}", GetTriggerStateName(), GetTriggerActionText(), GetTriggerDescText());
//Result += String.Format(" --actualParameter TriggerState={0} --actualParameter ActionText=\"{1}\" --actualParameter DescText=\"{2}\"", GetTriggerStateName(), GetTriggerActionText(), GetTriggerDescText());
}
return Result;
}
public override int TimeoutInMinutes()
{
return 0;
}
}
public class WaitForPromotionUserInput : WaitForUserInput
{
string PromotionLabelPrefix;
string PromotionLabelSuffix;
protected bool bLabelPromoted; // true if this is the promoted version
public WaitForPromotionUserInput(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
{
PromotionLabelPrefix = InPromotionLabelPrefix;
PromotionLabelSuffix = InPromotionLabelSuffix;
bLabelPromoted = bInLabelPromoted;
if (bLabelPromoted)
{
AddDependency(LabelPromotableNode.StaticGetFullName(PromotionLabelPrefix, false));
}
else
{
AddDependency(AggregatePromotableNode.StaticGetFullName(PromotionLabelPrefix));
}
}
public static string StaticGetFullName(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
{
return InPromotionLabelPrefix + (bInLabelPromoted ? "_WaitForPromotion" : "_WaitForPromotable") + InPromotionLabelSuffix;
}
public override string GetFullName()
{
return StaticGetFullName(PromotionLabelPrefix, PromotionLabelSuffix, bLabelPromoted);
}
}
public class WaitForGamePromotionUserInput : WaitForPromotionUserInput
{
BranchInfo.BranchUProject GameProj;
bool bCustomWorkflow;
public WaitForGamePromotionUserInput(GUBP bp, BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
: base(InGameProj.GameName, "", bInLabelPromoted)
{
GameProj = InGameProj;
var Options = InGameProj.Options(UnrealTargetPlatform.Win64);
bCustomWorkflow = Options.bCustomWorkflowForPromotion;
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
{
return WaitForPromotionUserInput.StaticGetFullName(InGameProj.GameName, "", bInLabelPromoted);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override string GetTriggerDescText()
{
if (bLabelPromoted)
{
return GameProj.GameName + " is ready for promotion.";
}
return GameProj.GameName + " is ready to make a promotable label and begin testing.";
}
public override string GetTriggerActionText()
{
if (bLabelPromoted)
{
return "Promote " + GameProj.GameName + ".";
}
return "Make a promotable label for " + GameProj.GameName + " and begin testing.";
}
public override string GetTriggerStateName()
{
if (bCustomWorkflow)
{
return GetFullName();
}
return base.GetTriggerStateName();
}
public override bool TriggerRequiresRecursiveWorkflow()
{
if (bCustomWorkflow)
{
return !bLabelPromoted; // the promotable starts the hardcoded chain
}
return base.TriggerRequiresRecursiveWorkflow();
}
}
public class WaitForSharedPromotionUserInput : WaitForPromotionUserInput
{
public WaitForSharedPromotionUserInput(GUBP bp, bool bInLabelPromoted)
: base("Shared", IsMainBranch(), bInLabelPromoted)
{
}
public override string GetTriggerDescText()
{
if (bLabelPromoted)
{
return "The shared promotable is ready for promotion.";
}
return "The shared promotable is ready to make a promotable label.";
}
public override string GetTriggerActionText()
{
if (bLabelPromoted)
{
return "Promote the shared promotable.";
}
return "Make the shared promotable label.";
}
public static string StaticGetFullName(bool bInLabelPromoted)
{
return WaitForPromotionUserInput.StaticGetFullName("Shared", IsMainBranch(), bInLabelPromoted);
}
public static string IsMainBranch()
{
string isMain = "";
if (P4Enabled)
{
string CurrentBranch = P4Env.BuildRootP4;
if (CurrentBranch == "//depot/UE4")
{
isMain = "_WithNightlys";
}
}
return isMain;
}
public override string GetTriggerStateName()
{
return GetFullName();
}
public override bool TriggerRequiresRecursiveWorkflow()
{
return !bLabelPromoted;
}
}
public class LabelPromotableNode : GUBPNode
{
string PromotionLabelPrefix;
protected bool bLabelPromoted; // true if this is the promoted version
public LabelPromotableNode(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
{
PromotionLabelPrefix = InPromotionLabelPrefix;
bLabelPromoted = bInLabelPromoted;
AddDependency(WaitForPromotionUserInput.StaticGetFullName(PromotionLabelPrefix, InPromotionLabelSuffix, bLabelPromoted));
}
string LabelName(bool bLocalLabelPromoted)
{
string LabelPrefix = PromotionLabelPrefix;
string CompleteLabelPrefix = (bLocalLabelPromoted ? "Promoted-" : "Promotable-") + LabelPrefix;
if (LabelPrefix == "Shared" && bLocalLabelPromoted)
{
// shared promotion has a shorter name
CompleteLabelPrefix = "Promoted";
}
if (LabelPrefix == "Shared" && !bLocalLabelPromoted)
{
//shared promotable has a shorter name
CompleteLabelPrefix = "Promotable";
}
if (GUBP.bPreflightBuild)
{
CompleteLabelPrefix = CompleteLabelPrefix + PreflightMangleSuffix;
}
return CompleteLabelPrefix;
}
public override bool IsSticky()
{
return true;
}
public override bool SendSuccessEmail()
{
return true;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
if (P4Enabled)
{
if (AllDependencyBuildProducts.Count == 0)
{
throw new AutomationException("{0} has no build products", GetFullName());
}
if (bLabelPromoted)
{
P4.MakeDownstreamLabelFromLabel(P4Env, LabelName(true), LabelName(false));
}
else
{
int WorkingCL = P4.CreateChange(P4Env.Client, String.Format("GUBP Node {0} built from changelist {1}", GetFullName(), bp.CL));
Log("Build from {0} Working in {1}", bp.CL, WorkingCL);
var ProductsToSubmit = new List<String>();
foreach (var Product in AllDependencyBuildProducts)
{
// hacks to keep certain things out of P4
if (
!Product.EndsWith("version.h", StringComparison.InvariantCultureIgnoreCase) &&
!Product.EndsWith("version.cpp", StringComparison.InvariantCultureIgnoreCase) &&
!Product.Replace('\\', '/').EndsWith("DotNetCommon/MetaData.cs", StringComparison.InvariantCultureIgnoreCase) &&
!Product.EndsWith("_Success.log", StringComparison.InvariantCultureIgnoreCase) &&
!Product.Replace('\\', '/').Contains("/Intermediate/") &&
!Product.Replace('\\', '/').Contains("/Engine/Saved/") &&
!Product.Replace('\\', '/').Contains("/DerivedDataCache/") &&
!Product.EndsWith(".lib") &&
!Product.EndsWith(".a") &&
!Product.EndsWith(".bc")
)
{
ProductsToSubmit.Add(Product);
}
}
// Open files for add or edit
UE4Build.AddBuildProductsToChangelist(WorkingCL, ProductsToSubmit);
// Check everything in!
int SubmittedCL;
P4.Submit(WorkingCL, out SubmittedCL, true, true);
// Label it
P4.MakeDownstreamLabel(P4Env, LabelName(false), null);
}
}
SaveRecordOfSuccessAndAddToBuildProducts();
}
public static string StaticGetFullName(string InPromotionLabelPrefix, bool bInLabelPromoted)
{
return InPromotionLabelPrefix + (bInLabelPromoted ? "_LabelPromoted" : "_LabelPromotable");
}
public override string GetFullName()
{
return StaticGetFullName(PromotionLabelPrefix, bLabelPromoted);
}
}
public class GameLabelPromotableNode : LabelPromotableNode
{
BranchInfo.BranchUProject GameProj;
public GameLabelPromotableNode(GUBP bp, BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
: base(InGameProj.GameName, "", bInLabelPromoted)
{
GameProj = InGameProj;
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
{
return LabelPromotableNode.StaticGetFullName(InGameProj.GameName, bInLabelPromoted);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
}
public class SharedLabelPromotableNode : LabelPromotableNode
{
public SharedLabelPromotableNode(GUBP bp, bool bInLabelPromoted)
: base("Shared", IsMainBranch(), bInLabelPromoted)
{
}
public static string StaticGetFullName(bool bInLabelPromoted)
{
return LabelPromotableNode.StaticGetFullName("Shared", bInLabelPromoted);
}
public static string IsMainBranch()
{
string isMain = "";
if (P4Enabled)
{
string CurrentBranch = P4Env.BuildRootP4;
if (CurrentBranch == "//depot/UE4")
{
isMain = "_WithNightlys";
}
}
return isMain;
}
}
public class SharedLabelPromotableSuccessNode : AggregateNode
{
public SharedLabelPromotableSuccessNode()
{
AddDependency(SharedLabelPromotableNode.StaticGetFullName(false));
}
public static string StaticGetFullName()
{
return SharedLabelPromotableNode.StaticGetFullName(false) + "Aggregate";
}
public override string GetFullName()
{
return StaticGetFullName();
}
}
public class WaitForTestShared : AggregateNode
{
public WaitForTestShared(GUBP bp)
{
}
public static string StaticGetFullName()
{
return "Shared_TestingAggregate";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 5;
}
}
public class CookNode : HostPlatformNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform TargetPlatform;
string CookPlatform;
bool bIsMassive;
public CookNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, string InCookPlatform)
: base(InHostPlatform)
{
GameProj = InGameProj;
TargetPlatform = InTargetPlatform;
CookPlatform = InCookPlatform;
bIsMassive = false;
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
if (bp.bOrthogonalizeEditorPlatforms)
{
if (TargetPlatform != HostPlatform && TargetPlatform != GUBP.GetAltHostPlatform(HostPlatform))
{
AddDependency(EditorPlatformNode.StaticGetFullName(HostPlatform, TargetPlatform));
}
}
bool bIsShared = false;
// is this the "base game" or a non code project?
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
var Options = InGameProj.Options(HostPlatform);
bIsMassive = Options.bIsMassive;
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
// add an arc to prevent cooks from running until promotable is labeled
if (Options.bIsPromotable)
{
if (!Options.bSeparateGamePromotion)
{
bIsShared = true;
}
}
else if (Options.bTestWithShared)
{
bIsShared = true;
}
if(!bp.BranchOptions.bNoMonolithicDependenciesForCooks)
{
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform));
}
}
else
{
bIsShared = true;
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
}
if (bIsShared)
{
// add an arc to prevent cooks from running until promotable is labeled
AddPseudodependency(WaitForTestShared.StaticGetFullName());
AgentSharingGroup = "SharedCooks" + StaticGetHostPlatformSuffix(HostPlatform);
// If the cook fails for the base engine, don't bother trying
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && bp.HasNode(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, CookPlatform)))
{
AddPseudodependency(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, CookPlatform));
}
// If the base cook platform fails, don't bother trying other ones
string BaseCookedPlatform = Platform.Platforms[HostPlatform].GetCookPlatform(false, false, "");
if (InGameProj.GameName == bp.Branch.BaseEngineProject.GameName && CookPlatform != BaseCookedPlatform &&
bp.HasNode(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, BaseCookedPlatform)))
{
AddPseudodependency(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, BaseCookedPlatform));
}
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InCookPlatform)
{
return InGameProj.GameName + "_" + InCookPlatform + "_Cook" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, CookPlatform);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 4 + (bIsMassive ? 1 : 0);
}
public override float Priority()
{
return 10.0f;
}
public override int AgentMemoryRequirement(GUBP bp)
{
return bIsMassive ? 32 : 0;
}
public override int TimeoutInMinutes()
{
return bIsMassive ? 240 : base.TimeoutInMinutes();
}
public override string RootIfAnyForTempStorage()
{
return CombinePaths(Path.GetDirectoryName(GameProj.FilePath), "Saved", "Cooked", CookPlatform);
}
public override void DoBuild(GUBP bp)
{
if (HostPlatform == UnrealTargetPlatform.Mac)
{
// not sure if we need something here or if the cook commandlet will automatically convert the exe name
}
var StartCook = DateTime.Now.ToString();
CommandUtils.CookCommandlet(GameProj.FilePath, "UE4Editor-Cmd.exe", null, null, null, null, CookPlatform);
var FinishCook = DateTime.Now.ToString();
PrintCSVFile(String.Format("UAT,Cook.{0}.{1},{2},{3}", GameProj.GameName, CookPlatform, StartCook, FinishCook));
var CookedPath = RootIfAnyForTempStorage();
var CookedFiles = CommandUtils.FindFiles("*", true, CookedPath);
if (CookedFiles.GetLength(0) < 1)
{
throw new AutomationException("CookedPath {1} did not produce any files.", CookedPath);
}
BuildProducts = new List<string>();
foreach (var CookedFile in CookedFiles)
{
AddBuildProduct(CookedFile);
}
}
}
public class DDCNode : HostPlatformNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform TargetPlatform;
string CookPlatform;
bool bIsMassive;
public DDCNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, string InCookPlatform)
: base(InHostPlatform)
{
GameProj = InGameProj;
TargetPlatform = InTargetPlatform;
CookPlatform = InCookPlatform;
bIsMassive = false;
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
if (bp.bOrthogonalizeEditorPlatforms)
{
if (TargetPlatform != HostPlatform && TargetPlatform != GUBP.GetAltHostPlatform(HostPlatform))
{
AddDependency(EditorPlatformNode.StaticGetFullName(HostPlatform, TargetPlatform));
}
}
bool bIsShared = false;
// is this the "base game" or a non code project?
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
var Options = InGameProj.Options(HostPlatform);
bIsMassive = Options.bIsMassive;
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
// add an arc to prevent DDCNode from running until promotable is labeled
if (Options.bIsPromotable)
{
if (Options.bSeparateGamePromotion)
{
// AddPseudodependency(GameLabelPromotableNode.StaticGetFullName(GameProj, false));
}
else
{
bIsShared = true;
}
}
else if (Options.bTestWithShared)
{
bIsShared = true;
}
//AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform));
}
else
{
bIsShared = true;
//AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
}
if (bIsShared)
{
// add an arc to prevent cooks from running until promotable is labeled
//AddPseudodependency(WaitForTestShared.StaticGetFullName());
//AgentSharingGroup = "SharedCooks" + StaticGetHostPlatformSuffix(HostPlatform);
// If the cook fails for the base engine, don't bother trying
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && bp.HasNode(DDCNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, CookPlatform)))
{
//AddPseudodependency(DDCNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, CookPlatform));
}
// If the base cook platform fails, don't bother trying other ones
string BaseCookedPlatform = Platform.Platforms[HostPlatform].GetCookPlatform(false, false, "");
if (InGameProj.GameName == bp.Branch.BaseEngineProject.GameName && CookPlatform != BaseCookedPlatform &&
bp.HasNode(DDCNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, BaseCookedPlatform)))
{
//AddPseudodependency(DDCNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, BaseCookedPlatform));
}
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InCookPlatform)
{
return InGameProj.GameName + "_" + InCookPlatform.Replace("+", "_") + "_DDC" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, CookPlatform);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override float Priority()
{
return base.Priority() + 10.0f;
}
public override int AgentMemoryRequirement(GUBP bp)
{
return bIsMassive ? 32 : 0;
}
public override int TimeoutInMinutes()
{
return bIsMassive ? 240 : base.TimeoutInMinutes();
}
public override void DoBuild(GUBP bp)
{
if (HostPlatform == UnrealTargetPlatform.Mac)
{
// not sure if we need something here or if the cook commandlet will automatically convert the exe name
}
CommandUtils.DDCCommandlet(GameProj.FilePath, "UE4Editor-Cmd.exe", null, CookPlatform, "-fill");
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class GamePlatformCookedAndCompiledNode : HostPlatformAggregateNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform TargetPlatform;
public GamePlatformCookedAndCompiledNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, bool bCodeProject)
: base(InHostPlatform)
{
GameProj = InGameProj;
TargetPlatform = InTargetPlatform;
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (bCodeProject)
{
if (GameProj.Properties.Targets.ContainsKey(Kind))
{
var Target = GameProj.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
if (Platforms.Contains(TargetPlatform) && Target.Rules.SupportsPlatform(TargetPlatform))
{
//@todo how do we get the client target platform?
string CookedPlatform = Platform.Platforms[TargetPlatform].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
if (Target.Rules.GUBP_AlternateCookPlatform(HostPlatform, CookedPlatform) != "")
{
CookedPlatform = Target.Rules.GUBP_AlternateCookPlatform(HostPlatform, CookedPlatform);
}
AddDependency(CookNode.StaticGetFullName(HostPlatform, GameProj, CookedPlatform));
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform));
if(Target.Rules.GUBP_BuildWindowsXPMonolithics())
{
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform, true));
}
}
}
}
else
{
if (Kind == TargetRules.TargetType.Game) //for now, non-code projects don't do client or server.
{
if (bp.Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
{
var Target = bp.Branch.BaseEngineProject.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
if (Platforms.Contains(TargetPlatform) && Target.Rules.SupportsPlatform(TargetPlatform))
{
//@todo how do we get the client target platform?
string CookedPlatform = Platform.Platforms[TargetPlatform].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
AddDependency(CookNode.StaticGetFullName(HostPlatform, GameProj, CookedPlatform));
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
}
}
}
}
}
// put these in the right agent group, even though they aren't exposed to EC to sort right.
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
var Options = InGameProj.Options(HostPlatform);
if ((Options.bIsPromotable || Options.bTestWithShared) && !Options.bSeparateGamePromotion)
{
AgentSharingGroup = "SharedCooks" + StaticGetHostPlatformSuffix(HostPlatform);
}
}
else
{
AgentSharingGroup = "SharedCooks" + StaticGetHostPlatformSuffix(HostPlatform);
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform)
{
return InGameProj.GameName + "_" + InTargetPlatform + "_CookedAndCompiled" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, TargetPlatform);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
};
public class FormalBuildNode : HostPlatformNode
{
BranchInfo.BranchUProject GameProj;
//CAUTION, these are lists, but it isn't clear that lists really work on all platforms, so we stick to one node per build
List<UnrealTargetPlatform> ClientTargetPlatforms;
List<UnrealTargetPlatform> ServerTargetPlatforms;
List<UnrealTargetConfiguration> ClientConfigs;
List<UnrealTargetConfiguration> ServerConfigs;
bool ClientNotGame;
bool bIsCode;
UnrealBuildTool.TargetRules.TargetType GameOrClient;
public FormalBuildNode(GUBP bp,
BranchInfo.BranchUProject InGameProj,
UnrealTargetPlatform InHostPlatform,
List<UnrealTargetPlatform> InClientTargetPlatforms = null,
List<UnrealTargetConfiguration> InClientConfigs = null,
List<UnrealTargetPlatform> InServerTargetPlatforms = null,
List<UnrealTargetConfiguration> InServerConfigs = null,
bool InClientNotGame = false
)
: base(InHostPlatform)
{
GameProj = InGameProj;
ClientTargetPlatforms = InClientTargetPlatforms;
ServerTargetPlatforms = InServerTargetPlatforms;
ClientConfigs = InClientConfigs;
ServerConfigs = InServerConfigs;
ClientNotGame = InClientNotGame;
GameOrClient = TargetRules.TargetType.Game;
if (ClientNotGame)
{
GameOrClient = TargetRules.TargetType.Client;
}
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
bIsCode = true;
}
else
{
bIsCode = false;
}
// verify we actually built these
var WorkingGameProject = InGameProj;
if (!WorkingGameProject.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
// this is a codeless project, use the base project
WorkingGameProject = bp.Branch.BaseEngineProject;
}
var AllTargetPlatforms = new List<UnrealTargetPlatform>();
if (ClientTargetPlatforms != null)
{
if (!WorkingGameProject.Properties.Targets.ContainsKey(GameOrClient))
{
throw new AutomationException("Can't make a game build for {0} because it doesn't have a {1} target.", WorkingGameProject.GameName, GameOrClient.ToString());
}
foreach (var Plat in ClientTargetPlatforms)
{
if (!AllTargetPlatforms.Contains(Plat))
{
AllTargetPlatforms.Add(Plat);
}
}
if (ClientConfigs == null)
{
ClientConfigs = new List<UnrealTargetConfiguration>() { UnrealTargetConfiguration.Development };
}
foreach (var Plat in ClientTargetPlatforms)
{
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform).Contains(Plat))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1}.", WorkingGameProject.GameName, Plat.ToString());
}
foreach (var Config in ClientConfigs)
{
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, Plat).Contains(Config))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1} config {2}.", WorkingGameProject.GameName, Plat.ToString(), Config.ToString());
}
}
}
}
if (ServerTargetPlatforms != null)
{
if (!WorkingGameProject.Properties.Targets.ContainsKey(TargetRules.TargetType.Server) && ServerTargetPlatforms != null)
{
throw new AutomationException("Can't make a server build for {0} because it doesn't have a server target.", WorkingGameProject.GameName);
}
foreach (var Plat in ServerTargetPlatforms)
{
if (!AllTargetPlatforms.Contains(Plat))
{
AllTargetPlatforms.Add(Plat);
}
}
if (ServerConfigs == null)
{
ServerConfigs = new List<UnrealTargetConfiguration>() { UnrealTargetConfiguration.Development };
}
foreach (var Plat in ServerTargetPlatforms)
{
if (!WorkingGameProject.Properties.Targets[TargetRules.TargetType.Server].Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform).Contains(Plat))
{
throw new AutomationException("Can't make a server build for {0} because we didn't build platform {1}.", WorkingGameProject.GameName, Plat.ToString());
}
foreach (var Config in ServerConfigs)
{
if (!WorkingGameProject.Properties.Targets[TargetRules.TargetType.Server].Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, Plat).Contains(Config))
{
throw new AutomationException("Can't make a server build for {0} because we didn't build platform {1} config {2}.", WorkingGameProject.GameName, Plat.ToString(), Config.ToString());
}
}
}
}
// add dependencies for cooked and compiled
foreach (var Plat in AllTargetPlatforms)
{
AddDependency(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, GameProj, Plat));
}
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InHostPlatform, List<UnrealTargetPlatform> InClientTargetPlatforms = null, List<UnrealTargetConfiguration> InClientConfigs = null, List<UnrealTargetPlatform> InServerTargetPlatforms = null, List<UnrealTargetConfiguration> InServerConfigs = null, bool InClientNotGame = false)
{
string Infix = "";
if (InClientNotGame)
{
if (InClientTargetPlatforms != null && InClientTargetPlatforms.Count == 1)
{
Infix = "_Client_" + InClientTargetPlatforms[0].ToString();
}
if (InClientConfigs != null && InClientConfigs.Count == 1)
{
Infix += "_Client_" + InClientConfigs[0].ToString();
}
}
else
{
if (InClientTargetPlatforms != null && InClientTargetPlatforms.Count == 1)
{
Infix = "_" + InClientTargetPlatforms[0].ToString();
}
if (InClientConfigs != null && InClientConfigs.Count == 1)
{
Infix += "_" + InClientConfigs[0].ToString();
}
}
if (InServerTargetPlatforms != null && InServerTargetPlatforms.Count == 1)
{
Infix = "_Serv_" + InServerTargetPlatforms[0].ToString();
}
if (InServerConfigs != null && InServerConfigs.Count == 1)
{
Infix += "_Serv_" + InServerConfigs[0].ToString();
}
return InGameProj.GameName + Infix + "_MakeBuild" + HostPlatformNode.StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(GameProj, HostPlatform, ClientTargetPlatforms, ClientConfigs, ServerTargetPlatforms, ServerConfigs, ClientNotGame);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override bool SendSuccessEmail()
{
return true;
}
public override string ECAgentString()
{
string Result = base.ECAgentString();
if (ClientTargetPlatforms != null)
{
if (!ClientNotGame)
{
foreach (UnrealTargetPlatform Plat in ClientTargetPlatforms)
{
if (Plat == UnrealTargetPlatform.XboxOne)
{
Result = MergeSpaceStrings(Result, Plat.ToString());
}
}
}
}
return Result;
}
public override float Priority()
{
return base.Priority() + 20.0f;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public static string GetArchiveDirectory(BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InHostPlatform, List<UnrealTargetPlatform> InClientTargetPlatforms = null, List<UnrealTargetConfiguration> InClientConfigs = null, List<UnrealTargetPlatform> InServerTargetPlatforms = null, List<UnrealTargetConfiguration> InServerConfigs = null, bool InClientNotGame = false)
{
string BaseDir = TempStorage.ResolveSharedBuildDirectory(InGameProj.GameName);
string NodeName = StaticGetFullName(InGameProj, InHostPlatform, InClientTargetPlatforms, InClientConfigs, InServerTargetPlatforms, InServerConfigs, InClientNotGame);
string Inner = P4Env.BuildRootEscaped + "-CL-" + P4Env.ChangelistString;
if (GUBP.bPreflightBuild)
{
Inner = Inner + PreflightMangleSuffix;
}
string ArchiveDirectory = CombinePaths(BaseDir, NodeName, Inner);
return ArchiveDirectory;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
string ProjectArg = "";
if (!String.IsNullOrEmpty(GameProj.FilePath))
{
ProjectArg = " -project=\"" + GameProj.FilePath + "\"";
}
string Args = String.Format("BuildCookRun{0} -SkipBuild -SkipCook -Stage -Pak -Package -NoSubmit", ProjectArg);
bool bXboxOneTarget = false;
if (ClientTargetPlatforms != null)
{
bool bFirstClient = true;
foreach (var Plat in ClientTargetPlatforms)
{
if (Plat == UnrealTargetPlatform.XboxOne)
{
bXboxOneTarget = true;
}
if (bFirstClient)
{
bFirstClient = false;
Args += String.Format(" -platform={0}", Plat.ToString());
}
else
{
Args += String.Format("+{0}", Plat.ToString());
}
if(bIsCode)
{
var Target = GameProj.Properties.Targets[TargetRules.TargetType.Game];
if(ClientNotGame)
{
Target = GameProj.Properties.Targets[TargetRules.TargetType.Client];
}
if (Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat) != "")
{
Args += " " + Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat);
}
}
}
bool bFirstClientConfig = true;
foreach (var Config in ClientConfigs)
{
if (bFirstClientConfig)
{
bFirstClientConfig = false;
Args += String.Format(" -clientconfig={0}", Config.ToString());
}
else
{
Args += String.Format("+{0}", Config.ToString());
}
}
if (ClientNotGame)
{
Args += " -client";
}
}
else
{
Args += " -noclient";
}
if (ServerTargetPlatforms != null)
{
Args += " -server";
bool bFirstServer = true;
foreach (var Plat in ServerTargetPlatforms)
{
if (Plat == UnrealTargetPlatform.XboxOne)
{
bXboxOneTarget = true;
}
if (bFirstServer)
{
bFirstServer = false;
Args += String.Format(" -serverplatform={0}", Plat.ToString());
}
else
{
Args += String.Format("+{0}", Plat.ToString());
}
if (bIsCode)
{
var Target = GameProj.Properties.Targets[TargetRules.TargetType.Server];
if (Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat) != "")
{
Args += " " + Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat);
}
}
}
bool bFirstServerConfig = true;
foreach (var Config in ServerConfigs)
{
if (bFirstServerConfig)
{
bFirstServerConfig = false;
Args += String.Format(" -serverconfig={0}", Config.ToString());
}
else
{
Args += String.Format("+{0}", Config.ToString());
}
}
}
string FinalArchiveDirectory = "";
string IntermediateArchiveDirectory = FinalArchiveDirectory;
if (P4Enabled)
{
FinalArchiveDirectory = GetArchiveDirectory(GameProj, HostPlatform, ClientTargetPlatforms, ClientConfigs, ServerTargetPlatforms, ServerConfigs, ClientNotGame);
IntermediateArchiveDirectory = FinalArchiveDirectory;
// Xbox One packaging does not function with remote file systems. Use a temp local directory to package and then move files into final location.
if (bXboxOneTarget)
{
IntermediateArchiveDirectory = Path.Combine(Path.GetTempPath(), "GUBP.XboxOne");
if (DirectoryExists_NoExceptions(IntermediateArchiveDirectory))
{
DeleteDirectory_NoExceptions(IntermediateArchiveDirectory);
}
CreateDirectory_NoExceptions(IntermediateArchiveDirectory);
}
CleanFormalBuilds(FinalArchiveDirectory);
if (DirectoryExists_NoExceptions(FinalArchiveDirectory))
{
if (IsBuildMachine)
{
throw new AutomationException("Archive directory already exists {0}", FinalArchiveDirectory);
}
DeleteDirectory_NoExceptions(FinalArchiveDirectory);
}
Args += String.Format(" -Archive -archivedirectory={0}", CommandUtils.MakePathSafeToUseWithCommandLine(IntermediateArchiveDirectory));
}
string LogFile = CommandUtils.RunUAT(CommandUtils.CmdEnv, Args);
if (P4Enabled)
{
if (!FinalArchiveDirectory.Equals(IntermediateArchiveDirectory, StringComparison.InvariantCultureIgnoreCase))
{
CopyDirectory_NoExceptions(IntermediateArchiveDirectory, FinalArchiveDirectory);
DeleteDirectory_NoExceptions(IntermediateArchiveDirectory);
}
}
SaveRecordOfSuccessAndAddToBuildProducts(CommandUtils.ReadAllText(LogFile));
}
}
public class TestNode : HostPlatformNode
{
public TestNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
}
public override float Priority()
{
return 0.0f;
}
public virtual void DoTest(GUBP bp)
{
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
DoTest(bp);
}
}
public class FormalBuildTestNode : TestNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform ClientTargetPlatform;
UnrealTargetConfiguration ClientConfig;
UnrealBuildTool.TargetRules.TargetType GameOrClient;
public FormalBuildTestNode(GUBP bp,
BranchInfo.BranchUProject InGameProj,
UnrealTargetPlatform InHostPlatform,
UnrealTargetPlatform InClientTargetPlatform,
UnrealTargetConfiguration InClientConfig
)
: base(InHostPlatform)
{
GameProj = InGameProj;
ClientTargetPlatform = InClientTargetPlatform;
ClientConfig = InClientConfig;
GameOrClient = TargetRules.TargetType.Game;
// verify we actually built these
var WorkingGameProject = InGameProj;
if (!WorkingGameProject.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
// this is a codeless project, use the base project
WorkingGameProject = bp.Branch.BaseEngineProject;
}
if (!WorkingGameProject.Properties.Targets.ContainsKey(GameOrClient))
{
throw new AutomationException("Can't make a game build for {0} because it doesn't have a {1} target.", WorkingGameProject.GameName, GameOrClient.ToString());
}
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform).Contains(ClientTargetPlatform))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1}.", WorkingGameProject.GameName, ClientTargetPlatform.ToString());
}
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, ClientTargetPlatform).Contains(ClientConfig))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1} config {2}.", WorkingGameProject.GameName, ClientTargetPlatform.ToString(), ClientConfig.ToString());
}
AddDependency(FormalBuildNode.StaticGetFullName(GameProj, HostPlatform, new List<UnrealTargetPlatform>() { ClientTargetPlatform }, InClientConfigs: new List<UnrealTargetConfiguration>() { ClientConfig }, InClientNotGame: GameOrClient == TargetRules.TargetType.Client));
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InClientTargetPlatform, UnrealTargetConfiguration InClientConfig)
{
string Infix = "_" + InClientTargetPlatform.ToString();
Infix += "_" + InClientConfig.ToString();
return InGameProj.GameName + Infix + "_TestBuild" + HostPlatformNode.StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(GameProj, HostPlatform, ClientTargetPlatform, ClientConfig);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override float Priority()
{
return base.Priority() - 20.0f;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public override void DoTest(GUBP bp)
{
string ProjectArg = "";
if (!String.IsNullOrEmpty(GameProj.FilePath))
{
ProjectArg = " -project=\"" + GameProj.FilePath + "\"";
}
string ArchiveDirectory = FormalBuildNode.GetArchiveDirectory(GameProj, HostPlatform, new List<UnrealTargetPlatform>() { ClientTargetPlatform }, InClientConfigs: new List<UnrealTargetConfiguration>() { ClientConfig }, InClientNotGame: GameOrClient == TargetRules.TargetType.Client);
if (!DirectoryExists_NoExceptions(ArchiveDirectory))
{
throw new AutomationException("Archive directory does not exist {0}, so we can't test the build.", ArchiveDirectory);
}
string WorkingCommandline = String.Format("TestFormalBuild {0} -Archive -alldevices -archivedirectory={1} -platform={2} -clientconfig={3} -runtimeoutseconds=300",
ProjectArg, CommandUtils.MakePathSafeToUseWithCommandLine(ArchiveDirectory), ClientTargetPlatform.ToString(), ClientConfig.ToString());
if (WorkingCommandline.Contains("-project=\"\""))
{
throw new AutomationException("Command line {0} contains -project=\"\" which is doomed to fail", WorkingCommandline);
}
string LogFile = RunUAT(CommandUtils.CmdEnv, WorkingCommandline);
SaveRecordOfSuccessAndAddToBuildProducts(CommandUtils.ReadAllText(LogFile));
}
public override string ECAgentString()
{
string Result = base.ECAgentString();
if (ClientTargetPlatform != HostPlatform)
{
Result = MergeSpaceStrings(Result, ClientTargetPlatform.ToString());
}
return Result;
}
}
public class NonUnityToolNode : TestNode
{
SingleTargetProperties ProgramTarget;
public NonUnityToolNode(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
ProgramTarget = InProgramTarget;
AddPseudodependency(SingleInternalToolsNode.StaticGetFullName(HostPlatform, ProgramTarget));
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InGameProj)
{
return InGameProj.TargetName + "_NonUnityTestCompile" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, ProgramTarget);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 2;
if (HostPlatform == UnrealTargetPlatform.Mac)
{
Result += 1;
}
return Result;
}
public override int AgentMemoryRequirement(GUBP bp)
{
int Result = base.AgentMemoryRequirement(bp);
if (HostPlatform == UnrealTargetPlatform.Mac)
{
Result = 32;
}
return Result;
}
public override void DoTest(GUBP bp)
{
var Build = new UE4Build(bp);
var Agenda = new UE4Build.BuildAgenda();
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development);
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-skipnonhostplatforms");
Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: false, InForceNonUnity: true, InForceNoXGE: true);
UE4Build.CheckBuildProducts(Build.BuildProductFiles);
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class NonUnityTestNode : TestNode
{
public NonUnityTestNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "NonUnityTestCompile" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 2;
if(HostPlatform == UnrealTargetPlatform.Mac)
{
Result += 1;
}
return Result;
}
public override int AgentMemoryRequirement(GUBP bp)
{
int Result = base.AgentMemoryRequirement(bp);
if(HostPlatform == UnrealTargetPlatform.Mac)
{
Result = 32;
}
return Result;
}
public override void DoTest(GUBP bp)
{
var Build = new UE4Build(bp);
var Agenda = new UE4Build.BuildAgenda();
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development);
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-skipnonhostplatforms -shadowvariableerrors");
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (bp.Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
{
var Target = bp.Branch.BaseEngineProject.Properties.Targets[Kind];
Agenda.AddTargets(new string[] { Target.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-shadowvariableerrors");
}
}
Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: false, InForceNonUnity: true, InForceNoXGE: true);
UE4Build.CheckBuildProducts(Build.BuildProductFiles);
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class IOSOnPCTestNode : TestNode
{
public IOSOnPCTestNode(GUBP bp)
: base(UnrealTargetPlatform.Win64)
{
AddDependency(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AddDependency(ToolsNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(UnrealTargetPlatform.Mac, bp.Branch.BaseEngineProject, UnrealTargetPlatform.IOS));
}
public static string StaticGetFullName()
{
return "IOSOnPCTestCompile";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public override void DoTest(GUBP bp)
{
var Build = new UE4Build(bp);
var Agenda = new UE4Build.BuildAgenda();
Agenda.AddTargets(new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Game].TargetName }, UnrealTargetPlatform.IOS, UnrealTargetConfiguration.Development);
Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: false);
UE4Build.CheckBuildProducts(Build.BuildProductFiles);
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class VSExpressTestNode : TestNode
{
public VSExpressTestNode(GUBP bp)
: base(UnrealTargetPlatform.Win64)
{
AddDependency(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AddDependency(RootEditorNode.StaticGetFullName(UnrealTargetPlatform.Win64));
}
public static string StaticGetFullName()
{
return "VSExpressTestCompile";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public override string ECAgentString()
{
return "VCTestAgent";
}
public override void DoTest(GUBP bp)
{
var Build = new UE4Build(bp);
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht";
if (bp.bOrthogonalizeEditorPlatforms)
{
AddArgs += " -skipnonhostplatforms";
}
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
}
}
Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: false);
UE4Build.CheckBuildProducts(Build.BuildProductFiles);
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class UATTestNode : TestNode
{
string TestName;
BranchInfo.BranchUProject GameProj;
string UATCommandLine;
bool DependsOnEditor;
List<UnrealTargetPlatform> DependsOnCooked;
float ECPriority;
public UATTestNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InTestName, string InUATCommandLine, string InAgentSharingGroup, bool InDependsOnEditor = true, List<UnrealTargetPlatform> InDependsOnCooked = null, float InECPriority = 0.0f)
: base(InHostPlatform)
{
AgentSharingGroup = InAgentSharingGroup;
ECPriority = InECPriority;
GameProj = InGameProj;
TestName = InTestName;
UATCommandLine = InUATCommandLine;
bool bWillCook = InUATCommandLine.IndexOf("-cook") >= 0;
DependsOnEditor = InDependsOnEditor || bWillCook;
if (InDependsOnCooked != null)
{
DependsOnCooked = InDependsOnCooked;
}
else
{
DependsOnCooked = new List<UnrealTargetPlatform>();
}
if (DependsOnEditor)
{
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
if (GameProj.GameName != bp.Branch.BaseEngineProject.GameName)
{
if (GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
}
}
}
foreach (var Plat in DependsOnCooked)
{
AddDependency(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, GameProj, Plat));
}
AddPseudodependency(WaitForTestShared.StaticGetFullName());
// If the same test fails for the base engine, don't bother trying
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName)
{
if (bp.HasNode(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TestName)))
{
AddPseudodependency(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, InTestName));
}
else
{
bool bFoundACook = false;
foreach (var Plat in DependsOnCooked)
{
var PlatTestName = "CookedGameTest_" + Plat.ToString();
if (bp.HasNode(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, PlatTestName)))
{
AddPseudodependency(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, PlatTestName));
bFoundACook = true;
}
}
if (!bFoundACook && bp.HasNode(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, "EditorTest")))
{
AddPseudodependency(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, "EditorTest"));
}
}
}
if (InGameProj.GameName == bp.Branch.BaseEngineProject.GameName)
{
ECPriority = ECPriority + 1.0f;
}
if (UATCommandLine.IndexOf("-RunAutomationTests", StringComparison.InvariantCultureIgnoreCase) >= 0)
{
ECPriority = ECPriority - 4.0f;
if (UATCommandLine.IndexOf("-EditorTest", StringComparison.InvariantCultureIgnoreCase) >= 0)
{
ECPriority = ECPriority - 4.0f;
}
}
else if (UATCommandLine.IndexOf("-EditorTest", StringComparison.InvariantCultureIgnoreCase) >= 0)
{
ECPriority = ECPriority + 2.0f;
}
}
public override float Priority()
{
return ECPriority;
}
public override bool IsTest()
{
return true;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 5;
}
public override string ECAgentString()
{
string Result = base.ECAgentString();
foreach (UnrealTargetPlatform platform in Enum.GetValues(typeof(UnrealTargetPlatform)))
{
if (platform != HostPlatform && platform != GetAltHostPlatform())
{
if (UATCommandLine.IndexOf("-platform=" + platform.ToString(), StringComparison.InvariantCultureIgnoreCase) >= 0)
{
Result = MergeSpaceStrings(Result, platform.ToString());
}
}
}
return Result;
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InTestName)
{
return InGameProj.GameName + "_" + InTestName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, TestName);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override void DoTest(GUBP bp)
{
string ProjectArg = "";
if (!String.IsNullOrEmpty(GameProj.FilePath) && UATCommandLine.IndexOf("-project=", StringComparison.InvariantCultureIgnoreCase) < 0)
{
ProjectArg = " -project=\"" + GameProj.FilePath + "\"";
}
string WorkingCommandline = UATCommandLine + ProjectArg + " -NoSubmit -addcmdline=\"-DisablePS4TMAPI\"";
if (WorkingCommandline.Contains("-project=\"\""))
{
throw new AutomationException("Command line {0} contains -project=\"\" which is doomed to fail", WorkingCommandline);
}
string LogFile = RunUAT(CommandUtils.CmdEnv, WorkingCommandline);
SaveRecordOfSuccessAndAddToBuildProducts(CommandUtils.ReadAllText(LogFile));
}
}
public class GameAggregateNode : HostPlatformAggregateNode
{
BranchInfo.BranchUProject GameProj;
string AggregateName;
float ECPriority;
public GameAggregateNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InAggregateName, List<string> Dependencies, float InECPriority = 100.0f)
: base(InHostPlatform)
{
GameProj = InGameProj;
AggregateName = InAggregateName;
ECPriority = InECPriority;
foreach (var Dep in Dependencies)
{
AddDependency(Dep);
}
}
public override float Priority()
{
return ECPriority;
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InAggregateName)
{
return InGameProj.GameName + "_" + InAggregateName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, AggregateName);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
};
public class CleanSharedTempStorageNode : GUBPNode
{
public CleanSharedTempStorageNode(GUBP bp)
{
var ToolsNode = bp.FindNode(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AgentSharingGroup = ToolsNode.AgentSharingGroup;
}
public override float Priority()
{
return -1E15f;
}
public static string StaticGetFullName()
{
return "CleanSharedTempStorage";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override void DoBuild(GUBP bp)
{
{
var StartTime = DateTime.UtcNow;
foreach (var NodeToDo in bp.GUBPNodes)
{
TempStorage.CleanSharedTempStorageDirectory(NodeToDo.Value.GameNameIfAnyForTempStorage());
}
var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
Log("Took {0}s to clear temp storage of old files.", BuildDuration / 1000);
}
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
};
}