You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
6358 lines
273 KiB
C#
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";
|
|
}
|