Files
UnrealEngineUWP/Engine/Source/Programs/AutomationTool/Scripts/GUBP.Automation.cs
Dmitry Rekman 5f61d39bc7 Fix inability to load some C# assemblies (UE-12883).
[CL 2529346 by Dmitry Rekman in Main branch]
2015-04-28 18:30:10 -04:00

7713 lines
317 KiB
C#

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using AutomationTool;
using UnrealBuildTool;
using System.Reflection;
using System.Xml;
using System.Linq;
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")
&& !PropertySheetChildDiagSheetElemInner2.InnerText.Contains("-MyJobStepId=")
&& !PropertySheetChildDiagSheetElemInner2.InnerText.Contains("CommandUtils.Run: Run: Took ")
)
{
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 string BranchName;
public int CL = 0;
public int TimeIndex = 0;
public bool bSignBuildProducts = false;
public bool bHasTests = 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 bForceIncrementalCompile = false;
public static string ECProject = null;
public string EmailHint;
static public bool bPreflightBuild = false;
public int PreflightShelveCL = 0;
static public string PreflightMangleSuffix = "";
public GUBPBranchHacker.BranchOptions BranchOptions = null;
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 abstract class GUBPNodeAdder
{
public virtual void AddNodes(GUBP bp, UnrealTargetPlatform InHostPlatform)
{
}
}
private static List<GUBPNodeAdder> Adders;
Type[] GetTypesFromAssembly(Assembly Asm)
{
Type[] AllTypesWeCanGet;
try
{
AllTypesWeCanGet = Asm.GetTypes();
}
catch(ReflectionTypeLoadException Exc)
{
AllTypesWeCanGet = Exc.Types;
}
return AllTypesWeCanGet;
}
private void AddCustomNodes(UnrealTargetPlatform InHostPlatform)
{
if (Adders == null)
{
Adders = new List<GUBPNodeAdder>();
Assembly[] LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var Dll in LoadedAssemblies)
{
Type[] AllTypes = GetTypesFromAssembly(Dll);
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 List<UnrealTargetPlatform> PlatformsToRemove = new List<UnrealTargetPlatform>();
public List<string> ExcludeNodes = new List<string>();
public List<UnrealTargetPlatform> ExcludePlatformsForEditor = new List<UnrealTargetPlatform>();
public List<UnrealTargetPlatform> RemovePlatformFromPromotable = new List<UnrealTargetPlatform>();
public bool bNoAutomatedTesting = false;
public bool bNoDocumentation = false;
public bool bNoInstalledEngine = false;
public bool bMakeFormalBuildWithoutLabelPromotable = false;
public Dictionary<string, sbyte> FrequencyBarriers = new Dictionary<string,sbyte>();
public int QuantumOverride = 0;
}
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 = GetTypesFromAssembly(Dll);
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;
}
public virtual bool VetoEmailingCausers(GUBP bp, string Branch, string NodeName)
{
return false; // People who have submitted since last-green will be included unless vetoed by overriding this method.
}
}
private static List<GUBPEmailHacker> EmailHackers;
private string HackEmails(string Emails, string Causers, 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 = GetTypesFromAssembly(Dll);
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(' '));
if(!EmailHackers.Any(x => x.VetoEmailingCausers(this, Branch, NodeName)))
{
Result.AddRange(Causers.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 GUBPFrequencyHacker
{
public virtual int GetNodeFrequency(GUBP bp, string Branch, string NodeName, int BaseFrequency)
{
return new int();
}
}
private static List<GUBPFrequencyHacker> FrequencyHackers;
private int HackFrequency(GUBP bp, string Branch, string NodeName, int BaseFrequency)
{
int Frequency = BaseFrequency;
if (FrequencyHackers == null)
{
FrequencyHackers = new List<GUBPFrequencyHacker>();
Assembly[] LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var Dll in LoadedAssemblies)
{
Type[] AllTypes = GetTypesFromAssembly(Dll);
foreach (var PotentialConfigType in AllTypes)
{
if (PotentialConfigType != typeof(GUBPFrequencyHacker) && typeof(GUBPFrequencyHacker).IsAssignableFrom(PotentialConfigType))
{
GUBPFrequencyHacker Config = Activator.CreateInstance(PotentialConfigType) as GUBPFrequencyHacker;
if (Config != null)
{
FrequencyHackers.Add(Config);
}
}
}
}
}
foreach(var FrequencyHacker in FrequencyHackers)
{
Frequency = FrequencyHacker.GetNodeFrequency(bp, Branch, NodeName, BaseFrequency);
}
return Frequency;
}
public abstract class GUBPNode
{
public List<string> FullNamesOfDependencies = new List<string>();
public List<string> FullNamesOfPseudosependencies = new List<string>(); //these are really only used for sorting. We want the editor to fail before the monolithics. Think of it as "can't possibly be useful without".
public List<string> FullNamesOfDependedOn = new List<string>();
public List<string> BuildProducts = null;
public List<string> DependentPromotions = new List<string>();
public List<string> AllDependencyBuildProducts = null;
public List<string> AllDependencies = null;
public List<string> CompletedDependencies = new List<string>();
public string AgentSharingGroup = "";
public int ComputedDependentCISFrequencyQuantumShift = -1;
public virtual string GetFullName()
{
throw new AutomationException("Unimplemented GetFullName.");
}
public virtual string GetDisplayGroupName()
{
return GetFullName();
}
public virtual string GameNameIfAnyForTempStorage()
{
return "";
}
public virtual string RootIfAnyForTempStorage()
{
return "";
}
public virtual void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public virtual void PostLoadFromSharedTempStorage(GUBP bp)
{
}
public virtual void DoFakeBuild(GUBP bp) // this is used to more rapidly test a build system, it does nothing but save a record of success as a build product
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public virtual bool IsSticky()
{
return false;
}
public virtual bool SendSuccessEmail()
{
return false;
}
public virtual bool RunInEC()
{
return true;
}
public virtual bool IsTest()
{
return false;
}
public virtual bool IsAggregate()
{
return false;
}
public virtual bool IsPromotableAggregate()
{
return false;
}
public virtual bool IsSeparatePromotable()
{
return false;
}
public virtual string NodeHostPlatform()
{
return "";
}
public virtual int AgentMemoryRequirement(GUBP bp)
{
return 0;
}
public virtual int TimeoutInMinutes()
{
return 90;
}
/// <summary>
/// When triggered by CIS (instead of a person) this dictates how often this node runs.
/// The builder has a increasing index, specified with -TimeIndex=N
/// If N mod (1 lshift CISFrequencyQuantumShift()) is not 0, the node is skipped
/// </summary>
public virtual int CISFrequencyQuantumShift(GUBP bp)
{
return 0;
}
/// <summary>
/// As above the maximum of all ancestors and pseudoancestors
/// </summary>
public int DependentCISFrequencyQuantumShift()
{
if (ComputedDependentCISFrequencyQuantumShift < 0)
{
throw new AutomationException("Asked for frequency shift before it was computed.");
}
return ComputedDependentCISFrequencyQuantumShift;
}
public virtual float Priority()
{
return 100.0f;
}
public virtual bool TriggerNode()
{
return false;
}
public virtual void SetAsExplicitTrigger()
{
}
public virtual string ECAgentString()
{
return "";
}
public virtual string ECProcedureInfix()
{
return "";
}
public virtual string ECProcedure()
{
if (IsSticky() && AgentSharingGroup != "")
{
throw new AutomationException("Node {0} is both agent sharing and sitcky.", GetFullName());
}
return String.Format("GUBP{0}_UAT_Node{1}{2}", ECProcedureInfix(), IsSticky() ? "" : "_Parallel", AgentSharingGroup != "" ? "_AgentShare" : "");
}
public virtual string ECProcedureParams()
{
var Result = String.Format(", {{actualParameterName => 'Sticky', value => '{0}'}}", IsSticky() ? 1 : 0);
if (AgentSharingGroup != "")
{
Result += String.Format(", {{actualParameterName => 'AgentSharingGroup', value => '{0}'}}", AgentSharingGroup);
}
return Result;
}
public static string MergeSpaceStrings(params string[] EmailLists)
{
var Emails = new List<string>();
foreach (var EmailList in EmailLists)
{
if (!String.IsNullOrEmpty(EmailList))
{
List<string> Parts = new List<string>(EmailList.Split(' '));
foreach (var Email in Parts)
{
if (!string.IsNullOrEmpty(Email) && !Emails.Contains(Email))
{
Emails.Add(Email);
}
}
}
}
string Result = "";
foreach (var Email in Emails)
{
if (Result != "")
{
Result += " ";
}
Result += Email;
}
return Result;
}
public void SaveRecordOfSuccessAndAddToBuildProducts(string Contents = "Just a record of success.")
{
string RecordOfSuccess = CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Logs", GetFullName() + "_Success.log");
CommandUtils.CreateDirectory(Path.GetDirectoryName(RecordOfSuccess));
CommandUtils.WriteAllText(RecordOfSuccess, Contents);
AddBuildProduct(RecordOfSuccess);
}
public void AddDependency(string Node)
{
if (!FullNamesOfDependencies.Contains(Node))
{
FullNamesOfDependencies.Add(Node);
}
}
public void AddPseudodependency(string Node)
{
if (!FullNamesOfPseudosependencies.Contains(Node))
{
FullNamesOfPseudosependencies.Add(Node);
}
}
public void AddCompletedDependency(string Node)
{
if(!CompletedDependencies.Contains(Node))
{
CompletedDependencies.Add(Node);
}
}
public void RemovePseudodependency(string Node)
{
if (FullNamesOfPseudosependencies.Contains(Node))
{
FullNamesOfPseudosependencies.Remove(Node);
}
}
public void AddBuildProduct(string Filename)
{
if (!CommandUtils.FileExists_NoExceptions(true, Filename))
{
throw new AutomationException("Cannot add build product {0} because it does not exist.", Filename);
}
FileInfo Info = new FileInfo(Filename);
if (!BuildProducts.Contains(Info.FullName))
{
BuildProducts.Add(Info.FullName);
}
}
public void AddDependentBuildProduct(string Filename)
{
if (!CommandUtils.FileExists_NoExceptions(true, Filename))
{
throw new AutomationException("Cannot add build product {0} because it does not exist.", Filename);
}
FileInfo Info = new FileInfo(Filename);
if (!AllDependencyBuildProducts.Contains(Info.FullName))
{
AllDependencyBuildProducts.Add(Info.FullName);
}
}
public void AddAllDependent(string Node)
{
if (!AllDependencies.Contains(Node))
{
AllDependencies.Add(Node);
}
}
public void RemoveOveralppingBuildProducts()
{
foreach (var ToRemove in AllDependencyBuildProducts)
{
BuildProducts.Remove(ToRemove);
}
}
}
public class VersionFilesNode : GUBPNode
{
public static string StaticGetFullName()
{
return "VersionFiles";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override void DoBuild(GUBP bp)
{
var UE4Build = new UE4Build(bp);
BuildProducts = UE4Build.UpdateVersionFiles(ActuallyUpdateVersionFiles: CommandUtils.P4Enabled && CommandUtils.AllowSubmit);
}
public override bool IsSticky()
{
return true;
}
}
public class HostPlatformNode : GUBPNode
{
protected UnrealTargetPlatform HostPlatform;
public HostPlatformNode(UnrealTargetPlatform InHostPlatform)
{
HostPlatform = InHostPlatform;
}
public override string GetDisplayGroupName()
{
string Name = GetFullName();
string Suffix = GetHostPlatformSuffix();
return Name.EndsWith(Suffix)? Name.Substring(0, Name.Length - Suffix.Length) : Name;
}
public static string StaticGetHostPlatformSuffix(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InAgentPlatform = UnrealTargetPlatform.Unknown)
{
if (InHostPlatform == UnrealTargetPlatform.Mac)
{
if (InAgentPlatform == UnrealTargetPlatform.Win64)
{
return "_ForMac";
}
return "_OnMac";
}
else if (InHostPlatform == UnrealTargetPlatform.Linux)
{
return "_OnLinux";
}
return "";
}
public virtual UnrealTargetPlatform GetAgentPlatform()
{
return HostPlatform;
}
public override string ECProcedureInfix()
{
if (GetAgentPlatform() == UnrealTargetPlatform.Mac)
{
if (IsSticky())
{
throw new AutomationException("Node {0} is sticky, but Mac hosted. Sticky nodes must be PC hosted.", GetFullName());
}
return "_Mac";
}
if(GetAgentPlatform() == UnrealTargetPlatform.Linux)
{
if(IsSticky())
{
throw new AutomationException("Node {0} is sticky, but Linux hosted. Sticky nodes must be PC hosted.", GetFullName());
}
return "_Linux";
}
return "";
}
public virtual string GetHostPlatformSuffix()
{
return StaticGetHostPlatformSuffix(HostPlatform, GetAgentPlatform());
}
public UnrealTargetPlatform GetAltHostPlatform()
{
return GUBP.GetAltHostPlatform(HostPlatform);
}
public override int TimeoutInMinutes()
{
return base.TimeoutInMinutes() + ((HostPlatform == UnrealTargetPlatform.Mac) ? 30 : 0); // Mac is slower and more resource constrained
}
}
public class CompileNode : HostPlatformNode
{
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 && HostPlatform != UnrealTargetPlatform.Mac)
{
return base.AgentMemoryRequirement(bp);
}
return 32;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
if (HostPlatform == UnrealTargetPlatform.Win64 && !GUBP.bForceIncrementalCompile)
{
Agenda.DotNetProjects.AddRange(
new string[]
{
@"Engine\Source\Programs\DotNETCommon\DotNETUtilities\DotNETUtilities.csproj",
@"Engine\Source\Programs\RPCUtility\RPCUtility.csproj",
}
);
}
string AddArgs = "-CopyAppBundleBackToDevice";
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
return Agenda;
}
public override void PostBuild(GUBP bp, UE4Build UE4Build)
{
if (HostPlatform == UnrealTargetPlatform.Win64)
{
UE4Build.AddUATFilesToBuildProducts();
UE4Build.AddUBTFilesToBuildProducts();
}
}
}
public class RootEditorNode : CompileNode
{
public RootEditorNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "RootEditor" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override bool DeleteBuildProducts()
{
return true;
}
public override void DoBuild(GUBP bp)
{
base.DoBuild(bp);
if(!bp.BranchOptions.bNoInstalledEngine)
{
FileFilter Filter = new FileFilter();
Filter.Include("/Engine/Intermediate/Build/" + HostPlatform.ToString() + "/UE4Editor/Inc/...");
Filter.Include("/Engine/Plugins/.../Intermediate/Build/" + HostPlatform.ToString() + "/UE4Editor/Inc/...");
string ZipFileName = StaticGetArchivedHeadersPath(HostPlatform);
CommandUtils.ZipFiles(ZipFileName, CommandUtils.CmdEnv.LocalRoot, Filter);
BuildProducts.Add(ZipFileName);
}
}
public static string StaticGetArchivedHeadersPath(UnrealTargetPlatform HostPlatform)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Precompiled", "Headers-RootEditor" + StaticGetHostPlatformSuffix(HostPlatform) + ".zip");
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht -precompile";
if (bp.bOrthogonalizeEditorPlatforms)
{
AddArgs += " -skipnonhostplatforms";
}
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
}
}
return Agenda;
}
void DeleteStaleDLLs(GUBP bp)
{
if (GUBP.bForceIncrementalCompile)
{
return;
}
var Targets = new List<string>{bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName};
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
{
Targets.Add(ProgramTarget.TargetName);
}
}
foreach (var Target in Targets)
{
var EnginePlatformBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Binaries", HostPlatform.ToString());
var Wildcard = Target + "-*";
Log("************Deleting stale editor DLLs, path {0} wildcard {1}", EnginePlatformBinaries, Wildcard);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePlatformBinaries))
{
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
var EnginePluginBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Plugins");
var HostSubstring = CommandUtils.CombinePaths("/", HostPlatform.ToString(), "/");
Log("************Deleting stale editor DLLs, path {0} wildcard {1} host {2}", EnginePluginBinaries, Wildcard, HostSubstring);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePluginBinaries))
{
if (DiskFile.IndexOf(HostSubstring, StringComparison.InvariantCultureIgnoreCase) < 0)
{
continue;
}
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
}
}
public override void PostLoadFromSharedTempStorage(GUBP bp)
{
DeleteStaleDLLs(bp);
}
public override void PostBuildProducts(GUBP bp)
{
DeleteStaleDLLs(bp);
}
}
public class RootEditorCrossCompileLinuxNode : CompileNode
{
public RootEditorCrossCompileLinuxNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddDependency(RootEditorNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AddDependency(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
}
public static string StaticGetFullName()
{
return "RootEditor_Linux";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht";
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
UnrealTargetPlatform.Linux, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(UnrealTargetPlatform.Linux))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, UnrealTargetPlatform.Linux, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
}
}
return Agenda;
}
void DeleteStaleDLLs(GUBP bp)
{
if (GUBP.bForceIncrementalCompile)
{
return;
}
var Targets = new List<string> { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName };
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(UnrealTargetPlatform.Linux))
{
Targets.Add(ProgramTarget.TargetName);
}
}
foreach (var Target in Targets)
{
var EnginePlatformBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Binaries", UnrealTargetPlatform.Linux.ToString());
var Wildcard = Target + "-*";
Log("************Deleting stale editor DLLs, path {0} wildcard {1}", EnginePlatformBinaries, Wildcard);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePlatformBinaries))
{
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
var EnginePluginBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Plugins");
var HostSubstring = CommandUtils.CombinePaths("/", UnrealTargetPlatform.Linux.ToString(), "/");
Log("************Deleting stale editor DLLs, path {0} wildcard {1} host {2}", EnginePluginBinaries, Wildcard, HostSubstring);
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePluginBinaries))
{
if (DiskFile.IndexOf(HostSubstring, StringComparison.InvariantCultureIgnoreCase) < 0)
{
continue;
}
bool IsBuildProduct = false;
foreach (var Product in BuildProducts)
{
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
{
IsBuildProduct = true;
break;
}
}
if (!IsBuildProduct)
{
DeleteFile(DiskFile);
}
}
}
}
public override void PostLoadFromSharedTempStorage(GUBP bp)
{
DeleteStaleDLLs(bp);
}
public override void PostBuildProducts(GUBP bp)
{
DeleteStaleDLLs(bp);
}
}
public class ToolsNode : CompileNode
{
public ToolsNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "Tools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override float Priority()
{
return base.Priority() - 1;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
if (HostPlatform == UnrealTargetPlatform.Win64)
{
if (!GUBP.bForceIncrementalCompile)
{
Agenda.DotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\UnrealControls\UnrealControls.csproj"),
}
);
}
Agenda.DotNetSolutions.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\NetworkProfiler\NetworkProfiler.sln"),
}
);
if (!GUBP.bForceIncrementalCompile)
{
Agenda.SwarmProject = CombinePaths(@"Engine\Source\Programs\UnrealSwarm\UnrealSwarm.sln");
}
bool WithIOS = !bp.BranchOptions.PlatformsToRemove.Contains(UnrealTargetPlatform.IOS);
if ( WithIOS )
{
Agenda.IOSDotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\IOS\iPhonePackager\iPhonePackager.csproj"),
CombinePaths(@"Engine\Source\Programs\IOS\DeploymentServer\DeploymentServer.csproj"),
CombinePaths(@"Engine\Source\Programs\IOS\MobileDeviceInterface\MobileDeviceInterface.csproj"),
CombinePaths(@"Engine\Source\Programs\IOS\DeploymentInterface\DeploymentInterface.csproj"),
}
);
}
}
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
bool CrossCompile;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && !bInternalOnly && !SeparateNode)
{
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
}
}
}
}
return Agenda;
}
}
public class ToolsCrossCompileNode : CompileNode
{
public ToolsCrossCompileNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddPseudodependency(RootEditorCrossCompileLinuxNode.StaticGetFullName());
AgentSharingGroup = "ToolsCrossCompileGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "LinuxTools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override float Priority()
{
return base.Priority() - 1;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
bool CrossCompile;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && !bInternalOnly && !SeparateNode && CrossCompile)
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, UnrealTargetPlatform.Linux, Config, InAddArgs: AddArgs);
}
}
}
return Agenda;
}
}
public class SingleToolsNode : CompileNode
{
SingleTargetProperties ProgramTarget;
public SingleToolsNode(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
ProgramTarget = InProgramTarget;
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
{
return "Tools_" + InProgramTarget.TargetName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, ProgramTarget);
}
public override float Priority()
{
return base.Priority() + 2;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
}
}
return Agenda;
}
}
public class InternalToolsNode : CompileNode
{
public InternalToolsNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "InternalTools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override float Priority()
{
return base.Priority() - 2;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 1;
return Result;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
bool bAnyAdded = false;
var Agenda = new UE4Build.BuildAgenda();
if (HostPlatform == UnrealTargetPlatform.Win64)
{
bAnyAdded = true;
Agenda.DotNetProjects.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportCommon\CrashReportCommon.csproj"),
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportReceiver\CrashReportReceiver.csproj"),
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportProcess\CrashReportProcess.csproj"),
CombinePaths(@"Engine\Source\Programs\CrashReporter\RegisterPII\RegisterPII.csproj"),
});
Agenda.DotNetSolutions.AddRange(
new string[]
{
CombinePaths(@"Engine\Source\Programs\UnrealDocTool\UnrealDocTool\UnrealDocTool.sln"),
}
);
Agenda.ExtraDotNetFiles.AddRange(
new string[]
{
"Interop.IWshRuntimeLibrary",
"UnrealMarkdown",
"CommonUnrealMarkdown",
}
);
}
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
bool CrossCompile;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && bInternalOnly && !SeparateNode)
{
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
bAnyAdded = true;
}
}
}
}
if (bAnyAdded)
{
return Agenda;
}
return null;
}
}
public class SingleInternalToolsNode : CompileNode
{
SingleTargetProperties ProgramTarget;
public SingleInternalToolsNode(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
SetupSingleInternalToolsNode(InProgramTarget);
}
public SingleInternalToolsNode(GUBP bp, UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
// Don't add rooteditor dependency if it isn't in the graph
var bRootEditorNodeDoesExit = bp.HasNode(RootEditorNode.StaticGetFullName(HostPlatform));
SetupSingleInternalToolsNode(InProgramTarget, !bRootEditorNodeDoesExit);
}
private void SetupSingleInternalToolsNode(SingleTargetProperties InProgramTarget, bool bSkipRootEditorPsuedoDependency = true)
{
ProgramTarget = InProgramTarget;
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
if (!bSkipRootEditorPsuedoDependency)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
{
return "InternalTools_" + InProgramTarget.TargetName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, ProgramTarget);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 1;
return Result;
}
public override float Priority()
{
return base.Priority() + 3;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
{
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
}
}
return Agenda;
}
}
public class EditorPlatformNode : CompileNode
{
UnrealTargetPlatform EditorPlatform;
public EditorPlatformNode(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform Plat)
: base(InHostPlatform)
{
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 int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp);
return Result;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
if (!bp.bOrthogonalizeEditorPlatforms)
{
throw new AutomationException("EditorPlatformNode node should not be used unless we are orthogonalizing editor platforms.");
}
var Agenda = new UE4Build.BuildAgenda();
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice -onlyplatformspecificfor=" + EditorPlatform.ToString());
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && ProgramTarget.Rules.GUBP_NeedsPlatformSpecificDLLs())
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice -onlyplatformspecificfor=" + EditorPlatform.ToString());
}
}
return Agenda;
}
}
public class EditorGameNode : CompileNode
{
List<BranchInfo.BranchUProject> GameProjects = new List<BranchInfo.BranchUProject>();
public EditorGameNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
: base(InHostPlatform)
{
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
GameProjects.Add(InGameProj);
AddDependency(RootEditorNode.StaticGetFullName(InHostPlatform));
}
public void AddProject(BranchInfo.BranchUProject InGameProj)
{
if(InGameProj.Options(HostPlatform).GroupName != GameProjects[0].Options(HostPlatform).GroupName)
{
throw new AutomationException("Attempt to merge projects with different group names");
}
GameProjects.Add(InGameProj);
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
{
return (InGameProj.Options(InHostPlatform).GroupName ?? InGameProj.GameName) + "_EditorGame" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProjects[0]);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProjects[0].Options(HostPlatform).GroupName ?? GameProjects[0].GameName;
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
var Agenda = new UE4Build.BuildAgenda();
string Args = "-nobuilduht -skipactionhistory -skipnonhostplatforms -CopyAppBundleBackToDevice -forceheadergeneration -precompile";
foreach(BranchInfo.BranchUProject GameProj in GameProjects)
{
Agenda.AddTargets(
new string[] { GameProj.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, GameProj.FilePath, InAddArgs: Args);
}
return Agenda;
}
}
public class MakeFeaturePacksNode : HostPlatformNode
{
List<BranchInfo.BranchUProject> Projects;
public MakeFeaturePacksNode(UnrealTargetPlatform InHostPlatform, IEnumerable<BranchInfo.BranchUProject> InProjects)
: base(InHostPlatform)
{
Projects = new List<BranchInfo.BranchUProject>(InProjects);
AddDependency(ToolsNode.StaticGetFullName(InHostPlatform)); // for UnrealPak
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
}
public static string GetOutputFile(BranchInfo.BranchUProject Project)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "FeaturePacks", Path.GetFileNameWithoutExtension(Project.GameName) + ".upack");
}
public static bool IsFeaturePack(BranchInfo.BranchUProject InGameProj)
{
// No obvious way to store this in the project options; it's a property of non-code projects too.
if(InGameProj.GameName == "StarterContent" || InGameProj.GameName == "MobileStarterContent" || InGameProj.GameName.StartsWith("FP_"))
{
return true;
}
if(InGameProj.GameName.StartsWith("TP_"))
{
return CommandUtils.FileExists(CommandUtils.CombinePaths(CommandUtils.GetDirectoryName(InGameProj.FilePath), "contents.txt"));
}
return false;
}
public static UnrealTargetPlatform GetDefaultBuildPlatform(GUBP bp)
{
if(bp.HostPlatforms.Contains(UnrealTargetPlatform.Win64))
{
return UnrealTargetPlatform.Win64;
}
else if(bp.HostPlatforms.Contains(UnrealTargetPlatform.Mac))
{
return UnrealTargetPlatform.Mac;
}
else
{
if (UnrealBuildTool.BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux && bp.HostPlatforms[0] != UnrealTargetPlatform.Linux)
{
throw new AutomationException("Linux is not (yet?) able to cross-compile nodes for platform {0}, did you forget -NoPC / -NoMac?", bp.HostPlatforms[0]);
}
return bp.HostPlatforms[0];
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "MakeFeaturePacks" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 2;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
foreach(BranchInfo.BranchUProject Project in Projects)
{
string ContentsFileName = CommandUtils.CombinePaths(CommandUtils.GetDirectoryName(Project.FilePath), "contents.txt");
// Make sure we delete the output file. It may be read-only.
string OutputFileName = GetOutputFile(Project);
CommandUtils.DeleteFile(OutputFileName);
// Get the command line
string CmdLine = CommandUtils.MakePathSafeToUseWithCommandLine(OutputFileName) + " " + CommandUtils.MakePathSafeToUseWithCommandLine("-create=" + ContentsFileName);
if (GlobalCommandLine.Installed)
{
CmdLine += " -installed";
}
if (GlobalCommandLine.UTF8Output)
{
CmdLine += " -UTF8Output";
}
// Run UnrealPak
string UnrealPakExe;
if(HostPlatform == UnrealTargetPlatform.Win64)
{
UnrealPakExe = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/Win64/UnrealPak.exe");
}
else
{
throw new AutomationException("Unknown path to UnrealPak for host platform ({0})", HostPlatform);
}
RunAndLog(CmdEnv, UnrealPakExe, CmdLine, Options: ERunOptions.Default | ERunOptions.AllowSpew | ERunOptions.UTF8Output);
// Add the build products
BuildProducts.Add(OutputFileName);
}
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class GamePlatformMonolithicsNode : CompileNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform TargetPlatform;
bool WithXp;
bool Precompiled; // If true, just builds targets which generate static libraries for the -UsePrecompiled option to UBT. If false, just build those that don't.
public GamePlatformMonolithicsNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, bool InWithXp = false, bool InPrecompiled = false)
: base(InHostPlatform)
{
GameProj = InGameProj;
TargetPlatform = InTargetPlatform;
WithXp = InWithXp;
Precompiled = InPrecompiled;
if (TargetPlatform == UnrealTargetPlatform.PS4 || TargetPlatform == UnrealTargetPlatform.XboxOne)
{
// Required for PS4MapFileUtil/XboxOnePDBFileUtil
AddDependency(ToolsNode.StaticGetFullName(InHostPlatform));
}
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
if (!bp.BranchOptions.ExcludePlatformsForEditor.Contains(InHostPlatform))
{
AddPseudodependency(EditorGameNode.StaticGetFullName(InHostPlatform, GameProj));
}
if (bp.HasNode(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform)))
{
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
}
}
else
{
if (TargetPlatform != InHostPlatform && bp.HasNode(GamePlatformMonolithicsNode.StaticGetFullName(InHostPlatform, bp.Branch.BaseEngineProject, InHostPlatform, Precompiled: Precompiled)))
{
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(InHostPlatform, bp.Branch.BaseEngineProject, InHostPlatform, Precompiled: Precompiled));
}
}
if (InGameProj.Options(InHostPlatform).bTestWithShared) /// compiling templates is only for testing purposes, and we will group them to avoid saturating the farm
{
AddPseudodependency(WaitForTestShared.StaticGetFullName());
AgentSharingGroup = "TemplateMonolithics" + StaticGetHostPlatformSuffix(InHostPlatform);
}
}
public override string GetDisplayGroupName()
{
return GameProj.GameName + "_Monolithics" + (Precompiled? "_Precompiled" : "");
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, bool WithXp = false, bool Precompiled = false)
{
string Name = InGameProj.GameName;
if(WithXp)
{
Name += "_WinXP_Mono";
}
else
{
Name += "_" + InTargetPlatform + "_Mono";
}
if(Precompiled)
{
Name += "_Precompiled";
}
return Name + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, TargetPlatform, WithXp, Precompiled);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override bool DeleteBuildProducts()
{
return true;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp);
if(GameProj.GameName != bp.Branch.BaseEngineProject.GameName || !Precompiled)
{
Result += 3; //only every 80m
}
else if (TargetPlatform != HostPlatform)
{
Result += 2; //only every 40m
}
return Result;
}
public static bool HasPrecompiledTargets(BranchInfo.BranchUProject Project, UnrealTargetPlatform HostPlatform, UnrealTargetPlatform TargetPlatform)
{
foreach(TargetRules.TargetType Kind in BranchInfo.MonolithicKinds)
{
if (Project.Properties.Targets.ContainsKey(Kind))
{
SingleTargetProperties Target = Project.Properties.Targets[Kind];
if(Target.Rules.GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly(HostPlatform, TargetPlatform).Any())
{
return true;
}
}
}
return false;
}
public override float Priority()
{
float Result = base.Priority();
if(Precompiled)
{
Result += 1.0f;
}
return Result;
}
public override void DoBuild(GUBP bp)
{
base.DoBuild(bp);
if(Precompiled)
{
// Get a list of all the build dependencies
UE4Build.BuildAgenda Agenda = GetAgenda(bp);
string FileListPath = new UE4Build(bp).GenerateExternalFileList(Agenda);
UnrealBuildTool.ExternalFileList FileList = UnrealBuildTool.Utils.ReadClass<UnrealBuildTool.ExternalFileList>(FileListPath);
// Make all the paths relative to the root
string FilterPrefix = CommandUtils.CombinePaths(PathSeparator.Slash, CommandUtils.CmdEnv.LocalRoot).TrimEnd('/') + "/";
for(int Idx = 0; Idx < FileList.FileNames.Count; Idx++)
{
if(FileList.FileNames[Idx].StartsWith(FilterPrefix, StringComparison.InvariantCultureIgnoreCase))
{
FileList.FileNames[Idx] = FileList.FileNames[Idx].Substring(FilterPrefix.Length);
}
else
{
CommandUtils.LogError("Referenced external file is not under local root: {0}", FileList.FileNames[Idx]);
}
}
// Write the resulting file list out to disk
string OutputFileListPath = StaticGetBuildDependenciesPath(HostPlatform, GameProj, TargetPlatform);
UnrealBuildTool.Utils.WriteClass<UnrealBuildTool.ExternalFileList>(FileList, OutputFileListPath, "");
AddBuildProduct(OutputFileListPath);
// Archive all the headers
FileFilter Filter = new FileFilter();
Filter.Include("/Engine/Intermediate/Build/" + HostPlatform.ToString() + "/UE4/Inc/...");
Filter.Include("/Engine/Plugins/.../Intermediate/Build/" + HostPlatform.ToString() + "/UE4/Inc/...");
string ZipFileName = StaticGetArchivedHeadersPath(HostPlatform, GameProj, TargetPlatform);
CommandUtils.ZipFiles(ZipFileName, CommandUtils.CmdEnv.LocalRoot, Filter);
BuildProducts.Add(ZipFileName);
}
}
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
{
if (!bp.ActivePlatforms.Contains(TargetPlatform))
{
throw new AutomationException("{0} is not a supported platform for {1}", TargetPlatform.ToString(), GetFullName());
}
var Agenda = new UE4Build.BuildAgenda();
string Args = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice";
if(Precompiled)
{
Args += " -precompile";
// MSVC doesn't provide a way to strip symbols from static libraries - you have to use PDBs, but that causes random OOM
// exceptions with the /FS arg because mspdbsrv is 32-bit. Just disable compiler debug info manually for now.
if(TargetPlatform == UnrealTargetPlatform.Win32 || TargetPlatform == UnrealTargetPlatform.Win64)
{
Args += " -nodebuginfo";
}
}
if (WithXp)
{
Args += " -winxp";
}
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (GameProj.Properties.Targets.ContainsKey(Kind))
{
var Target = GameProj.Properties.Targets[Kind];
var AllowXp = Target.Rules.GUBP_BuildWindowsXPMonolithics();
if (!WithXp || (AllowXp && WithXp))
{
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
var AdditionalPlatforms = Target.Rules.GUBP_GetBuildOnlyPlatforms_MonolithicOnly(HostPlatform);
var AllPlatforms = Platforms.Union(AdditionalPlatforms);
if (AllPlatforms.Contains(TargetPlatform) && Target.Rules.SupportsPlatform(TargetPlatform))
{
List<UnrealTargetConfiguration> Configs;
if(Precompiled)
{
Configs = Target.Rules.GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly(HostPlatform, TargetPlatform);
}
else
{
Configs = Target.Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, TargetPlatform).Except(Target.Rules.GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly(HostPlatform, TargetPlatform)).ToList();
}
foreach (var Config in Configs)
{
if (GameProj.GameName == bp.Branch.BaseEngineProject.GameName)
{
Agenda.AddTargets(new string[] { Target.TargetName }, TargetPlatform, Config, InAddArgs: Args);
}
else
{
Agenda.AddTargets(new string[] { Target.TargetName }, TargetPlatform, Config, GameProj.FilePath, InAddArgs: Args);
}
}
}
}
}
}
return Agenda;
}
public static string StaticGetArchivedHeadersPath(UnrealTargetPlatform HostPlatform, BranchInfo.BranchUProject GameProj, UnrealTargetPlatform TargetPlatform)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Precompiled", "Headers-" + StaticGetFullName(HostPlatform, GameProj, TargetPlatform) + ".zip");
}
public static string StaticGetBuildDependenciesPath(UnrealTargetPlatform HostPlatform, BranchInfo.BranchUProject GameProj, UnrealTargetPlatform TargetPlatform)
{
return CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Saved", "Precompiled", "BuildDependencies-" + StaticGetFullName(HostPlatform, GameProj, TargetPlatform) + ".xml");
}
}
public class SuccessNode : GUBPNode
{
public SuccessNode()
{
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public override bool SendSuccessEmail()
{
return true;
}
}
public class GeneralSuccessNode : GUBP.SuccessNode
{
string MyName;
public GeneralSuccessNode(string InMyName)
{
MyName = InMyName;
}
public static string StaticGetFullName(string InMyName)
{
return InMyName + "_Success";
}
public override string GetFullName()
{
return StaticGetFullName(MyName);
}
}
public class AggregateNode : GUBPNode
{
public AggregateNode()
{
}
public override bool RunInEC()
{
return false;
}
public override bool IsAggregate()
{
return true;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
}
public override void DoFakeBuild(GUBP bp) // this is used to more rapidly test a build system, it does nothing but save a record of success as a build product
{
BuildProducts = new List<string>();
}
}
public class HostPlatformAggregateNode : AggregateNode
{
protected UnrealTargetPlatform HostPlatform;
public HostPlatformAggregateNode(UnrealTargetPlatform InHostPlatform)
{
HostPlatform = InHostPlatform;
}
public static string StaticGetHostPlatformSuffix(UnrealTargetPlatform InHostPlatform)
{
return HostPlatformNode.StaticGetHostPlatformSuffix(InHostPlatform);
}
public virtual string GetHostPlatformSuffix()
{
return StaticGetHostPlatformSuffix(HostPlatform);
}
public UnrealTargetPlatform GetAltHostPlatform()
{
return GUBP.GetAltHostPlatform(HostPlatform);
}
}
public class EditorAndToolsNode : HostPlatformAggregateNode
{
public EditorAndToolsNode(GUBP bp, UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
AddDependency(ToolsNode.StaticGetFullName(HostPlatform));
AddDependency(InternalToolsNode.StaticGetFullName(HostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "BaseEditorAndTools" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
}
public class AggregatePromotableNode : AggregateNode
{
protected List<UnrealTargetPlatform> HostPlatforms;
string PromotionLabelPrefix;
public AggregatePromotableNode(List<UnrealTargetPlatform> InHostPlatforms, string InPromotionLabelPrefix)
{
HostPlatforms = InHostPlatforms;
foreach (var HostPlatform in HostPlatforms)
{
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
}
PromotionLabelPrefix = InPromotionLabelPrefix;
}
public static string StaticGetFullName(string InPromotionLabelPrefix)
{
return InPromotionLabelPrefix + "_Promotable_Aggregate";
}
public override bool IsPromotableAggregate()
{
return true;
}
public override string GetFullName()
{
return StaticGetFullName(PromotionLabelPrefix);
}
}
public class SharedCookAggregateNode : AggregateNode
{
public SharedCookAggregateNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms, Dictionary<string, List<UnrealTargetPlatform>> NonCodeProjectNames, Dictionary<string, List<TargetRules.GUBPFormalBuild>> NonCodeFormalBuilds)
{
foreach (var HostPlatform in InHostPlatforms)
{
{
var Options = bp.Branch.BaseEngineProject.Options(HostPlatform);
if (Options.bIsPromotable && !Options.bSeparateGamePromotion)
{
var Kind = TargetRules.TargetType.Game;
if (bp.Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
{
var Target = bp.Branch.BaseEngineProject.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
foreach (var Plat in Platforms)
{
if (bNoIOSOnPC && Plat == UnrealTargetPlatform.IOS && HostPlatform == UnrealTargetPlatform.Win64)
{
continue;
}
if (bp.ActivePlatforms.Contains(Plat))
{
string CookedPlatform = Platform.Platforms[Plat].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
foreach (var NonCodeProject in bp.Branch.NonCodeProjects)
{
if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) || !NonCodeProjectNames[NonCodeProject.GameName].Contains(Plat))
{
continue;
}
if(NonCodeFormalBuilds.ContainsKey(NonCodeProject.GameName))
{
var PlatList = NonCodeFormalBuilds[NonCodeProject.GameName];
foreach (var PlatPair in PlatList)
{
if(PlatPair.TargetPlatform == Plat)
{
AddCompletedDependency(CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform));
}
}
}
}
}
}
}
}
}
foreach (var CodeProj in bp.Branch.CodeProjects)
{
var Options = CodeProj.Options(HostPlatform);
if (!Options.bSeparateGamePromotion)
{
if (Options.bIsPromotable)
{
foreach (var Kind in BranchInfo.MonolithicKinds)
{
if (CodeProj.Properties.Targets.ContainsKey(Kind))
{
var Target = CodeProj.Properties.Targets[Kind];
var Platforms = new List<UnrealTargetPlatform>();
var PossPlats = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
var FormalBuilds = Target.Rules.GUBP_GetConfigsForFormalBuilds_MonolithicOnly(HostPlatform);
foreach (var FormalBuild in FormalBuilds)
{
var Platform = FormalBuild.TargetPlatform;
if(!Platforms.Contains(Platform) && PossPlats.Contains(Platform))
{
Platforms.Add(Platform);
}
}
foreach (var Plat in Platforms)
{
if (bNoIOSOnPC && Plat == UnrealTargetPlatform.IOS && HostPlatform == UnrealTargetPlatform.Win64)
{
continue;
}
if (bp.ActivePlatforms.Contains(Plat))
{
string CookedPlatform = Platform.Platforms[Plat].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
AddCompletedDependency(CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform));
}
}
}
}
}
}
}
}
}
public static string StaticGetFullName()
{
return "SharedCookAggregate";
}
public override string GetFullName()
{
return StaticGetFullName();
}
}
public class GameAggregatePromotableNode : AggregatePromotableNode
{
BranchInfo.BranchUProject GameProj;
public GameAggregatePromotableNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms, BranchInfo.BranchUProject InGameProj, bool IsSeparate)
: base(InHostPlatforms, InGameProj.GameName)
{
GameProj = InGameProj;
foreach (var HostPlatform in HostPlatforms)
{
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
}
// add all of the platforms I use
{
var Platforms = bp.GetMonolithicPlatformsForUProject(HostPlatform, InGameProj, false);
if (bp.bOrthogonalizeEditorPlatforms)
{
foreach (var Plat in Platforms)
{
AddDependency(EditorPlatformNode.StaticGetFullName(HostPlatform, Plat));
}
}
}
{
if (!GameProj.Options(HostPlatform).bPromoteEditorOnly)
{
var Platforms = bp.GetMonolithicPlatformsForUProject(HostPlatform, InGameProj, true);
foreach (var Plat in Platforms)
{
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, Plat));
if (Plat == UnrealTargetPlatform.Win32 && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Game))
{
if (GameProj.Properties.Targets[TargetRules.TargetType.Game].Rules.GUBP_BuildWindowsXPMonolithics())
{
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, Plat, true));
}
}
}
}
}
}
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj)
{
return AggregatePromotableNode.StaticGetFullName(InGameProj.GameName);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override bool IsSeparatePromotable()
{
bool IsSeparate = false;
foreach(UnrealTargetPlatform HostPlatform in HostPlatforms)
{
if(GameProj.Options(HostPlatform).bSeparateGamePromotion)
{
IsSeparate = true;
}
}
return IsSeparate;
}
}
public class SharedAggregatePromotableNode : AggregatePromotableNode
{
public SharedAggregatePromotableNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms)
: base(InHostPlatforms, "Shared")
{
foreach (var HostPlatform in HostPlatforms)
{
{
var Options = bp.Branch.BaseEngineProject.Options(HostPlatform);
if (Options.bIsPromotable && !Options.bSeparateGamePromotion)
{
AddDependency(GameAggregatePromotableNode.StaticGetFullName(bp.Branch.BaseEngineProject));
}
}
foreach (var CodeProj in bp.Branch.CodeProjects)
{
var Options = CodeProj.Options(HostPlatform);
if (!Options.bSeparateGamePromotion)
{
if (Options.bIsPromotable)
{
AddDependency(GameAggregatePromotableNode.StaticGetFullName(CodeProj));
}
else if (Options.bTestWithShared)
{
if (!Options.bIsNonCode)
{
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, CodeProj)); // if we are just testing, we will still include the editor stuff
}
}
}
}
if(HostPlatform == UnrealTargetPlatform.Win64 && bp.ActivePlatforms.Contains(UnrealTargetPlatform.Linux))
{
AddDependency(RootEditorCrossCompileLinuxNode.StaticGetFullName());
AddDependency(ToolsCrossCompileNode.StaticGetFullName(HostPlatform));
}
}
AddDependency(MakeFeaturePacksNode.StaticGetFullName(MakeFeaturePacksNode.GetDefaultBuildPlatform(bp)));
}
public override bool IsSeparatePromotable()
{
return true;
}
public static string StaticGetFullName()
{
return AggregatePromotableNode.StaticGetFullName("Shared");
}
}
public class WaitForUserInput : GUBPNode
{
protected bool bTriggerWasTriggered;
public WaitForUserInput()
{
bTriggerWasTriggered = false;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
SaveRecordOfSuccessAndAddToBuildProducts();
}
public override bool TriggerNode()
{
return true;
}
public override void SetAsExplicitTrigger()
{
bTriggerWasTriggered = true;
}
public override bool IsSticky()
{
return bTriggerWasTriggered;
}
public virtual string GetTriggerStateName()
{
return "GenericTrigger";
}
public virtual string GetTriggerDescText()
{
return "GenericTrigger no description text available";
}
public virtual string GetTriggerActionText()
{
return "GenericTrigger no action text available";
}
public virtual bool TriggerRequiresRecursiveWorkflow()
{
return true;
}
public override string ECProcedure()
{
if (bTriggerWasTriggered)
{
return base.ECProcedure(); // after this user hits the trigger, we want to run this as an ordinary node
}
if (TriggerRequiresRecursiveWorkflow())
{
return String.Format("GUBP_UAT_Trigger"); //here we run a recursive workflow to wait for the trigger
}
return String.Format("GUBP_Hardcoded_Trigger"); //here we advance the state in the hardcoded workflow so folks can approve
}
public override string ECProcedureParams()
{
var Result = base.ECProcedureParams();
if (!bTriggerWasTriggered)
{
Result += String.Format(", {{actualParameterName => 'TriggerState', value => '{0}'}}, {{actualParameterName => 'ActionText', value =>\"{1}\"}}, {{actualParameterName => 'DescText', value =>\"{2}\"}}", GetTriggerStateName(), GetTriggerActionText(), GetTriggerDescText());
//Result += String.Format(" --actualParameter TriggerState={0} --actualParameter ActionText=\"{1}\" --actualParameter DescText=\"{2}\"", GetTriggerStateName(), GetTriggerActionText(), GetTriggerDescText());
}
return Result;
}
public override int TimeoutInMinutes()
{
return 0;
}
}
public class WaitForFormalUserInput : WaitForUserInput
{
public WaitForFormalUserInput(GUBP bp)
{
AddDependency(SharedCookAggregateNode.StaticGetFullName());
AddCompletedDependency(SharedCookAggregateNode.StaticGetFullName());
}
public static string StaticGetFullName()
{
return "Shared_WaitToMakeBuilds";
}
public override string GetFullName()
{
return StaticGetFullName();
}
}
public class WaitForPromotionUserInput : WaitForUserInput
{
string PromotionLabelPrefix;
string PromotionLabelSuffix;
protected bool bLabelPromoted; // true if this is the promoted version
public WaitForPromotionUserInput(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
{
PromotionLabelPrefix = InPromotionLabelPrefix;
PromotionLabelSuffix = InPromotionLabelSuffix;
bLabelPromoted = bInLabelPromoted;
if (bLabelPromoted)
{
AddDependency(LabelPromotableNode.StaticGetFullName(PromotionLabelPrefix, false));
}
else
{
AddDependency(AggregatePromotableNode.StaticGetFullName(PromotionLabelPrefix));
}
}
public static string StaticGetFullName(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
{
return InPromotionLabelPrefix + (bInLabelPromoted ? "_WaitForPromotion" : "_WaitForPromotable") + InPromotionLabelSuffix;
}
public override string GetFullName()
{
return StaticGetFullName(PromotionLabelPrefix, PromotionLabelSuffix, bLabelPromoted);
}
}
public class WaitForGamePromotionUserInput : WaitForPromotionUserInput
{
BranchInfo.BranchUProject GameProj;
bool bCustomWorkflow;
public WaitForGamePromotionUserInput(GUBP bp, BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
: base(InGameProj.GameName, "", bInLabelPromoted)
{
GameProj = InGameProj;
var Options = InGameProj.Options(UnrealTargetPlatform.Win64);
bCustomWorkflow = Options.bCustomWorkflowForPromotion;
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
{
return WaitForPromotionUserInput.StaticGetFullName(InGameProj.GameName, "", bInLabelPromoted);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override string GetTriggerDescText()
{
if (bLabelPromoted)
{
return GameProj.GameName + " is ready for promotion.";
}
return GameProj.GameName + " is ready to make a promotable label and begin testing.";
}
public override string GetTriggerActionText()
{
if (bLabelPromoted)
{
return "Promote " + GameProj.GameName + ".";
}
return "Make a promotable label for " + GameProj.GameName + " and begin testing.";
}
public override string GetTriggerStateName()
{
if (bCustomWorkflow)
{
return GetFullName();
}
return base.GetTriggerStateName();
}
public override bool TriggerRequiresRecursiveWorkflow()
{
if (bCustomWorkflow)
{
return !bLabelPromoted; // the promotable starts the hardcoded chain
}
return base.TriggerRequiresRecursiveWorkflow();
}
}
public class WaitForSharedPromotionUserInput : WaitForPromotionUserInput
{
public WaitForSharedPromotionUserInput(GUBP bp, bool bInLabelPromoted)
: base("Shared", IsMainBranch(), bInLabelPromoted)
{
}
public override string GetTriggerDescText()
{
if (bLabelPromoted)
{
return "The shared promotable is ready for promotion.";
}
return "The shared promotable is ready to make a promotable label.";
}
public override string GetTriggerActionText()
{
if (bLabelPromoted)
{
return "Promote the shared promotable.";
}
return "Make the shared promotable label.";
}
public static string StaticGetFullName(bool bInLabelPromoted)
{
return WaitForPromotionUserInput.StaticGetFullName("Shared", IsMainBranch(), bInLabelPromoted);
}
public static string IsMainBranch()
{
string isMain = "";
if (P4Enabled)
{
string CurrentBranch = P4Env.BuildRootP4;
if (CurrentBranch == "//depot/UE4")
{
isMain = "_WithNightlys";
}
}
return isMain;
}
public override string GetTriggerStateName()
{
return GetFullName();
}
public override bool TriggerRequiresRecursiveWorkflow()
{
return !bLabelPromoted;
}
}
public class LabelPromotableNode : GUBPNode
{
string PromotionLabelPrefix;
protected bool bLabelPromoted; // true if this is the promoted version
public LabelPromotableNode(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
{
PromotionLabelPrefix = InPromotionLabelPrefix;
bLabelPromoted = bInLabelPromoted;
AddDependency(WaitForPromotionUserInput.StaticGetFullName(PromotionLabelPrefix, InPromotionLabelSuffix, bLabelPromoted));
}
string LabelName(bool bLocalLabelPromoted)
{
string LabelPrefix = PromotionLabelPrefix;
string CompleteLabelPrefix = (bLocalLabelPromoted ? "Promoted-" : "Promotable-") + LabelPrefix;
if (LabelPrefix == "Shared" && bLocalLabelPromoted)
{
// shared promotion has a shorter name
CompleteLabelPrefix = "Promoted";
}
if (LabelPrefix == "Shared" && !bLocalLabelPromoted)
{
//shared promotable has a shorter name
CompleteLabelPrefix = "Promotable";
}
if (GUBP.bPreflightBuild)
{
CompleteLabelPrefix = CompleteLabelPrefix + PreflightMangleSuffix;
}
return CompleteLabelPrefix;
}
public override bool IsSticky()
{
return true;
}
public override bool SendSuccessEmail()
{
return true;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
if (P4Enabled)
{
if (AllDependencyBuildProducts.Count == 0)
{
throw new AutomationException("{0} has no build products", GetFullName());
}
if (bLabelPromoted)
{
P4.MakeDownstreamLabelFromLabel(P4Env, LabelName(true), LabelName(false));
}
else
{
int WorkingCL = P4.CreateChange(P4Env.Client, String.Format("GUBP Node {0} built from changelist {1}", GetFullName(), bp.CL));
Log("Build from {0} Working in {1}", bp.CL, WorkingCL);
var ProductsToSubmit = new List<String>();
foreach (var Product in AllDependencyBuildProducts)
{
// hacks to keep certain things out of P4
if (
!Product.EndsWith("version.h", StringComparison.InvariantCultureIgnoreCase) &&
!Product.EndsWith("version.cpp", StringComparison.InvariantCultureIgnoreCase) &&
!Product.Replace('\\', '/').EndsWith("DotNetCommon/MetaData.cs", StringComparison.InvariantCultureIgnoreCase) &&
!Product.EndsWith("_Success.log", StringComparison.InvariantCultureIgnoreCase) &&
!Product.Replace('\\', '/').Contains("/Intermediate/") &&
!Product.Replace('\\', '/').Contains("/Engine/Saved/") &&
!Product.Replace('\\', '/').Contains("/DerivedDataCache/") &&
!Product.EndsWith(".lib") &&
!Product.EndsWith(".a") &&
!Product.EndsWith(".bc")
)
{
ProductsToSubmit.Add(Product);
}
}
// Open files for add or edit
UE4Build.AddBuildProductsToChangelist(WorkingCL, ProductsToSubmit);
// Check everything in!
int SubmittedCL;
P4.Submit(WorkingCL, out SubmittedCL, true, true);
// Label it
P4.MakeDownstreamLabel(P4Env, LabelName(false), null);
}
}
SaveRecordOfSuccessAndAddToBuildProducts();
}
public static string StaticGetFullName(string InPromotionLabelPrefix, bool bInLabelPromoted)
{
return InPromotionLabelPrefix + (bInLabelPromoted ? "_LabelPromoted" : "_LabelPromotable");
}
public override string GetFullName()
{
return StaticGetFullName(PromotionLabelPrefix, bLabelPromoted);
}
}
public class GameLabelPromotableNode : LabelPromotableNode
{
BranchInfo.BranchUProject GameProj;
public GameLabelPromotableNode(GUBP bp, BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
: base(InGameProj.GameName, "", bInLabelPromoted)
{
GameProj = InGameProj;
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
{
return LabelPromotableNode.StaticGetFullName(InGameProj.GameName, bInLabelPromoted);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
}
public class SharedLabelPromotableNode : LabelPromotableNode
{
public SharedLabelPromotableNode(GUBP bp, bool bInLabelPromoted)
: base("Shared", IsMainBranch(), bInLabelPromoted)
{
}
public static string StaticGetFullName(bool bInLabelPromoted)
{
return LabelPromotableNode.StaticGetFullName("Shared", bInLabelPromoted);
}
public static string IsMainBranch()
{
string isMain = "";
if (P4Enabled)
{
string CurrentBranch = P4Env.BuildRootP4;
if (CurrentBranch == "//depot/UE4")
{
isMain = "_WithNightlys";
}
}
return isMain;
}
}
public class SharedLabelPromotableSuccessNode : AggregateNode
{
public SharedLabelPromotableSuccessNode()
{
AddDependency(SharedLabelPromotableNode.StaticGetFullName(false));
}
public static string StaticGetFullName()
{
return SharedLabelPromotableNode.StaticGetFullName(false) + "Aggregate";
}
public override string GetFullName()
{
return StaticGetFullName();
}
}
public class WaitForTestShared : AggregateNode
{
public WaitForTestShared(GUBP bp)
{
}
public static string StaticGetFullName()
{
return "Shared_TestingAggregate";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 5;
}
}
public class CookNode : HostPlatformNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform TargetPlatform;
string CookPlatform;
bool bIsMassive;
public CookNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, string InCookPlatform)
: base(InHostPlatform)
{
GameProj = InGameProj;
TargetPlatform = InTargetPlatform;
CookPlatform = InCookPlatform;
bIsMassive = false;
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
if (bp.bOrthogonalizeEditorPlatforms)
{
if (TargetPlatform != HostPlatform && TargetPlatform != GUBP.GetAltHostPlatform(HostPlatform))
{
AddDependency(EditorPlatformNode.StaticGetFullName(HostPlatform, TargetPlatform));
}
}
bool bIsShared = false;
// is this the "base game" or a non code project?
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
var Options = InGameProj.Options(HostPlatform);
bIsMassive = Options.bIsMassive;
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
// add an arc to prevent cooks from running until promotable is labeled
if (Options.bIsPromotable)
{
if (!Options.bSeparateGamePromotion)
{
bIsShared = true;
}
}
else if (Options.bTestWithShared)
{
bIsShared = true;
}
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, 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, "");
if (Target.Rules.GUBP_AlternateCookPlatform(HostPlatform, CookedPlatform) != "")
{
CookedPlatform = Target.Rules.GUBP_AlternateCookPlatform(HostPlatform, CookedPlatform);
}
AddDependency(CookNode.StaticGetFullName(HostPlatform, GameProj, CookedPlatform));
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform));
if(Target.Rules.GUBP_BuildWindowsXPMonolithics())
{
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform, true));
}
}
}
}
else
{
if (Kind == TargetRules.TargetType.Game) //for now, non-code projects don't do client or server.
{
if (bp.Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
{
var Target = bp.Branch.BaseEngineProject.Properties.Targets[Kind];
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
if (Platforms.Contains(TargetPlatform) && Target.Rules.SupportsPlatform(TargetPlatform))
{
//@todo how do we get the client target platform?
string CookedPlatform = Platform.Platforms[TargetPlatform].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
AddDependency(CookNode.StaticGetFullName(HostPlatform, GameProj, CookedPlatform));
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
}
}
}
}
}
// put these in the right agent group, even though they aren't exposed to EC to sort right.
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
var Options = InGameProj.Options(HostPlatform);
if ((Options.bIsPromotable || Options.bTestWithShared) && !Options.bSeparateGamePromotion)
{
AgentSharingGroup = "SharedCooks" + StaticGetHostPlatformSuffix(HostPlatform);
}
}
else
{
AgentSharingGroup = "SharedCooks" + StaticGetHostPlatformSuffix(HostPlatform);
}
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform)
{
return InGameProj.GameName + "_" + InTargetPlatform + "_CookedAndCompiled" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, TargetPlatform);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
};
public class FormalBuildNode : HostPlatformNode
{
BranchInfo.BranchUProject GameProj;
//CAUTION, these are lists, but it isn't clear that lists really work on all platforms, so we stick to one node per build
List<UnrealTargetPlatform> ClientTargetPlatforms;
List<UnrealTargetPlatform> ServerTargetPlatforms;
List<UnrealTargetConfiguration> ClientConfigs;
List<UnrealTargetConfiguration> ServerConfigs;
bool ClientNotGame;
bool bIsCode;
UnrealBuildTool.TargetRules.TargetType GameOrClient;
public FormalBuildNode(GUBP bp,
BranchInfo.BranchUProject InGameProj,
UnrealTargetPlatform InHostPlatform,
List<UnrealTargetPlatform> InClientTargetPlatforms = null,
List<UnrealTargetConfiguration> InClientConfigs = null,
List<UnrealTargetPlatform> InServerTargetPlatforms = null,
List<UnrealTargetConfiguration> InServerConfigs = null,
bool InClientNotGame = false
)
: base(InHostPlatform)
{
GameProj = InGameProj;
ClientTargetPlatforms = InClientTargetPlatforms;
ServerTargetPlatforms = InServerTargetPlatforms;
ClientConfigs = InClientConfigs;
ServerConfigs = InServerConfigs;
ClientNotGame = InClientNotGame;
GameOrClient = TargetRules.TargetType.Game;
if (ClientNotGame)
{
GameOrClient = TargetRules.TargetType.Client;
}
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
bIsCode = true;
}
else
{
bIsCode = false;
}
// verify we actually built these
var WorkingGameProject = InGameProj;
if (!WorkingGameProject.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
// this is a codeless project, use the base project
WorkingGameProject = bp.Branch.BaseEngineProject;
}
var AllTargetPlatforms = new List<UnrealTargetPlatform>();
var Options = InGameProj.Options(HostPlatform);
if(!Options.bSeparateGamePromotion && !bp.BranchOptions.bMakeFormalBuildWithoutLabelPromotable)
{
AddPseudodependency(WaitForFormalUserInput.StaticGetFullName());
}
if (ClientTargetPlatforms != null)
{
if (!WorkingGameProject.Properties.Targets.ContainsKey(GameOrClient))
{
throw new AutomationException("Can't make a game build for {0} because it doesn't have a {1} target.", WorkingGameProject.GameName, GameOrClient.ToString());
}
foreach (var Plat in ClientTargetPlatforms)
{
if (!AllTargetPlatforms.Contains(Plat))
{
AllTargetPlatforms.Add(Plat);
}
}
if (ClientConfigs == null)
{
ClientConfigs = new List<UnrealTargetConfiguration>() { UnrealTargetConfiguration.Development };
}
foreach (var Plat in ClientTargetPlatforms)
{
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform).Contains(Plat))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1}.", WorkingGameProject.GameName, Plat.ToString());
}
foreach (var Config in ClientConfigs)
{
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, Plat).Contains(Config))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1} config {2}.", WorkingGameProject.GameName, Plat.ToString(), Config.ToString());
}
}
}
}
if (ServerTargetPlatforms != null)
{
if (!WorkingGameProject.Properties.Targets.ContainsKey(TargetRules.TargetType.Server) && ServerTargetPlatforms != null)
{
throw new AutomationException("Can't make a server build for {0} because it doesn't have a server target.", WorkingGameProject.GameName);
}
foreach (var Plat in ServerTargetPlatforms)
{
if (!AllTargetPlatforms.Contains(Plat))
{
AllTargetPlatforms.Add(Plat);
}
}
if (ServerConfigs == null)
{
ServerConfigs = new List<UnrealTargetConfiguration>() { UnrealTargetConfiguration.Development };
}
foreach (var Plat in ServerTargetPlatforms)
{
if (!WorkingGameProject.Properties.Targets[TargetRules.TargetType.Server].Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform).Contains(Plat))
{
throw new AutomationException("Can't make a server build for {0} because we didn't build platform {1}.", WorkingGameProject.GameName, Plat.ToString());
}
foreach (var Config in ServerConfigs)
{
if (!WorkingGameProject.Properties.Targets[TargetRules.TargetType.Server].Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, Plat).Contains(Config))
{
throw new AutomationException("Can't make a server build for {0} because we didn't build platform {1} config {2}.", WorkingGameProject.GameName, Plat.ToString(), Config.ToString());
}
}
}
}
// add dependencies for cooked and compiled
foreach (var Plat in AllTargetPlatforms)
{
AddDependency(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, GameProj, Plat));
}
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InHostPlatform, List<UnrealTargetPlatform> InClientTargetPlatforms = null, List<UnrealTargetConfiguration> InClientConfigs = null, List<UnrealTargetPlatform> InServerTargetPlatforms = null, List<UnrealTargetConfiguration> InServerConfigs = null, bool InClientNotGame = false)
{
string Infix = "";
if (InClientNotGame)
{
if (InClientTargetPlatforms != null && InClientTargetPlatforms.Count == 1)
{
Infix = "_Client_" + InClientTargetPlatforms[0].ToString();
}
if (InClientConfigs != null && InClientConfigs.Count == 1)
{
Infix += "_Client_" + InClientConfigs[0].ToString();
}
}
else
{
if (InClientTargetPlatforms != null && InClientTargetPlatforms.Count == 1)
{
Infix = "_" + InClientTargetPlatforms[0].ToString();
}
if (InClientConfigs != null && InClientConfigs.Count == 1)
{
Infix += "_" + InClientConfigs[0].ToString();
}
}
if (InServerTargetPlatforms != null && InServerTargetPlatforms.Count == 1)
{
Infix = "_Serv_" + InServerTargetPlatforms[0].ToString();
}
if (InServerConfigs != null && InServerConfigs.Count == 1)
{
Infix += "_Serv_" + InServerConfigs[0].ToString();
}
return InGameProj.GameName + Infix + "_MakeBuild" + HostPlatformNode.StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(GameProj, HostPlatform, ClientTargetPlatforms, ClientConfigs, ServerTargetPlatforms, ServerConfigs, ClientNotGame);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override bool SendSuccessEmail()
{
return true;
}
public override string ECAgentString()
{
string Result = base.ECAgentString();
if (ClientTargetPlatforms != null)
{
if (!ClientNotGame)
{
foreach (UnrealTargetPlatform Plat in ClientTargetPlatforms)
{
if (Plat == UnrealTargetPlatform.XboxOne)
{
Result = MergeSpaceStrings(Result, Plat.ToString());
}
}
}
}
return Result;
}
public override float Priority()
{
return base.Priority() + 20.0f;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public static string GetArchiveDirectory(BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InHostPlatform, List<UnrealTargetPlatform> InClientTargetPlatforms = null, List<UnrealTargetConfiguration> InClientConfigs = null, List<UnrealTargetPlatform> InServerTargetPlatforms = null, List<UnrealTargetConfiguration> InServerConfigs = null, bool InClientNotGame = false)
{
string BaseDir = ResolveSharedBuildDirectory(InGameProj.GameName);
string NodeName = StaticGetFullName(InGameProj, InHostPlatform, InClientTargetPlatforms, InClientConfigs, InServerTargetPlatforms, InServerConfigs, InClientNotGame);
string Inner = P4Env.BuildRootEscaped + "-CL-" + P4Env.ChangelistString;
if (GUBP.bPreflightBuild)
{
Inner = Inner + PreflightMangleSuffix;
}
string ArchiveDirectory = CombinePaths(BaseDir, NodeName, Inner);
return ArchiveDirectory;
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
string ProjectArg = "";
if (!String.IsNullOrEmpty(GameProj.FilePath))
{
ProjectArg = " -project=\"" + GameProj.FilePath + "\"";
}
string Args = String.Format("BuildCookRun{0} -SkipBuild -SkipCook -Stage -Pak -Package -NoSubmit", ProjectArg);
bool bXboxOneTarget = false;
if (ClientTargetPlatforms != null)
{
bool bFirstClient = true;
foreach (var Plat in ClientTargetPlatforms)
{
if (Plat == UnrealTargetPlatform.XboxOne)
{
bXboxOneTarget = true;
}
if (bFirstClient)
{
bFirstClient = false;
Args += String.Format(" -platform={0}", Plat.ToString());
}
else
{
Args += String.Format("+{0}", Plat.ToString());
}
if(bIsCode)
{
var Target = GameProj.Properties.Targets[TargetRules.TargetType.Game];
if(ClientNotGame)
{
Target = GameProj.Properties.Targets[TargetRules.TargetType.Client];
}
if (Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat) != "")
{
Args += " " + Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat);
}
}
}
bool bFirstClientConfig = true;
foreach (var Config in ClientConfigs)
{
if (bFirstClientConfig)
{
bFirstClientConfig = false;
Args += String.Format(" -clientconfig={0}", Config.ToString());
}
else
{
Args += String.Format("+{0}", Config.ToString());
}
}
if (ClientNotGame)
{
Args += " -client";
}
}
else
{
Args += " -noclient";
}
if (ServerTargetPlatforms != null)
{
Args += " -server";
bool bFirstServer = true;
foreach (var Plat in ServerTargetPlatforms)
{
if (Plat == UnrealTargetPlatform.XboxOne)
{
bXboxOneTarget = true;
}
if (bFirstServer)
{
bFirstServer = false;
Args += String.Format(" -serverplatform={0}", Plat.ToString());
}
else
{
Args += String.Format("+{0}", Plat.ToString());
}
if (bIsCode)
{
var Target = GameProj.Properties.Targets[TargetRules.TargetType.Server];
if (Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat) != "")
{
Args += " " + Target.Rules.GUBP_AdditionalPackageParameters(HostPlatform, Plat);
}
}
}
bool bFirstServerConfig = true;
foreach (var Config in ServerConfigs)
{
if (bFirstServerConfig)
{
bFirstServerConfig = false;
Args += String.Format(" -serverconfig={0}", Config.ToString());
}
else
{
Args += String.Format("+{0}", Config.ToString());
}
}
}
string FinalArchiveDirectory = "";
string IntermediateArchiveDirectory = FinalArchiveDirectory;
if (P4Enabled)
{
FinalArchiveDirectory = GetArchiveDirectory(GameProj, HostPlatform, ClientTargetPlatforms, ClientConfigs, ServerTargetPlatforms, ServerConfigs, ClientNotGame);
IntermediateArchiveDirectory = FinalArchiveDirectory;
// Xbox One packaging does not function with remote file systems. Use a temp local directory to package and then move files into final location.
if (bXboxOneTarget)
{
IntermediateArchiveDirectory = Path.Combine(Path.GetTempPath(), "GUBP.XboxOne");
if (DirectoryExists_NoExceptions(IntermediateArchiveDirectory))
{
DeleteDirectory_NoExceptions(IntermediateArchiveDirectory);
}
CreateDirectory_NoExceptions(IntermediateArchiveDirectory);
}
CleanFormalBuilds(FinalArchiveDirectory);
if (DirectoryExists_NoExceptions(FinalArchiveDirectory))
{
if (IsBuildMachine)
{
throw new AutomationException("Archive directory already exists {0}", FinalArchiveDirectory);
}
DeleteDirectory_NoExceptions(FinalArchiveDirectory);
}
Args += String.Format(" -Archive -archivedirectory={0}", CommandUtils.MakePathSafeToUseWithCommandLine(IntermediateArchiveDirectory));
}
string LogFile = CommandUtils.RunUAT(CommandUtils.CmdEnv, Args);
if (P4Enabled)
{
if (!FinalArchiveDirectory.Equals(IntermediateArchiveDirectory, StringComparison.InvariantCultureIgnoreCase))
{
CopyDirectory_NoExceptions(IntermediateArchiveDirectory, FinalArchiveDirectory);
DeleteDirectory_NoExceptions(IntermediateArchiveDirectory);
}
}
SaveRecordOfSuccessAndAddToBuildProducts(CommandUtils.ReadAllText(LogFile));
}
}
public class TestNode : HostPlatformNode
{
public TestNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
}
public override float Priority()
{
return 0.0f;
}
public virtual void DoTest(GUBP bp)
{
}
public override void DoBuild(GUBP bp)
{
BuildProducts = new List<string>();
DoTest(bp);
}
}
public class FormalBuildTestNode : TestNode
{
BranchInfo.BranchUProject GameProj;
UnrealTargetPlatform ClientTargetPlatform;
UnrealTargetConfiguration ClientConfig;
UnrealBuildTool.TargetRules.TargetType GameOrClient;
public FormalBuildTestNode(GUBP bp,
BranchInfo.BranchUProject InGameProj,
UnrealTargetPlatform InHostPlatform,
UnrealTargetPlatform InClientTargetPlatform,
UnrealTargetConfiguration InClientConfig
)
: base(InHostPlatform)
{
GameProj = InGameProj;
ClientTargetPlatform = InClientTargetPlatform;
ClientConfig = InClientConfig;
GameOrClient = TargetRules.TargetType.Game;
// verify we actually built these
var WorkingGameProject = InGameProj;
if (!WorkingGameProject.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
// this is a codeless project, use the base project
WorkingGameProject = bp.Branch.BaseEngineProject;
}
if (!WorkingGameProject.Properties.Targets.ContainsKey(GameOrClient))
{
throw new AutomationException("Can't make a game build for {0} because it doesn't have a {1} target.", WorkingGameProject.GameName, GameOrClient.ToString());
}
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform).Contains(ClientTargetPlatform))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1}.", WorkingGameProject.GameName, ClientTargetPlatform.ToString());
}
if (!WorkingGameProject.Properties.Targets[GameOrClient].Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, ClientTargetPlatform).Contains(ClientConfig))
{
throw new AutomationException("Can't make a game/client build for {0} because we didn't build platform {1} config {2}.", WorkingGameProject.GameName, ClientTargetPlatform.ToString(), ClientConfig.ToString());
}
AddDependency(FormalBuildNode.StaticGetFullName(GameProj, HostPlatform, new List<UnrealTargetPlatform>() { ClientTargetPlatform }, InClientConfigs: new List<UnrealTargetConfiguration>() { ClientConfig }, InClientNotGame: GameOrClient == TargetRules.TargetType.Client));
}
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InClientTargetPlatform, UnrealTargetConfiguration InClientConfig)
{
string Infix = "_" + InClientTargetPlatform.ToString();
Infix += "_" + InClientConfig.ToString();
return InGameProj.GameName + Infix + "_TestBuild" + HostPlatformNode.StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(GameProj, HostPlatform, ClientTargetPlatform, ClientConfig);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override float Priority()
{
return base.Priority() - 20.0f;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public override void DoTest(GUBP bp)
{
string ProjectArg = "";
if (!String.IsNullOrEmpty(GameProj.FilePath))
{
ProjectArg = " -project=\"" + GameProj.FilePath + "\"";
}
string ArchiveDirectory = FormalBuildNode.GetArchiveDirectory(GameProj, HostPlatform, new List<UnrealTargetPlatform>() { ClientTargetPlatform }, InClientConfigs: new List<UnrealTargetConfiguration>() { ClientConfig }, InClientNotGame: GameOrClient == TargetRules.TargetType.Client);
if (!DirectoryExists_NoExceptions(ArchiveDirectory))
{
throw new AutomationException("Archive directory does not exist {0}, so we can't test the build.", ArchiveDirectory);
}
string WorkingCommandline = String.Format("TestFormalBuild {0} -Archive -alldevices -archivedirectory={1} -platform={2} -clientconfig={3} -runtimeoutseconds=300",
ProjectArg, CommandUtils.MakePathSafeToUseWithCommandLine(ArchiveDirectory), ClientTargetPlatform.ToString(), ClientConfig.ToString());
if (WorkingCommandline.Contains("-project=\"\""))
{
throw new AutomationException("Command line {0} contains -project=\"\" which is doomed to fail", WorkingCommandline);
}
string LogFile = RunUAT(CommandUtils.CmdEnv, WorkingCommandline);
SaveRecordOfSuccessAndAddToBuildProducts(CommandUtils.ReadAllText(LogFile));
}
public override string ECAgentString()
{
string Result = base.ECAgentString();
if (ClientTargetPlatform != HostPlatform)
{
Result = MergeSpaceStrings(Result, ClientTargetPlatform.ToString());
}
return Result;
}
}
public class NonUnityToolNode : TestNode
{
SingleTargetProperties ProgramTarget;
public NonUnityToolNode(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
: base(InHostPlatform)
{
ProgramTarget = InProgramTarget;
AddPseudodependency(SingleInternalToolsNode.StaticGetFullName(HostPlatform, ProgramTarget));
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InGameProj)
{
return InGameProj.TargetName + "_NonUnityTestCompile" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, ProgramTarget);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 2;
if (HostPlatform == UnrealTargetPlatform.Mac)
{
Result += 1;
}
return Result;
}
public override int AgentMemoryRequirement(GUBP bp)
{
int Result = base.AgentMemoryRequirement(bp);
if (HostPlatform == UnrealTargetPlatform.Mac)
{
Result = 32;
}
return Result;
}
public override void DoTest(GUBP bp)
{
var Build = new UE4Build(bp);
var Agenda = new UE4Build.BuildAgenda();
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development);
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-skipnonhostplatforms");
Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: false, InForceNonUnity: true);
UE4Build.CheckBuildProducts(Build.BuildProductFiles);
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class NonUnityTestNode : TestNode
{
public NonUnityTestNode(UnrealTargetPlatform InHostPlatform)
: base(InHostPlatform)
{
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
{
return "NonUnityTestCompile" + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform);
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
int Result = base.CISFrequencyQuantumShift(bp) + 2;
if(HostPlatform == UnrealTargetPlatform.Mac)
{
Result += 1;
}
return Result;
}
public override int AgentMemoryRequirement(GUBP bp)
{
int Result = base.AgentMemoryRequirement(bp);
if(HostPlatform == UnrealTargetPlatform.Mac)
{
Result = 32;
}
return Result;
}
public override void DoTest(GUBP bp)
{
var Build = new UE4Build(bp);
var Agenda = new UE4Build.BuildAgenda();
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development);
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: "-skipnonhostplatforms");
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 VSExpressTestNode : TestNode
{
public VSExpressTestNode(GUBP bp)
: base(UnrealTargetPlatform.Win64)
{
AddDependency(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AddDependency(RootEditorNode.StaticGetFullName(UnrealTargetPlatform.Win64));
}
public static string StaticGetFullName()
{
return "VSExpressTestCompile";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 3;
}
public override string ECAgentString()
{
return "VCTestAgent";
}
public override void DoTest(GUBP bp)
{
var Build = new UE4Build(bp);
var Agenda = new UE4Build.BuildAgenda();
string AddArgs = "-nobuilduht";
if (bp.bOrthogonalizeEditorPlatforms)
{
AddArgs += " -skipnonhostplatforms";
}
Agenda.AddTargets(
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
{
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
{
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
}
}
Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: false);
UE4Build.CheckBuildProducts(Build.BuildProductFiles);
SaveRecordOfSuccessAndAddToBuildProducts();
}
}
public class UATTestNode : TestNode
{
string TestName;
BranchInfo.BranchUProject GameProj;
string UATCommandLine;
bool DependsOnEditor;
List<UnrealTargetPlatform> DependsOnCooked;
float ECPriority;
public UATTestNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InTestName, string InUATCommandLine, string InAgentSharingGroup, bool InDependsOnEditor = true, List<UnrealTargetPlatform> InDependsOnCooked = null, float InECPriority = 0.0f)
: base(InHostPlatform)
{
AgentSharingGroup = InAgentSharingGroup;
ECPriority = InECPriority;
GameProj = InGameProj;
TestName = InTestName;
UATCommandLine = InUATCommandLine;
bool bWillCook = InUATCommandLine.IndexOf("-cook") >= 0;
DependsOnEditor = InDependsOnEditor || bWillCook;
if (InDependsOnCooked != null)
{
DependsOnCooked = InDependsOnCooked;
}
else
{
DependsOnCooked = new List<UnrealTargetPlatform>();
}
if (DependsOnEditor)
{
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
if (GameProj.GameName != bp.Branch.BaseEngineProject.GameName)
{
if (GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
}
}
}
foreach (var Plat in DependsOnCooked)
{
AddDependency(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, GameProj, Plat));
}
AddPseudodependency(WaitForTestShared.StaticGetFullName());
// If the same test fails for the base engine, don't bother trying
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName)
{
if (bp.HasNode(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TestName)))
{
AddPseudodependency(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, InTestName));
}
else
{
bool bFoundACook = false;
foreach (var Plat in DependsOnCooked)
{
var PlatTestName = "CookedGameTest_" + Plat.ToString();
if (bp.HasNode(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, PlatTestName)))
{
AddPseudodependency(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, PlatTestName));
bFoundACook = true;
}
}
if (!bFoundACook && bp.HasNode(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, "EditorTest")))
{
AddPseudodependency(UATTestNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, "EditorTest"));
}
}
}
if (InGameProj.GameName == bp.Branch.BaseEngineProject.GameName)
{
ECPriority = ECPriority + 1.0f;
}
if (UATCommandLine.IndexOf("-RunAutomationTests", StringComparison.InvariantCultureIgnoreCase) >= 0)
{
ECPriority = ECPriority - 4.0f;
if (UATCommandLine.IndexOf("-EditorTest", StringComparison.InvariantCultureIgnoreCase) >= 0)
{
ECPriority = ECPriority - 4.0f;
}
}
else if (UATCommandLine.IndexOf("-EditorTest", StringComparison.InvariantCultureIgnoreCase) >= 0)
{
ECPriority = ECPriority + 2.0f;
}
}
public override float Priority()
{
return ECPriority;
}
public override bool IsTest()
{
return true;
}
public override int CISFrequencyQuantumShift(GUBP bp)
{
return base.CISFrequencyQuantumShift(bp) + 5;
}
public override string ECAgentString()
{
string Result = base.ECAgentString();
foreach (UnrealTargetPlatform platform in Enum.GetValues(typeof(UnrealTargetPlatform)))
{
if (platform != HostPlatform && platform != GetAltHostPlatform())
{
if (UATCommandLine.IndexOf("-platform=" + platform.ToString(), StringComparison.InvariantCultureIgnoreCase) >= 0)
{
Result = MergeSpaceStrings(Result, platform.ToString());
}
}
}
return Result;
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InTestName)
{
return InGameProj.GameName + "_" + InTestName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, TestName);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
public override void DoTest(GUBP bp)
{
string ProjectArg = "";
if (!String.IsNullOrEmpty(GameProj.FilePath) && UATCommandLine.IndexOf("-project=", StringComparison.InvariantCultureIgnoreCase) < 0)
{
ProjectArg = " -project=\"" + GameProj.FilePath + "\"";
}
string WorkingCommandline = UATCommandLine + ProjectArg + " -NoSubmit -addcmdline=\"-DisablePS4TMAPI\"";
if (WorkingCommandline.Contains("-project=\"\""))
{
throw new AutomationException("Command line {0} contains -project=\"\" which is doomed to fail", WorkingCommandline);
}
string LogFile = RunUAT(CommandUtils.CmdEnv, WorkingCommandline);
SaveRecordOfSuccessAndAddToBuildProducts(CommandUtils.ReadAllText(LogFile));
}
}
public class GameAggregateNode : HostPlatformAggregateNode
{
BranchInfo.BranchUProject GameProj;
string AggregateName;
float ECPriority;
public GameAggregateNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InAggregateName, List<string> Dependencies, float InECPriority = 100.0f)
: base(InHostPlatform)
{
GameProj = InGameProj;
AggregateName = InAggregateName;
ECPriority = InECPriority;
foreach (var Dep in Dependencies)
{
AddDependency(Dep);
}
}
public override float Priority()
{
return ECPriority;
}
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InAggregateName)
{
return InGameProj.GameName + "_" + InAggregateName + StaticGetHostPlatformSuffix(InHostPlatform);
}
public override string GetFullName()
{
return StaticGetFullName(HostPlatform, GameProj, AggregateName);
}
public override string GameNameIfAnyForTempStorage()
{
return GameProj.GameName;
}
};
public class CleanSharedTempStorageNode : GUBPNode
{
public CleanSharedTempStorageNode(GUBP bp)
{
var ToolsNode = bp.FindNode(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
AgentSharingGroup = ToolsNode.AgentSharingGroup;
}
public override float Priority()
{
return -1E15f;
}
public static string StaticGetFullName()
{
return "CleanSharedTempStorage";
}
public override string GetFullName()
{
return StaticGetFullName();
}
public override void DoBuild(GUBP bp)
{
{
var StartTime = DateTime.UtcNow;
foreach (var NodeToDo in bp.GUBPNodes)
{
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 GUBPNode TryFindNode(string Node)
{
GUBPNode Result;
GUBPNodes.TryGetValue(Node, out Result);
return Result;
}
public void RemovePseudodependencyFromNode(string Node, string Dep)
{
if (!GUBPNodes.ContainsKey(Node))
{
throw new AutomationException("Node {0} not found", Node);
}
GUBPNodes[Node].RemovePseudodependency(Dep);
}
public void RemoveAllPseudodependenciesFromNode(string Node)
{
if (!GUBPNodes.ContainsKey(Node))
{
throw new AutomationException("Node {0} not found", Node);
}
GUBPNodes[Node].FullNamesOfPseudosependencies.Clear();
}
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> GetCompletedOnlyDependencies(string NodeToDo, bool bFlat = false, bool ECOnly = true)
{
var Result = new List<string>();
foreach (var Node in GUBPNodes[NodeToDo].CompletedDependencies)
{
bool Usable = GUBPNodes[Node].RunInEC() || !ECOnly;
if(Usable)
{
if(!Result.Contains(Node))
{
Result.Add(Node);
}
}
if (bFlat || !Usable)
{
foreach (var RNode in GetCompletedOnlyDependencies(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 TimeQuantum = 20;
if(BranchOptions.QuantumOverride != 0)
{
TimeQuantum = BranchOptions.QuantumOverride;
}
int Minutes = TimeQuantum * (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, Dictionary<string, int> FrequencyOverrides)
{
int Result = GUBPNodes[NodeToDo].ComputedDependentCISFrequencyQuantumShift;
if (Result < 0)
{
Result = GUBPNodes[NodeToDo].CISFrequencyQuantumShift(this);
Result = GetFrequencyForNode(this, GUBPNodes[NodeToDo].GetFullName(), Result);
int FrequencyOverride;
if(FrequencyOverrides.TryGetValue(NodeToDo, out FrequencyOverride) && Result > FrequencyOverride)
{
Result = FrequencyOverride;
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
{
Result = Math.Max(ComputeDependentCISFrequencyQuantumShift(Dep, FrequencyOverrides), Result);
}
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
{
Result = Math.Max(ComputeDependentCISFrequencyQuantumShift(Dep, FrequencyOverrides), Result);
}
foreach (var Dep in GUBPNodes[NodeToDo].CompletedDependencies)
{
Result = Math.Max(ComputeDependentCISFrequencyQuantumShift(Dep, FrequencyOverrides), 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(GameNameIfAny != "" && Result == false)
{
Result = TempStorageExists(CmdEnv, NodeStoreName, "", 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", "--timeout 900 " + Args, Options: Opts);
}
}
void WriteECPerl(List<string> Args)
{
Args.Add("$batch->submit();");
string ECPerlFile = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LogFolder, "jobsteps.pl");
WriteAllLines_NoExceptions(ECPerlFile, Args.ToArray());
}
string GetEMailListForNode(GUBP bp, string NodeToDo, string Emails, string Causers)
{
var BranchForEmail = "";
if (P4Enabled)
{
BranchForEmail = P4Env.BuildRootP4;
}
return HackEmails(Emails, Causers, BranchForEmail, NodeToDo);
}
int GetFrequencyForNode(GUBP bp, string NodeToDo, int BaseFrequency)
{
return HackFrequency(bp, BranchName, NodeToDo, BaseFrequency);
}
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 bShowDependednOn = bp.ParseParam("ShowDependedOn");
bool bShowDependentPromotions = bp.ParseParam("ShowDependentPromotions");
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(ParseParamValue("AgentOverride") != "" && !GUBPNodes[NodeToDo].GetFullName().Contains("Mac"))
{
Agent = ParseParamValue("AgentOverride");
}
if (Agent != "")
{
Agent = "[" + Agent + "]";
}
string MemoryReq = "[" + GUBPNodes[NodeToDo].AgentMemoryRequirement(bp).ToString() + "]";
if(MemoryReq == "[0]")
{
MemoryReq = "";
}
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,
MemoryReq,
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);
}
foreach (var Dep in GUBPNodes[NodeToDo].CompletedDependencies)
{
Log(" cdep>{0}", Dep);
}
}
if (bShowECDependencies)
{
foreach (var Dep in GetECDependencies(NodeToDo))
{
Log(" {0}", Dep);
}
foreach (var Dep in GetCompletedOnlyDependencies(NodeToDo))
{
Log(" compDep> {0}", Dep);
}
}
if(bShowDependednOn)
{
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependedOn)
{
Log(" depOn> {0}", Dep);
}
}
if (bShowDependentPromotions)
{
foreach (var Dep in GUBPNodes[NodeToDo].DependentPromotions)
{
Log(" depPro> {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);
var AdditionalPlatforms = Target.Rules.GUBP_GetBuildOnlyPlatforms_MonolithicOnly(HostPlatform);
var AllPlatforms = Platforms.Union(AdditionalPlatforms);
foreach (var Plat in AllPlatforms)
{
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);
}
string GetPropertyFromStep(string PropertyPath)
{
string Property = "";
Property = RunECTool("getProperty \"" + PropertyPath + "\"");
Property = Property.TrimEnd('\r', '\n');
return Property;
}
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;
bool bCompReady = 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;
}
}
foreach (var Dep in GUBPNodes[NodeToDo].CompletedDependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Completed Dependency {0} node found.", Dep);
}
if (NodesToDo.Contains(Dep))
{
bCompReady = 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 (!bCompReady)
{
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);
}
}
void GetFailureEmails(string NodeToDo, string CLString, bool OnlyLateUpdates = false)
{
var StartTime = DateTime.UtcNow;
string EMails;
string FailCauserEMails = "";
string EMailNote = "";
bool SendSuccessForGreenAfterRed = false;
int NumPeople = 0;
if (GUBPNodesHistory.ContainsKey(NodeToDo))
{
var History = GUBPNodesHistory[NodeToDo];
RunECTool(String.Format("setProperty \"/myWorkflow/LastGreen/{0}\" \"{1}\"", NodeToDo, History.LastSucceeded), true);
RunECTool(String.Format("setProperty \"/myWorkflow/LastGreen/{0}\" \"{1}\"", NodeToDo, History.FailedString), true);
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
{
RunECTool(String.Format("setProperty \"/myWorkflow/LastGreen/{0}\" \"{1}\"", NodeToDo, "0"));
RunECTool(String.Format("setProperty \"/myWorkflow/RedsSince/{0}\" \"{1}\"", NodeToDo, ""));
}
RunECTool(String.Format("setProperty \"/myWorkflow/FailCausers/{0}\" \"{1}\"", NodeToDo, FailCauserEMails));
RunECTool(String.Format("setProperty \"/myWorkflow/EmailNotes/{0}\" \"{1}\"", NodeToDo, EMailNote));
{
var AdditionalEmails = "";
string Causers = "";
if (ParseParam("CIS") && !GUBPNodes[NodeToDo].SendSuccessEmail() && !GUBPNodes[NodeToDo].TriggerNode())
{
Causers = FailCauserEMails;
}
string AddEmails = ParseParamValue("AddEmails");
if (!String.IsNullOrEmpty(AddEmails))
{
AdditionalEmails = GUBPNode.MergeSpaceStrings(AddEmails, AdditionalEmails);
}
EMails = GetEMailListForNode(this, NodeToDo, AdditionalEmails, Causers);
RunECTool(String.Format("setProperty \"/myWorkflow/FailEmails/{0}\" \"{1}\"", NodeToDo, EMails));
}
if (GUBPNodes[NodeToDo].SendSuccessEmail() || SendSuccessForGreenAfterRed)
{
RunECTool(String.Format("setProperty \"/myWorkflow/SendSuccessEmail/{0}\" \"{1}\"", NodeToDo, "1"));
}
else
{
RunECTool(String.Format("setProperty \"/myWorkflow/SendSuccessEmail/{0}\" \"{1}\"", NodeToDo, "0"));
}
}
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 = "";
var AdditonalEmails = "";
string Causers = "";
string AddEmails = ParseParamValue("AddEmails");
if (!String.IsNullOrEmpty(AddEmails))
{
AdditonalEmails = GUBPNode.MergeSpaceStrings(AddEmails, AdditonalEmails);
}
EMails = GetEMailListForNode(this, NodeToDo, AdditonalEmails, Causers);
ECProps.Add("FailEmails/" + NodeToDo + "=" + EMails);
if (!OnlyLateUpdates)
{
string AgentReq = GUBPNodes[NodeToDo].ECAgentString();
if(ParseParamValue("AgentOverride") != "" && !GUBPNodes[NodeToDo].GetFullName().Contains("OnMac"))
{
AgentReq = ParseParamValue("AgentOverride");
}
ECProps.Add(string.Format("AgentRequirementString/{0}={1}", NodeToDo, AgentReq));
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;
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));
}
}
void UpdateECBuildTime(string NodeToDo, double BuildDuration)
{
try
{
Log("Updating duration prop for node {0}", NodeToDo);
RunECTool(String.Format("setProperty \"/myWorkflow/NodeDuration/{0}\" \"{1}\"", NodeToDo, BuildDuration.ToString()));
RunECTool(String.Format("setProperty \"/myJobStep/NodeDuration\" \"{0}\"", BuildDuration.ToString()));
}
catch (Exception Ex)
{
Log(System.Diagnostics.TraceEventType.Warning, "Failed to UpdateECBuildTime.");
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+Linux")]
[Help("NoLinux", "Toggle to exclude the Linux (PC, 64-bit) host platform, default is Win64+Mac+Linux")]
[Help("NoPC", "Toggle to exclude the PC host platform, default is Win64+Mac+Linux")]
[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("UserTimeIndex=", "An integer used to determine subsets to run based on DependentCISFrequencyQuantumShift, this one overrides TimeIndex")]
[Help("PreflightUID=", "A unique integer tag from EC used as part of the tempstorage, builds and label names to distinguish multiple attempts.")]
[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(String.Format( "{0} does not look like a CL", PreflightShelveCL));
}
}
bPreflightBuild = true;
}
ECProject = ParseParamValue("ECProject");
if (ECProject == null)
{
ECProject = "";
}
HostPlatforms = new List<UnrealTargetPlatform>();
if (!ParseParam("NoPC"))
{
HostPlatforms.Add(UnrealTargetPlatform.Win64);
}
if (P4Enabled)
{
BranchName = P4Env.BuildRootP4;
}
else
{
BranchName = ParseParamValue("BranchName", "");
}
BranchOptions = GetBranchOptions(BranchName);
bool WithMac = !BranchOptions.PlatformsToRemove.Contains(UnrealTargetPlatform.Mac);
if (ParseParam("NoMac"))
{
WithMac = false;
}
if (WithMac)
{
HostPlatforms.Add(UnrealTargetPlatform.Mac);
}
bool WithLinux = !BranchOptions.PlatformsToRemove.Contains(UnrealTargetPlatform.Linux);
bool WithoutLinux = ParseParam("NoLinux");
// @TODO: exclude temporarily unless running on a Linux machine to prevent spurious GUBP failures
if (UnrealBuildTool.BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Linux || ParseParam("NoLinux"))
{
WithLinux = false;
}
if (WithLinux)
{
HostPlatforms.Add(UnrealTargetPlatform.Linux);
}
bForceIncrementalCompile = ParseParam("ForceIncrementalCompile");
bool bNoAutomatedTesting = ParseParam("NoAutomatedTesting") || BranchOptions.bNoAutomatedTesting;
StoreName = ParseParamValue("Store");
string StoreSuffix = ParseParamValue("StoreSuffix", "");
if (bPreflightBuild)
{
int PreflightUID = ParseParamInt("PreflightUID", 0);
PreflightMangleSuffix = String.Format("-PF-{0}-{1}", PreflightShelveCL, PreflightUID);
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);
if (TimeIndex == 0)
{
TimeIndex = ParseParamInt("UserTimeIndex", 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;
int TimeQuantum = 20;
if(BranchOptions.QuantumOverride != 0)
{
TimeQuantum = BranchOptions.QuantumOverride;
}
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);
GUBPNodes = new Dictionary<string, GUBPNode>();
Branch = new BranchInfo(HostPlatforms);
if (IsBuildMachine || ParseParam("AllPlatforms"))
{
ActivePlatforms = new List<UnrealTargetPlatform>();
List<BranchInfo.BranchUProject> BranchCodeProjects = new List<BranchInfo.BranchUProject>();
BranchCodeProjects.Add(Branch.BaseEngineProject);
BranchCodeProjects.AddRange(Branch.CodeProjects);
BranchCodeProjects.RemoveAll(Project => BranchOptions.ExcludeNodes.Contains(Project.GameName));
foreach (var GameProj in BranchCodeProjects)
{
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);
var AdditionalPlatforms = Target.Rules.GUBP_GetBuildOnlyPlatforms_MonolithicOnly(HostPlatform);
var AllPlatforms = Platforms.Union(AdditionalPlatforms);
foreach (var Plat in AllPlatforms)
{
if (Target.Rules.SupportsPlatform(Plat) && !ActivePlatforms.Contains(Plat))
{
ActivePlatforms.Add(Plat);
}
}
}
}
}
}
}
else
{
ActivePlatforms = new List<UnrealTargetPlatform>(CommandUtils.KnownTargetPlatforms);
}
var SupportedPlatforms = new List<UnrealTargetPlatform>();
foreach(var Plat in ActivePlatforms)
{
if(!BranchOptions.PlatformsToRemove.Contains(Plat))
{
SupportedPlatforms.Add(Plat);
}
}
ActivePlatforms = SupportedPlatforms;
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));
if (!BranchOptions.ExcludePlatformsForEditor.Contains(HostPlatform))
{
AddNode(new RootEditorNode(HostPlatform));
AddNode(new ToolsNode(HostPlatform));
AddNode(new InternalToolsNode(HostPlatform));
if (HostPlatform == UnrealTargetPlatform.Win64 && ActivePlatforms.Contains(UnrealTargetPlatform.Linux))
{
if (!BranchOptions.ExcludePlatformsForEditor.Contains(UnrealTargetPlatform.Linux))
{
AddNode(new ToolsCrossCompileNode(HostPlatform));
}
}
foreach (var ProgramTarget in Branch.BaseEngineProject.Properties.Programs)
{
bool bInternalOnly;
bool SeparateNode;
bool CrossCompile;
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && SeparateNode)
{
if (bInternalOnly)
{
AddNode(new SingleInternalToolsNode(HostPlatform, ProgramTarget));
}
else
{
AddNode(new SingleToolsNode(HostPlatform, ProgramTarget));
}
}
if (ProgramTarget.Rules.GUBP_IncludeNonUnityToolTest())
{
AddNode(new NonUnityToolNode(HostPlatform, ProgramTarget));
}
}
foreach(var CodeProj in Branch.CodeProjects)
{
foreach(var ProgramTarget in CodeProj.Properties.Programs)
{
bool bInternalNodeOnly;
bool SeparateNode;
bool CrossCompile;
if(ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, out bInternalNodeOnly, out SeparateNode, out CrossCompile) && ProgramTarget.Rules.SupportsPlatform(HostPlatform) && SeparateNode)
{
if(bInternalNodeOnly)
{
AddNode(new SingleInternalToolsNode(HostPlatform, ProgramTarget));
}
else
{
AddNode(new SingleToolsNode(HostPlatform, ProgramTarget));
}
}
if(ProgramTarget.Rules.GUBP_IncludeNonUnityToolTest())
{
AddNode(new NonUnityToolNode(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)
{
if (!Branch.NonCodeProjects.Contains(Proj))
{
Branch.NonCodeProjects.Add(Proj);
NonCodeProjectNames.Add(Codeless.Key, Codeless.Value);
}
}
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;
AddNode(new NonUnityTestNode(HostPlatform));
if (DoASharedPromotable)
{
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.Win64) //temp hack till automated testing works on other platforms than Win64
{
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)))
{
if(GamePlatformMonolithicsNode.HasPrecompiledTargets(Branch.BaseEngineProject, HostPlatform, Plat))
{
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, Branch.BaseEngineProject, Plat, InPrecompiled: true));
}
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, Branch.BaseEngineProject, Plat));
}
if (Plat == UnrealTargetPlatform.Win32 && Target.Rules.GUBP_BuildWindowsXPMonolithics() && Kind == TargetRules.TargetType.Game)
{
if (!GUBPNodes.ContainsKey(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, Plat, true)))
{
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, Branch.BaseEngineProject, Plat, true));
}
}
}
}
}
}
var CookedAgentSharingGroup = "Shared_CookedTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
var GameTestNodes = new List<string>();
var GameCookNodes = new List<string>();
//var FormalAgentSharingGroup = "Shared_FormalBuilds" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
//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}));
if(PlatPair.bBeforeTrigger)
{
RemovePseudodependencyFromNode(FormalBuildNode.StaticGetFullName(NonCodeProject, HostPlatform, new List<UnrealTargetPlatform>() { Plat }, new List<UnrealTargetConfiguration>() { PlatPair.TargetConfig }), WaitForFormalUserInput.StaticGetFullName());
}
// 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());
string BuildAgentSharingGroup = "";
if (Options.bSeparateGamePromotion)
{
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;
}
else
{
//GUBPNodes[NodeName].AgentSharingGroup = FormalAgentSharingGroup;
if(Plat == UnrealTargetPlatform.XboxOne)
{
GUBPNodes[NodeName].AgentSharingGroup = "";
}
}
if (PlatPair.bTest)
{
AddNode(new FormalBuildTestNode(this, NonCodeProject, HostPlatform, Plat, PlatPair.TargetConfig));
}
}
}
}
}
if (!bNoAutomatedTesting)
{
if (HostPlatform == UnrealTargetPlatform.Mac || HostPlatform == UnrealTargetPlatform.Linux) continue; //temp hack till Linux and 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));
}
}
if (!GUBPNodes.ContainsKey(SharedCookAggregateNode.StaticGetFullName()))
{
AddNode(new SharedCookAggregateNode(this, HostPlatforms, NonCodeProjectNames, NonCodeFormalBuilds));
}
if(HostPlatform == MakeFeaturePacksNode.GetDefaultBuildPlatform(this))
{
AddNode(new MakeFeaturePacksNode(HostPlatform, Branch.AllProjects.Where(x => MakeFeaturePacksNode.IsFeaturePack(x))));
}
foreach (var CodeProj in Branch.CodeProjects)
{
var Options = CodeProj.Options(HostPlatform);
if (!Options.bIsPromotable && !Options.bTestWithShared && !Options.bIsNonCode)
{
continue; // we skip things that aren't promotable and aren't tested - except noncode as code situations
}
var AgentShareName = CodeProj.GameName;
if (!Options.bSeparateGamePromotion)
{
AgentShareName = "Shared";
}
if (!BranchOptions.ExcludePlatformsForEditor.Contains(HostPlatform) && !Options.bIsNonCode)
{
EditorGameNode Node = (EditorGameNode)TryFindNode(EditorGameNode.StaticGetFullName(HostPlatform, CodeProj));
if(Node == null)
{
AddNode(new EditorGameNode(this, HostPlatform, CodeProj));
}
else
{
Node.AddProject(CodeProj);
}
}
if (!bNoAutomatedTesting && HostPlatform == UnrealTargetPlatform.Win64) //temp hack till automated testing works on other platforms than Win64
{
if (CodeProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
{
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 (!Options.bTestWithShared || !HasNode(WaitForTestShared.StaticGetFullName()))
{
RemovePseudodependencyFromNode((UATTestNode.StaticGetFullName(HostPlatform, CodeProj, Test.Key)), WaitForTestShared.StaticGetFullName());
}
}
if (EditorTestNodes.Count > 0)
{
AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "AllEditorTests", EditorTestNodes, 0.0f));
}
}
}
var CookedAgentSharingGroup = AgentShareName + "_CookedTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
//var FormalAgentSharingGroup = "Shared_FormalBuilds" + 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);
var AdditionalPlatforms = Target.Rules.GUBP_GetBuildOnlyPlatforms_MonolithicOnly(HostPlatform);
var AllPlatforms = Platforms.Union(AdditionalPlatforms);
foreach (var Plat in AllPlatforms)
{
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(Plat == UnrealTargetPlatform.Win32 && Target.Rules.GUBP_BuildWindowsXPMonolithics())
{
if(!GUBPNodes.ContainsKey(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, CodeProj, Plat, true)))
{
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, CodeProj, Plat, true));
}
}
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)))
{
if(GamePlatformMonolithicsNode.HasPrecompiledTargets(CodeProj, HostPlatform, Plat))
{
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, CodeProj, Plat, InPrecompiled: true));
}
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, CodeProj, Plat));
}
if (!AdditionalPlatforms.Contains(Plat))
{
string CookedPlatform = Platform.Platforms[Plat].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
if (Target.Rules.GUBP_AlternateCookPlatform(HostPlatform, CookedPlatform) != "")
{
CookedPlatform = Target.Rules.GUBP_AlternateCookPlatform(HostPlatform, CookedPlatform);
}
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)
{
if (Plat == Config.TargetPlatform)
{
FormalNodeName = AddNode(new FormalBuildNode(this, CodeProj, HostPlatform, InClientTargetPlatforms: new List<UnrealTargetPlatform>() { Config.TargetPlatform }, InClientConfigs: new List<UnrealTargetConfiguration>() { Config.TargetConfig }, InClientNotGame: true));
}
}
else if (Kind == TargetRules.TargetType.Server)
{
if (Plat == Config.TargetPlatform)
{
FormalNodeName = AddNode(new FormalBuildNode(this, CodeProj, HostPlatform, InServerTargetPlatforms: new List<UnrealTargetPlatform>() { Config.TargetPlatform }, InServerConfigs: new List<UnrealTargetConfiguration>() { Config.TargetConfig }));
}
}
else if (Kind == TargetRules.TargetType.Game)
{
if (Plat == Config.TargetPlatform)
{
FormalNodeName = AddNode(new FormalBuildNode(this, CodeProj, HostPlatform, InClientTargetPlatforms: new List<UnrealTargetPlatform>() { Config.TargetPlatform }, InClientConfigs: new List<UnrealTargetConfiguration>() { Config.TargetConfig }));
}
}
if (FormalNodeName != null)
{
if (Config.bBeforeTrigger)
{
RemovePseudodependencyFromNode(FormalBuildNode.StaticGetFullName(CodeProj, HostPlatform, new List<UnrealTargetPlatform>() { Config.TargetPlatform }, new List<UnrealTargetConfiguration>() { Config.TargetConfig }), WaitForFormalUserInput.StaticGetFullName());
}
// 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());
string BuildAgentSharingGroup = "";
if (Options.bSeparateGamePromotion)
{
BuildAgentSharingGroup = CodeProj.GameName + "_MakeFormalBuild_" + Plat.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
if (Plat == UnrealTargetPlatform.IOS || Plat == UnrealTargetPlatform.Android || Plat == UnrealTargetPlatform.XboxOne) // 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;
}
else
{
//GUBPNodes[FormalNodeName].AgentSharingGroup = FormalAgentSharingGroup;
if (Plat == UnrealTargetPlatform.XboxOne)
{
GUBPNodes[FormalNodeName].AgentSharingGroup = "";
}
}
if (Config.bTest)
{
AddNode(new FormalBuildTestNode(this, CodeProj, HostPlatform, Plat, Config.TargetConfig));
}
}
}
if (!bNoAutomatedTesting)
{
if (HostPlatform == UnrealTargetPlatform.Mac || HostPlatform == UnrealTargetPlatform.Linux) continue; //temp hack till Linux and 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 (!Options.bTestWithShared || !HasNode(WaitForTestShared.StaticGetFullName()))
{
RemovePseudodependencyFromNode((UATTestNode.StaticGetFullName(HostPlatform, CodeProj, TestNodeName)), WaitForTestShared.StaticGetFullName());
}
}
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 || HostPlatform == UnrealTargetPlatform.Linux) continue; //temp hack till Linux and 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)
{
if (!BranchOptions.ExcludePlatformsForEditor.Contains(HostPlatform) && !BranchOptions.RemovePlatformFromPromotable.Contains(HostPlatform))
{
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, true));
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, false));
AddNode(new SharedAggregatePromotableNode(this, HostPlatforms));
AddNode(new WaitForSharedPromotionUserInput(this, false));
AddNode(new SharedLabelPromotableNode(this, false));
AddNode(new SharedLabelPromotableSuccessNode());
AddNode(new WaitForTestShared(this));
AddNode(new WaitForSharedPromotionUserInput(this, true));
AddNode(new SharedLabelPromotableNode(this, true));
AddNode(new WaitForFormalUserInput(this));
}
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)); - Disable IOSOnPCTest until a1011 crash is fixed
}
AddNode(new VSExpressTestNode(this));
if (ActivePlatforms.Contains(UnrealTargetPlatform.Linux) && !BranchOptions.ExcludePlatformsForEditor.Contains(UnrealTargetPlatform.Linux))
{
AddNode(new RootEditorCrossCompileLinuxNode(UnrealTargetPlatform.Win64));
}
if (!bPreflightBuild)
{
AddNode(new CleanSharedTempStorageNode(this));
}
}
#if false
// this doesn't work for lots of reasons...we can't figure out what the dependencies are until far later
if (bPreflightBuild)
{
GeneralSuccessNode PreflightSuccessNode = new GeneralSuccessNode("Preflight");
foreach (var NodeToDo in GUBPNodes)
{
if (NodeToDo.Value.RunInEC())
{
PreflightSuccessNode.AddPseudodependency(NodeToDo.Key);
}
}
AddNode(PreflightSuccessNode);
}
#endif
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);
}
}
}
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 FullNodeDirectDependencies = new Dictionary<string, string>();
var FullNodeDependedOnBy = new Dictionary<string, string>();
var FullNodeDependentPromotions = new Dictionary<string, string>();
var SeparatePromotables = new List<string>();
{
foreach (var NodeToDo in GUBPNodes)
{
if (GUBPNodes[NodeToDo.Key].IsSeparatePromotable())
{
SeparatePromotables.Add(GUBPNodes[NodeToDo.Key].GetFullName());
List<string> Dependencies = new List<string>();
Dependencies = GetECDependencies(NodeToDo.Key);
foreach (var Dep in Dependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the graph. It is a dependency of {1}.", Dep, NodeToDo);
}
if (!GUBPNodes[Dep].IsPromotableAggregate())
{
if (!GUBPNodes[Dep].DependentPromotions.Contains(NodeToDo.Key))
{
GUBPNodes[Dep].DependentPromotions.Add(NodeToDo.Key);
}
}
}
}
}
// Make sure that everything that's listed as a frequency barrier is completed with the given interval
Dictionary<string, int> FrequencyOverrides = new Dictionary<string,int>();
foreach (KeyValuePair<string, sbyte> Barrier in BranchOptions.FrequencyBarriers)
{
// All the nodes which are dependencies of the barrier node
HashSet<string> IncludedNodes = new HashSet<string> { Barrier.Key };
// Find all the nodes which are indirect dependencies of this node
List<string> SearchNodes = new List<string> { Barrier.Key };
for (int Idx = 0; Idx < SearchNodes.Count; Idx++)
{
GUBPNode Node = GUBPNodes[SearchNodes[Idx]];
foreach (string DependencyName in Node.FullNamesOfDependencies.Union(Node.FullNamesOfPseudosependencies))
{
if (!IncludedNodes.Contains(DependencyName))
{
IncludedNodes.Add(DependencyName);
SearchNodes.Add(DependencyName);
}
}
}
// Make sure that everything included in this list is before the cap, and everything not in the list is after it
foreach (KeyValuePair<string, GUBPNode> NodePair in GUBPNodes)
{
if (IncludedNodes.Contains(NodePair.Key))
{
int Frequency;
if(FrequencyOverrides.TryGetValue(NodePair.Key, out Frequency))
{
Frequency = Math.Min(Frequency, Barrier.Value);
}
else
{
Frequency = Barrier.Value;
}
FrequencyOverrides[NodePair.Key] = Frequency;
}
}
}
// Compute all the frequencies
foreach (var NodeToDo in GUBPNodes)
{
ComputeDependentCISFrequencyQuantumShift(NodeToDo.Key, FrequencyOverrides);
}
foreach (var NodeToDo in GUBPNodes)
{
var Deps = GUBPNodes[NodeToDo.Key].DependentPromotions;
string All = "";
foreach (var Dep in Deps)
{
if (All != "")
{
All += " ";
}
All += Dep;
}
FullNodeDependentPromotions.Add(NodeToDo.Key, All);
}
}
{
Log("******* {0} GUBP Nodes", GUBPNodes.Count);
var SortedNodes = TopologicalSort(new HashSet<string>(GUBPNodes.Keys), LocalOnly: true, DoNotConsiderCompletion: true);
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);
}
else
{
Log(" {0}: {1} [Aggregate]", Node, Note);
}
}
}
Dictionary<string, int> FullNodeListSortKey = GetDisplayOrder(FullNodeList.Keys.ToList(), FullNodeDirectDependencies, GUBPNodes);
bool bOnlyNode = false;
bool bRelatedToNode = false;
bool bGraphSubset = 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))
{
bGraphSubset = true;
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))
{
bGraphSubset = true;
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;
}
//Remove Plat if specified
if(WithoutLinux)
{
var NewNodesToDo = new HashSet<string>();
foreach(var NodeToDo in NodesToDo)
{
if(!GUBPNodes[NodeToDo].GetFullName().Contains("Linux"))
{
NewNodesToDo.Add(NodeToDo);
}
else
{
Log(" Rejecting {0} because -NoLinux was requested", NodeToDo);
}
}
NodesToDo = NewNodesToDo;
}
//find things that depend on our nodes and setup commander dictionary
if (!bOnlyNode)
{
foreach(var NodeToDo in NodesToDo)
{
if (!GUBPNodes[NodeToDo].IsAggregate() && !GUBPNodes[NodeToDo].IsTest())
{
List<string> ECDependencies = new List<string>();
ECDependencies = GetECDependencies(NodeToDo);
foreach (var Dep in ECDependencies)
{
if (!GUBPNodes.ContainsKey(Dep))
{
throw new AutomationException("Node {0} is not in the graph. It is a dependency of {1}.", Dep, NodeToDo);
}
if (!GUBPNodes[Dep].FullNamesOfDependedOn.Contains(NodeToDo))
{
GUBPNodes[Dep].FullNamesOfDependedOn.Add(NodeToDo);
}
}
}
}
foreach(var NodeToDo in NodesToDo)
{
var Deps = GUBPNodes[NodeToDo].FullNamesOfDependedOn;
string All = "";
foreach (var Dep in Deps)
{
if (All != "")
{
All += " ";
}
All += Dep;
}
FullNodeDependedOnBy.Add(NodeToDo, All);
}
}
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();
}
}
}
}
}
if (bPreflightBuild)
{
Log("Culling triggers and downstream for preflight builds ");
var NewNodesToDo = new HashSet<string>();
foreach (var NodeToDo in NodesToDo)
{
var TriggerDot = GetControllingTriggerDotName(NodeToDo);
if (TriggerDot == "" && !GUBPNodes[NodeToDo].TriggerNode())
{
Log(" Keeping {0}", NodeToDo);
NewNodesToDo.Add(NodeToDo);
}
else
{
Log(" Rejecting {0}", NodeToDo);
}
}
NodesToDo = NewNodesToDo;
}
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;
}
if(GUBPNodes[NodeToDo].IsTest())
{
bHasTests = true;
}
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);
}
}
}
}
string FakeFail = ParseParamValue("FakeFail");
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));
}
foreach (var NodePair in FullNodeDependedOnBy)
{
ECProps.Add(string.Format("DependedOnBy/{0}={1}", NodePair.Key, NodePair.Value));
}
foreach (var NodePair in FullNodeDependentPromotions)
{
ECProps.Add(string.Format("DependentPromotions/{0}={1}", NodePair.Key, NodePair.Value));
}
foreach (var Node in SeparatePromotables)
{
ECProps.Add(string.Format("PossiblePromotables/{0}={1}", Node, ""));
}
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;
List<string> StepList = new List<string>();
StepList.Add("use strict;");
StepList.Add("use diagnostics;");
StepList.Add("use ElectricCommander();");
StepList.Add("my $ec = new ElectricCommander;");
StepList.Add("$ec->setTimeout(600);");
StepList.Add("my $batch = $ec->newBatch(\"serial\");");
// 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("$batch->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 => [{{actualParameterName => 'NodeName', value => 'Noop'}}, {{actualParameterName => 'Sticky', value =>'1' }}], releaseMode => 'release'}});", BaseArgs);
StepList.Add(Args);
bHasNoop = true;
}
var FakeECArgs = new List<string>();
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();
if(GUBPNodes[NodeToDo].IsSticky() && NodeToDo == LastSticky)
{
Procedure = Procedure + "_Release";
}
string Args = String.Format("{0}, subprocedure => '{1}', parallel => '{2}', jobStepName => '{3}', actualParameter => [{{actualParameterName => 'NodeName', value =>'{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 + ", {actualParameterName => 'EmailsForTrigger', value => \'" + EMails + "\'}";
}
Args = Args + "]";
string PreCondition = "";
string RunCondition = "";
var UncompletedEcDeps = new List<string>();
var UncompletedCompletedDeps = new List<string>();
string MyAgentGroup = GUBPNodes[NodeToDo].AgentSharingGroup;
bool bDoNestedJobstep = false;
bool bDoFirstNestedJobstep = false;
string NodeParentPath = ParentPath;
string PreconditionParentPath;
if (GUBPNodes[NodeToDo].GetFullName().Contains("MakeBuild") && GUBPNodes[NodeToDo].FullNamesOfPseudosependencies.Contains(WaitForFormalUserInput.StaticGetFullName()) && !bGraphSubset)
{
RemovePseudodependencyFromNode(NodeToDo, WaitForFormalUserInput.StaticGetFullName());
PreconditionParentPath = GetPropertyFromStep("/myWorkflow/ParentJob");
UncompletedEcDeps = GetECDependencies(NodeToDo);
}
else
{
PreconditionParentPath = ParentPath;
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);
}
}
}
var PreConditionUncompletedEcDeps = UncompletedEcDeps;
var CompletedDeps = GetCompletedOnlyDependencies(NodeToDo);
foreach (var Dep in CompletedDeps)
{
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);
}
UncompletedCompletedDeps.Add(Dep);
}
}
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(PreconditionParentPath, Dep) + "/status\') == \'completed\'";
Index++;
if (Index != PreConditionUncompletedEcDeps.Count)
{
PreCondition = PreCondition + " && ";
}
}
PreCondition = PreCondition + ") true;]\"";
}
if(UncompletedCompletedDeps.Count > 0)
{
PreCondition = "\"\\$\" . \"[/javascript if(";
// these run "parallel", but we add preconditions to serialize them
int Index = 0;
foreach (var Dep in CompletedDeps)
{
if (GUBPNodes[Dep].RunInEC())
{
PreCondition = PreCondition + "getProperty('" + GetJobStep(PreconditionParentPath, Dep) + "/status\') == \'completed\'";
Index++;
if (Index != CompletedDeps.Count)
{
PreCondition = PreCondition + " && ";
}
}
}
PreCondition = PreCondition + ") true;]\"";
}
if (UncompletedEcDeps.Count > 0)
{
RunCondition = "\"\\$\" . \"[/javascript if(";
int Index = 0;
foreach (var Dep in UncompletedEcDeps)
{
RunCondition = RunCondition + "((\'\\$\" . \"[" + GetJobStep(PreconditionParentPath, Dep) + "/outcome]\' == \'success\') || ";
RunCondition = RunCondition + "(\'\\$\" . \"[" + GetJobStep(PreconditionParentPath, Dep) + "/outcome]\' == \'warning\'))";
Index++;
if (Index != UncompletedEcDeps.Count)
{
RunCondition = RunCondition + " && ";
}
}
RunCondition = RunCondition + ")true; else false;]\"";
}
if (bDoNestedJobstep)
{
if (bDoFirstNestedJobstep)
{
{
string NestArgs = String.Format("$batch->createJobStep({{parentPath => '{0}', jobStepName => '{1}', parallel => '1'",
ParentPath, MyAgentGroup);
if (!String.IsNullOrEmpty(PreCondition))
{
NestArgs = NestArgs + ", precondition => " + PreCondition;
}
NestArgs = NestArgs + "});";
StepList.Add(NestArgs);
}
{
string NestArgs = String.Format("$batch->createJobStep({{parentPath => '{0}/jobSteps[{1}]', jobStepName => '{2}_GetPool', subprocedure => 'GUBP{3}_AgentShare_GetPool', parallel => '1', actualParameter => [{{actualParameterName => 'AgentSharingGroup', value => '{4}'}}, {{actualParameterName => 'NodeName', value => '{5}'}}]",
ParentPath, MyAgentGroup, MyAgentGroup, GUBPNodes[NodeToDo].ECProcedureInfix(), MyAgentGroup, NodeToDo);
if (!String.IsNullOrEmpty(PreCondition))
{
NestArgs = NestArgs + ", precondition => " + PreCondition;
}
NestArgs = NestArgs + "});";
StepList.Add(NestArgs);
}
{
string NestArgs = String.Format("$batch->createJobStep({{parentPath => '{0}/jobSteps[{1}]', jobStepName => '{2}_GetAgent', subprocedure => 'GUBP{3}_AgentShare_GetAgent', parallel => '1', exclusiveMode => 'call', resourceName => '{4}', actualParameter => [{{actualParameterName => 'AgentSharingGroup', value => '{5}'}}, {{actualParameterName => 'NodeName', value=> '{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('" + PreconditionParentPath + "/jobSteps[" + MyAgentGroup + "]/jobSteps[" + MyAgentGroup + "_GetPool]/status') == 'completed'";
NestArgs = NestArgs + ") true;]\"";
}
NestArgs = NestArgs + "});";
StepList.Add(NestArgs);
}
{
PreCondition = "\"\\$\" . \"[/javascript if(";
PreCondition = PreCondition + "getProperty('" + PreconditionParentPath + "/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'";
}
Args = Args + "});";
StepList.Add(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('" + PreconditionParentPath + "/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 => [{{actualParameterName => 'AgentSharingGroup', valued => '{2}'}}], releaseMode => 'release', precondition => '{3}'",
BaseArgs, MyAgentGroup, MyAgentGroup, RelPreCondition);
StepList.Add(RelArgs);
}
}
}
}
WriteECPerl(StepList);
RunECTool(String.Format("setProperty \"/myWorkflow/HasTests\" \"{0}\"", bHasTests));
{
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);
}
}
else
{
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;
try
{
GUBPNodes[NodeToDo].BuildProducts = RetrieveFromTempStorage(CmdEnv, NodeStoreName, out WasLocal, GameNameIfAny, StorageRootIfAny);
}
catch
{
if(GameNameIfAny != "")
{
GUBPNodes[NodeToDo].BuildProducts = RetrieveFromTempStorage(CmdEnv, NodeStoreName, out WasLocal, "", StorageRootIfAny);
}
else
{
throw new AutomationException("Build Products cannot be found for node {0}", NodeToDo);
}
}
if (!WasLocal)
{
GUBPNodes[NodeToDo].PostLoadFromSharedTempStorage(this);
}
}
}
else
{
if (SaveSuccessRecords)
{
SaveStatus(NodeToDo, StartedTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny);
}
var BuildDuration = 0.0;
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);
var StartTime = DateTime.UtcNow;
GUBPNodes[NodeToDo].DoBuild(this);
BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds / 1000;
}
if (!GUBPNodes[NodeToDo].IsAggregate())
{
var StoreDuration = 0.0;
var StartTime = DateTime.UtcNow;
StoreToTempStorage(CmdEnv, NodeStoreName, GUBPNodes[NodeToDo].BuildProducts, !bSaveSharedTempStorage, GameNameIfAny, StorageRootIfAny);
StoreDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds / 1000;
Log("Took {0} seconds to store build products", StoreDuration);
if (IsBuildMachine)
{
RunECTool(String.Format("setProperty \"/myJobStep/StoreDuration\" \"{0}\"", StoreDuration.ToString()));
}
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);
if (IsBuildMachine)
{
GetFailureEmails(NodeToDo, CLString);
}
UpdateECBuildTime(NodeToDo, BuildDuration);
}
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);
if (IsBuildMachine)
{
GetFailureEmails(NodeToDo, CLString);
}
UpdateECBuildTime(NodeToDo, BuildDuration);
}
}
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();
}
/// <summary>
/// Sorts a list of nodes to display in EC. The default order is based on execution order and agent groups, whereas this function arranges nodes by
/// frequency then execution order, while trying to group nodes on parallel paths (eg. Mac/Windows editor nodes) together.
/// </summary>
static Dictionary<string, int> GetDisplayOrder(List<string> NodeNames, Dictionary<string, string> InitialNodeDependencyNames, Dictionary<string, GUBPNode> GUBPNodes)
{
// Split the nodes into separate lists for each frequency
SortedDictionary<int, List<string>> NodesByFrequency = new SortedDictionary<int,List<string>>();
foreach(string NodeName in NodeNames)
{
List<string> NodesByThisFrequency;
if(!NodesByFrequency.TryGetValue(GUBPNodes[NodeName].DependentCISFrequencyQuantumShift(), out NodesByThisFrequency))
{
NodesByThisFrequency = new List<string>();
NodesByFrequency.Add(GUBPNodes[NodeName].DependentCISFrequencyQuantumShift(), NodesByThisFrequency);
}
NodesByThisFrequency.Add(NodeName);
}
// Build the output list by scanning each frequency in order
HashSet<string> VisitedNodes = new HashSet<string>();
Dictionary<string, int> SortedNodes = new Dictionary<string,int>();
foreach(List<string> NodesByThisFrequency in NodesByFrequency.Values)
{
// Find a list of nodes in each display group. If the group name matches the node name, put that node at the front of the list.
Dictionary<string, string> DisplayGroups = new Dictionary<string,string>();
foreach(string NodeName in NodesByThisFrequency)
{
string GroupName = GUBPNodes[NodeName].GetDisplayGroupName();
if(!DisplayGroups.ContainsKey(GroupName))
{
DisplayGroups.Add(GroupName, NodeName);
}
else if(GroupName == NodeName)
{
DisplayGroups[GroupName] = NodeName + " " + DisplayGroups[GroupName];
}
else
{
DisplayGroups[GroupName] = DisplayGroups[GroupName] + " " + NodeName;
}
}
// Build a list of ordering dependencies, putting all Mac nodes after Windows nodes with the same names.
Dictionary<string, string> NodeDependencyNames = new Dictionary<string,string>(InitialNodeDependencyNames);
foreach(KeyValuePair<string, string> DisplayGroup in DisplayGroups)
{
string[] GroupNodes = DisplayGroup.Value.Split(' ');
for(int Idx = 1; Idx < GroupNodes.Length; Idx++)
{
NodeDependencyNames[GroupNodes[Idx]] += " " + GroupNodes[0];
}
}
// Add nodes for each frequency into the master list, trying to match up different groups along the way
foreach(string FirstNodeName in NodesByThisFrequency)
{
string[] GroupNodeNames = DisplayGroups[GUBPNodes[FirstNodeName].GetDisplayGroupName()].Split(' ');
foreach(string GroupNodeName in GroupNodeNames)
{
AddNodeAndDependencies(GroupNodeName, NodeDependencyNames, VisitedNodes, SortedNodes);
}
}
}
return SortedNodes;
}
static void AddNodeAndDependencies(string NodeName, Dictionary<string, string> NodeDependencyNames, HashSet<string> VisitedNodes, Dictionary<string, int> SortedNodes)
{
if(!VisitedNodes.Contains(NodeName))
{
VisitedNodes.Add(NodeName);
foreach(string NodeDependencyName in NodeDependencyNames[NodeName].Split(new char[]{ ' ' }, StringSplitOptions.RemoveEmptyEntries))
{
AddNodeAndDependencies(NodeDependencyName, NodeDependencyNames, VisitedNodes, SortedNodes);
}
SortedNodes.Add(NodeName, SortedNodes.Count);
}
}
string StartedTempStorageSuffix = "_Started";
string FailedTempStorageSuffix = "_Failed";
string SucceededTempStorageSuffix = "_Succeeded";
}