Files
UnrealEngineUWP/Engine/Source/Programs/AutomationTool/Scripts/GUBP.Automation.cs
2014-06-16 17:07:24 -04:00

6358 lines
273 KiB
C#

// Copyright 1998-2014 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;
public class ECJobPropsUtils
{
public static HashSet<string> ErrorsFromProps(string Filename)
{
var Result = new HashSet<string>();
XmlDocument Doc = new XmlDocument();
Doc.Load(Filename);
foreach (XmlElement ChildNode in Doc.FirstChild.ChildNodes)
{
if (ChildNode.Name == "propertySheet")
{
foreach (XmlElement PropertySheetChild in ChildNode.ChildNodes)
{
if (PropertySheetChild.Name == "property")
{
bool IsDiag = false;
foreach (XmlElement PropertySheetChildDiag in PropertySheetChild.ChildNodes)
{
if (PropertySheetChildDiag.Name == "propertyName" && PropertySheetChildDiag.InnerText == "ec_diagnostics")
{
IsDiag = true;
}
if (IsDiag && PropertySheetChildDiag.Name == "propertySheet")
{
foreach (XmlElement PropertySheetChildDiagSheet in PropertySheetChildDiag.ChildNodes)
{
if (PropertySheetChildDiagSheet.Name == "property")
{
bool IsError = false;
foreach (XmlElement PropertySheetChildDiagSheetElem in PropertySheetChildDiagSheet.ChildNodes)
{
if (PropertySheetChildDiagSheetElem.Name == "propertyName" && PropertySheetChildDiagSheetElem.InnerText.StartsWith("error-"))
{
IsError = true;
}
if (IsError && PropertySheetChildDiagSheetElem.Name == "propertySheet")
{
foreach (XmlElement PropertySheetChildDiagSheetElemInner in PropertySheetChildDiagSheetElem.ChildNodes)
{
if (PropertySheetChildDiagSheetElemInner.Name == "property")
{
bool IsMessage = false;
foreach (XmlElement PropertySheetChildDiagSheetElemInner2 in PropertySheetChildDiagSheetElemInner.ChildNodes)
{
if (PropertySheetChildDiagSheetElemInner2.Name == "propertyName" && PropertySheetChildDiagSheetElemInner2.InnerText == "message")
{
IsMessage = true;
}
if (IsMessage && PropertySheetChildDiagSheetElemInner2.Name == "value")
{
if (!PropertySheetChildDiagSheetElemInner2.InnerText.Contains("LogTailsAndChanges"))
{
Result.Add(PropertySheetChildDiagSheetElemInner2.InnerText);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
return Result;
}
}
public class TestECJobErrorParse : BuildCommand
{
public override void ExecuteBuild()
{
Log("*********************** TestECJobErrorParse");
string Filename = CombinePaths(@"P:\Builds\UE4\GUBP\++depot+UE4-2104401-RootEditor_Failed\Engine\Saved\Logs", "RootEditor_Failed.log");
var Errors = ECJobPropsUtils.ErrorsFromProps(Filename);
foreach (var ThisError in Errors)
{
Log("Error: {0}", ThisError);
}
}
}
public class GUBP : BuildCommand
{
public string StoreName = null;
public int CL = 0;
public int TimeIndex = 0;
public bool bSignBuildProducts = false;
public List<UnrealTargetPlatform> ActivePlatforms = null;
public BranchInfo Branch = null;
public bool bOrthogonalizeEditorPlatforms = false;
public List<UnrealTargetPlatform> HostPlatforms;
public bool bFake = false;
public static bool bNoIOSOnPC = false;
public static bool bBuildRocket = false;
public static bool bForceIncrementalCompile = false;
public static string ECProject = null;
public string EmailHint;
static public bool bPreflightBuild = false;
public int PreflightShelveCL = 0;
static public string PreflightMangleSuffix = "";
Dictionary<string, GUBPNode> GUBPNodes;
Dictionary<string, bool> GUBPNodesCompleted;
Dictionary<string, string> GUBPNodesControllingTrigger;
Dictionary<string, string> GUBPNodesControllingTriggerDotName;
class NodeHistory
{
public int LastSucceeded = 0;
public int LastFailed = 0;
public List<int> InProgress = new List<int>();
public string InProgressString = "";
public List<int> Failed = new List<int>();
public string FailedString = "";
public List<int> AllStarted = new List<int>();
public List<int> AllSucceeded = new List<int>();
public List<int> AllFailed = new List<int>();
};
Dictionary<string, NodeHistory> GUBPNodesHistory;
public string RocketUBTArgs(bool bUseRocketInsteadOfBuildRocket = false)
{
if (bBuildRocket)
{
return " -NoSimplygon -NoSpeedTree " + (bUseRocketInsteadOfBuildRocket ? "-Rocket" : "-BuildRocket");
}
return "";
}
public abstract class GUBPNodeAdder
{
public virtual void AddNodes(GUBP bp, UnrealTargetPlatform InHostPlatform)
{
}
}
private static List<GUBPNodeAdder> Adders;
private void AddCustomNodes(UnrealTargetPlatform InHostPlatform)
{
if (Adders == null)
{
Adders = new List<GUBPNodeAdder>();
Assembly[] LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var Dll in LoadedAssemblies)
{
Type[] AllTypes = Dll.GetTypes();
foreach (var PotentialConfigType in AllTypes)
{
if (PotentialConfigType != typeof(GUBPNodeAdder) && typeof(GUBPNodeAdder).IsAssignableFrom(PotentialConfigType))
{
GUBPNodeAdder Config = Activator.CreateInstance(PotentialConfigType) as GUBPNodeAdder;
if (Config != null)
{
Adders.Add(Config);
}
}
}
}
}
foreach(var Adder in Adders)
{
Adder.AddNodes(this, InHostPlatform);
}
}
public abstract class GUBPBranchHacker
{
public class BranchOptions
{
public bool NoMac = false;
}
public virtual void ModifyOptions(GUBP bp, ref BranchOptions Options, string Branch)
{
}
}
private static List<GUBPBranchHacker> BranchHackers;
private GUBPBranchHacker.BranchOptions GetBranchOptions(string Branch)
{
if (BranchHackers == null)
{
BranchHackers = new List<GUBPBranchHacker>();
Assembly[] LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var Dll in LoadedAssemblies)
{
Type[] AllTypes = Dll.GetTypes();
foreach (var PotentialConfigType in AllTypes)
{
if (PotentialConfigType != typeof(GUBPBranchHacker) && typeof(GUBPBranchHacker).IsAssignableFrom(PotentialConfigType))
{
GUBPBranchHacker Config = Activator.CreateInstance(PotentialConfigType) as GUBPBranchHacker;
if (Config != null)
{
BranchHackers.Add(Config);
}
}
}
}
}
var Result = new GUBPBranchHacker.BranchOptions();
foreach (var Hacker in BranchHackers)
{
Hacker.ModifyOptions(this, ref Result, Branch);
}
return Result;
}
public abstract class GUBPEmailHacker
{
public virtual List<string> AddEmails(GUBP bp, string Branch, string NodeName)
{
return new List<string>();
}
public virtual List<string> ModifyEmail(string Email, GUBP bp, string Branch, string NodeName)
{
return new List<string>{Email};
}
public virtual List<string> FinalizeEmails(List<string> Emails, GUBP bp, string Branch, string NodeName)
{
return Emails;
}
}
private static List<GUBPEmailHacker> EmailHackers;
private string HackEmails(string Emails, string Branch, string NodeName)
{
string OnlyEmail = ParseParamValue("OnlyEmail");
if (!String.IsNullOrEmpty(OnlyEmail))
{
return OnlyEmail;
}
EmailHint = ParseParamValue("EmailHint");
if(EmailHint == null)
{
EmailHint = "";
}
if (EmailHackers == null)
{
EmailHackers = new List<GUBPEmailHacker>();
Assembly[] LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var Dll in LoadedAssemblies)
{
Type[] AllTypes = Dll.GetTypes();
foreach (var PotentialConfigType in AllTypes)
{
if (PotentialConfigType != typeof(GUBPEmailHacker) && typeof(GUBPEmailHacker).IsAssignableFrom(PotentialConfigType))
{
GUBPEmailHacker Config = Activator.CreateInstance(PotentialConfigType) as GUBPEmailHacker;
if (Config != null)
{
EmailHackers.Add(Config);
}
}
}
}
}
List<string> Result = new List<string>(Emails.Split(' '));
foreach (var EmailHacker in EmailHackers)
{
Result.AddRange(EmailHacker.AddEmails(this, Branch, NodeName));
}
foreach (var EmailHacker in EmailHackers)
{
var NewResult = new List<string>();
foreach (var Email in Result)
{
NewResult.AddRange(EmailHacker.ModifyEmail(Email, this, Branch, NodeName));
}
Result = NewResult;
}
foreach (var EmailHacker in EmailHackers)
{
Result = EmailHacker.FinalizeEmails(Result, this, Branch, NodeName);
}
string FinalEmails = "";
int Count = 0;
foreach (var Email in Result)
{
FinalEmails = GUBPNode.MergeSpaceStrings(FinalEmails, Email);
Count++;
}
return FinalEmails;
}
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> BuildProducts = null;
public List<string> AllDependencyBuildProducts = null;
public List<string> AllDependencies = null;
public string AgentSharingGroup = "";
public int ComputedDependentCISFrequencyQuantumShift = -1;
public virtual string GetFullName()
{
throw new AutomationException("Unimplemented 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 IsAggregate()
{
return false;
}
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("--actualParameter Sticky={0}", IsSticky() ? 1 : 0);
if (AgentSharingGroup != "")
{
Result += String.Format(" --actualParameter AgentSharingGroup={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 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 static string StaticGetHostPlatformSuffix(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InAgentPlatform = UnrealTargetPlatform.Unknown)
{
if (InHostPlatform == UnrealTargetPlatform.Mac)
{
if (InAgentPlatform == UnrealTargetPlatform.Win64)
{
return "_ForMac";
}
return "_OnMac";
}
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";
}
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 override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp);
return Result;
}
}
public class CompileNode : HostPlatformNode
{
public CompileNode(UnrealTargetPlatform InHostPlatform, bool DependentOnCompileTools = true)
: base(InHostPlatform)
{
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
UE4Build.Build(Agenda, InDeleteBuildProducts: ReallyDeleteBuildProducts, InUpdateVersionFiles: false, InForceUnity: true);
PostBuild(bp, UE4Build);
UE4Build.CheckBuildProducts(UE4Build.BuildProductFiles);
foreach (var Product in UE4Build.BuildProductFiles)
{
AddBuildProduct(Product);
}
RemoveOveralppingBuildProducts();
if (bp.bSignBuildProducts)
{
// Sign everything we built
CodeSign.SignMultipleIfEXEOrDLL(bp, BuildProducts);
}
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
{
public ToolsForCompileNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform, false)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "ToolsForCompile" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override bool DeleteBuildProducts()
{
return true;
}
public override int AgentMemoryRequirement(GUBP bp)
{
if (bp.ParseParam("Launcher") || bp.TimeIndex != 0)
{
return base.AgentMemoryRequirement(bp);
}
return 32;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
if (HostPlatform != UnrealTargetPlatform.Mac && !GUBP.bForceIncrementalCompile)
{
Agenda.DotNetProjects.AddRange(
new string[]
{
@"Engine\Source\Programs\DotNETCommon\DotNETUtilities\DotNETUtilities.csproj",
@"Engine\Source\Programs\RPCUtility\RPCUtility.csproj",
}
);
}
string AddArgs = "-CopyAppBundleBackToDevice" + bp.RocketUBTArgs();
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
return Agenda;
}
public override void PostBuild(GUBP bp, UE4Build UE4Build)
{
if (HostPlatform != UnrealTargetPlatform.Mac)
{
UE4Build.AddUATFilesToBuildProducts();
UE4Build.AddUBTFilesToBuildProducts();
}
}
}
public class RootEditorNode : CompileNode
{
public RootEditorNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
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 UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht" + bp.RocketUBTArgs();
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 ToolsNode : CompileNode
{
public ToolsNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
{
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.Mac)
{
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");
}
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"),
}
);
}
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice" + bp.RocketUBTArgs(); ;
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode) && 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);
}
}
}
}
return Agenda;
}
}
public class SingleToolsNode : CompileNode
{
SingleTargetProperties ProgramTarget;
public SingleToolsNode(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
ProgramTarget = InProgramTarget;
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
{
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" + bp.RocketUBTArgs(); ;
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(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
{
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.Mac)
{
bAnyAdded = true;
Agenda.DotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\Distill\Distill.csproj"),
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" + bp.RocketUBTArgs(); ;
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode) && 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)
{
ProgramTarget = InProgramTarget;
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(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" + bp.RocketUBTArgs(); ;
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)
{
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 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
{
BranchInfo.BranchUProject GameProj;
public EditorGameNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
: base(InHostPlatform)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
GameProj = InGameProj;
AddDependency(RootEditorNode.StaticGetFullName(InHostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
{
return InGameProj.GameName + "_EditorGame" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override float Priority()
{
float Result = base.Priority();
if (GameProj.Options(HostPlatform).bTestWithShared)
{
Result -= 1;
}
return Result;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp);
if (GameProj.Options(HostPlatform).bTestWithShared)
{
Result += 3;
}
return Result;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string Args = "-nobuilduht -skipactionhistory -skipnonhostplatforms -CopyAppBundleBackToDevice -forceheadergeneration" + bp.RocketUBTArgs(true);
Agenda.AddTargets(
new string[] { GameProj.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, GameProj.FilePath, InAddArgs: Args);
return Agenda;
}
}
public class GamePlatformMonolithicsNode : CompileNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform TargetPlatform;
public GamePlatformMonolithicsNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform)
: base(InHostPlatform)
{
GameProj = InGameProj;
TargetPlatform = InTargetPlatform;
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
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))
&& !GUBP.bBuildRocket) // more errors and more performance by just starting, for example, IOS without waiting for mac
{
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(InHostPlatform, bp.Branch.BaseEngineProject, InHostPlatform));
}
if (GUBP.bBuildRocket && InGameProj.GameName == bp.Branch.BaseEngineProject.GameName)
{
AgentSharingGroup = "UE4_" + InTargetPlatform + "_Mono" + StaticGetHostPlatformSuffix(InHostPlatform);
// lets just start this right away AddPseudodependency(RootEditorHeadersNode.StaticGetFullName(HostPlatform)); // maybe we should start these sooner, but that rather tangles the agent groups
}
}
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);
}
if (GUBP.bBuildRocket)
{
var Target = bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Game];
var Configs = Target.Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, TargetPlatform);
foreach (var Config in Configs)
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
if (TargetPlatform == UnrealTargetPlatform.Win32 && Config != UnrealTargetConfiguration.Shipping)
{
continue;
}
if (TargetPlatform == UnrealTargetPlatform.Win64 && Config != UnrealTargetConfiguration.Development)
{
continue;
}
if (TargetPlatform == UnrealTargetPlatform.Android && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
{
continue;
}
}
else if (Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
{
continue;
}
Log("Building {0} for Host={1} Target={2} Config={3} for rocket. Node={4}", Target.TargetName, HostPlatform, TargetPlatform, Config, GetFullName());
}
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform)
{
return InGameProj.GameName + "_" + InTargetPlatform + "_Monolithics" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, TargetPlatform);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp);
if (GameProj.GameName != bp.Branch.BaseEngineProject.GameName)
{
Result += 3; // only every 80m
}
else if (TargetPlatform != HostPlatform)
{
Result += 2; // only every 40m
}
return Result;
}
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" + bp.RocketUBTArgs();
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (GUBP.bBuildRocket && Kind != TargetRules.TargetType.Game)
{
continue;
}
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))
{
var Configs = Target.Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, TargetPlatform);
foreach (var Config in Configs)
{
if (GUBP.bBuildRocket)
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
if (TargetPlatform == UnrealTargetPlatform.Win32 && Config != UnrealTargetConfiguration.Shipping)
{
continue;
}
if (TargetPlatform == UnrealTargetPlatform.Win64 && Config != UnrealTargetConfiguration.Development)
{
continue;
}
if (TargetPlatform == UnrealTargetPlatform.Android && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
{
continue;
}
}
else if (Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
{
continue;
}
}
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 class RootEditorHeadersNode : HostPlatformNode
{
public RootEditorHeadersNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(HostPlatform);
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "RootEditorHeaders" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override float Priority()
{
return 1000000.0f; // right after the root editor
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
foreach (var FileToCopy in CommandUtils.FindFiles("*.h", true, CommandUtils.CombinePaths(CmdEnv.LocalRoot, @"Engine\Intermediate\Build\", HostPlatform.ToString(), "Inc")))
{
AddBuildProduct(FileToCopy);
}
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)
{
foreach (var FileToCopy in CommandUtils.FindFiles("*.h", true, CommandUtils.CombinePaths(CmdEnv.LocalRoot, @"Engine\Intermediate\Build\", HostPlatform.ToString(), Target, "Inc")))
{
AddBuildProduct(FileToCopy);
}
}
var EnginePluginsDirectory = Path.Combine(CommandUtils.CmdEnv.LocalRoot, "Engine/Plugins");
var EnginePlugins = new List<PluginInfo>();
Plugins.FindPluginsIn(EnginePluginsDirectory, PluginInfo.LoadedFromType.Engine, ref EnginePlugins);
foreach (var EnginePlugin in EnginePlugins)
{
foreach (var FileToCopy in CommandUtils.FindFiles("*", true, CommandUtils.CombinePaths(EnginePlugin.Directory, @"Intermediate\Build", HostPlatform.ToString(), "Inc")))
{
AddBuildProduct(FileToCopy);
}
}
RemoveOveralppingBuildProducts();
}
}
public class GameMonolithicHeadersNode : HostPlatformNode
{
UnrealTargetPlatform TargetPlatform;
public GameMonolithicHeadersNode(GUBP bp, UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InTargetPlatform)
: base(InHostPlatform)
{
TargetPlatform = InTargetPlatform;
AgentSharingGroup = "UE4_" + InTargetPlatform + "_Mono" + StaticGetHostPlatformSuffix(InHostPlatform);
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InTargetPlatform)
{
return "UE4_" + InTargetPlatform + "_MonolithicHeaders" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, TargetPlatform);
}
public override float Priority()
{
return 1000000.0f; // right after the game monolithics
}
public override void DoBuild(GUBP bp)
{
if (!UnrealBuildTool.Utils.IsRunningOnMono && TargetPlatform == UnrealTargetPlatform.IOS)
{
throw new AutomationException("these rocket header node require real mac path.");
}
BuildProducts = new List<string>();
if (TargetPlatform != HostPlatform)
{
// host platform overlaps with the editor, so we don't want them here
var PlatformDir = TargetPlatform.ToString();
if (TargetPlatform == UnrealTargetPlatform.Android)
{
PlatformDir = "Android-armv7";
}
foreach (var FileToCopy in CommandUtils.FindFiles("*.h", true, CommandUtils.CombinePaths(CmdEnv.LocalRoot, @"Engine\Intermediate\Build\", PlatformDir, "Inc")))
{
AddBuildProduct(FileToCopy);
}
// these may not be any new build products here, but we will check anyway
var EnginePluginsDirectory = Path.Combine(CommandUtils.CmdEnv.LocalRoot, "Engine/Plugins");
var EnginePlugins = new List<PluginInfo>();
Plugins.FindPluginsIn(EnginePluginsDirectory, PluginInfo.LoadedFromType.Engine, ref EnginePlugins);
foreach (var EnginePlugin in EnginePlugins)
{
foreach (var FileToCopy in CommandUtils.FindFiles("*", true, CommandUtils.CombinePaths(EnginePlugin.Directory, @"Intermediate\Build", PlatformDir, "Inc")))
{
AddBuildProduct(FileToCopy);
}
}
}
RemoveOveralppingBuildProducts();
if (BuildProducts.Count == 0)
{
SaveRecordOfSuccessAndAddToBuildProducts(); // could be empty
}
}
}
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));
if(InHostPlatform == UnrealTargetPlatform.Win64)
{
var VersionSelector = bp.Branch.FindProgram("UnrealVersionSelector");
if (VersionSelector.Rules != null)
{
AddDependency(SingleInternalToolsNode.StaticGetFullName(HostPlatform, VersionSelector));
}
}
}
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;
foreach (var HostPlatform in HostPlatforms)
{
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
}
PromotionLabelPrefix = InPromotionLabelPrefix;
}
public static string StaticGetFullName(string InPromotionLabelPrefix)
{
return InPromotionLabelPrefix + "_Promotable_Aggregate";
}
public override string GetFullName()
{
return StaticGetFullName(PromotionLabelPrefix);
}
}
public class GameAggregatePromotableNode : AggregatePromotableNode
{
BranchInfo.BranchUProject GameProj;
public GameAggregatePromotableNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms, BranchInfo.BranchUProject InGameProj)
: base(InHostPlatforms, InGameProj.GameName)
{
GameProj = InGameProj;
foreach (var HostPlatform in HostPlatforms)
{
AddDependency(RootEditorNode.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));
}
}
}
{
var Platforms = bp.GetMonolithicPlatformsForUProject(HostPlatform, InGameProj, true);
foreach (var Plat in Platforms)
{
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, Plat));
}
}
}
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj)
{
return AggregatePromotableNode.StaticGetFullName(InGameProj.GameName);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
}
public class SharedAggregatePromotableNode : AggregatePromotableNode
{
public SharedAggregatePromotableNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms)
: base(InHostPlatforms, "Shared")
{
foreach (var HostPlatform in HostPlatforms)
{
{
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)
{
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, CodeProj)); // if we are just testing, we will still include the editor stuff
}
}
}
}
}
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(" --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.bBuildRocket)
{
CompleteLabelPrefix = "Rocket-" + CompleteLabelPrefix;
}
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)
)
{
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 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;
}
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
}
CommandUtils.CookCommandlet(GameProj.FilePath, "UE4Editor-Cmd.exe", null, null, CookPlatform);
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, "");
AddDependency(CookNode.StaticGetFullName(HostPlatform, GameProj, CookedPlatform));
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform));
}
}
}
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;
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;
}
// 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 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 = ResolveSharedBuildDirectory(InGameProj.GameName);
string NodeName = StaticGetFullName(InGameProj, InHostPlatform, InClientTargetPlatforms, InClientConfigs, InServerTargetPlatforms, InServerConfigs, InClientNotGame);
string ArchiveDirectory = CombinePaths(BaseDir, NodeName, P4Env.BuildRootEscaped + "-CL-" + P4Env.ChangelistString);
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);
if (ClientTargetPlatforms != null)
{
bool bFirstClient = true;
foreach (var Plat in ClientTargetPlatforms)
{
if (bFirstClient)
{
bFirstClient = false;
Args += String.Format(" -platform={0}", Plat.ToString());
}
else
{
Args += String.Format("+{0}", Plat.ToString());
}
}
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 (bFirstServer)
{
bFirstServer = false;
Args += String.Format(" -serverplatform={0}", Plat.ToString());
}
else
{
Args += String.Format("+{0}", Plat.ToString());
}
}
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());
}
}
}
if (P4Enabled)
{
string ArchiveDirectory = GetArchiveDirectory(GameProj, HostPlatform, ClientTargetPlatforms, ClientConfigs, ServerTargetPlatforms, ServerConfigs, ClientNotGame);
CleanFormalBuilds(ArchiveDirectory);
if (DirectoryExists_NoExceptions(ArchiveDirectory))
{
if (IsBuildMachine)
{
throw new AutomationException("Archive directory already exists {0}", ArchiveDirectory);
}
DeleteDirectory_NoExceptions(ArchiveDirectory);
}
Args += String.Format(" -Archive -archivedirectory={0}", CommandUtils.MakePathSafeToUseWithCommandLine(ArchiveDirectory));
}
string LogFile = CommandUtils.RunUAT(CommandUtils.CmdEnv, Args);
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 -archivedirectory={1} -platform={2} -clientconfig={3}",
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 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)
{
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[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development);
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-skipnonhostplatforms");
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);
}
}
Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: false, InForceNonUnity: 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 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));
}
var Options = InGameProj.Options(HostPlatform);
// defer tests until later
if (Options.bSeparateGamePromotion)
{
AddPseudodependency(GameLabelPromotableNode.StaticGetFullName(GameProj, false));
}
else
{
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 int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 5 /*++ (bIsMassive ? 1 : 0);*/ ;
}
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)
{
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;
}
};
public string AddNode(GUBPNode Node)
{
string Name = Node.GetFullName();
if (GUBPNodes.ContainsKey(Name))
{
throw new AutomationException("Attempt to add a duplicate node {0}", Node.GetFullName());
}
GUBPNodes.Add(Name, Node);
return Name;
}
public bool HasNode(string Node)
{
return GUBPNodes.ContainsKey(Node);
}
public GUBPNode FindNode(string Node)
{
return GUBPNodes[Node];
}
public void RemovePseudodependencyFromNode(string Node, string Dep)
{
if (!GUBPNodes.ContainsKey(Node))
{
throw new AutomationException("Node {0} not found", Node);
}
GUBPNodes[Node].RemovePseudodependency(Dep);
}
List<string> GetDependencies(string NodeToDo, bool bFlat = false, bool ECOnly = false)
{
var Result = new List<string>();
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
bool Usable = GUBPNodes[Node].RunInEC() || !ECOnly;
if (Usable)
{
if (!Result.Contains(Node))
{
Result.Add(Node);
}
}
if (bFlat || !Usable)
{
foreach (var RNode in GetDependencies(Node, bFlat, ECOnly))
{
if (!Result.Contains(RNode))
{
Result.Add(RNode);
}
}
}
}
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
bool Usable = GUBPNodes[Node].RunInEC() || !ECOnly;
if (Usable)
{
if (!Result.Contains(Node))
{
Result.Add(Node);
}
}
if (bFlat || !Usable)
{
foreach (var RNode in GetDependencies(Node, bFlat, ECOnly))
{
if (!Result.Contains(RNode))
{
Result.Add(RNode);
}
}
}
}
return Result;
}
List<string> GetECDependencies(string NodeToDo, bool bFlat = false)
{
return GetDependencies(NodeToDo, bFlat, true);
}
bool NodeDependsOn(string Rootward, string Leafward)
{
var Deps = GetDependencies(Leafward, true);
return Deps.Contains(Rootward);
}
string GetControllingTrigger(string NodeToDo)
{
if (GUBPNodesControllingTrigger.ContainsKey(NodeToDo))
{
return GUBPNodesControllingTrigger[NodeToDo];
}
var Result = "";
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
if (!GUBPNodes.ContainsKey(Node))
{
throw new AutomationException("Dependency {0} in {1} not found.", Node, NodeToDo);
}
bool IsTrigger = GUBPNodes[Node].TriggerNode();
if (IsTrigger)
{
if (Node != Result && !string.IsNullOrEmpty(Result))
{
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, Node, Result);
}
Result = Node;
}
else
{
string NewResult = GetControllingTrigger(Node);
if (!String.IsNullOrEmpty(NewResult))
{
if (NewResult != Result && !string.IsNullOrEmpty(Result))
{
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, NewResult, Result);
}
Result = NewResult;
}
}
}
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
if (!GUBPNodes.ContainsKey(Node))
{
throw new AutomationException("Pseudodependency {0} in {1} not found.", Node, NodeToDo);
}
bool IsTrigger = GUBPNodes[Node].TriggerNode();
if (IsTrigger)
{
if (Node != Result && !string.IsNullOrEmpty(Result))
{
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, Node, Result);
}
Result = Node;
}
else
{
string NewResult = GetControllingTrigger(Node);
if (!String.IsNullOrEmpty(NewResult))
{
if (NewResult != Result && !string.IsNullOrEmpty(Result))
{
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, NewResult, Result);
}
Result = NewResult;
}
}
}
GUBPNodesControllingTrigger.Add(NodeToDo, Result);
return Result;
}
string GetControllingTriggerDotName(string NodeToDo)
{
if (GUBPNodesControllingTriggerDotName.ContainsKey(NodeToDo))
{
return GUBPNodesControllingTriggerDotName[NodeToDo];
}
string Result = "";
string WorkingNode = NodeToDo;
while (true)
{
string ThisResult = GetControllingTrigger(WorkingNode);
if (ThisResult == "")
{
break;
}
if (Result != "")
{
Result = "." + Result;
}
Result = ThisResult + Result;
WorkingNode = ThisResult;
}
GUBPNodesControllingTriggerDotName.Add(NodeToDo, Result);
return Result;
}
public string CISFrequencyQuantumShiftString(string NodeToDo)
{
string FrequencyString = "";
int Quantum = GUBPNodes[NodeToDo].DependentCISFrequencyQuantumShift();
if (Quantum > 0)
{
int Minutes = 20 * (1 << Quantum);
if (Minutes < 60)
{
FrequencyString = string.Format(" ({0}m)", Minutes);
}
else
{
FrequencyString = string.Format(" ({0}h{1}m)", Minutes / 60, Minutes % 60);
}
}
return FrequencyString;
}
public int ComputeDependentCISFrequencyQuantumShift(string NodeToDo)
{
int Result = GUBPNodes[NodeToDo].ComputedDependentCISFrequencyQuantumShift;
if (Result < 0)
{
Result = GUBPNodes[NodeToDo].CISFrequencyQuantumShift(this);
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
Result = Math.Max(ComputeDependentCISFrequencyQuantumShift(Dep), Result);
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
Result = Math.Max(ComputeDependentCISFrequencyQuantumShift(Dep), Result);
}
if (Result < 0)
{
throw new AutomationException("Failed to compute shift.");
}
GUBPNodes[NodeToDo].ComputedDependentCISFrequencyQuantumShift = Result;
}
return Result;
}
bool NodeIsAlreadyComplete(string NodeToDo, bool LocalOnly)
{
if (GUBPNodesCompleted.ContainsKey(NodeToDo))
{
return GUBPNodesCompleted[NodeToDo];
}
string NodeStoreName = StoreName + "-" + GUBPNodes[NodeToDo].GetFullName();
string GameNameIfAny = GUBPNodes[NodeToDo].GameNameIfAnyForTempStorage();
bool Result;
if (LocalOnly)
{
Result = LocalTempStorageExists(CmdEnv, NodeStoreName, bQuiet : true);
}
else
{
Result = TempStorageExists(CmdEnv, NodeStoreName, GameNameIfAny, bQuiet: true);
}
if (Result)
{
Log("***** GUBP Trigger Node was already triggered {0} -> {1} : {2}", GUBPNodes[NodeToDo].GetFullName(), GameNameIfAny, NodeStoreName);
}
else
{
Log("***** GUBP Trigger Node was NOT yet triggered {0} -> {1} : {2}", GUBPNodes[NodeToDo].GetFullName(), GameNameIfAny, NodeStoreName);
}
GUBPNodesCompleted.Add(NodeToDo, Result);
return Result;
}
string RunECTool(string Args, bool bQuiet = false)
{
if (ParseParam("FakeEC"))
{
Log("***** Would have ran ectool {0}", Args);
return "We didn't actually run ectool";
}
else
{
ERunOptions Opts = ERunOptions.Default;
if (bQuiet)
{
Opts = (Opts & ~ERunOptions.AllowSpew) | ERunOptions.NoLoggingOfRunCommand;
}
return RunAndLog("ectool", Args, Options: Opts);
}
}
string GetEMailListForNode(GUBP bp, string NodeToDo, string Causers)
{
var BranchForEmail = "";
if (P4Enabled)
{
BranchForEmail = P4Env.BuildRootP4;
}
return HackEmails(Causers, BranchForEmail, NodeToDo);
}
List<P4Connection.ChangeRecord> GetChanges(int LastOutputForChanges, int TopCL, int LastGreen)
{
var Result = new List<P4Connection.ChangeRecord>();
if (TopCL > LastGreen)
{
if (LastOutputForChanges > 1990000)
{
string Cmd = String.Format("{0}@{1},{2} {3}@{4},{5}",
CombinePaths(PathSeparator.Slash, P4Env.BuildRootP4, "*", "Source", "..."), LastOutputForChanges + 1, TopCL,
CombinePaths(PathSeparator.Slash, P4Env.BuildRootP4, "*", "Build", "..."), LastOutputForChanges + 1, TopCL
);
List<P4Connection.ChangeRecord> ChangeRecords;
if (P4.Changes(out ChangeRecords, Cmd, false, true, LongComment: true))
{
foreach (var Record in ChangeRecords)
{
if (!Record.User.Equals("buildmachine", StringComparison.InvariantCultureIgnoreCase))
{
Result.Add(Record);
}
}
}
else
{
throw new AutomationException("Could not get changes; cmdline: p4 changes {0}", Cmd);
}
}
else
{
throw new AutomationException("That CL looks pretty far off {0}", LastOutputForChanges);
}
}
return Result;
}
int PrintChanges(int LastOutputForChanges, int TopCL, int LastGreen)
{
var ChangeRecords = GetChanges(LastOutputForChanges, TopCL, LastGreen);
foreach (var Record in ChangeRecords)
{
var Summary = Record.Summary.Replace("\r", "\n");
if (Summary.IndexOf("\n") > 0)
{
Summary = Summary.Substring(0, Summary.IndexOf("\n"));
}
Log(" {0} {1} {2}", Record.CL, Record.UserEmail, Summary);
}
return TopCL;
}
void PrintDetailedChanges(NodeHistory History, bool bShowAllChanges = false)
{
var StartTime = DateTime.UtcNow;
string Me = String.Format("{0} <<<< local sync", P4Env.Changelist);
int LastOutputForChanges = 0;
int LastGreen = History.LastSucceeded;
if (bShowAllChanges)
{
if (History.AllStarted.Count > 0)
{
LastGreen = History.AllStarted[0];
}
}
foreach (var cl in History.AllStarted)
{
if (cl < LastGreen)
{
continue;
}
if (P4Env.Changelist < cl && Me != "")
{
LastOutputForChanges = PrintChanges(LastOutputForChanges, P4Env.Changelist, LastGreen);
Log(" {0}", Me);
Me = "";
}
string Status = "In Process";
if (History.AllSucceeded.Contains(cl))
{
Status = "ok";
}
if (History.AllFailed.Contains(cl))
{
Status = "FAIL";
}
LastOutputForChanges = PrintChanges(LastOutputForChanges, cl, LastGreen);
Log(" {0} {1}", cl, Status);
}
if (Me != "")
{
LastOutputForChanges = PrintChanges(LastOutputForChanges, P4Env.Changelist, LastGreen);
Log(" {0}", Me);
}
var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
Log("Took {0}s to get P4 history", BuildDuration / 1000);
}
void PrintNodes(GUBP bp, List<string> Nodes, bool LocalOnly, List<string> UnfinishedTriggers = null)
{
bool bShowAllChanges = bp.ParseParam("AllChanges") && GUBPNodesHistory != null;
bool bShowChanges = (bp.ParseParam("Changes") && GUBPNodesHistory != null) || bShowAllChanges;
bool bShowDetailedHistory = (bp.ParseParam("History") && GUBPNodesHistory != null) || bShowChanges;
bool bShowDependencies = bp.ParseParam("ShowDependencies");
bool bShowECDependencies = bp.ParseParam("ShowECDependencies");
bool bShowHistory = !bp.ParseParam("NoHistory") && GUBPNodesHistory != null;
bool AddEmailProps = bp.ParseParam("ShowEmails");
bool ECProc = bp.ParseParam("ShowECProc");
bool ECOnly = bp.ParseParam("ShowECOnly");
bool bShowTriggers = true;
string LastControllingTrigger = "";
string LastAgentGroup = "";
foreach (var NodeToDo in Nodes)
{
if (ECOnly && !GUBPNodes[NodeToDo].RunInEC())
{
continue;
}
string EMails = "";
if (AddEmailProps)
{
EMails = GetEMailListForNode(bp, NodeToDo, "");
}
if (bShowTriggers)
{
string MyControllingTrigger = GetControllingTriggerDotName(NodeToDo);
if (MyControllingTrigger != LastControllingTrigger)
{
LastControllingTrigger = MyControllingTrigger;
if (MyControllingTrigger != "")
{
string Finished = "";
if (UnfinishedTriggers != null)
{
string MyShortControllingTrigger = GetControllingTrigger(NodeToDo);
if (UnfinishedTriggers.Contains(MyShortControllingTrigger))
{
Finished = "(not yet triggered)";
}
else
{
Finished = "(already triggered)";
}
}
Log(" Controlling Trigger: {0} {1}", MyControllingTrigger, Finished);
}
}
}
if (GUBPNodes[NodeToDo].AgentSharingGroup != LastAgentGroup && GUBPNodes[NodeToDo].AgentSharingGroup != "")
{
Log(" Agent Group: {0}", GUBPNodes[NodeToDo].AgentSharingGroup);
}
LastAgentGroup = GUBPNodes[NodeToDo].AgentSharingGroup;
string Agent = GUBPNodes[NodeToDo].ECAgentString();
if (Agent != "")
{
Agent = "[" + Agent + "]";
}
string FrequencyString = CISFrequencyQuantumShiftString(NodeToDo);
Log(" {0}{1}{2}{3}{4}{5}{6} {7} {8}",
(LastAgentGroup != "" ? " " : ""),
NodeToDo,
FrequencyString,
NodeIsAlreadyComplete(NodeToDo, LocalOnly) ? " - (Completed)" : "",
GUBPNodes[NodeToDo].TriggerNode() ? " - (TriggerNode)" : "",
GUBPNodes[NodeToDo].IsSticky() ? " - (Sticky)" : "",
Agent,
EMails,
ECProc ? GUBPNodes[NodeToDo].ECProcedure() : ""
);
if (bShowHistory && GUBPNodesHistory.ContainsKey(NodeToDo))
{
var History = GUBPNodesHistory[NodeToDo];
if (bShowDetailedHistory)
{
PrintDetailedChanges(History, bShowAllChanges);
}
else
{
Log(" Last Success: {0}", History.LastSucceeded);
Log(" Last Fail : {0}", History.LastFailed);
Log(" Fails Since: {0}", History.FailedString);
Log(" InProgress Since: {0}", History.InProgressString);
}
}
if (bShowDependencies)
{
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
Log(" dep> {0}", Dep);
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
Log(" pdep> {0}", Dep);
}
}
if (bShowECDependencies)
{
foreach (var Dep in GetECDependencies(NodeToDo))
{
Log(" {0}", Dep);
}
}
}
}
public void SaveGraphVisualization(List<string> Nodes)
{
var GraphNodes = new List<GraphNode>();
var NodeToGraphNodeMap = new Dictionary<string, GraphNode>();
for (var NodeIndex = 0; NodeIndex < Nodes.Count; ++NodeIndex)
{
var Node = Nodes[NodeIndex];
var GraphNode = new GraphNode()
{
Id = GraphNodes.Count,
Label = Node
};
GraphNodes.Add(GraphNode);
NodeToGraphNodeMap.Add(Node, GraphNode);
}
// Connect everything together
var GraphEdges = new List<GraphEdge>();
for (var NodeIndex = 0; NodeIndex < Nodes.Count; ++NodeIndex)
{
var Node = Nodes[NodeIndex];
GraphNode NodeGraphNode = NodeToGraphNodeMap[Node];
foreach (var Dep in GUBPNodes[Node].FullNamesOfDependencies)
{
GraphNode PrerequisiteFileGraphNode;
if (NodeToGraphNodeMap.TryGetValue(Dep, out PrerequisiteFileGraphNode))
{
// Connect a file our action is dependent on, to our action itself
var NewGraphEdge = new GraphEdge()
{
Id = GraphEdges.Count,
Source = PrerequisiteFileGraphNode,
Target = NodeGraphNode,
Color = new GraphColor() { R = 0.0f, G = 0.0f, B = 0.0f, A = 0.75f }
};
GraphEdges.Add(NewGraphEdge);
}
}
foreach (var Dep in GUBPNodes[Node].FullNamesOfPseudosependencies)
{
GraphNode PrerequisiteFileGraphNode;
if (NodeToGraphNodeMap.TryGetValue(Dep, out PrerequisiteFileGraphNode))
{
// Connect a file our action is dependent on, to our action itself
var NewGraphEdge = new GraphEdge()
{
Id = GraphEdges.Count,
Source = PrerequisiteFileGraphNode,
Target = NodeGraphNode,
Color = new GraphColor() { R = 0.0f, G = 0.0f, B = 0.0f, A = 0.25f }
};
GraphEdges.Add(NewGraphEdge);
}
}
}
string Filename = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LogFolder, "GubpGraph.gexf");
Log("Writing graph to {0}", Filename);
GraphVisualization.WriteGraphFile(Filename, "GUBP Nodes", GraphNodes, GraphEdges);
Log("Wrote graph to {0}", Filename);
}
// when the host is win64, this is win32 because those are also "host platforms"
static public UnrealTargetPlatform GetAltHostPlatform(UnrealTargetPlatform HostPlatform)
{
UnrealTargetPlatform AltHostPlatform = UnrealTargetPlatform.Unknown; // when the host is win64, this is win32 because those are also "host platforms"
if (HostPlatform == UnrealTargetPlatform.Win64)
{
AltHostPlatform = UnrealTargetPlatform.Win32;
}
return AltHostPlatform;
}
public List<UnrealTargetPlatform> GetMonolithicPlatformsForUProject(UnrealTargetPlatform HostPlatform, BranchInfo.BranchUProject GameProj, bool bIncludeHostPlatform)
{
UnrealTargetPlatform AltHostPlatform = GetAltHostPlatform(HostPlatform);
var Result = new List<UnrealTargetPlatform>();
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (GameProj.Properties.Targets.ContainsKey(Kind))
{
var Target = GameProj.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
foreach (var Plat in Platforms)
{
if (GUBP.bNoIOSOnPC && Plat == UnrealTargetPlatform.IOS && HostPlatform == UnrealTargetPlatform.Win64)
{
continue;
}
if (ActivePlatforms.Contains(Plat) && Target.Rules.SupportsPlatform(Plat) &&
((Plat != HostPlatform && Plat != AltHostPlatform) || bIncludeHostPlatform))
{
Result.Add(Plat);
}
}
}
}
return Result;
}
List<int> ConvertCLToIntList(List<string> Strings)
{
var Result = new List<int>();
foreach (var ThisString in Strings)
{
int ThisInt = int.Parse(ThisString);
if (ThisInt < 1960000 || ThisInt > 3000000)
{
Log("CL {0} appears to be out of range", ThisInt);
}
Result.Add(ThisInt);
}
Result.Sort();
return Result;
}
void SaveStatus(string NodeToDo, string Suffix, string NodeStoreName, bool bSaveSharedTempStorage, string GameNameIfAny, string JobStepIDForFailure = null)
{
string Contents = "Just a status record: " + Suffix;
if (!String.IsNullOrEmpty(JobStepIDForFailure) && IsBuildMachine)
{
try
{
Contents = RunECTool(String.Format("getProperties --jobStepId {0} --recurse 1", JobStepIDForFailure), true);
}
catch (Exception Ex)
{
Log(System.Diagnostics.TraceEventType.Warning, "Failed to get properties for jobstep to save them.");
Log(System.Diagnostics.TraceEventType.Warning, LogUtils.FormatException(Ex));
}
}
string RecordOfSuccess = CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Logs", NodeToDo + Suffix +".log");
CreateDirectory(Path.GetDirectoryName(RecordOfSuccess));
WriteAllText(RecordOfSuccess, Contents);
StoreToTempStorage(CmdEnv, NodeStoreName + Suffix, new List<string> { RecordOfSuccess }, !bSaveSharedTempStorage, GameNameIfAny);
}
int CountZeros(int Num)
{
if (Num < 0)
{
throw new AutomationException("Bad CountZeros");
}
if (Num == 0)
{
return 31;
}
int Result = 0;
while ((Num & 1) == 0)
{
Result++;
Num >>= 1;
}
return Result;
}
List<string> TopologicalSort(HashSet<string> NodesToDo, string ExplicitTrigger = "", bool LocalOnly = false, bool SubSort = false, bool DoNotConsiderCompletion = false)
{
var StartTime = DateTime.UtcNow;
var OrdereredToDo = new List<string>();
var SortedAgentGroupChains = new Dictionary<string, List<string>>();
if (!SubSort)
{
var AgentGroupChains = new Dictionary<string, List<string>>();
foreach (var NodeToDo in NodesToDo)
{
string MyAgentGroup = GUBPNodes[NodeToDo].AgentSharingGroup;
if (MyAgentGroup != "")
{
if (!AgentGroupChains.ContainsKey(MyAgentGroup))
{
AgentGroupChains.Add(MyAgentGroup, new List<string> { NodeToDo });
}
else
{
AgentGroupChains[MyAgentGroup].Add(NodeToDo);
}
}
}
foreach (var Chain in AgentGroupChains)
{
SortedAgentGroupChains.Add(Chain.Key, TopologicalSort(new HashSet<string>(Chain.Value), ExplicitTrigger, LocalOnly, true, DoNotConsiderCompletion));
}
Log("***************Done with recursion");
}
// here we do a topological sort of the nodes, subject to a lexographical and priority sort
while (NodesToDo.Count > 0)
{
bool bProgressMade = false;
float BestPriority = -1E20f;
string BestNode = "";
bool BestPseudoReady = false;
var NonReadyAgentGroups = new HashSet<string>();
var NonPeudoReadyAgentGroups = new HashSet<string>();
var ExaminedAgentGroups = new HashSet<string>();
foreach (var NodeToDo in NodesToDo)
{
bool bReady = true;
bool bPseudoReady = true;
if (!SubSort && GUBPNodes[NodeToDo].AgentSharingGroup != "")
{
if (ExaminedAgentGroups.Contains(GUBPNodes[NodeToDo].AgentSharingGroup))
{
bReady = !NonReadyAgentGroups.Contains(GUBPNodes[NodeToDo].AgentSharingGroup);
bPseudoReady = !NonPeudoReadyAgentGroups.Contains(GUBPNodes[NodeToDo].AgentSharingGroup); //this might not be accurate if bReady==false
}
else
{
ExaminedAgentGroups.Add(GUBPNodes[NodeToDo].AgentSharingGroup);
foreach (var ChainNode in SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup])
{
foreach (var Dep in GUBPNodes[ChainNode].FullNamesOfDependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Dependency {0} node found.", Dep);
}
if (!SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup].Contains(Dep) && NodesToDo.Contains(Dep))
{
bReady = false;
break;
}
}
if (!bReady)
{
NonReadyAgentGroups.Add(GUBPNodes[NodeToDo].AgentSharingGroup);
break;
}
foreach (var Dep in GUBPNodes[ChainNode].FullNamesOfPseudosependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Pseudodependency {0} node found.", Dep);
}
if (!SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup].Contains(Dep) && NodesToDo.Contains(Dep))
{
bPseudoReady = false;
NonPeudoReadyAgentGroups.Add(GUBPNodes[NodeToDo].AgentSharingGroup);
break;
}
}
}
}
}
else
{
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Dependency {0} node found.", Dep);
}
if (NodesToDo.Contains(Dep))
{
bReady = false;
break;
}
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Pseudodependency {0} node found.", Dep);
}
if (NodesToDo.Contains(Dep))
{
bPseudoReady = false;
break;
}
}
}
var Priority = GUBPNodes[NodeToDo].Priority();
if (bReady && BestNode != "")
{
if (String.Compare(GetControllingTriggerDotName(BestNode), GetControllingTriggerDotName(NodeToDo)) < 0) //sorted by controlling trigger
{
bReady = false;
}
else if (String.Compare(GetControllingTriggerDotName(BestNode), GetControllingTriggerDotName(NodeToDo)) == 0) //sorted by controlling trigger
{
if (GUBPNodes[BestNode].IsSticky() && !GUBPNodes[NodeToDo].IsSticky()) //sticky nodes first
{
bReady = false;
}
else if (GUBPNodes[BestNode].IsSticky() == GUBPNodes[NodeToDo].IsSticky())
{
if (BestPseudoReady && !bPseudoReady)
{
bReady = false;
}
else if (BestPseudoReady == bPseudoReady)
{
bool IamLateTrigger = !DoNotConsiderCompletion && GUBPNodes[NodeToDo].TriggerNode() && NodeToDo != ExplicitTrigger && !NodeIsAlreadyComplete(NodeToDo, LocalOnly);
bool BestIsLateTrigger = !DoNotConsiderCompletion && GUBPNodes[BestNode].TriggerNode() && BestNode != ExplicitTrigger && !NodeIsAlreadyComplete(BestNode, LocalOnly);
if (BestIsLateTrigger && !IamLateTrigger)
{
bReady = false;
}
else if (BestIsLateTrigger == IamLateTrigger)
{
if (Priority < BestPriority)
{
bReady = false;
}
else if (Priority == BestPriority)
{
if (BestNode.CompareTo(NodeToDo) < 0)
{
bReady = false;
}
}
}
}
}
}
}
if (bReady)
{
BestPriority = Priority;
BestNode = NodeToDo;
BestPseudoReady = bPseudoReady;
bProgressMade = true;
}
}
if (bProgressMade)
{
if (!SubSort && GUBPNodes[BestNode].AgentSharingGroup != "")
{
foreach (var ChainNode in SortedAgentGroupChains[GUBPNodes[BestNode].AgentSharingGroup])
{
OrdereredToDo.Add(ChainNode);
NodesToDo.Remove(ChainNode);
}
}
else
{
OrdereredToDo.Add(BestNode);
NodesToDo.Remove(BestNode);
}
}
if (!bProgressMade && NodesToDo.Count > 0)
{
Log("Cycle in GUBP, could not resolve:");
foreach (var NodeToDo in NodesToDo)
{
string Deps = "";
if (!SubSort && GUBPNodes[NodeToDo].AgentSharingGroup != "")
{
foreach (var ChainNode in SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup])
{
foreach (var Dep in GUBPNodes[ChainNode].FullNamesOfDependencies)
{
if (!SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup].Contains(Dep) && NodesToDo.Contains(Dep))
{
Deps = Deps + Dep + "[" + ChainNode + "->" + GUBPNodes[NodeToDo].AgentSharingGroup + "]" + " ";
}
}
}
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
if (NodesToDo.Contains(Dep))
{
Deps = Deps + Dep + " ";
}
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
if (NodesToDo.Contains(Dep))
{
Deps = Deps + Dep + " ";
}
}
Log(" {0} deps: {1}", NodeToDo, Deps);
}
throw new AutomationException("Cycle in GUBP");
}
}
if (!SubSort)
{
var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
Log("Took {0}s to sort {1} nodes", BuildDuration / 1000, OrdereredToDo.Count);
}
return OrdereredToDo;
}
string GetJobStepPath(string Dep)
{
if (Dep != "Noop" && GUBPNodes[Dep].AgentSharingGroup != "")
{
return "jobSteps[" + GUBPNodes[Dep].AgentSharingGroup + "]/jobSteps[" + Dep + "]";
}
return "jobSteps[" + Dep + "]";
}
string GetJobStep(string ParentPath, string Dep)
{
return ParentPath + "/" + GetJobStepPath(Dep);
}
void UpdateNodeHistory(string Node, string CLString)
{
if (GUBPNodes[Node].RunInEC() && !GUBPNodes[Node].TriggerNode() && CLString != "")
{
string GameNameIfAny = GUBPNodes[Node].GameNameIfAnyForTempStorage();
string NodeStoreWildCard = StoreName.Replace(CLString, "*") + "-" + GUBPNodes[Node].GetFullName();
var History = new NodeHistory();
History.AllStarted = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + StartedTempStorageSuffix, false, true, GameNameIfAny));
History.AllSucceeded = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + SucceededTempStorageSuffix, false, true, GameNameIfAny));
History.AllFailed = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + FailedTempStorageSuffix, false, true, GameNameIfAny));
if (History.AllFailed.Count > 0)
{
History.LastFailed = History.AllFailed[History.AllFailed.Count - 1];
}
if (History.AllSucceeded.Count > 0)
{
History.LastSucceeded = History.AllSucceeded[History.AllSucceeded.Count - 1];
foreach (var Failed in History.AllFailed)
{
if (Failed > History.LastSucceeded)
{
History.Failed.Add(Failed);
History.FailedString = GUBPNode.MergeSpaceStrings(History.FailedString, String.Format("{0}", Failed));
}
}
foreach (var Started in History.AllStarted)
{
if (Started > History.LastSucceeded && !History.Failed.Contains(Started))
{
History.InProgress.Add(Started);
History.InProgressString = GUBPNode.MergeSpaceStrings(History.InProgressString, String.Format("{0}", Started));
}
}
if (GUBPNodesHistory.ContainsKey(Node))
{
GUBPNodesHistory.Remove(Node);
}
GUBPNodesHistory.Add(Node, History);
}
}
}
bool HashSetEqual(HashSet<string> A, HashSet<string> B)
{
if (A.Count != B.Count)
{
return false;
}
foreach (var Elem in A)
{
if (!B.Contains(Elem))
{
return false;
}
}
foreach (var Elem in B)
{
if (!A.Contains(Elem))
{
return false;
}
}
return true;
}
int FindLastNonDuplicateFail(string NodeToDo, string CLString)
{
var History = GUBPNodesHistory[NodeToDo];
int Result = P4Env.Changelist;
string GameNameIfAny = GUBPNodes[NodeToDo].GameNameIfAnyForTempStorage();
string NodeStore = StoreName + "-" + GUBPNodes[NodeToDo].GetFullName() + FailedTempStorageSuffix;
var BackwardsFails = new List<int>(History.AllFailed);
BackwardsFails.Add(P4Env.Changelist);
BackwardsFails.Sort();
BackwardsFails.Reverse();
HashSet<string> CurrentErrors = null;
foreach (var CL in BackwardsFails)
{
if (CL > P4Env.Changelist)
{
continue;
}
if (CL <= History.LastSucceeded)
{
break;
}
var ThisNodeStore = NodeStore.Replace(CLString, String.Format("{0}", CL));
DeleteLocalTempStorage(CmdEnv, ThisNodeStore, true); // these all clash locally, which is fine we just retrieve them from shared
List<string> Files = null;
try
{
bool WasLocal;
Files = RetrieveFromTempStorage(CmdEnv, ThisNodeStore, out WasLocal, GameNameIfAny); // this will fail on our CL if we didn't fail or we are just setting up the branch
}
catch (Exception)
{
}
if (Files == null)
{
continue;
}
if (Files.Count != 1)
{
throw new AutomationException("Unexpected number of files for fail record {0}", Files.Count);
}
string ErrorFile = Files[0];
var ThisErrors = ECJobPropsUtils.ErrorsFromProps(ErrorFile);
if (CurrentErrors == null)
{
CurrentErrors = ThisErrors;
}
else
{
if (CurrentErrors.Count == 0 || !HashSetEqual(CurrentErrors, ThisErrors))
{
break;
}
Result = CL;
}
}
return Result;
}
List<string> GetECPropsForNode(string NodeToDo, string CLString, out string EMails, bool OnlyLateUpdates = false)
{
var StartTime = DateTime.UtcNow;
var ECProps = new List<string>();
EMails = "";
string FailCauserEMails = "";
string EMailNote = "";
bool SendSuccessForGreenAfterRed = false;
int NumPeople = 0;
if (GUBPNodesHistory.ContainsKey(NodeToDo))
{
var History = GUBPNodesHistory[NodeToDo];
ECProps.Add(string.Format("LastGreen/{0}={1}", NodeToDo, History.LastSucceeded));
ECProps.Add(string.Format("RedsSince/{0}={1}", NodeToDo, History.FailedString));
ECProps.Add(string.Format("InProgress/{0}={1}", NodeToDo, History.InProgressString));
if (History.LastSucceeded > 0 && History.LastSucceeded < P4Env.Changelist)
{
int LastNonDuplicateFail = P4Env.Changelist;
try
{
if (OnlyLateUpdates)
{
LastNonDuplicateFail = FindLastNonDuplicateFail(NodeToDo, CLString);
if (LastNonDuplicateFail < P4Env.Changelist)
{
Log("*** Red-after-red spam reduction, changed CL {0} to CL {1} because the errors didn't change.", P4Env.Changelist, LastNonDuplicateFail);
}
}
}
catch (Exception Ex)
{
LastNonDuplicateFail = P4Env.Changelist;
Log(System.Diagnostics.TraceEventType.Warning, "Failed to FindLastNonDuplicateFail.");
Log(System.Diagnostics.TraceEventType.Warning, LogUtils.FormatException(Ex));
}
var ChangeRecords = GetChanges(History.LastSucceeded, LastNonDuplicateFail, History.LastSucceeded);
foreach (var Record in ChangeRecords)
{
FailCauserEMails = GUBPNode.MergeSpaceStrings(FailCauserEMails, Record.UserEmail);
}
if (!String.IsNullOrEmpty(FailCauserEMails))
{
NumPeople++;
foreach (var AChar in FailCauserEMails.ToCharArray())
{
if (AChar == ' ')
{
NumPeople++;
}
}
if (NumPeople > 50)
{
EMailNote = String.Format("This step has been broken for more than 50 changes. It last succeeded at CL {0}. ", History.LastSucceeded);
}
}
}
else if (History.LastSucceeded <= 0)
{
EMailNote = String.Format("This step has been broken for more than a few days, so there is no record of it ever succeeding. ");
}
if (EMailNote != "" && !String.IsNullOrEmpty(History.FailedString))
{
EMailNote += String.Format("It has failed at CLs {0}. ", History.FailedString);
}
if (EMailNote != "" && !String.IsNullOrEmpty(History.InProgressString))
{
EMailNote += String.Format("These CLs are being built right now {0}. ", History.InProgressString);
}
if (History.LastSucceeded > 0 && History.LastSucceeded < P4Env.Changelist && History.LastFailed > History.LastSucceeded && History.LastFailed < P4Env.Changelist)
{
SendSuccessForGreenAfterRed = ParseParam("CIS");
}
}
else
{
ECProps.Add(string.Format("LastGreen/{0}=0", NodeToDo));
ECProps.Add(string.Format("RedsSince/{0}=", NodeToDo));
ECProps.Add(string.Format("InProgress/{0}=", NodeToDo));
}
ECProps.Add(string.Format("FailCausers/{0}={1}", NodeToDo, FailCauserEMails));
ECProps.Add(string.Format("EmailNotes/{0}={1}", NodeToDo, EMailNote));
{
var AdditonalEmails = "";
if (ParseParam("CIS") && !GUBPNodes[NodeToDo].SendSuccessEmail() && !GUBPNodes[NodeToDo].TriggerNode())
{
if (NumPeople > 50 || NumPeople == 0)
{
FailCauserEMails = "[CISHeros]";
}
AdditonalEmails = FailCauserEMails;
}
string AddEmails = ParseParamValue("AddEmails");
if (!String.IsNullOrEmpty(AddEmails))
{
AdditonalEmails = GUBPNode.MergeSpaceStrings(AddEmails, AdditonalEmails);
}
EMails = GetEMailListForNode(this, NodeToDo, AdditonalEmails);
ECProps.Add("FailEmails/" + NodeToDo + "=" + EMails);
}
if (GUBPNodes[NodeToDo].SendSuccessEmail() || SendSuccessForGreenAfterRed)
{
ECProps.Add("SendSuccessEmail/" + NodeToDo + "=1");
}
else
{
ECProps.Add("SendSuccessEmail/" + NodeToDo + "=0");
}
if (!OnlyLateUpdates)
{
ECProps.Add(string.Format("AgentRequirementString/{0}={1}", NodeToDo, GUBPNodes[NodeToDo].ECAgentString()));
ECProps.Add(string.Format("RequiredMemory/{0}={1}", NodeToDo, GUBPNodes[NodeToDo].AgentMemoryRequirement(this)));
ECProps.Add(string.Format("Timeouts/{0}={1}", NodeToDo, GUBPNodes[NodeToDo].TimeoutInMinutes()));
ECProps.Add(string.Format("JobStepPath/{0}={1}", NodeToDo, GetJobStepPath(NodeToDo)));
}
var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
Log("Took {0}s to get P4 history for node {1}", BuildDuration / 1000, NodeToDo);
return ECProps;
}
void UpdateECProps(string NodeToDo, string CLString)
{
try
{
Log("Updating node props for node {0}", NodeToDo);
string EMails;
var Props = GetECPropsForNode(NodeToDo, CLString, out EMails, true);
foreach (var Prop in Props)
{
var Parts = Prop.Split("=".ToCharArray());
RunECTool(String.Format("setProperty \"/myWorkflow/{0}\" \"{1}\"", Parts[0], Parts[1]), true);
}
}
catch (Exception Ex)
{
Log(System.Diagnostics.TraceEventType.Warning, "Failed to UpdateECProps.");
Log(System.Diagnostics.TraceEventType.Warning, LogUtils.FormatException(Ex));
}
}
[Help("Runs one, several or all of the GUBP nodes")]
[Help(typeof(UE4Build))]
[Help("NoMac", "Toggle to exclude the Mac host platform, default is Win64+Mac")]
[Help("NoPC", "Toggle to exclude the PC host platform, default is Win64+Mac")]
[Help("CleanLocal", "delete the local temp storage before we start")]
[Help("Store=", "Sets the name of the temp storage block, normally, this is built for you.")]
[Help("StoreSuffix=", "Tacked onto a store name constructed from CL, branch, etc")]
[Help("TimeIndex=", "An integer used to determine subsets to run based on DependentCISFrequencyQuantumShift")]
[Help("Node=", "Nodes to process, -node=Node1+Node2+Node3, if no nodes or games are specified, defaults to all nodes.")]
[Help("SetupNode=", "Like -Node, but only applies with CommanderJobSetupOnly")]
[Help("RelatedToNode=", "Nodes to process, -RelatedToNode=Node1+Node2+Node3, use all nodes that either depend on these nodes or these nodes depend on them.")]
[Help("SetupRelatedToNode=", "Like -RelatedToNode, but only applies with CommanderJobSetupOnly")]
[Help("OnlyNode=", "Nodes to process NO dependencies, -OnlyNode=Node1+Node2+Node3, if no nodes or games are specified, defaults to all nodes.")]
[Help("TriggerNode=", "Trigger Nodes to process, -triggernode=Node.")]
[Help("Game=", "Games to process, -game=Game1+Game2+Game3, if no games or nodes are specified, defaults to all nodes.")]
[Help("ListOnly", "List Nodes in this branch")]
[Help("SaveGraph", "Save graph as an xml file")]
[Help("CommanderJobSetupOnly", "Set up the EC branch info via ectool and quit")]
[Help("FakeEC", "don't run ectool, rather just do it locally, emulating what EC would have done.")]
[Help("Fake", "Don't actually build anything, just store a record of success as the build product for each node.")]
[Help("AllPlatforms", "Regardless of what is installed on this machine, set up the graph for all platforms; true by default on build machines.")]
[Help("SkipTriggers", "ignore all triggers")]
[Help("CL", "force the CL to something, disregarding the P4 value.")]
[Help("History", "Like ListOnly, except gives you a full history. Must have -P4 for this to work.")]
[Help("Changes", "Like history, but also shows the P4 changes. Must have -P4 for this to work.")]
[Help("AllChanges", "Like changes except includes changes before the last green. Must have -P4 for this to work.")]
[Help("EmailOnly", "Only emails the folks given in the argument.")]
[Help("AddEmails", "Add these space delimited emails too all email lists.")]
[Help("ShowDependencies", "Show node dependencies.")]
[Help("ShowECDependencies", "Show EC node dependencies instead.")]
[Help("ShowECProc", "Show EC proc names.")]
[Help("BuildRocket", "Build in rocket mode.")]
[Help("ShowECOnly", "Only show EC nodes.")]
[Help("ECProject", "From EC, the name of the project, used to get a version number.")]
[Help("CIS", "This is a CIS run, assign TimeIndex based on the history.")]
[Help("ForceIncrementalCompile", "make sure all compiles are incremental")]
[Help("AutomatedTesting", "Allow automated testing, currently disabled.")]
[Help("StompCheck", "Look for stomped build products.")]
public override void ExecuteBuild()
{
Log("************************* GUBP");
string PreflightShelveCLString = GetEnvVar("uebp_PreflightShelveCL");
if ((!String.IsNullOrEmpty(PreflightShelveCLString) && IsBuildMachine) || ParseParam("PreflightTest"))
{
Log("**** Preflight shelve {0}", PreflightShelveCLString);
if (!String.IsNullOrEmpty(PreflightShelveCLString))
{
PreflightShelveCL = int.Parse(PreflightShelveCLString);
if (PreflightShelveCL < 2000000)
{
throw new AutomationException("{0} does not look like a CL");
}
}
bPreflightBuild = true;
var OpenFiles = P4.OpenedOutput();
if (OpenFiles.Contains("File(s) not opened on this client."))
{
throw new AutomationException("Apparently, nothing was unshelved for this preflight.");
}
}
ECProject = ParseParamValue("ECProject");
if (ECProject == null)
{
ECProject = "";
}
HostPlatforms = new List<UnrealTargetPlatform>();
if (!ParseParam("NoPC"))
{
HostPlatforms.Add(UnrealTargetPlatform.Win64);
}
var BranchForOptions = "";
if (P4Enabled)
{
BranchForOptions = P4Env.BuildRootP4;
}
var BranchOptions = GetBranchOptions(BranchForOptions);
bool WithMac = !BranchOptions.NoMac;
if (ParseParam("NoMac"))
{
WithMac = false;
}
if (WithMac)
{
HostPlatforms.Add(UnrealTargetPlatform.Mac);
}
bBuildRocket = ParseParam("BuildRocket");
bForceIncrementalCompile = ParseParam("ForceIncrementalCompile");
bool bNoAutomatedTesting = ParseParam("NoAutomatedTesting");
StoreName = ParseParamValue("Store");
string StoreSuffix = ParseParamValue("StoreSuffix", "");
if (bBuildRocket)
{
StoreSuffix = StoreSuffix + "-Rkt";
}
if (bPreflightBuild)
{
int TotalSeconds = (int)((DateTime.UtcNow - new DateTime(2014, 6, 1)).TotalSeconds);
PreflightMangleSuffix = String.Format("-PF-{0}-{1}", PreflightShelveCL, TotalSeconds);
StoreSuffix = StoreSuffix + PreflightMangleSuffix;
}
CL = ParseParamInt("CL", 0);
bool bCleanLocalTempStorage = ParseParam("CleanLocal");
bool bChanges = ParseParam("Changes") || ParseParam("AllChanges");
bool bHistory = ParseParam("History") || bChanges;
bool bListOnly = ParseParam("ListOnly") || bHistory;
bool bSkipTriggers = ParseParam("SkipTriggers");
bFake = ParseParam("fake");
bool bFakeEC = ParseParam("FakeEC");
TimeIndex = ParseParamInt("TimeIndex", 0);
bNoIOSOnPC = HostPlatforms.Contains(UnrealTargetPlatform.Mac);
bool bSaveSharedTempStorage = false;
if (bHistory && !P4Enabled)
{
throw new AutomationException("-Changes and -History require -P4.");
}
bool LocalOnly = true;
string CLString = "";
if (String.IsNullOrEmpty(StoreName))
{
if (P4Enabled)
{
if (CL == 0)
{
CL = P4Env.Changelist;
}
CLString = String.Format("{0}", CL);
StoreName = P4Env.BuildRootEscaped + "-" + CLString;
bSaveSharedTempStorage = CommandUtils.IsBuildMachine;
LocalOnly = false;
}
else
{
StoreName = "TempLocal";
bSaveSharedTempStorage = false;
}
}
StoreName = StoreName + StoreSuffix;
if (bFakeEC)
{
LocalOnly = true;
}
if (bSaveSharedTempStorage)
{
if (!HaveSharedTempStorage(true))
{
throw new AutomationException("Request to save to temp storage, but {0} is unavailable.", UE4TempStorageDirectory());
}
bSignBuildProducts = true;
}
else if (!LocalOnly && !HaveSharedTempStorage(false))
{
Log("Looks like we want to use shared temp storage, but since we don't have it, we won't use it.");
LocalOnly = true;
}
bool CommanderSetup = ParseParam("CommanderJobSetupOnly");
string ExplicitTrigger = "";
if (CommanderSetup)
{
ExplicitTrigger = ParseParamValue("TriggerNode");
if (ExplicitTrigger == null)
{
ExplicitTrigger = "";
}
}
if (ParseParam("CIS") && ExplicitTrigger == "" && CommanderSetup) // explicit triggers will already have a time index assigned
{
if (!P4Enabled)
{
throw new AutomationException("Can't have -CIS without P4 support");
}
var P4IndexFileP4 = CombinePaths(PathSeparator.Slash, CommandUtils.P4Env.BuildRootP4, "Engine", "Build", "CISCounter.txt");
var P4IndexFileLocal = CombinePaths(CmdEnv.LocalRoot, "Engine", "Build", "CISCounter.txt");
int Retry = 0;
bool bDone = false;
while (++Retry < 20 && !bDone)
{
int NowMinutes = (int)((DateTime.UtcNow - new DateTime(2014, 1, 1)).TotalMinutes);
if (NowMinutes < 3 * 30 * 24)
{
throw new AutomationException("bad date calc");
}
if (!FileExists_NoExceptions(P4IndexFileLocal))
{
Log("{0} doesn't exist, checking in a new one", P4IndexFileP4);
WriteAllText(P4IndexFileLocal, "-1 0");
int WorkingCL = -1;
try
{
WorkingCL = P4.CreateChange(P4Env.Client, "Adding new CIS Counter");
P4.Add(WorkingCL, P4IndexFileP4);
int SubmittedCL;
P4.Submit(WorkingCL, out SubmittedCL);
}
catch (Exception)
{
Log("Add of CIS counter failed, assuming it now exists.");
if (WorkingCL > 0)
{
P4.DeleteChange(WorkingCL);
}
}
}
P4.Sync("-f " + P4IndexFileP4 + "#head");
if (!FileExists_NoExceptions(P4IndexFileLocal))
{
Log("{0} doesn't exist, checking in a new one", P4IndexFileP4);
WriteAllText(P4IndexFileLocal, "-1 0");
int WorkingCL = -1;
try
{
WorkingCL = P4.CreateChange(P4Env.Client, "Adding new CIS Counter");
P4.Add(WorkingCL, P4IndexFileP4);
int SubmittedCL;
P4.Submit(WorkingCL, out SubmittedCL);
}
catch (Exception)
{
Log("Add of CIS counter failed, assuming it now exists.");
if (WorkingCL > 0)
{
P4.DeleteChange(WorkingCL);
}
}
}
var Data = ReadAllText(P4IndexFileLocal);
var Parts = Data.Split(" ".ToCharArray());
int Index = int.Parse(Parts[0]);
int Minutes = int.Parse(Parts[1]);
int DeltaMinutes = NowMinutes - Minutes;
const int TimeQuantum = 20;
int NewIndex = Index + 1;
if (DeltaMinutes > TimeQuantum * 2)
{
if (DeltaMinutes > TimeQuantum * (1 << 8))
{
// it has been forever, lets just start over
NewIndex = 0;
}
else
{
int WorkingIndex = NewIndex + 1;
for (int WorkingDelta = DeltaMinutes - TimeQuantum; WorkingDelta > 0; WorkingDelta -= TimeQuantum, WorkingIndex++)
{
if (CountZeros(NewIndex) < CountZeros(WorkingIndex))
{
NewIndex = WorkingIndex;
}
}
}
}
{
var Line = String.Format("{0} {1}", NewIndex, NowMinutes);
Log("Attempting to write {0} with {1}", P4IndexFileP4, Line);
int WorkingCL = -1;
try
{
WorkingCL = P4.CreateChange(P4Env.Client, "Updating CIS Counter");
P4.Edit(WorkingCL, P4IndexFileP4);
WriteAllText(P4IndexFileLocal, Line);
int SubmittedCL;
P4.Submit(WorkingCL, out SubmittedCL);
bDone = true;
TimeIndex = NewIndex;
}
catch (Exception)
{
Log("Edit of CIS counter failed, assuming someone else checked in, retrying.");
if (WorkingCL > 0)
{
P4.DeleteChange(WorkingCL);
}
System.Threading.Thread.Sleep(30000);
}
}
}
if (!bDone)
{
throw new AutomationException("Failed to update the CIS counter after 20 tries.");
}
Log("Setting TimeIndex to {0}", TimeIndex);
}
Log("************************* CL: {0}", CL);
Log("************************* P4Enabled: {0}", P4Enabled);
foreach (var HostPlatform in HostPlatforms)
{
Log("************************* HostPlatform: {0}", HostPlatform.ToString());
}
Log("************************* StoreName: {0}", StoreName.ToString());
Log("************************* bCleanLocalTempStorage: {0}", bCleanLocalTempStorage);
Log("************************* bSkipTriggers: {0}", bSkipTriggers);
Log("************************* bSaveSharedTempStorage: {0}", bSaveSharedTempStorage);
Log("************************* bSignBuildProducts: {0}", bSignBuildProducts);
Log("************************* bFake: {0}", bFake);
Log("************************* bFakeEC: {0}", bFakeEC);
Log("************************* bHistory: {0}", bHistory);
Log("************************* TimeIndex: {0}", TimeIndex);
Log("************************* bBuildRocket: {0}", bBuildRocket);
GUBPNodes = new Dictionary<string, GUBPNode>();
Branch = new BranchInfo(HostPlatforms);
if (IsBuildMachine || ParseParam("AllPlatforms"))
{
ActivePlatforms = new List<UnrealTargetPlatform>();
foreach (var GameProj in Branch.CodeProjects)
{
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (GameProj.Properties.Targets.ContainsKey(Kind))
{
var Target = GameProj.Properties.Targets[Kind];
foreach (var HostPlatform in HostPlatforms)
{
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
foreach (var Plat in Platforms)
{
if (Target.Rules.SupportsPlatform(Plat) && !ActivePlatforms.Contains(Plat))
{
ActivePlatforms.Add(Plat);
}
}
}
}
}
}
}
else
{
ActivePlatforms = new List<UnrealTargetPlatform>(CommandUtils.KnownTargetPlatforms);
}
if (bBuildRocket)
{
var FilteredActivePlatforms = new List<UnrealTargetPlatform>();
foreach (var Plat in ActivePlatforms)
{
if (Plat != UnrealTargetPlatform.HTML5 &&
Plat != UnrealTargetPlatform.Linux &&
(Plat != UnrealTargetPlatform.PS4 || ParseParam("WithPS4")) &&
Plat != UnrealTargetPlatform.WinRT &&
Plat != UnrealTargetPlatform.WinRT_ARM &&
Plat != UnrealTargetPlatform.XboxOne
)
{
FilteredActivePlatforms.Add(Plat);
}
}
ActivePlatforms = FilteredActivePlatforms;
}
foreach (var Plat in ActivePlatforms)
{
Log("Active Platform: {0}", Plat.ToString());
}
if (HostPlatforms.Count == 2)
{
// make sure each project is set up with the right assumptions on monolithics that prefer a platform.
foreach (var CodeProj in Branch.CodeProjects)
{
var OptionsMac = CodeProj.Options(UnrealTargetPlatform.Mac);
var OptionsPC = CodeProj.Options(UnrealTargetPlatform.Win64);
var MacMonos = GetMonolithicPlatformsForUProject(UnrealTargetPlatform.Mac, CodeProj, false);
var PCMonos = GetMonolithicPlatformsForUProject(UnrealTargetPlatform.Win64, CodeProj, false);
if (!OptionsMac.bIsPromotable && OptionsPC.bIsPromotable &&
(MacMonos.Contains(UnrealTargetPlatform.IOS) || PCMonos.Contains(UnrealTargetPlatform.IOS)))
{
throw new AutomationException("Project {0} is promotable for PC, not promotable for Mac and uses IOS monothics. Since Mac is the preferred platform for IOS, please add Mac as a promotable platform.", CodeProj.GameName);
}
if (OptionsMac.bIsPromotable && !OptionsPC.bIsPromotable &&
(MacMonos.Contains(UnrealTargetPlatform.Android) || PCMonos.Contains(UnrealTargetPlatform.Android)))
{
throw new AutomationException("Project {0} is not promotable for PC, promotable for Mac and uses Android monothics. Since PC is the preferred platform for Android, please add PC as a promotable platform.", CodeProj.GameName);
}
}
}
AddNode(new VersionFilesNode());
foreach (var HostPlatform in HostPlatforms)
{
AddNode(new ToolsForCompileNode(HostPlatform));
AddNode(new RootEditorNode(HostPlatform));
if (bBuildRocket)
{
AddNode(new RootEditorHeadersNode(HostPlatform));
}
AddNode(new ToolsNode(HostPlatform));
AddNode(new InternalToolsNode(HostPlatform));
foreach (var ProgramTarget in Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && SeparateNode)
{
if (bInternalOnly)
{
AddNode(new SingleInternalToolsNode(HostPlatform, ProgramTarget));
}
else
{
AddNode(new SingleToolsNode(HostPlatform, ProgramTarget));
}
}
}
AddNode(new EditorAndToolsNode(this, HostPlatform));
if (bOrthogonalizeEditorPlatforms)
{
foreach (var Plat in ActivePlatforms)
{
if (Plat != HostPlatform && Plat != GetAltHostPlatform(HostPlatform))
{
if (Platform.Platforms[HostPlatform].CanHostPlatform(Plat))
{
AddNode(new EditorPlatformNode(HostPlatform, Plat));
}
}
}
}
bool DoASharedPromotable = false;
int NumSharedCode = 0;
foreach (var CodeProj in Branch.CodeProjects)
{
var Options = CodeProj.Options(HostPlatform);
if (Options.bIsPromotable && !Options.bSeparateGamePromotion)
{
NumSharedCode++;
}
}
var NonCodeProjectNames = new Dictionary<string, List<UnrealTargetPlatform>>();
var NonCodeFormalBuilds = new Dictionary<string, List<TargetRules.GUBPFormalBuild>>();
{
var Target = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor];
foreach (var Codeless in Target.Rules.GUBP_NonCodeProjects_BaseEditorTypeOnly(HostPlatform))
{
var Proj = Branch.FindGame(Codeless.Key);
if (Proj == null)
{
Log(System.Diagnostics.TraceEventType.Information, "{0} was listed as a codeless project by GUBP_NonCodeProjects_BaseEditorTypeOnly, however it does not exist in this branch.", Codeless.Key);
}
else if (Proj.Properties.bIsCodeBasedProject)
{
throw new AutomationException("{0} was listed as a codeless project by GUBP_NonCodeProjects_BaseEditorTypeOnly, however it is a code based project.", Codeless.Key);
}
else
{
NonCodeProjectNames.Add(Codeless.Key, Codeless.Value);
}
}
var TempNonCodeFormalBuilds = Target.Rules.GUBP_GetNonCodeFormalBuilds_BaseEditorTypeOnly();
var HostMonos = GetMonolithicPlatformsForUProject(HostPlatform, Branch.BaseEngineProject, true);
foreach (var Codeless in TempNonCodeFormalBuilds)
{
if (NonCodeProjectNames.ContainsKey(Codeless.Key))
{
var PlatList = Codeless.Value;
var NewPlatList = new List<TargetRules.GUBPFormalBuild>();
foreach (var PlatPair in PlatList)
{
if (HostMonos.Contains(PlatPair.TargetPlatform))
{
NewPlatList.Add(PlatPair);
}
}
if (NewPlatList.Count > 0)
{
NonCodeFormalBuilds.Add(Codeless.Key, NewPlatList);
}
}
else
{
Log(System.Diagnostics.TraceEventType.Information, "{0} was listed as a codeless formal build GUBP_GetNonCodeFormalBuilds_BaseEditorTypeOnly, however it does not exist in this branch.", Codeless.Key);
}
}
}
DoASharedPromotable = NumSharedCode > 0 || NonCodeProjectNames.Count > 0 || NonCodeFormalBuilds.Count > 0;
if (!DoASharedPromotable && bBuildRocket)
{
throw new AutomationException("we were asked to make a rocket build, but this branch does not have a shared promotable.");
}
if (DoASharedPromotable)
{
AddNode(new NonUnityTestNode(HostPlatform));
var AgentSharingGroup = "Shared_EditorTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
var Options = Branch.BaseEngineProject.Options(HostPlatform);
if (!Options.bIsPromotable || Options.bSeparateGamePromotion)
{
throw new AutomationException("We assume that if we have shared promotable, the base engine is in it.");
}
if (HostPlatform != UnrealTargetPlatform.Mac) //temp hack till mac automated testing works
{
var EditorTests = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].Rules.GUBP_GetEditorTests_EditorTypeOnly(HostPlatform);
var EditorTestNodes = new List<string>();
foreach (var Test in EditorTests)
{
if (!bNoAutomatedTesting)
{
EditorTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, Branch.BaseEngineProject, Test.Key, Test.Value, AgentSharingGroup)));
foreach (var NonCodeProject in Branch.NonCodeProjects)
{
if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName))
{
continue;
}
if (HostPlatform == UnrealTargetPlatform.Mac) continue; //temp hack till mac automated testing works
EditorTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, Test.Key, Test.Value, AgentSharingGroup)));
}
}
}
if (EditorTestNodes.Count > 0)
{
AddNode(new GameAggregateNode(this, HostPlatform, Branch.BaseEngineProject, "AllEditorTests", EditorTestNodes, 0.0f));
}
}
var ServerPlatforms = new List<UnrealTargetPlatform>();
var GamePlatforms = new List<UnrealTargetPlatform>();
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
{
var Target = Branch.BaseEngineProject.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
if (Platforms.Contains(HostPlatform))
{
// we want the host platform first since some some pseudodependencies look to see if the shared promotable exists.
Platforms.Remove(HostPlatform);
Platforms.Insert(0, HostPlatform);
}
foreach (var Plat in Platforms)
{
if (!Platform.Platforms[HostPlatform].CanHostPlatform(Plat))
{
throw new AutomationException("Project {0} asked for platform {1} with host {2}, but the host platform cannot build that platform.", Branch.BaseEngineProject.GameName, Plat.ToString(), HostPlatform.ToString());
}
if (bNoIOSOnPC && Plat == UnrealTargetPlatform.IOS && HostPlatform == UnrealTargetPlatform.Win64)
{
continue;
}
if (ActivePlatforms.Contains(Plat))
{
if (Kind == TargetRules.TargetType.Server && !ServerPlatforms.Contains(Plat))
{
ServerPlatforms.Add(Plat);
}
if (Kind == TargetRules.TargetType.Game && !GamePlatforms.Contains(Plat))
{
GamePlatforms.Add(Plat);
}
if (!GUBPNodes.ContainsKey(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, Plat)))
{
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, Branch.BaseEngineProject, Plat));
if (bBuildRocket)
{
AddNode(new GameMonolithicHeadersNode(this, HostPlatform, Plat));
}
}
}
}
}
}
var CookedAgentSharingGroup = "Shared_CookedTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
var GameTestNodes = new List<string>();
var GameCookNodes = new List<string>();
//foreach (var Kind in BranchInfo.MonolithicKinds)//for now, non-code projects don't do client or server.
{
var Kind = TargetRules.TargetType.Game;
if (Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
{
var Target = Branch.BaseEngineProject.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
foreach (var Plat in Platforms)
{
if (!Platform.Platforms[HostPlatform].CanHostPlatform(Plat))
{
throw new AutomationException("Project {0} asked for platform {1} with host {2}, but the host platform cannot build that platform.", Branch.BaseEngineProject.GameName, Plat.ToString(), HostPlatform.ToString());
}
if (bNoIOSOnPC && Plat == UnrealTargetPlatform.IOS && HostPlatform == UnrealTargetPlatform.Win64)
{
continue;
}
if (ActivePlatforms.Contains(Plat))
{
string CookedPlatform = Platform.Platforms[Plat].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
if (!GUBPNodes.ContainsKey(CookNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, CookedPlatform)))
{
GameCookNodes.Add(AddNode(new CookNode(this, HostPlatform, Branch.BaseEngineProject, Plat, CookedPlatform)));
}
if (!GUBPNodes.ContainsKey(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, Plat)))
{
AddNode(new GamePlatformCookedAndCompiledNode(this, HostPlatform, Branch.BaseEngineProject, Plat, false));
}
var GameTests = Target.Rules.GUBP_GetGameTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), Plat);
var RequiredPlatforms = new List<UnrealTargetPlatform> { Plat };
if (!bNoAutomatedTesting)
{
var ThisMonoGameTestNodes = new List<string>();
foreach (var Test in GameTests)
{
var TestName = Test.Key + "_" + Plat.ToString();
ThisMonoGameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, Branch.BaseEngineProject, TestName, Test.Value, CookedAgentSharingGroup, false, RequiredPlatforms)));
}
if (ThisMonoGameTestNodes.Count > 0)
{
GameTestNodes.Add(AddNode(new GameAggregateNode(this, HostPlatform, Branch.BaseEngineProject, "CookedTests_" + Plat.ToString() + "_" + Kind.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform), ThisMonoGameTestNodes, 0.0f)));
}
}
foreach (var NonCodeProject in Branch.NonCodeProjects)
{
if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) || !NonCodeProjectNames[NonCodeProject.GameName].Contains(Plat))
{
continue;
}
if (!GUBPNodes.ContainsKey(CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform)))
{
GameCookNodes.Add(AddNode(new CookNode(this, HostPlatform, NonCodeProject, Plat, CookedPlatform)));
}
if (!GUBPNodes.ContainsKey(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, NonCodeProject, Plat)))
{
AddNode(new GamePlatformCookedAndCompiledNode(this, HostPlatform, NonCodeProject, Plat, false));
if (NonCodeFormalBuilds.ContainsKey(NonCodeProject.GameName))
{
var PlatList = NonCodeFormalBuilds[NonCodeProject.GameName];
foreach (var PlatPair in PlatList)
{
if (PlatPair.TargetPlatform == Plat)
{
var NodeName = AddNode(new FormalBuildNode(this, NonCodeProject, HostPlatform, new List<UnrealTargetPlatform>() { Plat }, new List<UnrealTargetConfiguration>() {PlatPair.TargetConfig}));
// we don't want this delayed
// this would normally wait for the testing phase, we just want to build it right away
RemovePseudodependencyFromNode(
CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform),
WaitForTestShared.StaticGetFullName());
RemovePseudodependencyFromNode(
CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform),
CookNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, CookedPlatform));
string BuildAgentSharingGroup = NonCodeProject.GameName + "_MakeFormalBuild_" + Plat.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
if (Plat == UnrealTargetPlatform.IOS || Plat == UnrealTargetPlatform.Android) // These trash build products, so we need to use different agents
{
BuildAgentSharingGroup = "";
}
GUBPNodes[CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform)].AgentSharingGroup = BuildAgentSharingGroup;
GUBPNodes[GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, NonCodeProject, Plat)].AgentSharingGroup = BuildAgentSharingGroup;
GUBPNodes[NodeName].AgentSharingGroup = BuildAgentSharingGroup;
if (PlatPair.bTest)
{
AddNode(new FormalBuildTestNode(this, NonCodeProject, HostPlatform, Plat, PlatPair.TargetConfig));
}
}
}
}
}
if (!bNoAutomatedTesting)
{
if (HostPlatform == UnrealTargetPlatform.Mac) continue; //temp hack till mac automated testing works
var ThisMonoGameTestNodes = new List<string>();
foreach (var Test in GameTests)
{
var TestName = Test.Key + "_" + Plat.ToString();
ThisMonoGameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, TestName, Test.Value, CookedAgentSharingGroup, false, RequiredPlatforms)));
}
if (ThisMonoGameTestNodes.Count > 0)
{
GameTestNodes.Add(AddNode(new GameAggregateNode(this, HostPlatform, NonCodeProject, "CookedTests_" + Plat.ToString() + "_" + Kind.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform), ThisMonoGameTestNodes, 0.0f)));
}
}
}
}
}
}
}
#if false
//for now, non-code projects don't do client or server.
foreach (var ServerPlatform in ServerPlatforms)
{
var ServerTarget = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Server];
foreach (var GamePlatform in GamePlatforms)
{
var Target = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Game];
foreach (var NonCodeProject in Branch.NonCodeProjects)
{
if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) || !NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) ||
!NonCodeProjectNames[NonCodeProject.GameName].Contains(ServerPlatform) || !NonCodeProjectNames[NonCodeProject.GameName].Contains(GamePlatform) )
{
continue;
}
var ClientServerTests = Target.Rules.GUBP_GetClientServerTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), ServerPlatform, GamePlatform);
var RequiredPlatforms = new List<UnrealTargetPlatform> { ServerPlatform };
if (ServerPlatform != GamePlatform)
{
RequiredPlatforms.Add(GamePlatform);
}
foreach (var Test in ClientServerTests)
{
GameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, Test.Key + "_" + GamePlatform.ToString() + "_" + ServerPlatform.ToString(), Test.Value, false, RequiredPlatforms, true)));
}
}
}
}
#endif
if (GameTestNodes.Count > 0)
{
AddNode(new GameAggregateNode(this, HostPlatform, Branch.BaseEngineProject, "AllCookedTests", GameTestNodes));
}
}
foreach (var CodeProj in Branch.CodeProjects)
{
var Options = CodeProj.Options(HostPlatform);
if (!Options.bIsPromotable && !Options.bTestWithShared)
{
continue; // we skip things that aren't promotable and aren't tested
}
var AgentShareName = CodeProj.GameName;
if (!Options.bSeparateGamePromotion)
{
AgentShareName = "Shared";
}
AddNode(new EditorGameNode(this, HostPlatform, CodeProj));
if (!bNoAutomatedTesting && HostPlatform != UnrealTargetPlatform.Mac) //temp hack till mac automated testing works
{
var EditorTests = CodeProj.Properties.Targets[TargetRules.TargetType.Editor].Rules.GUBP_GetEditorTests_EditorTypeOnly(HostPlatform);
var EditorTestNodes = new List<string>();
string AgentSharingGroup = "";
if (EditorTests.Count > 1)
{
AgentSharingGroup = AgentShareName + "_EditorTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
}
foreach (var Test in EditorTests)
{
EditorTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, Test.Key, Test.Value, AgentSharingGroup)));
}
if (EditorTestNodes.Count > 0)
{
AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "AllEditorTests", EditorTestNodes, 0.0f));
}
}
var CookedAgentSharingGroup = AgentShareName + "_CookedTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
var ServerPlatforms = new List<UnrealTargetPlatform>();
var GamePlatforms = new List<UnrealTargetPlatform>();
var GameTestNodes = new List<string>();
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (CodeProj.Properties.Targets.ContainsKey(Kind))
{
var Target = CodeProj.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
foreach (var Plat in Platforms)
{
if (!Platform.Platforms[HostPlatform].CanHostPlatform(Plat))
{
throw new AutomationException("Project {0} asked for platform {1} with host {2}, but the host platform cannot build that platform.", CodeProj.GameName, Plat.ToString(), HostPlatform.ToString());
}
if (bNoIOSOnPC && Plat == UnrealTargetPlatform.IOS && HostPlatform == UnrealTargetPlatform.Win64)
{
continue;
}
if (ActivePlatforms.Contains(Plat))
{
if (Kind == TargetRules.TargetType.Server && !ServerPlatforms.Contains(Plat))
{
ServerPlatforms.Add(Plat);
}
if (Kind == TargetRules.TargetType.Game && !GamePlatforms.Contains(Plat))
{
GamePlatforms.Add(Plat);
}
if (!GUBPNodes.ContainsKey(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, CodeProj, Plat)))
{
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, CodeProj, Plat));
}
string CookedPlatform = Platform.Platforms[Plat].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
if (!GUBPNodes.ContainsKey(CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform)))
{
AddNode(new CookNode(this, HostPlatform, CodeProj, Plat, CookedPlatform));
}
if (!GUBPNodes.ContainsKey(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, CodeProj, Plat)))
{
AddNode(new GamePlatformCookedAndCompiledNode(this, HostPlatform, CodeProj, Plat, true));
}
var FormalBuildConfigs = Target.Rules.GUBP_GetConfigsForFormalBuilds_MonolithicOnly(HostPlatform);
foreach (var Config in FormalBuildConfigs)
{
string FormalNodeName = null;
if (Kind == TargetRules.TargetType.Client)
{
FormalNodeName = AddNode(new FormalBuildNode(this, CodeProj, HostPlatform, InClientTargetPlatforms: new List<UnrealTargetPlatform>() { Plat }, InClientConfigs: new List<UnrealTargetConfiguration>() { Config.TargetConfig }, InClientNotGame : true));
}
else if (Kind == TargetRules.TargetType.Server)
{
FormalNodeName = AddNode(new FormalBuildNode(this, CodeProj, HostPlatform, InServerTargetPlatforms: new List<UnrealTargetPlatform>() { Plat }, InServerConfigs: new List<UnrealTargetConfiguration>() { Config.TargetConfig }));
}
else if (Kind == TargetRules.TargetType.Game)
{
FormalNodeName = AddNode(new FormalBuildNode(this, CodeProj, HostPlatform, InClientTargetPlatforms: new List<UnrealTargetPlatform>() { Plat }, InClientConfigs: new List<UnrealTargetConfiguration>() { Config.TargetConfig }));
}
// we don't want this delayed
// this would normally wait for the testing phase, we just want to build it right away
RemovePseudodependencyFromNode(
CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform),
WaitForTestShared.StaticGetFullName());
RemovePseudodependencyFromNode(
CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform),
CookNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, CookedPlatform));
string BuildAgentSharingGroup = CodeProj.GameName + "_MakeFormalBuild_" + Plat.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
if (Plat == UnrealTargetPlatform.IOS || Plat == UnrealTargetPlatform.Android) // These trash build products, so we need to use different agents
{
BuildAgentSharingGroup = "";
}
GUBPNodes[CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform)].AgentSharingGroup = BuildAgentSharingGroup;
GUBPNodes[GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, CodeProj, Plat)].AgentSharingGroup = BuildAgentSharingGroup;
GUBPNodes[FormalNodeName].AgentSharingGroup = BuildAgentSharingGroup;
if (Config.bTest)
{
AddNode(new FormalBuildTestNode(this, CodeProj, HostPlatform, Plat, Config.TargetConfig));
}
}
if (!bNoAutomatedTesting)
{
if (HostPlatform == UnrealTargetPlatform.Mac) continue; //temp hack till mac automated testing works
var GameTests = Target.Rules.GUBP_GetGameTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), Plat);
var RequiredPlatforms = new List<UnrealTargetPlatform> { Plat };
var ThisMonoGameTestNodes = new List<string>();
foreach (var Test in GameTests)
{
var TestNodeName = Test.Key + "_" + Plat.ToString();
ThisMonoGameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, TestNodeName, Test.Value, CookedAgentSharingGroup, false, RequiredPlatforms)));
}
if (ThisMonoGameTestNodes.Count > 0)
{
GameTestNodes.Add(AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "CookedTests_" + Plat.ToString() + "_" + Kind.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform), ThisMonoGameTestNodes, 0.0f)));
}
}
}
}
}
}
if (!bNoAutomatedTesting)
{
foreach (var ServerPlatform in ServerPlatforms)
{
foreach (var GamePlatform in GamePlatforms)
{
if (HostPlatform == UnrealTargetPlatform.Mac) continue; //temp hack till mac automated testing works
var Target = CodeProj.Properties.Targets[TargetRules.TargetType.Game];
var ClientServerTests = Target.Rules.GUBP_GetClientServerTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), ServerPlatform, GamePlatform);
var RequiredPlatforms = new List<UnrealTargetPlatform> { ServerPlatform };
if (ServerPlatform != GamePlatform)
{
RequiredPlatforms.Add(GamePlatform);
}
foreach (var Test in ClientServerTests)
{
var TestNodeName = Test.Key + "_" + GamePlatform.ToString() + "_" + ServerPlatform.ToString();
GameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, TestNodeName, Test.Value, CookedAgentSharingGroup, false, RequiredPlatforms)));
}
}
}
if (GameTestNodes.Count > 0)
{
AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "AllCookedTests", GameTestNodes));
}
}
}
}
int NumSharedAllHosts = 0;
foreach (var CodeProj in Branch.CodeProjects)
{
if (CodeProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
bool AnySeparate = false;
var PromotedHosts = new List<UnrealTargetPlatform>();
foreach (var HostPlatform in HostPlatforms)
{
var Options = CodeProj.Options(HostPlatform);
AnySeparate = AnySeparate || Options.bSeparateGamePromotion;
if (Options.bIsPromotable)
{
if (!Options.bSeparateGamePromotion)
{
NumSharedAllHosts++;
}
PromotedHosts.Add(HostPlatform);
}
}
if (PromotedHosts.Count > 0)
{
AddNode(new GameAggregatePromotableNode(this, PromotedHosts, CodeProj));
if (AnySeparate)
{
AddNode(new WaitForGamePromotionUserInput(this, CodeProj, false));
AddNode(new GameLabelPromotableNode(this, CodeProj, false));
AddNode(new WaitForGamePromotionUserInput(this, CodeProj, true));
AddNode(new GameLabelPromotableNode(this, CodeProj, true));
}
}
}
}
if (NumSharedAllHosts > 0)
{
AddNode(new GameAggregatePromotableNode(this, HostPlatforms, Branch.BaseEngineProject));
AddNode(new SharedAggregatePromotableNode(this, HostPlatforms));
AddNode(new WaitForSharedPromotionUserInput(this, false));
AddNode(new SharedLabelPromotableNode(this, false));
AddNode(new WaitForTestShared(this));
AddNode(new WaitForSharedPromotionUserInput(this, true));
AddNode(new SharedLabelPromotableNode(this, true));
}
foreach (var HostPlatform in HostPlatforms)
{
AddCustomNodes(HostPlatform);
}
if (HasNode(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64)))
{
if (HasNode(GamePlatformMonolithicsNode.StaticGetFullName(UnrealTargetPlatform.Mac, Branch.BaseEngineProject, UnrealTargetPlatform.IOS)) && HasNode(ToolsNode.StaticGetFullName(UnrealTargetPlatform.Win64)))
{
AddNode(new IOSOnPCTestNode(this));
}
if (!bPreflightBuild)
{
AddNode(new CleanSharedTempStorageNode(this));
}
}
if (bPreflightBuild)
{
GeneralSuccessNode PreflightSuccessNode = new GeneralSuccessNode("Preflight");
foreach (var NodeToDo in GUBPNodes)
{
if (NodeToDo.Value.RunInEC())
{
PreflightSuccessNode.AddPseudodependency(NodeToDo.Key);
}
}
AddNode(PreflightSuccessNode);
}
foreach (var NodeToDo in GUBPNodes)
{
foreach (var Dep in GUBPNodes[NodeToDo.Key].FullNamesOfDependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the full graph. It is a dependency of {1}.", Dep, NodeToDo.Key);
}
if (Dep == NodeToDo.Key)
{
throw new AutomationException("Node {0} has a self arc.", NodeToDo.Key);
}
}
foreach (var Dep in GUBPNodes[NodeToDo.Key].FullNamesOfPseudosependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the full graph. It is a pseudodependency of {1}.", Dep, NodeToDo.Key);
}
if (Dep == NodeToDo.Key)
{
throw new AutomationException("Node {0} has a self pseudoarc.", NodeToDo.Key);
}
}
}
foreach (var NodeToDo in GUBPNodes)
{
ComputeDependentCISFrequencyQuantumShift(NodeToDo.Key);
}
if (bCleanLocalTempStorage) // shared temp storage can never be wiped
{
DeleteLocalTempStorageManifests(CmdEnv);
}
GUBPNodesControllingTrigger = new Dictionary<string, string>();
GUBPNodesControllingTriggerDotName = new Dictionary<string, string>();
var FullNodeList = new Dictionary<string, string>();
var FullNodeListSortKey = new Dictionary<string, int>();
var FullNodeDirectDependencies = new Dictionary<string, string>();
{
Log("******* {0} GUBP Nodes", GUBPNodes.Count);
var SortedNodes = TopologicalSort(new HashSet<string>(GUBPNodes.Keys), LocalOnly: true, DoNotConsiderCompletion: true);
int Count = 0;
foreach (var Node in SortedNodes)
{
string Note = GetControllingTriggerDotName(Node);
if (Note == "")
{
Note = CISFrequencyQuantumShiftString(Node);
}
if (Note == "")
{
Note = "always";
}
if (GUBPNodes[Node].RunInEC())
{
var Deps = GetECDependencies(Node);
string All = "";
foreach (var Dep in Deps)
{
if (All != "")
{
All += " ";
}
All += Dep;
}
Log(" {0}: {1} {2}", Node, Note, All);
FullNodeList.Add(Node, Note);
FullNodeDirectDependencies.Add(Node, All);
FullNodeListSortKey.Add(Node, Count);
Count++;
}
else
{
Log(" {0}: {1} [Aggregate]", Node, Note);
}
}
}
bool bOnlyNode = false;
bool bRelatedToNode = false;
var NodesToDo = new HashSet<string>();
{
string NodeSpec = ParseParamValue("Node");
if (String.IsNullOrEmpty(NodeSpec))
{
NodeSpec = ParseParamValue("RelatedToNode");
if (!String.IsNullOrEmpty(NodeSpec))
{
bRelatedToNode = true;
}
}
if (String.IsNullOrEmpty(NodeSpec) && CommanderSetup)
{
NodeSpec = ParseParamValue("SetupNode");
if (String.IsNullOrEmpty(NodeSpec))
{
NodeSpec = ParseParamValue("SetupRelatedToNode");
if (!String.IsNullOrEmpty(NodeSpec))
{
bRelatedToNode = true;
}
}
}
if (String.IsNullOrEmpty(NodeSpec))
{
NodeSpec = ParseParamValue("OnlyNode");
if (!String.IsNullOrEmpty(NodeSpec))
{
bOnlyNode = true;
}
}
if (String.IsNullOrEmpty(NodeSpec) && bBuildRocket)
{
// rocket is the shared promotable plus some other stuff and nothing else
bRelatedToNode = true;
NodeSpec = "Rocket_Aggregate";
if (!ParseParam("RocketValidate"))
{
//build a full promotable NodeSpec = SharedAggregatePromotableNode.StaticGetFullName() + "+" + NodeSpec;
}
else if (!bFake)
{
NodeSpec = "Rocket_MakeBuild+" + NodeSpec;
}
}
if (!String.IsNullOrEmpty(NodeSpec))
{
if (NodeSpec.Equals("Noop", StringComparison.InvariantCultureIgnoreCase))
{
Log("Request for Noop node, done.");
PrintRunTime();
return;
}
List<string> Nodes = new List<string>(NodeSpec.Split('+'));
foreach (var NodeArg in Nodes)
{
var NodeName = NodeArg.Trim();
bool bFoundAnything = false;
if (!String.IsNullOrEmpty(NodeName))
{
foreach (var Node in GUBPNodes)
{
if (Node.Value.GetFullName().Equals(NodeArg, StringComparison.InvariantCultureIgnoreCase) ||
Node.Value.AgentSharingGroup.Equals(NodeArg, StringComparison.InvariantCultureIgnoreCase)
)
{
if (!NodesToDo.Contains(Node.Key))
{
NodesToDo.Add(Node.Key);
}
bFoundAnything = true;
}
}
if (!bFoundAnything)
{
throw new AutomationException("Could not find node named {0}", NodeName);
}
}
}
}
}
string GameSpec = ParseParamValue("Game");
if (!String.IsNullOrEmpty(GameSpec))
{
List<string> Games = new List<string>(GameSpec.Split('+'));
foreach (var GameArg in Games)
{
var GameName = GameArg.Trim();
if (!String.IsNullOrEmpty(GameName))
{
foreach (var GameProj in Branch.CodeProjects)
{
if (GameProj.GameName.Equals(GameName, StringComparison.InvariantCultureIgnoreCase))
{
NodesToDo.Add(GameAggregatePromotableNode.StaticGetFullName(GameProj));
foreach (var Node in GUBPNodes)
{
if (Node.Value.GameNameIfAnyForTempStorage() == GameProj.GameName)
{
NodesToDo.Add(Node.Key);
}
}
GameName = null;
}
}
if (GameName != null)
{
foreach (var GameProj in Branch.NonCodeProjects)
{
if (GameProj.GameName.Equals(GameName, StringComparison.InvariantCultureIgnoreCase))
{
foreach (var Node in GUBPNodes)
{
if (Node.Value.GameNameIfAnyForTempStorage() == GameProj.GameName)
{
NodesToDo.Add(Node.Key);
}
}
GameName = null;
}
}
}
if (GameName != null)
{
throw new AutomationException("Could not find game named {0}", GameName);
}
}
}
}
if (NodesToDo.Count == 0)
{
Log("No nodes specified, adding all nodes");
foreach (var Node in GUBPNodes)
{
NodesToDo.Add(Node.Key);
}
}
else if (TimeIndex != 0)
{
Log("Check to make sure we didn't ask for nodes that will be culled by time index");
foreach (var NodeToDo in NodesToDo)
{
if (TimeIndex % (1 << GUBPNodes[NodeToDo].DependentCISFrequencyQuantumShift()) != 0)
{
throw new AutomationException("You asked specifically for node {0}, but it is culled by the time quantum: TimeIndex = {1}, DependentCISFrequencyQuantumShift = {2}.", NodeToDo, TimeIndex, GUBPNodes[NodeToDo].DependentCISFrequencyQuantumShift());
}
}
}
Log("Desired Nodes");
foreach (var NodeToDo in NodesToDo)
{
Log(" {0}", NodeToDo);
}
// if we are doing related to, then find things that depend on the selected nodes
if (bRelatedToNode)
{
bool bDoneWithDependencies = false;
while (!bDoneWithDependencies)
{
bDoneWithDependencies = true;
var Fringe = new HashSet<string>();
foreach (var NodeToDo in GUBPNodes)
{
if (!NodesToDo.Contains(NodeToDo.Key))
{
foreach (var Dep in GUBPNodes[NodeToDo.Key].FullNamesOfDependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the graph. It is a dependency of {1}.", Dep, NodeToDo.Key);
}
if (NodesToDo.Contains(Dep))
{
Fringe.Add(NodeToDo.Key);
bDoneWithDependencies = false;
}
}
foreach (var Dep in GUBPNodes[NodeToDo.Key].FullNamesOfPseudosependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the graph. It is a pseudodependency of {1}.", Dep, NodeToDo.Key);
}
}
}
}
NodesToDo.UnionWith(Fringe);
}
}
// find things that our nodes depend on
if (!bOnlyNode)
{
bool bDoneWithDependencies = false;
while (!bDoneWithDependencies)
{
bDoneWithDependencies = true;
var Fringe = new HashSet<string>();
foreach (var NodeToDo in NodesToDo)
{
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the graph. It is a dependency of {1}.", Dep, NodeToDo);
}
if (!NodesToDo.Contains(Dep))
{
Fringe.Add(Dep);
bDoneWithDependencies = false;
}
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the graph. It is a pseudodependency of {1}.", Dep, NodeToDo);
}
}
}
NodesToDo.UnionWith(Fringe);
}
}
if (TimeIndex != 0)
{
Log("Culling based on time index");
var NewNodesToDo = new HashSet<string>();
foreach (var NodeToDo in NodesToDo)
{
if (TimeIndex % (1 << GUBPNodes[NodeToDo].DependentCISFrequencyQuantumShift()) == 0)
{
Log(" Keeping {0}", NodeToDo);
NewNodesToDo.Add(NodeToDo);
}
else
{
Log(" Rejecting {0}", NodeToDo);
}
}
NodesToDo = NewNodesToDo;
}
string FakeFail = ParseParamValue("FakeFail");
if (CommanderSetup)
{
if (!String.IsNullOrEmpty(ExplicitTrigger))
{
bool bFoundIt = false;
foreach (var Node in GUBPNodes)
{
if (Node.Value.GetFullName().Equals(ExplicitTrigger, StringComparison.InvariantCultureIgnoreCase))
{
if (Node.Value.TriggerNode() && Node.Value.RunInEC())
{
Node.Value.SetAsExplicitTrigger();
bFoundIt = true;
break;
}
}
}
if (!bFoundIt)
{
throw new AutomationException("Could not find trigger node named {0}", ExplicitTrigger);
}
}
else
{
if (bSkipTriggers)
{
foreach (var Node in GUBPNodes)
{
if (Node.Value.TriggerNode() && Node.Value.RunInEC())
{
Node.Value.SetAsExplicitTrigger();
}
}
}
}
}
GUBPNodesCompleted = new Dictionary<string, bool>();
GUBPNodesHistory = new Dictionary<string, NodeHistory>();
Log("******* Caching completion");
{
var StartTime = DateTime.UtcNow;
foreach (var Node in NodesToDo)
{
Log("** {0}", Node);
NodeIsAlreadyComplete(Node, LocalOnly); // cache these now to avoid spam later
GetControllingTriggerDotName(Node);
}
var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
Log("Took {0}s to cache completion for {1} nodes", BuildDuration / 1000, NodesToDo.Count);
}
if (CLString != "" && StoreName.Contains(CLString) && !ParseParam("NoHistory"))
{
Log("******* Updating history");
var StartTime = DateTime.UtcNow;
foreach (var Node in NodesToDo)
{
if (!NodeIsAlreadyComplete(Node, LocalOnly))
{
UpdateNodeHistory(Node, CLString);
}
}
var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
Log("Took {0}s to get history for {1} nodes", BuildDuration / 1000, NodesToDo.Count);
}
var OrdereredToDo = TopologicalSort(NodesToDo, ExplicitTrigger, LocalOnly);
// find all unfinished triggers, excepting the one we are triggering right now
var UnfinishedTriggers = new List<string>();
if (!bSkipTriggers)
{
foreach (var NodeToDo in OrdereredToDo)
{
if (GUBPNodes[NodeToDo].TriggerNode() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly))
{
if (String.IsNullOrEmpty(ExplicitTrigger) || ExplicitTrigger != NodeToDo)
{
UnfinishedTriggers.Add(NodeToDo);
}
}
}
}
Log("*********** Desired And Dependent Nodes, in order.");
PrintNodes(this, OrdereredToDo, LocalOnly, UnfinishedTriggers);
//check sorting
{
foreach (var NodeToDo in OrdereredToDo)
{
if (GUBPNodes[NodeToDo].TriggerNode() && (GUBPNodes[NodeToDo].IsSticky() || NodeIsAlreadyComplete(NodeToDo, LocalOnly))) // these sticky triggers are ok, everything is already completed anyway
{
continue;
}
int MyIndex = OrdereredToDo.IndexOf(NodeToDo);
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
int DepIndex = OrdereredToDo.IndexOf(Dep);
if (DepIndex >= MyIndex)
{
throw new AutomationException("Topological sort error, node {0} has a dependency of {1} which sorted after it.", NodeToDo, Dep);
}
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
int DepIndex = OrdereredToDo.IndexOf(Dep);
if (DepIndex >= MyIndex)
{
throw new AutomationException("Topological sort error, node {0} has a pseduodependency of {1} which sorted after it.", NodeToDo, Dep);
}
}
}
}
if (CommanderSetup)
{
if (OrdereredToDo.Count == 0)
{
throw new AutomationException("No nodes to do!");
}
var ECProps = new List<string>();
ECProps.Add(String.Format("TimeIndex={0}", TimeIndex));
foreach (var NodePair in FullNodeList)
{
ECProps.Add(string.Format("AllNodes/{0}={1}", NodePair.Key, NodePair.Value));
}
foreach (var NodePair in FullNodeDirectDependencies)
{
ECProps.Add(string.Format("DirectDependencies/{0}={1}", NodePair.Key, NodePair.Value));
}
foreach (var NodePair in FullNodeListSortKey)
{
ECProps.Add(string.Format("SortKey/{0}={1}", NodePair.Key, NodePair.Value));
}
var ECJobProps = new List<string>();
if (ExplicitTrigger != "")
{
ECJobProps.Add("IsRoot=0");
}
else
{
ECJobProps.Add("IsRoot=1");
}
var FilteredOrdereredToDo = new List<string>();
// remove nodes that have unfinished triggers
foreach (var NodeToDo in OrdereredToDo)
{
string ControllingTrigger = GetControllingTrigger(NodeToDo);
bool bNoUnfinishedTriggers = !UnfinishedTriggers.Contains(ControllingTrigger);
if (bNoUnfinishedTriggers)
{
// if we are triggering, then remove nodes that are not controlled by the trigger or are dependencies of this trigger
if (!String.IsNullOrEmpty(ExplicitTrigger))
{
if (ExplicitTrigger != NodeToDo && !NodeDependsOn(NodeToDo, ExplicitTrigger) && !NodeDependsOn(ExplicitTrigger, NodeToDo))
{
continue; // this wasn't on the chain related to the trigger we are triggering, so it is not relevant
}
}
if (bPreflightBuild && !bSkipTriggers && GUBPNodes[NodeToDo].TriggerNode())
{
// in preflight builds, we are either skipping triggers (and running things downstream) or we just stop at triggers and don't make them available for triggering.
continue;
}
FilteredOrdereredToDo.Add(NodeToDo);
}
}
OrdereredToDo = FilteredOrdereredToDo;
Log("*********** EC Nodes, in order.");
PrintNodes(this, OrdereredToDo, LocalOnly, UnfinishedTriggers);
// here we are just making sure everything before the explicit trigger is completed.
if (!String.IsNullOrEmpty(ExplicitTrigger))
{
foreach (var NodeToDo in FilteredOrdereredToDo)
{
if (GUBPNodes[NodeToDo].RunInEC() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly) && NodeToDo != ExplicitTrigger && !NodeDependsOn(ExplicitTrigger, NodeToDo)) // if something is already finished, we don't put it into EC
{
throw new AutomationException("We are being asked to process node {0}, however, this is an explicit trigger {1}, so everything before it should already be handled. It seems likely that you waited too long to run the trigger. You will have to do a new build from scratch.", NodeToDo, ExplicitTrigger);
}
}
}
string LastSticky = "";
bool HitNonSticky = false;
bool bHaveECNodes = false;
// sticky nodes are ones that we run on the main agent. We run then first and they must not be intermixed with parallel jobs
foreach (var NodeToDo in OrdereredToDo)
{
if (GUBPNodes[NodeToDo].RunInEC() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly)) // if something is already finished, we don't put it into EC
{
bHaveECNodes = true;
if (GUBPNodes[NodeToDo].IsSticky())
{
LastSticky = NodeToDo;
if (HitNonSticky && !bSkipTriggers)
{
throw new AutomationException("Sticky and non-sticky jobs did not sort right.");
}
}
else
{
HitNonSticky = true;
}
}
}
string ParentPath = ParseParamValue("ParentPath");
string BaseArgs = String.Format("createJobStep --parentPath {0}", ParentPath);
bool bHasNoop = false;
if (LastSticky == "" && bHaveECNodes)
{
// if we don't have any sticky nodes and we have other nodes, we run a fake noop just to release the resource
string Args = String.Format("{0} --subprocedure GUBP_UAT_Node --parallel 0 --jobStepName Noop --actualParameter NodeName=Noop --actualParameter Sticky=1 --releaseMode release", BaseArgs);
RunECTool(Args);
bHasNoop = true;
}
var FakeECArgs = new List<string>();
var BranchForEmail = "";
if (P4Enabled)
{
BranchForEmail = P4Env.BuildRootP4;
}
var AgentGroupChains = new Dictionary<string, List<string>>();
foreach (var NodeToDo in OrdereredToDo)
{
if (GUBPNodes[NodeToDo].RunInEC() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly)) // if something is already finished, we don't put it into EC
{
string MyAgentGroup = GUBPNodes[NodeToDo].AgentSharingGroup;
if (MyAgentGroup != "")
{
if (!AgentGroupChains.ContainsKey(MyAgentGroup))
{
AgentGroupChains.Add(MyAgentGroup, new List<string>{NodeToDo});
}
else
{
AgentGroupChains[MyAgentGroup].Add(NodeToDo);
}
}
}
}
foreach (var NodeToDo in OrdereredToDo)
{
if (GUBPNodes[NodeToDo].RunInEC() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly)) // if something is already finished, we don't put it into EC
{
string EMails;
var NodeProps = GetECPropsForNode(NodeToDo, CLString, out EMails);
ECProps.AddRange(NodeProps);
bool Sticky = GUBPNodes[NodeToDo].IsSticky();
bool DoParallel = !Sticky;
if (Sticky && GUBPNodes[NodeToDo].ECAgentString() != "")
{
throw new AutomationException("Node {1} is sticky but has agent requirements.", NodeToDo);
}
string Procedure = GUBPNodes[NodeToDo].ECProcedure();
string Args = String.Format("{0} --subprocedure {1} --parallel {2} --jobStepName {3} --actualParameter NodeName={4}",
BaseArgs, Procedure, DoParallel ? 1 : 0, NodeToDo, NodeToDo);
string ProcedureParams = GUBPNodes[NodeToDo].ECProcedureParams();
if (!String.IsNullOrEmpty(ProcedureParams))
{
Args = Args + " " + ProcedureParams;
}
if ((Procedure == "GUBP_UAT_Trigger" || Procedure == "GUBP_Hardcoded_Trigger") && !String.IsNullOrEmpty(EMails))
{
Args = Args + " --actualParameter EmailsForTrigger=\"" + EMails + "\"";
}
string PreCondition = "";
string RunCondition = "";
var UncompletedEcDeps = new List<string>();
{
var EcDeps = GetECDependencies(NodeToDo);
foreach (var Dep in EcDeps)
{
if (GUBPNodes[Dep].RunInEC() && !NodeIsAlreadyComplete(Dep, LocalOnly) && OrdereredToDo.Contains(Dep)) // if something is already finished, we don't put it into EC
{
if (OrdereredToDo.IndexOf(Dep) > OrdereredToDo.IndexOf(NodeToDo))
{
throw new AutomationException("Topological sort error, node {0} has a dependency of {1} which sorted after it.", NodeToDo, Dep);
}
UncompletedEcDeps.Add(Dep);
}
}
}
string MyAgentGroup = GUBPNodes[NodeToDo].AgentSharingGroup;
bool bDoNestedJobstep = false;
bool bDoFirstNestedJobstep = false;
var PreConditionUncompletedEcDeps = UncompletedEcDeps;
string NodeParentPath = ParentPath;
if (MyAgentGroup != "")
{
bDoNestedJobstep = true;
NodeParentPath = ParentPath + "/jobSteps[" + MyAgentGroup + "]";
PreConditionUncompletedEcDeps = new List<string>();
var MyChain = AgentGroupChains[MyAgentGroup];
int MyIndex = MyChain.IndexOf(NodeToDo);
if (MyIndex > 0)
{
PreConditionUncompletedEcDeps.Add(MyChain[MyIndex - 1]);
}
else
{
bDoFirstNestedJobstep = bDoNestedJobstep;
// to avoid idle agents (and also EC doesn't actually reserve our agent!), we promote all dependencies to the first one
foreach (var Chain in MyChain)
{
var EcDeps = GetECDependencies(Chain);
foreach (var Dep in EcDeps)
{
if (GUBPNodes[Dep].RunInEC() && !NodeIsAlreadyComplete(Dep, LocalOnly) && OrdereredToDo.Contains(Dep)) // if something is already finished, we don't put it into EC
{
if (OrdereredToDo.IndexOf(Dep) > OrdereredToDo.IndexOf(Chain))
{
throw new AutomationException("Topological sort error, node {0} has a dependency of {1} which sorted after it.", Chain, Dep);
}
if (!MyChain.Contains(Dep) && !PreConditionUncompletedEcDeps.Contains(Dep))
{
PreConditionUncompletedEcDeps.Add(Dep);
}
}
}
}
}
}
if (bHasNoop && PreConditionUncompletedEcDeps.Count == 0)
{
PreConditionUncompletedEcDeps.Add("Noop");
}
if (PreConditionUncompletedEcDeps.Count > 0)
{
PreCondition = "\"$[/javascript if(";
// these run "parallel", but we add preconditions to serialize them
int Index = 0;
foreach (var Dep in PreConditionUncompletedEcDeps)
{
PreCondition = PreCondition + "getProperty('" + GetJobStep(ParentPath, Dep) + "/status') == 'completed'";
Index++;
if (Index != PreConditionUncompletedEcDeps.Count)
{
PreCondition = PreCondition + " && ";
}
}
PreCondition = PreCondition + ") true;]\"";
}
if (UncompletedEcDeps.Count > 0)
{
RunCondition = "\"$[/javascript if(";
int Index = 0;
foreach (var Dep in UncompletedEcDeps)
{
RunCondition = RunCondition + "('$[" + GetJobStep(ParentPath, Dep) + "/outcome]' == 'success' || ";
RunCondition = RunCondition + "'$[" + GetJobStep(ParentPath, Dep) + "/outcome]' == 'warning')";
Index++;
if (Index != UncompletedEcDeps.Count)
{
RunCondition = RunCondition + " && ";
}
}
RunCondition = RunCondition + ") true; else false;]\"";
}
if (bDoNestedJobstep)
{
if (bDoFirstNestedJobstep)
{
{
string NestArgs = String.Format("createJobStep --parentPath {0} --jobStepName {1} --parallel 1",
ParentPath, MyAgentGroup);
if (!String.IsNullOrEmpty(PreCondition))
{
NestArgs = NestArgs + " --precondition " + PreCondition;
}
RunECTool(NestArgs);
}
{
string NestArgs = String.Format("createJobStep --parentPath {0}/jobSteps[{1}] --jobStepName {2}_GetPool --subprocedure GUBP{3}_AgentShare_GetPool --parallel 1 --actualParameter AgentSharingGroup={4} --actualParameter NodeName={5}",
ParentPath, MyAgentGroup, MyAgentGroup, GUBPNodes[NodeToDo].ECProcedureInfix(), MyAgentGroup, NodeToDo);
if (!String.IsNullOrEmpty(PreCondition))
{
NestArgs = NestArgs + " --precondition " + PreCondition;
}
RunECTool(NestArgs);
}
{
string NestArgs = String.Format("createJobStep --parentPath {0}/jobSteps[{1}] --jobStepName {2}_GetAgent --subprocedure GUBP{3}_AgentShare_GetAgent --parallel 1 --exclusiveMode call --resourceName {4} --actualParameter AgentSharingGroup={5} --actualParameter NodeName={6}",
ParentPath, MyAgentGroup, MyAgentGroup, GUBPNodes[NodeToDo].ECProcedureInfix(),
String.Format("$[/myJob/jobSteps[{0}]/ResourcePool]", MyAgentGroup),
MyAgentGroup, NodeToDo);
{
NestArgs = NestArgs + " --precondition ";
NestArgs = NestArgs + "\"$[/javascript if(";
NestArgs = NestArgs + "getProperty('" + ParentPath + "/jobSteps[" + MyAgentGroup + "]/jobSteps[" + MyAgentGroup + "_GetPool]/status') == 'completed'";
NestArgs = NestArgs + ") true;]\"";
}
RunECTool(NestArgs);
}
{
PreCondition = "\"$[/javascript if(";
PreCondition = PreCondition + "getProperty('" + ParentPath + "/jobSteps[" + MyAgentGroup + "]/jobSteps[" + MyAgentGroup + "_GetAgent]/status') == 'completed'";
PreCondition = PreCondition + ") true;]\"";
}
}
Args = Args.Replace(String.Format("--parentPath {0} ", ParentPath), String.Format("--parentPath {0} ", NodeParentPath));
Args = Args.Replace("UAT_Node_Parallel_AgentShare", "UAT_Node_Parallel_AgentShare3");
}
if (!String.IsNullOrEmpty(PreCondition))
{
Args = Args + " --precondition " + PreCondition;
}
if (!String.IsNullOrEmpty(RunCondition))
{
Args = Args + " --condition " + RunCondition;
}
#if false
// this doesn't work because it includes precondition time
if (GUBPNodes[NodeToDo].TimeoutInMinutes() > 0)
{
Args = Args + String.Format(" --timeLimitUnits minutes --timeLimit {0}", GUBPNodes[NodeToDo].TimeoutInMinutes());
}
#endif
if (Sticky && NodeToDo == LastSticky)
{
Args = Args + " --releaseMode release";
}
RunECTool(Args);
if (bFakeEC &&
!UnfinishedTriggers.Contains(NodeToDo) &&
(GUBPNodes[NodeToDo].ECProcedure().StartsWith("GUBP_UAT_Node") || GUBPNodes[NodeToDo].ECProcedure().StartsWith("GUBP_Mac_UAT_Node")) // other things we really can't test
) // unfinished triggers are never run directly by EC, rather it does another job setup
{
string Arg = String.Format("gubp -Node={0} -FakeEC {1} {2} {3} {4} {5}",
NodeToDo,
bFake ? "-Fake" : "" ,
ParseParam("AllPlatforms") ? "-AllPlatforms" : "",
ParseParam("UnfinishedTriggersFirst") ? "-UnfinishedTriggersFirst" : "",
ParseParam("UnfinishedTriggersParallel") ? "-UnfinishedTriggersParallel" : "",
ParseParam("WithMac") ? "-WithMac" : ""
);
string Node = ParseParamValue("-Node");
if (!String.IsNullOrEmpty(Node))
{
Arg = Arg + " -Node=" + Node;
}
if (!String.IsNullOrEmpty(FakeFail))
{
Arg = Arg + " -FakeFail=" + FakeFail;
}
FakeECArgs.Add(Arg);
}
if (MyAgentGroup != "" && !bDoNestedJobstep)
{
var MyChain = AgentGroupChains[MyAgentGroup];
int MyIndex = MyChain.IndexOf(NodeToDo);
if (MyIndex == MyChain.Count - 1)
{
var RelPreCondition = "\"$[/javascript if(";
// this runs "parallel", but we a precondition to serialize it
RelPreCondition = RelPreCondition + "getProperty('" + ParentPath + "/jobSteps[" + NodeToDo + "]/status') == 'completed'";
RelPreCondition = RelPreCondition + ") true;]\"";
// we need to release the resource
string RelArgs = String.Format("{0} --subprocedure GUBP_Release_AgentShare --parallel 1 --jobStepName Release_{1} --actualParameter AgentSharingGroup={2} --releaseMode release --precondition {3}",
BaseArgs, MyAgentGroup, MyAgentGroup, RelPreCondition);
RunECTool(RelArgs);
}
}
}
}
{
ECProps.Add("GUBP_LoadedProps=1");
string BranchDefFile = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LogFolder, "BranchDef.properties");
CommandUtils.WriteAllLines(BranchDefFile, ECProps.ToArray());
RunECTool(String.Format("setProperty \"/myWorkflow/BranchDefFile\" \"{0}\"", BranchDefFile.Replace("\\", "\\\\")));
}
{
ECProps.Add("GUBP_LoadedJobProps=1");
string BranchJobDefFile = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LogFolder, "BranchJobDef.properties");
CommandUtils.WriteAllLines(BranchJobDefFile, ECProps.ToArray());
RunECTool(String.Format("setProperty \"/myJob/BranchJobDefFile\" \"{0}\"", BranchJobDefFile.Replace("\\", "\\\\")));
}
if (bFakeEC)
{
foreach (var Args in FakeECArgs)
{
RunUAT(CmdEnv, Args);
}
}
Log("Commander setup only, done.");
PrintRunTime();
return;
}
if (ParseParam("SaveGraph"))
{
SaveGraphVisualization(OrdereredToDo);
}
if (bListOnly)
{
Log("List only, done.");
return;
}
var BuildProductToNodeMap = new Dictionary<string, string>();
foreach (var NodeToDo in OrdereredToDo)
{
if (GUBPNodes[NodeToDo].BuildProducts != null || GUBPNodes[NodeToDo].AllDependencyBuildProducts != null)
{
throw new AutomationException("topological sort error");
}
GUBPNodes[NodeToDo].AllDependencyBuildProducts = new List<string>();
GUBPNodes[NodeToDo].AllDependencies = new List<string>();
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
GUBPNodes[NodeToDo].AddAllDependent(Dep);
if (GUBPNodes[Dep].AllDependencies == null)
{
if (!bOnlyNode)
{
throw new AutomationException("Node {0} was not processed yet3? Processing {1}", Dep, NodeToDo);
}
}
foreach (var DepDep in GUBPNodes[Dep].AllDependencies)
{
GUBPNodes[NodeToDo].AddAllDependent(DepDep);
}
if (GUBPNodes[Dep].BuildProducts == null)
{
if (!bOnlyNode)
{
throw new AutomationException("Node {0} was not processed yet? Processing {1}", Dep, NodeToDo);
}
}
else
{
foreach (var Prod in GUBPNodes[Dep].BuildProducts)
{
GUBPNodes[NodeToDo].AddDependentBuildProduct(Prod);
}
if (GUBPNodes[Dep].AllDependencyBuildProducts == null)
{
throw new AutomationException("Node {0} was not processed yet2? Processing {1}", Dep, NodeToDo);
}
foreach (var Prod in GUBPNodes[Dep].AllDependencyBuildProducts)
{
GUBPNodes[NodeToDo].AddDependentBuildProduct(Prod);
}
}
}
string NodeStoreName = StoreName + "-" + GUBPNodes[NodeToDo].GetFullName();
string GameNameIfAny = GUBPNodes[NodeToDo].GameNameIfAnyForTempStorage();
string StorageRootIfAny = GUBPNodes[NodeToDo].RootIfAnyForTempStorage();
if (bFake)
{
StorageRootIfAny = ""; // we don't rebase fake runs since those are entirely "records of success", which are always in the logs folder
}
// this is kinda complicated
bool SaveSuccessRecords = (IsBuildMachine || bFakeEC) && // no real reason to make these locally except for fakeEC tests
(!GUBPNodes[NodeToDo].TriggerNode() || GUBPNodes[NodeToDo].IsSticky()) // trigger nodes are run twice, one to start the new workflow and once when it is actually triggered, we will save reconds for the latter
&& (GUBPNodes[NodeToDo].RunInEC() || !GUBPNodes[NodeToDo].IsAggregate()); //aggregates not in EC can be "run" multiple times, so we can't track those
Log("***** Running GUBP Node {0} -> {1} : {2}", GUBPNodes[NodeToDo].GetFullName(), GameNameIfAny, NodeStoreName);
if (NodeIsAlreadyComplete(NodeToDo, LocalOnly))
{
if (NodeToDo == VersionFilesNode.StaticGetFullName() && !IsBuildMachine)
{
Log("***** NOT ****** Retrieving GUBP Node {0} from {1}; it is the version files.", GUBPNodes[NodeToDo].GetFullName(), NodeStoreName);
GUBPNodes[NodeToDo].BuildProducts = new List<string>();
}
else
{
Log("***** Retrieving GUBP Node {0} from {1}", GUBPNodes[NodeToDo].GetFullName(), NodeStoreName);
bool WasLocal;
GUBPNodes[NodeToDo].BuildProducts = RetrieveFromTempStorage(CmdEnv, NodeStoreName, out WasLocal, GameNameIfAny, StorageRootIfAny);
if (!WasLocal)
{
GUBPNodes[NodeToDo].PostLoadFromSharedTempStorage(this);
}
}
}
else
{
if (SaveSuccessRecords)
{
SaveStatus(NodeToDo, StartedTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny);
}
try
{
if (!String.IsNullOrEmpty(FakeFail) && FakeFail.Equals(NodeToDo, StringComparison.InvariantCultureIgnoreCase))
{
throw new AutomationException("Failing node {0} by request.", NodeToDo);
}
if (bFake)
{
Log("***** FAKE!! Building GUBP Node {0} for {1}", NodeToDo, NodeStoreName);
GUBPNodes[NodeToDo].DoFakeBuild(this);
}
else
{
Log("***** Building GUBP Node {0} for {1}", NodeToDo, NodeStoreName);
GUBPNodes[NodeToDo].DoBuild(this);
}
if (!GUBPNodes[NodeToDo].IsAggregate())
{
StoreToTempStorage(CmdEnv, NodeStoreName, GUBPNodes[NodeToDo].BuildProducts, !bSaveSharedTempStorage, GameNameIfAny, StorageRootIfAny);
if (ParseParam("StompCheck"))
{
foreach (var Dep in GUBPNodes[NodeToDo].AllDependencies)
{
try
{
bool WasLocal;
RetrieveFromTempStorage(CmdEnv, NodeStoreName, out WasLocal, GameNameIfAny, StorageRootIfAny);
if (!WasLocal)
{
throw new AutomationException("Retrieve was not local?");
}
}
catch(Exception Ex)
{
throw new AutomationException("Node {0} stomped Node {1} Ex: {2}", NodeToDo, Dep, LogUtils.FormatException(Ex));
}
}
}
}
}
catch (Exception Ex)
{
if (SaveSuccessRecords)
{
UpdateNodeHistory(NodeToDo, CLString);
SaveStatus(NodeToDo, FailedTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny, ParseParamValue("MyJobStepId"));
UpdateECProps(NodeToDo, CLString);
}
Log("{0}", ExceptionToString(Ex));
if (GUBPNodesHistory.ContainsKey(NodeToDo))
{
var History = GUBPNodesHistory[NodeToDo];
Log("Changes since last green *********************************");
Log("");
Log("");
Log("");
PrintDetailedChanges(History);
Log("End changes since last green");
}
string FailInfo = "";
FailInfo += "********************************* Main log file";
FailInfo += Environment.NewLine + Environment.NewLine;
FailInfo += LogUtils.GetLogTail();
FailInfo += Environment.NewLine + Environment.NewLine + Environment.NewLine;
string OtherLog = "See logfile for details: '";
if (FailInfo.Contains(OtherLog))
{
string LogFile = FailInfo.Substring(FailInfo.IndexOf(OtherLog) + OtherLog.Length);
if (LogFile.Contains("'"))
{
LogFile = CombinePaths(CmdEnv.LogFolder, LogFile.Substring(0, LogFile.IndexOf("'")));
if (FileExists_NoExceptions(LogFile))
{
FailInfo += "********************************* Sub log file " + LogFile;
FailInfo += Environment.NewLine + Environment.NewLine;
FailInfo += LogUtils.GetLogTail(LogFile);
FailInfo += Environment.NewLine + Environment.NewLine + Environment.NewLine;
}
}
}
string Filename = CombinePaths(CmdEnv.LogFolder, "LogTailsAndChanges.log");
WriteAllText(Filename, FailInfo);
throw(Ex);
}
if (SaveSuccessRecords)
{
UpdateNodeHistory(NodeToDo, CLString);
SaveStatus(NodeToDo, SucceededTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny);
UpdateECProps(NodeToDo, CLString);
}
}
foreach (var Product in GUBPNodes[NodeToDo].BuildProducts)
{
if (BuildProductToNodeMap.ContainsKey(Product))
{
throw new AutomationException("Overlapping build product: {0} and {1} both produce {2}", BuildProductToNodeMap[Product], NodeToDo, Product);
}
BuildProductToNodeMap.Add(Product, NodeToDo);
}
}
PrintRunTime();
}
string StartedTempStorageSuffix = "_Started";
string FailedTempStorageSuffix = "_Failed";
string SucceededTempStorageSuffix = "_Succeeded";
}