You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Updated UBT to allow us to setup platform information prior to setting the intermediate directory, ran by RobM - Removed some code that allowed uis to rename binaries. We now use architecture for our _xp suffix. - Added clarity to the WindowsPlatform XP checks. We can now specify XP support "If Available" which atm is only the case for win32. We have an IsWindowsXPSupported() call now in the windows build platform. - Windows XP support can now be requested through UBT on the commandline. Specify using -winxp Kellans GUBP changes included. #CodeReview Kellan.Carr [CL 2422021 by Terence Burns in Main branch]
7237 lines
301 KiB
C#
7237 lines
301 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 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 bBuildRocket = false;
|
|
public static bool bPostedRocketBuild = false;
|
|
public static bool bBuildOnlySamples = false;
|
|
public static List<string> Samples = new List<string>();
|
|
public static string RocketBuild;
|
|
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 string RocketUBTArgs(bool bUseRocketInsteadOfBuildRocket = false)
|
|
{
|
|
string Args = "";
|
|
if (bBuildRocket)
|
|
{
|
|
Args = " -NoSimplygon " + (bUseRocketInsteadOfBuildRocket ? "-Rocket" : "-BuildRocket");
|
|
if(bPostedRocketBuild && bUseRocketInsteadOfBuildRocket)
|
|
{
|
|
Args += " -PostedRocket";
|
|
}
|
|
}
|
|
return Args;
|
|
}
|
|
|
|
public abstract class GUBPNodeAdder
|
|
{
|
|
public virtual void AddNodes(GUBP bp, UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static List<GUBPNodeAdder> Adders;
|
|
private void AddCustomNodes(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
if (Adders == null)
|
|
{
|
|
Adders = new List<GUBPNodeAdder>();
|
|
Assembly[] LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
foreach (var Dll in LoadedAssemblies)
|
|
{
|
|
Type[] AllTypes = Dll.GetTypes();
|
|
foreach (var PotentialConfigType in AllTypes)
|
|
{
|
|
if (PotentialConfigType != typeof(GUBPNodeAdder) && typeof(GUBPNodeAdder).IsAssignableFrom(PotentialConfigType))
|
|
{
|
|
GUBPNodeAdder Config = Activator.CreateInstance(PotentialConfigType) as GUBPNodeAdder;
|
|
if (Config != null)
|
|
{
|
|
Adders.Add(Config);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach(var Adder in Adders)
|
|
{
|
|
Adder.AddNodes(this, InHostPlatform);
|
|
}
|
|
}
|
|
|
|
public abstract class GUBPBranchHacker
|
|
{
|
|
public class BranchOptions
|
|
{
|
|
public List<UnrealTargetPlatform> PlatformsToRemove = new List<UnrealTargetPlatform>();
|
|
public List<string> ExcludeNodes = new List<string>();
|
|
public bool bNoAutomatedTesting = false;
|
|
public bool bNoDocumentation = false;
|
|
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 = Dll.GetTypes();
|
|
foreach (var PotentialConfigType in AllTypes)
|
|
{
|
|
if (PotentialConfigType != typeof(GUBPBranchHacker) && typeof(GUBPBranchHacker).IsAssignableFrom(PotentialConfigType))
|
|
{
|
|
GUBPBranchHacker Config = Activator.CreateInstance(PotentialConfigType) as GUBPBranchHacker;
|
|
if (Config != null)
|
|
{
|
|
BranchHackers.Add(Config);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var Result = new GUBPBranchHacker.BranchOptions();
|
|
foreach (var Hacker in BranchHackers)
|
|
{
|
|
Hacker.ModifyOptions(this, ref Result, Branch);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
public abstract class GUBPEmailHacker
|
|
{
|
|
public virtual List<string> AddEmails(GUBP bp, string Branch, string NodeName)
|
|
{
|
|
return new List<string>();
|
|
}
|
|
public virtual List<string> ModifyEmail(string Email, GUBP bp, string Branch, string NodeName)
|
|
{
|
|
return new List<string>{Email};
|
|
}
|
|
public virtual List<string> FinalizeEmails(List<string> Emails, GUBP bp, string Branch, string NodeName)
|
|
{
|
|
return Emails;
|
|
}
|
|
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 = Dll.GetTypes();
|
|
foreach (var PotentialConfigType in AllTypes)
|
|
{
|
|
if (PotentialConfigType != typeof(GUBPEmailHacker) && typeof(GUBPEmailHacker).IsAssignableFrom(PotentialConfigType))
|
|
{
|
|
GUBPEmailHacker Config = Activator.CreateInstance(PotentialConfigType) as GUBPEmailHacker;
|
|
if (Config != null)
|
|
{
|
|
EmailHackers.Add(Config);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
List<string> Result = new List<string>(Emails.Split(' '));
|
|
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 = Dll.GetTypes();
|
|
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 string AgentSharingGroup = "";
|
|
public int ComputedDependentCISFrequencyQuantumShift = -1;
|
|
|
|
public virtual string GetFullName()
|
|
{
|
|
throw new AutomationException("Unimplemented GetFullName.");
|
|
}
|
|
public virtual string GameNameIfAnyForTempStorage()
|
|
{
|
|
return "";
|
|
}
|
|
public virtual string RootIfAnyForTempStorage()
|
|
{
|
|
return "";
|
|
}
|
|
public virtual void DoBuild(GUBP bp)
|
|
{
|
|
BuildProducts = new List<string>();
|
|
SaveRecordOfSuccessAndAddToBuildProducts();
|
|
}
|
|
public virtual void PostLoadFromSharedTempStorage(GUBP bp)
|
|
{
|
|
}
|
|
public virtual void DoFakeBuild(GUBP bp) // this is used to more rapidly test a build system, it does nothing but save a record of success as a build product
|
|
{
|
|
BuildProducts = new List<string>();
|
|
SaveRecordOfSuccessAndAddToBuildProducts();
|
|
}
|
|
public virtual bool IsSticky()
|
|
{
|
|
return false;
|
|
}
|
|
public virtual bool SendSuccessEmail()
|
|
{
|
|
return false;
|
|
}
|
|
public virtual bool RunInEC()
|
|
{
|
|
return true;
|
|
}
|
|
public virtual bool IsTest()
|
|
{
|
|
return false;
|
|
}
|
|
public virtual bool IsAggregate()
|
|
{
|
|
return false;
|
|
}
|
|
public virtual bool IsPromotableAggregate()
|
|
{
|
|
return false;
|
|
}
|
|
public virtual bool IsSeparatePromotable()
|
|
{
|
|
return false;
|
|
}
|
|
public virtual bool IsRocketSample()
|
|
{
|
|
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 RemovePseudodependency(string Node)
|
|
{
|
|
if (FullNamesOfPseudosependencies.Contains(Node))
|
|
{
|
|
FullNamesOfPseudosependencies.Remove(Node);
|
|
}
|
|
}
|
|
public void AddBuildProduct(string Filename)
|
|
{
|
|
if (!CommandUtils.FileExists_NoExceptions(true, Filename))
|
|
{
|
|
throw new AutomationException("Cannot add build product {0} because it does not exist.", Filename);
|
|
}
|
|
FileInfo Info = new FileInfo(Filename);
|
|
if (!BuildProducts.Contains(Info.FullName))
|
|
{
|
|
BuildProducts.Add(Info.FullName);
|
|
}
|
|
}
|
|
public void AddDependentBuildProduct(string Filename)
|
|
{
|
|
if (!CommandUtils.FileExists_NoExceptions(true, Filename))
|
|
{
|
|
throw new AutomationException("Cannot add build product {0} because it does not exist.", Filename);
|
|
}
|
|
FileInfo Info = new FileInfo(Filename);
|
|
if (!AllDependencyBuildProducts.Contains(Info.FullName))
|
|
{
|
|
AllDependencyBuildProducts.Add(Info.FullName);
|
|
}
|
|
}
|
|
public void AddAllDependent(string Node)
|
|
{
|
|
if (!AllDependencies.Contains(Node))
|
|
{
|
|
AllDependencies.Add(Node);
|
|
}
|
|
}
|
|
public void RemoveOveralppingBuildProducts()
|
|
{
|
|
foreach (var ToRemove in AllDependencyBuildProducts)
|
|
{
|
|
BuildProducts.Remove(ToRemove);
|
|
}
|
|
}
|
|
|
|
}
|
|
public class VersionFilesNode : GUBPNode
|
|
{
|
|
public static string StaticGetFullName()
|
|
{
|
|
return "VersionFiles";
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName();
|
|
}
|
|
|
|
public override void DoBuild(GUBP bp)
|
|
{
|
|
var UE4Build = new UE4Build(bp);
|
|
BuildProducts = UE4Build.UpdateVersionFiles(ActuallyUpdateVersionFiles: CommandUtils.P4Enabled && CommandUtils.AllowSubmit);
|
|
}
|
|
public override bool IsSticky()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public class HostPlatformNode : GUBPNode
|
|
{
|
|
protected UnrealTargetPlatform HostPlatform;
|
|
public HostPlatformNode(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
HostPlatform = InHostPlatform;
|
|
}
|
|
public static string StaticGetHostPlatformSuffix(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InAgentPlatform = UnrealTargetPlatform.Unknown)
|
|
{
|
|
if (InHostPlatform == UnrealTargetPlatform.Mac)
|
|
{
|
|
if (InAgentPlatform == UnrealTargetPlatform.Win64)
|
|
{
|
|
return "_ForMac";
|
|
}
|
|
return "_OnMac";
|
|
}
|
|
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" + bp.RocketUBTArgs();
|
|
|
|
Agenda.AddTargets(new string[] { "UnrealHeaderTool" }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
|
|
return Agenda;
|
|
}
|
|
public override void PostBuild(GUBP bp, UE4Build UE4Build)
|
|
{
|
|
if (HostPlatform == UnrealTargetPlatform.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 UE4Build.BuildAgenda GetAgenda(GUBP bp)
|
|
{
|
|
var Agenda = new UE4Build.BuildAgenda();
|
|
|
|
string AddArgs = "-nobuilduht" + bp.RocketUBTArgs();
|
|
if (bp.bOrthogonalizeEditorPlatforms)
|
|
{
|
|
AddArgs += " -skipnonhostplatforms";
|
|
}
|
|
Agenda.AddTargets(
|
|
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
|
|
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
|
|
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
|
|
{
|
|
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
|
|
}
|
|
}
|
|
return Agenda;
|
|
}
|
|
void DeleteStaleDLLs(GUBP bp)
|
|
{
|
|
if (GUBP.bForceIncrementalCompile)
|
|
{
|
|
return;
|
|
}
|
|
var Targets = new List<string>{bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName};
|
|
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
|
|
{
|
|
Targets.Add(ProgramTarget.TargetName);
|
|
}
|
|
}
|
|
|
|
|
|
foreach (var Target in Targets)
|
|
{
|
|
var EnginePlatformBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Binaries", HostPlatform.ToString());
|
|
var Wildcard = Target + "-*";
|
|
Log("************Deleting stale editor DLLs, path {0} wildcard {1}", EnginePlatformBinaries, Wildcard);
|
|
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePlatformBinaries))
|
|
{
|
|
bool IsBuildProduct = false;
|
|
foreach (var Product in BuildProducts)
|
|
{
|
|
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
IsBuildProduct = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!IsBuildProduct)
|
|
{
|
|
DeleteFile(DiskFile);
|
|
}
|
|
}
|
|
var EnginePluginBinaries = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Plugins");
|
|
var HostSubstring = CommandUtils.CombinePaths("/", HostPlatform.ToString(), "/");
|
|
Log("************Deleting stale editor DLLs, path {0} wildcard {1} host {2}", EnginePluginBinaries, Wildcard, HostSubstring);
|
|
foreach (var DiskFile in FindFiles(Wildcard, true, EnginePluginBinaries))
|
|
{
|
|
if (DiskFile.IndexOf(HostSubstring, StringComparison.InvariantCultureIgnoreCase) < 0)
|
|
{
|
|
continue;
|
|
}
|
|
bool IsBuildProduct = false;
|
|
foreach (var Product in BuildProducts)
|
|
{
|
|
if (Product.Equals(DiskFile, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
IsBuildProduct = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!IsBuildProduct)
|
|
{
|
|
DeleteFile(DiskFile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public override void PostLoadFromSharedTempStorage(GUBP bp)
|
|
{
|
|
DeleteStaleDLLs(bp);
|
|
}
|
|
public override void PostBuildProducts(GUBP bp)
|
|
{
|
|
DeleteStaleDLLs(bp);
|
|
}
|
|
}
|
|
public class RootEditorCrossCompileLinuxNode : CompileNode
|
|
{
|
|
public RootEditorCrossCompileLinuxNode(UnrealTargetPlatform InHostPlatform)
|
|
: base(InHostPlatform)
|
|
{
|
|
AddDependency(RootEditorNode.StaticGetFullName(UnrealTargetPlatform.Win64));
|
|
AddDependency(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64));
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
return "RootEditor_Linux";
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform);
|
|
}
|
|
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" + bp.RocketUBTArgs();
|
|
Agenda.AddTargets(
|
|
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
|
|
UnrealTargetPlatform.Linux, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
|
|
return Agenda;
|
|
}
|
|
}
|
|
public class ToolsNode : CompileNode
|
|
{
|
|
public ToolsNode(UnrealTargetPlatform InHostPlatform)
|
|
: base(InHostPlatform)
|
|
{
|
|
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
|
|
{
|
|
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
|
|
}
|
|
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
return "Tools" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform);
|
|
}
|
|
public override float Priority()
|
|
{
|
|
return base.Priority() - 1;
|
|
}
|
|
public override bool DeleteBuildProducts()
|
|
{
|
|
return true;
|
|
}
|
|
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
|
|
{
|
|
var Agenda = new UE4Build.BuildAgenda();
|
|
|
|
if (HostPlatform == UnrealTargetPlatform.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" + bp.RocketUBTArgs(); ;
|
|
|
|
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
bool bInternalOnly;
|
|
bool SeparateNode;
|
|
bool CrossCompile;
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, GUBP.bBuildRocket, 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)
|
|
{
|
|
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
|
|
{
|
|
AddPseudodependency(RootEditorCrossCompileLinuxNode.StaticGetFullName(HostPlatform));
|
|
}
|
|
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" + bp.RocketUBTArgs(); ;
|
|
|
|
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
bool bInternalOnly;
|
|
bool SeparateNode;
|
|
bool CrossCompile;
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, GUBP.bBuildRocket, 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;
|
|
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
|
|
{
|
|
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
|
|
}
|
|
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
|
|
{
|
|
return "Tools_" + InProgramTarget.TargetName + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform, ProgramTarget);
|
|
}
|
|
public override float Priority()
|
|
{
|
|
return base.Priority() + 2;
|
|
}
|
|
public override bool DeleteBuildProducts()
|
|
{
|
|
return true;
|
|
}
|
|
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
|
|
{
|
|
var Agenda = new UE4Build.BuildAgenda();
|
|
|
|
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice" + bp.RocketUBTArgs(); ;
|
|
|
|
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
|
|
{
|
|
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
|
|
{
|
|
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
|
|
}
|
|
}
|
|
return Agenda;
|
|
}
|
|
}
|
|
|
|
public class InternalToolsNode : CompileNode
|
|
{
|
|
public InternalToolsNode(UnrealTargetPlatform InHostPlatform)
|
|
: base(InHostPlatform)
|
|
{
|
|
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
|
|
{
|
|
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
|
|
}
|
|
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
return "InternalTools" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform);
|
|
}
|
|
public override float Priority()
|
|
{
|
|
return base.Priority() - 2;
|
|
}
|
|
public override int CISFrequencyQuantumShift(GUBP bp)
|
|
{
|
|
int Result = base.CISFrequencyQuantumShift(bp) + 1;
|
|
return Result;
|
|
}
|
|
public override bool DeleteBuildProducts()
|
|
{
|
|
return true;
|
|
}
|
|
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
|
|
{
|
|
bool bAnyAdded = false;
|
|
var Agenda = new UE4Build.BuildAgenda();
|
|
|
|
if (HostPlatform == UnrealTargetPlatform.Win64)
|
|
{
|
|
bAnyAdded = true;
|
|
Agenda.DotNetProjects.AddRange(
|
|
new string[]
|
|
{
|
|
CombinePaths(@"Engine\Source\Programs\Distill\Distill.csproj"),
|
|
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportCommon\CrashReportCommon.csproj"),
|
|
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportReceiver\CrashReportReceiver.csproj"),
|
|
CombinePaths(@"Engine\Source\Programs\NotForLicensees\CrashReportServer\CrashReportProcess\CrashReportProcess.csproj"),
|
|
CombinePaths(@"Engine\Source\Programs\CrashReporter\RegisterPII\RegisterPII.csproj"),
|
|
});
|
|
Agenda.DotNetSolutions.AddRange(
|
|
new string[]
|
|
{
|
|
CombinePaths(@"Engine\Source\Programs\UnrealDocTool\UnrealDocTool\UnrealDocTool.sln"),
|
|
}
|
|
);
|
|
Agenda.ExtraDotNetFiles.AddRange(
|
|
new string[]
|
|
{
|
|
"Interop.IWshRuntimeLibrary",
|
|
"UnrealMarkdown",
|
|
"CommonUnrealMarkdown",
|
|
}
|
|
);
|
|
}
|
|
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice" + bp.RocketUBTArgs(); ;
|
|
|
|
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
bool bInternalOnly;
|
|
bool SeparateNode;
|
|
bool CrossCompile;
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, GUBP.bBuildRocket, 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)
|
|
{
|
|
ProgramTarget = InProgramTarget;
|
|
if (!GUBP.bBuildRocket) // more errors and more performance by just starting before the root editor is done
|
|
{
|
|
AddPseudodependency(RootEditorNode.StaticGetFullName(HostPlatform));
|
|
}
|
|
AgentSharingGroup = "ToolsGroup" + StaticGetHostPlatformSuffix(HostPlatform);
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, SingleTargetProperties InProgramTarget)
|
|
{
|
|
return "InternalTools_" + InProgramTarget.TargetName + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform, ProgramTarget);
|
|
}
|
|
public override int CISFrequencyQuantumShift(GUBP bp)
|
|
{
|
|
int Result = base.CISFrequencyQuantumShift(bp) + 1;
|
|
return Result;
|
|
}
|
|
public override float Priority()
|
|
{
|
|
return base.Priority() + 3;
|
|
}
|
|
public override bool DeleteBuildProducts()
|
|
{
|
|
return true;
|
|
}
|
|
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
|
|
{
|
|
var Agenda = new UE4Build.BuildAgenda();
|
|
|
|
string AddArgs = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice" + bp.RocketUBTArgs(); ;
|
|
|
|
foreach (var Plat in ProgramTarget.Rules.GUBP_ToolPlatforms(HostPlatform))
|
|
{
|
|
foreach (var Config in ProgramTarget.Rules.GUBP_ToolConfigs(HostPlatform))
|
|
{
|
|
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, Plat, Config, InAddArgs: AddArgs);
|
|
}
|
|
}
|
|
return Agenda;
|
|
}
|
|
}
|
|
|
|
|
|
public class EditorPlatformNode : CompileNode
|
|
{
|
|
UnrealTargetPlatform EditorPlatform;
|
|
|
|
public EditorPlatformNode(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform Plat)
|
|
: base(InHostPlatform)
|
|
{
|
|
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
EditorPlatform = Plat;
|
|
AddDependency(RootEditorNode.StaticGetFullName(InHostPlatform));
|
|
}
|
|
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform Plat)
|
|
{
|
|
return Plat.ToString() + "_EditorPlatform" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform, EditorPlatform);
|
|
}
|
|
public override float Priority()
|
|
{
|
|
return base.Priority() + 1;
|
|
}
|
|
public override 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
|
|
{
|
|
BranchInfo.BranchUProject GameProj;
|
|
|
|
public EditorGameNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
|
|
: base(InHostPlatform)
|
|
{
|
|
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
GameProj = InGameProj;
|
|
AddDependency(RootEditorNode.StaticGetFullName(InHostPlatform));
|
|
}
|
|
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj)
|
|
{
|
|
return InGameProj.GameName + "_EditorGame" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform, GameProj);
|
|
}
|
|
public override string GameNameIfAnyForTempStorage()
|
|
{
|
|
return GameProj.GameName;
|
|
}
|
|
public override float Priority()
|
|
{
|
|
float Result = base.Priority();
|
|
if (GameProj.Options(HostPlatform).bTestWithShared)
|
|
{
|
|
Result -= 1;
|
|
}
|
|
return Result;
|
|
}
|
|
public override int CISFrequencyQuantumShift(GUBP bp)
|
|
{
|
|
int Result = base.CISFrequencyQuantumShift(bp);
|
|
if(GameProj.Options(HostPlatform).bTestWithShared)
|
|
{
|
|
Result += 3;
|
|
}
|
|
return Result;
|
|
}
|
|
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
|
|
{
|
|
var Agenda = new UE4Build.BuildAgenda();
|
|
|
|
string Args = "-nobuilduht -skipactionhistory -skipnonhostplatforms -CopyAppBundleBackToDevice -forceheadergeneration" + bp.RocketUBTArgs(true);
|
|
|
|
Agenda.AddTargets(
|
|
new string[] { GameProj.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
|
|
HostPlatform, UnrealTargetConfiguration.Development, GameProj.FilePath, InAddArgs: Args);
|
|
|
|
return Agenda;
|
|
}
|
|
}
|
|
|
|
public class GamePlatformMonolithicsNode : CompileNode
|
|
{
|
|
BranchInfo.BranchUProject GameProj;
|
|
UnrealTargetPlatform TargetPlatform;
|
|
bool WithXp;
|
|
public GamePlatformMonolithicsNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, bool InWithXp = false)
|
|
: base(InHostPlatform)
|
|
{
|
|
GameProj = InGameProj;
|
|
TargetPlatform = InTargetPlatform;
|
|
WithXp = InWithXp;
|
|
if (TargetPlatform == UnrealTargetPlatform.PS4)
|
|
{
|
|
var PS4MapFileUtil = bp.Branch.FindProgram("PS4MapFileUtil");
|
|
if(PS4MapFileUtil.Rules == null)
|
|
{
|
|
throw new AutomationException("PS4MapFileUtil is not is this branch, but is required to build PS4 monolithics");
|
|
}
|
|
AddDependency(SingleToolsNode.StaticGetFullName(HostPlatform, PS4MapFileUtil));
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.XboxOne)
|
|
{
|
|
var XboxOnePDBFileUtil = bp.Branch.FindProgram("XboxOnePDBFileUtil");
|
|
if (XboxOnePDBFileUtil.Rules == null)
|
|
{
|
|
throw new AutomationException("XboxOnePDBFileUtil is not is this branch, but is required to build Xbox One monolithics");
|
|
}
|
|
AddDependency(SingleToolsNode.StaticGetFullName(HostPlatform, XboxOnePDBFileUtil));
|
|
}
|
|
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
|
|
{
|
|
AddPseudodependency(EditorGameNode.StaticGetFullName(InHostPlatform, GameProj));
|
|
if (bp.HasNode(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform)))
|
|
{
|
|
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TargetPlatform != InHostPlatform && bp.HasNode(GamePlatformMonolithicsNode.StaticGetFullName(InHostPlatform, bp.Branch.BaseEngineProject, InHostPlatform))
|
|
&& !GUBP.bBuildRocket) // more errors and more performance by just starting, for example, IOS without waiting for mac
|
|
{
|
|
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(InHostPlatform, bp.Branch.BaseEngineProject, InHostPlatform));
|
|
}
|
|
if (GUBP.bBuildRocket && InGameProj.GameName == bp.Branch.BaseEngineProject.GameName)
|
|
{
|
|
AgentSharingGroup = "UE4_" + InTargetPlatform + "_Mono" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
// lets just start this right away AddPseudodependency(RootEditorHeadersNode.StaticGetFullName(HostPlatform)); // maybe we should start these sooner, but that rather tangles the agent groups
|
|
}
|
|
}
|
|
if (InGameProj.Options(InHostPlatform).bTestWithShared) /// compiling templates is only for testing purposes, and we will group them to avoid saturating the farm
|
|
{
|
|
AddPseudodependency(WaitForTestShared.StaticGetFullName());
|
|
AgentSharingGroup = "TemplateMonolithics" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
|
|
if (GUBP.bBuildRocket)
|
|
{
|
|
var Target = bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Game];
|
|
var Configs = Target.Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, TargetPlatform);
|
|
foreach (var Config in Configs)
|
|
{
|
|
if (HostPlatform == UnrealTargetPlatform.Win64)
|
|
{
|
|
if (TargetPlatform == UnrealTargetPlatform.Win32 && Config != UnrealTargetConfiguration.Shipping)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.Win64 && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.Android && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.HTML5 && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.Linux && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
Log("Building {0} for Host={1} Target={2} Config={3} for rocket. Node={4}", Target.TargetName, HostPlatform, TargetPlatform, Config, GetFullName());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, bool WithXp = false)
|
|
{
|
|
string Name;
|
|
if (!WithXp)
|
|
{
|
|
Name = InGameProj.GameName + "_" + InTargetPlatform + "_Mono" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
else
|
|
{
|
|
Name = InGameProj.GameName + "_WinXP_Mono" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
return Name;
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform, GameProj, TargetPlatform, WithXp);
|
|
}
|
|
public override string GameNameIfAnyForTempStorage()
|
|
{
|
|
return GameProj.GameName;
|
|
}
|
|
public override bool DeleteBuildProducts()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override int CISFrequencyQuantumShift(GUBP bp)
|
|
{
|
|
int Result = base.CISFrequencyQuantumShift(bp);
|
|
if(GameProj.GameName != bp.Branch.BaseEngineProject.GameName)
|
|
{
|
|
Result += 3; //only every 80m
|
|
}
|
|
else if (TargetPlatform != HostPlatform)
|
|
{
|
|
Result += 2; //only every 40m
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
public override UE4Build.BuildAgenda GetAgenda(GUBP bp)
|
|
{
|
|
if (!bp.ActivePlatforms.Contains(TargetPlatform))
|
|
{
|
|
throw new AutomationException("{0} is not a supported platform for {1}", TargetPlatform.ToString(), GetFullName());
|
|
}
|
|
var Agenda = new UE4Build.BuildAgenda();
|
|
|
|
string Args = "-nobuilduht -skipactionhistory -CopyAppBundleBackToDevice" + bp.RocketUBTArgs();
|
|
|
|
foreach (var Kind in BranchInfo.MonolithicKinds)
|
|
{
|
|
if (GUBP.bBuildRocket && Kind != TargetRules.TargetType.Game)
|
|
{
|
|
continue;
|
|
}
|
|
if (GameProj.Properties.Targets.ContainsKey(Kind))
|
|
{
|
|
var Target = GameProj.Properties.Targets[Kind];
|
|
var 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))
|
|
{
|
|
var Configs = Target.Rules.GUBP_GetConfigs_MonolithicOnly(HostPlatform, TargetPlatform);
|
|
foreach (var Config in Configs)
|
|
{
|
|
if (WithXp)
|
|
{
|
|
Args += " -winxp";
|
|
}
|
|
|
|
if (GUBP.bBuildRocket)
|
|
{
|
|
if (HostPlatform == UnrealTargetPlatform.Win64)
|
|
{
|
|
if (TargetPlatform == UnrealTargetPlatform.Win32 && Config != UnrealTargetConfiguration.Shipping)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.Win64 && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.Android && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.HTML5 && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
if (TargetPlatform == UnrealTargetPlatform.Linux && Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (Config != UnrealTargetConfiguration.Shipping && Config != UnrealTargetConfiguration.Development)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if (GameProj.GameName == bp.Branch.BaseEngineProject.GameName)
|
|
{
|
|
Agenda.AddTargets(new string[] { Target.TargetName }, TargetPlatform, Config, InAddArgs: Args);
|
|
}
|
|
else
|
|
{
|
|
Agenda.AddTargets(new string[] { Target.TargetName }, TargetPlatform, Config, GameProj.FilePath, InAddArgs: Args);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Agenda;
|
|
}
|
|
}
|
|
|
|
public static class HeadersNode
|
|
{
|
|
public static void ZipHeaders(IEnumerable<string> HeaderFiles, string ZipFileName)
|
|
{
|
|
string NormalizedPrefix = CommandUtils.ConvertSeparators(PathSeparator.Slash, CmdEnv.LocalRoot).TrimEnd('/') + "/";
|
|
|
|
Ionic.Zip.ZipFile Zip = new Ionic.Zip.ZipFile();
|
|
foreach(string HeaderFile in HeaderFiles)
|
|
{
|
|
string NormalizedDirectoryName = CommandUtils.ConvertSeparators(PathSeparator.Slash, Path.GetDirectoryName(HeaderFile));
|
|
if(NormalizedDirectoryName.StartsWith(NormalizedPrefix))
|
|
{
|
|
Zip.AddFile(HeaderFile, NormalizedDirectoryName.Substring(NormalizedPrefix.Length));
|
|
}
|
|
else
|
|
{
|
|
throw new AutomationException("Header file '{0}' was not under root directory ('{1}')", NormalizedDirectoryName, NormalizedPrefix);
|
|
}
|
|
}
|
|
Zip.Save(ZipFileName);
|
|
}
|
|
}
|
|
|
|
public class RootEditorHeadersNode : HostPlatformNode
|
|
{
|
|
public RootEditorHeadersNode(UnrealTargetPlatform InHostPlatform)
|
|
: base(InHostPlatform)
|
|
{
|
|
AgentSharingGroup = "Editor" + StaticGetHostPlatformSuffix(HostPlatform);
|
|
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
return "RootEditorHeaders" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform);
|
|
}
|
|
public override float Priority()
|
|
{
|
|
return 1000000.0f; // right after the root editor
|
|
}
|
|
public override void DoBuild(GUBP bp)
|
|
{
|
|
HashSet<string> HeaderFiles = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
|
foreach (var FileToCopy in CommandUtils.FindFiles("*.h", true, CommandUtils.CombinePaths(CmdEnv.LocalRoot, @"Engine\Intermediate\Build\", HostPlatform.ToString(), "Inc")))
|
|
{
|
|
HeaderFiles.Add(FileToCopy);
|
|
}
|
|
var Targets = new List<string> { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName };
|
|
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
|
|
{
|
|
Targets.Add(ProgramTarget.TargetName);
|
|
}
|
|
}
|
|
foreach (var Target in Targets)
|
|
{
|
|
foreach (var FileToCopy in CommandUtils.FindFiles("*.h", true, CommandUtils.CombinePaths(CmdEnv.LocalRoot, @"Engine\Intermediate\Build\", HostPlatform.ToString(), Target, "Inc")))
|
|
{
|
|
HeaderFiles.Add(FileToCopy);
|
|
}
|
|
}
|
|
|
|
var EnginePluginsDirectory = Path.Combine(CommandUtils.CmdEnv.LocalRoot, "Engine/Plugins");
|
|
var EnginePlugins = new List<PluginInfo>();
|
|
Plugins.FindPluginsIn(EnginePluginsDirectory, PluginInfo.LoadedFromType.Engine, ref EnginePlugins);
|
|
|
|
foreach (var EnginePlugin in EnginePlugins)
|
|
{
|
|
foreach (var FileToCopy in CommandUtils.FindFiles("*", true, CommandUtils.CombinePaths(EnginePlugin.Directory, @"Intermediate\Build", HostPlatform.ToString(), "Inc")))
|
|
{
|
|
HeaderFiles.Add(FileToCopy);
|
|
}
|
|
}
|
|
|
|
// Create a zip file containing the headers.
|
|
string ZipFileName = CommandUtils.CombinePaths(CmdEnv.LocalRoot, "Engine\\Intermediate\\Build", HostPlatform.ToString(), "RootEditorHeaders.zip");
|
|
HeadersNode.ZipHeaders(HeaderFiles, ZipFileName);
|
|
BuildProducts = new List<string>{ ZipFileName };
|
|
}
|
|
}
|
|
|
|
public class GameMonolithicHeadersNode : HostPlatformNode
|
|
{
|
|
UnrealTargetPlatform TargetPlatform;
|
|
|
|
public GameMonolithicHeadersNode(GUBP bp, UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InTargetPlatform)
|
|
: base(InHostPlatform)
|
|
{
|
|
TargetPlatform = InTargetPlatform;
|
|
AgentSharingGroup = "UE4_" + InTargetPlatform + "_Mono" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, UnrealTargetPlatform InTargetPlatform)
|
|
{
|
|
return "UE4_" + InTargetPlatform + "_MonolithicHeaders" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform, TargetPlatform);
|
|
}
|
|
public override float Priority()
|
|
{
|
|
return 1000000.0f; // right after the game monolithics
|
|
}
|
|
public override void DoBuild(GUBP bp)
|
|
{
|
|
if (!UnrealBuildTool.Utils.IsRunningOnMono && TargetPlatform == UnrealTargetPlatform.IOS)
|
|
{
|
|
throw new AutomationException("these rocket header node require real mac path.");
|
|
}
|
|
|
|
HashSet<string> HeaderFiles = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
|
if (TargetPlatform != HostPlatform)
|
|
{
|
|
// host platform overlaps with the editor, so we don't want them here
|
|
var PlatformDir = TargetPlatform.ToString();
|
|
foreach (var FileToCopy in CommandUtils.FindFiles("*.h", true, CommandUtils.CombinePaths(CmdEnv.LocalRoot, @"Engine\Intermediate\Build\", PlatformDir, "Inc")))
|
|
{
|
|
HeaderFiles.Add(FileToCopy);
|
|
}
|
|
|
|
// these may not be any new build products here, but we will check anyway
|
|
var EnginePluginsDirectory = Path.Combine(CommandUtils.CmdEnv.LocalRoot, "Engine/Plugins");
|
|
var EnginePlugins = new List<PluginInfo>();
|
|
Plugins.FindPluginsIn(EnginePluginsDirectory, PluginInfo.LoadedFromType.Engine, ref EnginePlugins);
|
|
|
|
foreach (var EnginePlugin in EnginePlugins)
|
|
{
|
|
foreach (var FileToCopy in CommandUtils.FindFiles("*", true, CommandUtils.CombinePaths(EnginePlugin.Directory, @"Intermediate\Build", PlatformDir, "Inc")))
|
|
{
|
|
HeaderFiles.Add(FileToCopy);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create a zip containing all the headers
|
|
string ZipFileName = CommandUtils.CombinePaths(CmdEnv.LocalRoot, "Engine\\Intermediate\\Build", TargetPlatform.ToString(), "MonolithicHeaders.zip");
|
|
HeadersNode.ZipHeaders(HeaderFiles, ZipFileName);
|
|
BuildProducts = new List<string>{ ZipFileName };
|
|
}
|
|
}
|
|
|
|
public class SuccessNode : GUBPNode
|
|
{
|
|
public SuccessNode()
|
|
{
|
|
}
|
|
public override void DoBuild(GUBP bp)
|
|
{
|
|
BuildProducts = new List<string>();
|
|
SaveRecordOfSuccessAndAddToBuildProducts();
|
|
}
|
|
public override bool SendSuccessEmail()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public class GeneralSuccessNode : GUBP.SuccessNode
|
|
{
|
|
string MyName;
|
|
public GeneralSuccessNode(string InMyName)
|
|
{
|
|
MyName = InMyName;
|
|
}
|
|
public static string StaticGetFullName(string InMyName)
|
|
{
|
|
return InMyName + "_Success";
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(MyName);
|
|
}
|
|
}
|
|
|
|
public class AggregateNode : GUBPNode
|
|
{
|
|
public AggregateNode()
|
|
{
|
|
}
|
|
public override bool RunInEC()
|
|
{
|
|
return false;
|
|
}
|
|
public override bool IsAggregate()
|
|
{
|
|
return true;
|
|
}
|
|
public override void DoBuild(GUBP bp)
|
|
{
|
|
BuildProducts = new List<string>();
|
|
}
|
|
public override void DoFakeBuild(GUBP bp) // this is used to more rapidly test a build system, it does nothing but save a record of success as a build product
|
|
{
|
|
BuildProducts = new List<string>();
|
|
}
|
|
}
|
|
|
|
public class HostPlatformAggregateNode : AggregateNode
|
|
{
|
|
protected UnrealTargetPlatform HostPlatform;
|
|
public HostPlatformAggregateNode(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
HostPlatform = InHostPlatform;
|
|
}
|
|
public static string StaticGetHostPlatformSuffix(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
return HostPlatformNode.StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public virtual string GetHostPlatformSuffix()
|
|
{
|
|
return StaticGetHostPlatformSuffix(HostPlatform);
|
|
}
|
|
public UnrealTargetPlatform GetAltHostPlatform()
|
|
{
|
|
return GUBP.GetAltHostPlatform(HostPlatform);
|
|
}
|
|
}
|
|
|
|
public class EditorAndToolsNode : HostPlatformAggregateNode
|
|
{
|
|
public EditorAndToolsNode(GUBP bp, UnrealTargetPlatform InHostPlatform)
|
|
: base(InHostPlatform)
|
|
{
|
|
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
|
|
AddDependency(ToolsNode.StaticGetFullName(HostPlatform));
|
|
AddDependency(InternalToolsNode.StaticGetFullName(HostPlatform));
|
|
|
|
if(InHostPlatform == UnrealTargetPlatform.Win64)
|
|
{
|
|
var VersionSelector = bp.Branch.FindProgram("UnrealVersionSelector");
|
|
if (VersionSelector.Rules != null)
|
|
{
|
|
AddDependency(SingleInternalToolsNode.StaticGetFullName(HostPlatform, VersionSelector));
|
|
}
|
|
}
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform)
|
|
{
|
|
return "BaseEditorAndTools" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform);
|
|
}
|
|
}
|
|
|
|
|
|
public class AggregatePromotableNode : AggregateNode
|
|
{
|
|
protected List<UnrealTargetPlatform> HostPlatforms;
|
|
string PromotionLabelPrefix;
|
|
|
|
public AggregatePromotableNode(List<UnrealTargetPlatform> InHostPlatforms, string InPromotionLabelPrefix)
|
|
{
|
|
HostPlatforms = InHostPlatforms;
|
|
foreach (var HostPlatform in HostPlatforms)
|
|
{
|
|
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
|
|
}
|
|
PromotionLabelPrefix = InPromotionLabelPrefix;
|
|
}
|
|
public static string StaticGetFullName(string InPromotionLabelPrefix)
|
|
{
|
|
return InPromotionLabelPrefix + "_Promotable_Aggregate";
|
|
}
|
|
public override bool IsPromotableAggregate()
|
|
{
|
|
return true;
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(PromotionLabelPrefix);
|
|
}
|
|
}
|
|
|
|
public class GameAggregatePromotableNode : AggregatePromotableNode
|
|
{
|
|
BranchInfo.BranchUProject GameProj;
|
|
|
|
public GameAggregatePromotableNode(GUBP bp, List<UnrealTargetPlatform> InHostPlatforms, BranchInfo.BranchUProject InGameProj, bool IsSeparate)
|
|
: base(InHostPlatforms, InGameProj.GameName)
|
|
{
|
|
GameProj = InGameProj;
|
|
|
|
foreach (var HostPlatform in HostPlatforms)
|
|
{
|
|
AddDependency(RootEditorNode.StaticGetFullName(HostPlatform));
|
|
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
|
|
{
|
|
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
|
|
}
|
|
// add all of the platforms I use
|
|
{
|
|
var Platforms = bp.GetMonolithicPlatformsForUProject(HostPlatform, InGameProj, false);
|
|
if (bp.bOrthogonalizeEditorPlatforms)
|
|
{
|
|
foreach (var Plat in Platforms)
|
|
{
|
|
AddDependency(EditorPlatformNode.StaticGetFullName(HostPlatform, Plat));
|
|
}
|
|
}
|
|
}
|
|
{
|
|
var Platforms = bp.GetMonolithicPlatformsForUProject(HostPlatform, InGameProj, true);
|
|
foreach (var Plat in Platforms)
|
|
{
|
|
AddDependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, Plat));
|
|
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)
|
|
{
|
|
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, CodeProj)); // if we are just testing, we will still include the editor stuff
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public override bool IsSeparatePromotable()
|
|
{
|
|
return true;
|
|
}
|
|
public static string StaticGetFullName()
|
|
{
|
|
return AggregatePromotableNode.StaticGetFullName("Shared");
|
|
}
|
|
}
|
|
|
|
public class WaitForUserInput : GUBPNode
|
|
{
|
|
protected bool bTriggerWasTriggered;
|
|
public WaitForUserInput()
|
|
{
|
|
bTriggerWasTriggered = false;
|
|
}
|
|
|
|
public override void DoBuild(GUBP bp)
|
|
{
|
|
BuildProducts = new List<string>();
|
|
SaveRecordOfSuccessAndAddToBuildProducts();
|
|
}
|
|
public override bool TriggerNode()
|
|
{
|
|
return true;
|
|
}
|
|
public override void SetAsExplicitTrigger()
|
|
{
|
|
bTriggerWasTriggered = true;
|
|
}
|
|
public override bool IsSticky()
|
|
{
|
|
return bTriggerWasTriggered;
|
|
}
|
|
public virtual string GetTriggerStateName()
|
|
{
|
|
return "GenericTrigger";
|
|
}
|
|
public virtual string GetTriggerDescText()
|
|
{
|
|
return "GenericTrigger no description text available";
|
|
}
|
|
public virtual string GetTriggerActionText()
|
|
{
|
|
return "GenericTrigger no action text available";
|
|
}
|
|
public virtual bool TriggerRequiresRecursiveWorkflow()
|
|
{
|
|
return true;
|
|
}
|
|
public override string ECProcedure()
|
|
{
|
|
if (bTriggerWasTriggered)
|
|
{
|
|
return base.ECProcedure(); // after this user hits the trigger, we want to run this as an ordinary node
|
|
}
|
|
if (TriggerRequiresRecursiveWorkflow())
|
|
{
|
|
return String.Format("GUBP_UAT_Trigger"); //here we run a recursive workflow to wait for the trigger
|
|
}
|
|
return String.Format("GUBP_Hardcoded_Trigger"); //here we advance the state in the hardcoded workflow so folks can approve
|
|
}
|
|
public override string ECProcedureParams()
|
|
{
|
|
var Result = base.ECProcedureParams();
|
|
if (!bTriggerWasTriggered)
|
|
{
|
|
Result += String.Format(", {{actualParameterName => 'TriggerState', value => '{0}'}}, {{actualParameterName => 'ActionText', value =>\"{1}\"}}, {{actualParameterName => 'DescText', value =>\"{2}\"}}", GetTriggerStateName(), GetTriggerActionText(), GetTriggerDescText());
|
|
//Result += String.Format(" --actualParameter TriggerState={0} --actualParameter ActionText=\"{1}\" --actualParameter DescText=\"{2}\"", GetTriggerStateName(), GetTriggerActionText(), GetTriggerDescText());
|
|
}
|
|
return Result;
|
|
}
|
|
public override int TimeoutInMinutes()
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public class WaitForPromotionUserInput : WaitForUserInput
|
|
{
|
|
string PromotionLabelPrefix;
|
|
string PromotionLabelSuffix;
|
|
protected bool bLabelPromoted; // true if this is the promoted version
|
|
|
|
public WaitForPromotionUserInput(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
|
|
{
|
|
PromotionLabelPrefix = InPromotionLabelPrefix;
|
|
PromotionLabelSuffix = InPromotionLabelSuffix;
|
|
bLabelPromoted = bInLabelPromoted;
|
|
if (bLabelPromoted)
|
|
{
|
|
AddDependency(LabelPromotableNode.StaticGetFullName(PromotionLabelPrefix, false));
|
|
}
|
|
else
|
|
{
|
|
AddDependency(AggregatePromotableNode.StaticGetFullName(PromotionLabelPrefix));
|
|
}
|
|
}
|
|
public static string StaticGetFullName(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
|
|
{
|
|
return InPromotionLabelPrefix + (bInLabelPromoted ? "_WaitForPromotion" : "_WaitForPromotable") + InPromotionLabelSuffix;
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(PromotionLabelPrefix, PromotionLabelSuffix, bLabelPromoted);
|
|
}
|
|
}
|
|
|
|
public class WaitForGamePromotionUserInput : WaitForPromotionUserInput
|
|
{
|
|
BranchInfo.BranchUProject GameProj;
|
|
bool bCustomWorkflow;
|
|
public WaitForGamePromotionUserInput(GUBP bp, BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
|
|
: base(InGameProj.GameName, "", bInLabelPromoted)
|
|
{
|
|
GameProj = InGameProj;
|
|
var Options = InGameProj.Options(UnrealTargetPlatform.Win64);
|
|
bCustomWorkflow = Options.bCustomWorkflowForPromotion;
|
|
}
|
|
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
|
|
{
|
|
return WaitForPromotionUserInput.StaticGetFullName(InGameProj.GameName, "", bInLabelPromoted);
|
|
}
|
|
public override string GameNameIfAnyForTempStorage()
|
|
{
|
|
return GameProj.GameName;
|
|
}
|
|
public override string GetTriggerDescText()
|
|
{
|
|
if (bLabelPromoted)
|
|
{
|
|
return GameProj.GameName + " is ready for promotion.";
|
|
}
|
|
return GameProj.GameName + " is ready to make a promotable label and begin testing.";
|
|
}
|
|
public override string GetTriggerActionText()
|
|
{
|
|
if (bLabelPromoted)
|
|
{
|
|
return "Promote " + GameProj.GameName + ".";
|
|
}
|
|
return "Make a promotable label for " + GameProj.GameName + " and begin testing.";
|
|
}
|
|
public override string GetTriggerStateName()
|
|
{
|
|
if (bCustomWorkflow)
|
|
{
|
|
return GetFullName();
|
|
}
|
|
return base.GetTriggerStateName();
|
|
}
|
|
public override bool TriggerRequiresRecursiveWorkflow()
|
|
{
|
|
if (bCustomWorkflow)
|
|
{
|
|
return !bLabelPromoted; // the promotable starts the hardcoded chain
|
|
}
|
|
return base.TriggerRequiresRecursiveWorkflow();
|
|
}
|
|
}
|
|
|
|
public class WaitForSharedPromotionUserInput : WaitForPromotionUserInput
|
|
{
|
|
|
|
public WaitForSharedPromotionUserInput(GUBP bp, bool bInLabelPromoted)
|
|
: base("Shared", IsMainBranch(), bInLabelPromoted)
|
|
{
|
|
}
|
|
public override string GetTriggerDescText()
|
|
{
|
|
if (bLabelPromoted)
|
|
{
|
|
return "The shared promotable is ready for promotion.";
|
|
}
|
|
return "The shared promotable is ready to make a promotable label.";
|
|
}
|
|
public override string GetTriggerActionText()
|
|
{
|
|
if (bLabelPromoted)
|
|
{
|
|
return "Promote the shared promotable.";
|
|
}
|
|
return "Make the shared promotable label.";
|
|
}
|
|
public static string StaticGetFullName(bool bInLabelPromoted)
|
|
{
|
|
return WaitForPromotionUserInput.StaticGetFullName("Shared", IsMainBranch(), bInLabelPromoted);
|
|
}
|
|
public static string IsMainBranch()
|
|
{
|
|
string isMain = "";
|
|
if (P4Enabled)
|
|
{
|
|
string CurrentBranch = P4Env.BuildRootP4;
|
|
if (CurrentBranch == "//depot/UE4")
|
|
{
|
|
isMain = "_WithNightlys";
|
|
}
|
|
}
|
|
return isMain;
|
|
}
|
|
public override string GetTriggerStateName()
|
|
{
|
|
return GetFullName();
|
|
}
|
|
public override bool TriggerRequiresRecursiveWorkflow()
|
|
{
|
|
return !bLabelPromoted;
|
|
}
|
|
}
|
|
|
|
public class LabelPromotableNode : GUBPNode
|
|
{
|
|
string PromotionLabelPrefix;
|
|
protected bool bLabelPromoted; // true if this is the promoted version
|
|
|
|
public LabelPromotableNode(string InPromotionLabelPrefix, string InPromotionLabelSuffix, bool bInLabelPromoted)
|
|
{
|
|
PromotionLabelPrefix = InPromotionLabelPrefix;
|
|
bLabelPromoted = bInLabelPromoted;
|
|
AddDependency(WaitForPromotionUserInput.StaticGetFullName(PromotionLabelPrefix, InPromotionLabelSuffix, bLabelPromoted));
|
|
}
|
|
string LabelName(bool bLocalLabelPromoted)
|
|
{
|
|
string LabelPrefix = PromotionLabelPrefix;
|
|
string CompleteLabelPrefix = (bLocalLabelPromoted ? "Promoted-" : "Promotable-") + LabelPrefix;
|
|
if (LabelPrefix == "Shared" && bLocalLabelPromoted)
|
|
{
|
|
// shared promotion has a shorter name
|
|
CompleteLabelPrefix = "Promoted";
|
|
}
|
|
if (LabelPrefix == "Shared" && !bLocalLabelPromoted)
|
|
{
|
|
//shared promotable has a shorter name
|
|
CompleteLabelPrefix = "Promotable";
|
|
}
|
|
if (GUBP.bBuildRocket)
|
|
{
|
|
CompleteLabelPrefix = "Rocket-" + CompleteLabelPrefix;
|
|
}
|
|
if (GUBP.bPreflightBuild)
|
|
{
|
|
CompleteLabelPrefix = CompleteLabelPrefix + PreflightMangleSuffix;
|
|
}
|
|
return CompleteLabelPrefix;
|
|
}
|
|
|
|
public override bool IsSticky()
|
|
{
|
|
return true;
|
|
}
|
|
public override bool SendSuccessEmail()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
public override void DoBuild(GUBP bp)
|
|
{
|
|
BuildProducts = new List<string>();
|
|
|
|
if (P4Enabled)
|
|
{
|
|
if (AllDependencyBuildProducts.Count == 0)
|
|
{
|
|
throw new AutomationException("{0} has no build products", GetFullName());
|
|
|
|
}
|
|
|
|
if (bLabelPromoted)
|
|
{
|
|
P4.MakeDownstreamLabelFromLabel(P4Env, LabelName(true), LabelName(false));
|
|
}
|
|
else
|
|
{
|
|
int WorkingCL = P4.CreateChange(P4Env.Client, String.Format("GUBP Node {0} built from changelist {1}", GetFullName(), bp.CL));
|
|
Log("Build from {0} Working in {1}", bp.CL, WorkingCL);
|
|
|
|
var ProductsToSubmit = new List<String>();
|
|
|
|
foreach (var Product in AllDependencyBuildProducts)
|
|
{
|
|
// hacks to keep certain things out of P4
|
|
if (
|
|
!Product.EndsWith("version.h", StringComparison.InvariantCultureIgnoreCase) &&
|
|
!Product.EndsWith("version.cpp", StringComparison.InvariantCultureIgnoreCase) &&
|
|
!Product.Replace('\\', '/').EndsWith("DotNetCommon/MetaData.cs", StringComparison.InvariantCultureIgnoreCase) &&
|
|
!Product.EndsWith("_Success.log", StringComparison.InvariantCultureIgnoreCase)
|
|
)
|
|
{
|
|
ProductsToSubmit.Add(Product);
|
|
}
|
|
}
|
|
|
|
// Open files for add or edit
|
|
UE4Build.AddBuildProductsToChangelist(WorkingCL, ProductsToSubmit);
|
|
|
|
// Check everything in!
|
|
int SubmittedCL;
|
|
P4.Submit(WorkingCL, out SubmittedCL, true, true);
|
|
|
|
// Label it
|
|
P4.MakeDownstreamLabel(P4Env, LabelName(false), null);
|
|
}
|
|
}
|
|
SaveRecordOfSuccessAndAddToBuildProducts();
|
|
}
|
|
public static string StaticGetFullName(string InPromotionLabelPrefix, bool bInLabelPromoted)
|
|
{
|
|
return InPromotionLabelPrefix + (bInLabelPromoted ? "_LabelPromoted" : "_LabelPromotable");
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(PromotionLabelPrefix, bLabelPromoted);
|
|
}
|
|
}
|
|
|
|
public class GameLabelPromotableNode : LabelPromotableNode
|
|
{
|
|
BranchInfo.BranchUProject GameProj;
|
|
public GameLabelPromotableNode(GUBP bp, BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
|
|
: base(InGameProj.GameName, "", bInLabelPromoted)
|
|
{
|
|
GameProj = InGameProj;
|
|
}
|
|
|
|
public static string StaticGetFullName(BranchInfo.BranchUProject InGameProj, bool bInLabelPromoted)
|
|
{
|
|
return LabelPromotableNode.StaticGetFullName(InGameProj.GameName, bInLabelPromoted);
|
|
}
|
|
public override string GameNameIfAnyForTempStorage()
|
|
{
|
|
return GameProj.GameName;
|
|
}
|
|
}
|
|
|
|
public class SharedLabelPromotableNode : LabelPromotableNode
|
|
{
|
|
public SharedLabelPromotableNode(GUBP bp, bool bInLabelPromoted)
|
|
: base("Shared", IsMainBranch(), bInLabelPromoted)
|
|
{
|
|
}
|
|
|
|
public static string StaticGetFullName(bool bInLabelPromoted)
|
|
{
|
|
return LabelPromotableNode.StaticGetFullName("Shared", bInLabelPromoted);
|
|
}
|
|
public static string IsMainBranch()
|
|
{
|
|
string isMain = "";
|
|
if (P4Enabled)
|
|
{
|
|
string CurrentBranch = P4Env.BuildRootP4;
|
|
if (CurrentBranch == "//depot/UE4")
|
|
{
|
|
isMain = "_WithNightlys";
|
|
}
|
|
}
|
|
return isMain;
|
|
}
|
|
}
|
|
|
|
public class WaitForTestShared : AggregateNode
|
|
{
|
|
public WaitForTestShared(GUBP bp)
|
|
{
|
|
}
|
|
public static string StaticGetFullName()
|
|
{
|
|
return "Shared_TestingAggregate";
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName();
|
|
}
|
|
public override int CISFrequencyQuantumShift(GUBP bp)
|
|
{
|
|
return base.CISFrequencyQuantumShift(bp) + 5;
|
|
}
|
|
}
|
|
|
|
public class CookNode : HostPlatformNode
|
|
{
|
|
BranchInfo.BranchUProject GameProj;
|
|
UnrealTargetPlatform TargetPlatform;
|
|
string CookPlatform;
|
|
bool bIsMassive;
|
|
|
|
|
|
public CookNode(GUBP bp, UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, UnrealTargetPlatform InTargetPlatform, string InCookPlatform)
|
|
: base(InHostPlatform)
|
|
{
|
|
GameProj = InGameProj;
|
|
TargetPlatform = InTargetPlatform;
|
|
CookPlatform = InCookPlatform;
|
|
bIsMassive = false;
|
|
AddDependency(EditorAndToolsNode.StaticGetFullName(HostPlatform));
|
|
if (bp.bOrthogonalizeEditorPlatforms)
|
|
{
|
|
if (TargetPlatform != HostPlatform && TargetPlatform != GUBP.GetAltHostPlatform(HostPlatform))
|
|
{
|
|
AddDependency(EditorPlatformNode.StaticGetFullName(HostPlatform, TargetPlatform));
|
|
}
|
|
}
|
|
bool bIsShared = false;
|
|
// is this the "base game" or a non code project?
|
|
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && GameProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
|
|
{
|
|
var Options = InGameProj.Options(HostPlatform);
|
|
bIsMassive = Options.bIsMassive;
|
|
AddDependency(EditorGameNode.StaticGetFullName(HostPlatform, GameProj));
|
|
// add an arc to prevent cooks from running until promotable is labeled
|
|
if (Options.bIsPromotable)
|
|
{
|
|
if (!Options.bSeparateGamePromotion)
|
|
{
|
|
bIsShared = true;
|
|
}
|
|
}
|
|
else if (Options.bTestWithShared)
|
|
{
|
|
bIsShared = true;
|
|
}
|
|
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, GameProj, TargetPlatform));
|
|
}
|
|
else
|
|
{
|
|
bIsShared = true;
|
|
AddPseudodependency(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, TargetPlatform));
|
|
}
|
|
if (bIsShared)
|
|
{
|
|
// add an arc to prevent cooks from running until promotable is labeled
|
|
AddPseudodependency(WaitForTestShared.StaticGetFullName());
|
|
AgentSharingGroup = "SharedCooks" + StaticGetHostPlatformSuffix(HostPlatform);
|
|
|
|
// If the cook fails for the base engine, don't bother trying
|
|
if (InGameProj.GameName != bp.Branch.BaseEngineProject.GameName && bp.HasNode(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, CookPlatform)))
|
|
{
|
|
AddPseudodependency(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, CookPlatform));
|
|
}
|
|
|
|
// If the base cook platform fails, don't bother trying other ones
|
|
string BaseCookedPlatform = Platform.Platforms[HostPlatform].GetCookPlatform(false, false, "");
|
|
if (InGameProj.GameName == bp.Branch.BaseEngineProject.GameName && CookPlatform != BaseCookedPlatform &&
|
|
bp.HasNode(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, BaseCookedPlatform)))
|
|
{
|
|
AddPseudodependency(CookNode.StaticGetFullName(HostPlatform, bp.Branch.BaseEngineProject, BaseCookedPlatform));
|
|
}
|
|
}
|
|
}
|
|
public static string StaticGetFullName(UnrealTargetPlatform InHostPlatform, BranchInfo.BranchUProject InGameProj, string InCookPlatform)
|
|
{
|
|
return InGameProj.GameName + "_" + InCookPlatform + "_Cook" + StaticGetHostPlatformSuffix(InHostPlatform);
|
|
}
|
|
public override string GetFullName()
|
|
{
|
|
return StaticGetFullName(HostPlatform, GameProj, CookPlatform);
|
|
}
|
|
public override string GameNameIfAnyForTempStorage()
|
|
{
|
|
return GameProj.GameName;
|
|
}
|
|
public override int CISFrequencyQuantumShift(GUBP bp)
|
|
{
|
|
return base.CISFrequencyQuantumShift(bp) + 3 + (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)
|
|
{
|
|
AddPseudodependency(SharedLabelPromotableNode.StaticGetFullName(false));
|
|
}
|
|
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;
|
|
var Options = InGameProj.Options(HostPlatform);
|
|
if(!Options.bSeparateGamePromotion)
|
|
{
|
|
AddPseudodependency(SharedLabelPromotableNode.StaticGetFullName(false));
|
|
}
|
|
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" + bp.RocketUBTArgs();
|
|
if (bp.bOrthogonalizeEditorPlatforms)
|
|
{
|
|
AddArgs += " -skipnonhostplatforms";
|
|
}
|
|
|
|
Agenda.AddTargets(
|
|
new string[] { bp.Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].TargetName },
|
|
HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
|
|
foreach (var ProgramTarget in bp.Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithBaseEditor() && ProgramTarget.Rules.SupportsPlatform(HostPlatform))
|
|
{
|
|
Agenda.AddTargets(new string[] { ProgramTarget.TargetName }, HostPlatform, UnrealTargetConfiguration.Development, InAddArgs: AddArgs);
|
|
}
|
|
}
|
|
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 void RemovePseudodependencyFromNode(string Node, string Dep)
|
|
{
|
|
if (!GUBPNodes.ContainsKey(Node))
|
|
{
|
|
throw new AutomationException("Node {0} not found", Node);
|
|
}
|
|
GUBPNodes[Node].RemovePseudodependency(Dep);
|
|
}
|
|
|
|
List<string> GetDependencies(string NodeToDo, bool bFlat = false, bool ECOnly = false)
|
|
{
|
|
var Result = new List<string>();
|
|
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfDependencies)
|
|
{
|
|
bool Usable = GUBPNodes[Node].RunInEC() || !ECOnly;
|
|
if (Usable)
|
|
{
|
|
if (!Result.Contains(Node))
|
|
{
|
|
Result.Add(Node);
|
|
}
|
|
}
|
|
if (bFlat || !Usable)
|
|
{
|
|
foreach (var RNode in GetDependencies(Node, bFlat, ECOnly))
|
|
{
|
|
if (!Result.Contains(RNode))
|
|
{
|
|
Result.Add(RNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
|
|
{
|
|
bool Usable = GUBPNodes[Node].RunInEC() || !ECOnly;
|
|
if (Usable)
|
|
{
|
|
if (!Result.Contains(Node))
|
|
{
|
|
Result.Add(Node);
|
|
}
|
|
}
|
|
if (bFlat || !Usable)
|
|
{
|
|
foreach (var RNode in GetDependencies(Node, bFlat, ECOnly))
|
|
{
|
|
if (!Result.Contains(RNode))
|
|
{
|
|
Result.Add(RNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
List<string> GetECDependencies(string NodeToDo, bool bFlat = false)
|
|
{
|
|
return GetDependencies(NodeToDo, bFlat, true);
|
|
}
|
|
bool NodeDependsOn(string Rootward, string Leafward)
|
|
{
|
|
var Deps = GetDependencies(Leafward, true);
|
|
return Deps.Contains(Rootward);
|
|
}
|
|
|
|
string GetControllingTrigger(string NodeToDo)
|
|
{
|
|
if (GUBPNodesControllingTrigger.ContainsKey(NodeToDo))
|
|
{
|
|
return GUBPNodesControllingTrigger[NodeToDo];
|
|
}
|
|
var Result = "";
|
|
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfDependencies)
|
|
{
|
|
if (!GUBPNodes.ContainsKey(Node))
|
|
{
|
|
throw new AutomationException("Dependency {0} in {1} not found.", Node, NodeToDo);
|
|
}
|
|
bool IsTrigger = GUBPNodes[Node].TriggerNode();
|
|
if (IsTrigger)
|
|
{
|
|
if (Node != Result && !string.IsNullOrEmpty(Result))
|
|
{
|
|
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, Node, Result);
|
|
}
|
|
Result = Node;
|
|
}
|
|
else
|
|
{
|
|
string NewResult = GetControllingTrigger(Node);
|
|
if (!String.IsNullOrEmpty(NewResult))
|
|
{
|
|
if (NewResult != Result && !string.IsNullOrEmpty(Result))
|
|
{
|
|
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, NewResult, Result);
|
|
}
|
|
Result = NewResult;
|
|
}
|
|
}
|
|
}
|
|
foreach (var Node in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
|
|
{
|
|
if (!GUBPNodes.ContainsKey(Node))
|
|
{
|
|
throw new AutomationException("Pseudodependency {0} in {1} not found.", Node, NodeToDo);
|
|
}
|
|
bool IsTrigger = GUBPNodes[Node].TriggerNode();
|
|
if (IsTrigger)
|
|
{
|
|
if (Node != Result && !string.IsNullOrEmpty(Result))
|
|
{
|
|
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, Node, Result);
|
|
}
|
|
Result = Node;
|
|
}
|
|
else
|
|
{
|
|
string NewResult = GetControllingTrigger(Node);
|
|
if (!String.IsNullOrEmpty(NewResult))
|
|
{
|
|
if (NewResult != Result && !string.IsNullOrEmpty(Result))
|
|
{
|
|
throw new AutomationException("Node {0} has two controlling triggers {1} and {2}.", NodeToDo, NewResult, Result);
|
|
}
|
|
Result = NewResult;
|
|
}
|
|
}
|
|
}
|
|
GUBPNodesControllingTrigger.Add(NodeToDo, Result);
|
|
return Result;
|
|
}
|
|
string GetControllingTriggerDotName(string NodeToDo)
|
|
{
|
|
if (GUBPNodesControllingTriggerDotName.ContainsKey(NodeToDo))
|
|
{
|
|
return GUBPNodesControllingTriggerDotName[NodeToDo];
|
|
}
|
|
string Result = "";
|
|
string WorkingNode = NodeToDo;
|
|
while (true)
|
|
{
|
|
string ThisResult = GetControllingTrigger(WorkingNode);
|
|
if (ThisResult == "")
|
|
{
|
|
break;
|
|
}
|
|
if (Result != "")
|
|
{
|
|
Result = "." + Result;
|
|
}
|
|
Result = ThisResult + Result;
|
|
WorkingNode = ThisResult;
|
|
}
|
|
GUBPNodesControllingTriggerDotName.Add(NodeToDo, Result);
|
|
return Result;
|
|
}
|
|
|
|
public string CISFrequencyQuantumShiftString(string NodeToDo)
|
|
{
|
|
string FrequencyString = "";
|
|
int Quantum = GUBPNodes[NodeToDo].DependentCISFrequencyQuantumShift();
|
|
if (Quantum > 0)
|
|
{
|
|
int 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)
|
|
{
|
|
int Result = GUBPNodes[NodeToDo].ComputedDependentCISFrequencyQuantumShift;
|
|
if (Result < 0)
|
|
{
|
|
Result = GUBPNodes[NodeToDo].CISFrequencyQuantumShift(this);
|
|
Result = GetFrequencyForNode(this, GUBPNodes[NodeToDo].GetFullName(), Result);
|
|
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
|
|
{
|
|
Result = Math.Max(ComputeDependentCISFrequencyQuantumShift(Dep), Result);
|
|
}
|
|
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
|
|
{
|
|
Result = Math.Max(ComputeDependentCISFrequencyQuantumShift(Dep), Result);
|
|
}
|
|
if (Result < 0)
|
|
{
|
|
throw new AutomationException("Failed to compute shift.");
|
|
}
|
|
GUBPNodes[NodeToDo].ComputedDependentCISFrequencyQuantumShift = Result;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
bool NodeIsAlreadyComplete(string NodeToDo, bool LocalOnly)
|
|
{
|
|
if (GUBPNodesCompleted.ContainsKey(NodeToDo))
|
|
{
|
|
return GUBPNodesCompleted[NodeToDo];
|
|
}
|
|
string NodeStoreName = StoreName + "-" + GUBPNodes[NodeToDo].GetFullName();
|
|
string GameNameIfAny = GUBPNodes[NodeToDo].GameNameIfAnyForTempStorage();
|
|
bool Result;
|
|
if (LocalOnly)
|
|
{
|
|
Result = LocalTempStorageExists(CmdEnv, NodeStoreName, bQuiet : true);
|
|
}
|
|
else
|
|
{
|
|
Result = TempStorageExists(CmdEnv, NodeStoreName, GameNameIfAny, bQuiet: true);
|
|
if(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", 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)
|
|
{
|
|
var BranchForFrequency = "";
|
|
if (P4Enabled)
|
|
{
|
|
BranchForFrequency = P4Env.BuildRootP4;
|
|
}
|
|
return HackFrequency(bp, BranchForFrequency, 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);
|
|
}
|
|
}
|
|
if (bShowECDependencies)
|
|
{
|
|
foreach (var Dep in GetECDependencies(NodeToDo))
|
|
{
|
|
Log(" {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;
|
|
if (!SubSort && GUBPNodes[NodeToDo].AgentSharingGroup != "")
|
|
{
|
|
if (ExaminedAgentGroups.Contains(GUBPNodes[NodeToDo].AgentSharingGroup))
|
|
{
|
|
bReady = !NonReadyAgentGroups.Contains(GUBPNodes[NodeToDo].AgentSharingGroup);
|
|
bPseudoReady = !NonPeudoReadyAgentGroups.Contains(GUBPNodes[NodeToDo].AgentSharingGroup); //this might not be accurate if bReady==false
|
|
}
|
|
else
|
|
{
|
|
ExaminedAgentGroups.Add(GUBPNodes[NodeToDo].AgentSharingGroup);
|
|
foreach (var ChainNode in SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup])
|
|
{
|
|
foreach (var Dep in GUBPNodes[ChainNode].FullNamesOfDependencies)
|
|
{
|
|
if (!GUBPNodes.ContainsKey(Dep))
|
|
{
|
|
throw new AutomationException("Dependency {0} node found.", Dep);
|
|
}
|
|
if (!SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup].Contains(Dep) && NodesToDo.Contains(Dep))
|
|
{
|
|
bReady = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!bReady)
|
|
{
|
|
NonReadyAgentGroups.Add(GUBPNodes[NodeToDo].AgentSharingGroup);
|
|
break;
|
|
}
|
|
foreach (var Dep in GUBPNodes[ChainNode].FullNamesOfPseudosependencies)
|
|
{
|
|
if (!GUBPNodes.ContainsKey(Dep))
|
|
{
|
|
throw new AutomationException("Pseudodependency {0} node found.", Dep);
|
|
}
|
|
if (!SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup].Contains(Dep) && NodesToDo.Contains(Dep))
|
|
{
|
|
bPseudoReady = false;
|
|
NonPeudoReadyAgentGroups.Add(GUBPNodes[NodeToDo].AgentSharingGroup);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
|
|
{
|
|
if (!GUBPNodes.ContainsKey(Dep))
|
|
{
|
|
throw new AutomationException("Dependency {0} node found.", Dep);
|
|
}
|
|
if (NodesToDo.Contains(Dep))
|
|
{
|
|
bReady = false;
|
|
break;
|
|
}
|
|
}
|
|
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
|
|
{
|
|
if (!GUBPNodes.ContainsKey(Dep))
|
|
{
|
|
throw new AutomationException("Pseudodependency {0} node found.", Dep);
|
|
}
|
|
if (NodesToDo.Contains(Dep))
|
|
{
|
|
bPseudoReady = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
var Priority = GUBPNodes[NodeToDo].Priority();
|
|
|
|
if (bReady && BestNode != "")
|
|
{
|
|
if (String.Compare(GetControllingTriggerDotName(BestNode), GetControllingTriggerDotName(NodeToDo)) < 0) //sorted by controlling trigger
|
|
{
|
|
bReady = false;
|
|
}
|
|
else if (String.Compare(GetControllingTriggerDotName(BestNode), GetControllingTriggerDotName(NodeToDo)) == 0) //sorted by controlling trigger
|
|
{
|
|
if (GUBPNodes[BestNode].IsSticky() && !GUBPNodes[NodeToDo].IsSticky()) //sticky nodes first
|
|
{
|
|
bReady = false;
|
|
}
|
|
else if (GUBPNodes[BestNode].IsSticky() == GUBPNodes[NodeToDo].IsSticky())
|
|
{
|
|
if (BestPseudoReady && !bPseudoReady)
|
|
{
|
|
bReady = false;
|
|
}
|
|
else if (BestPseudoReady == bPseudoReady)
|
|
{
|
|
bool IamLateTrigger = !DoNotConsiderCompletion && GUBPNodes[NodeToDo].TriggerNode() && NodeToDo != ExplicitTrigger && !NodeIsAlreadyComplete(NodeToDo, LocalOnly);
|
|
bool BestIsLateTrigger = !DoNotConsiderCompletion && GUBPNodes[BestNode].TriggerNode() && BestNode != ExplicitTrigger && !NodeIsAlreadyComplete(BestNode, LocalOnly);
|
|
if (BestIsLateTrigger && !IamLateTrigger)
|
|
{
|
|
bReady = false;
|
|
}
|
|
else if (BestIsLateTrigger == IamLateTrigger)
|
|
{
|
|
|
|
if (Priority < BestPriority)
|
|
{
|
|
bReady = false;
|
|
}
|
|
else if (Priority == BestPriority)
|
|
{
|
|
if (BestNode.CompareTo(NodeToDo) < 0)
|
|
{
|
|
bReady = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bReady)
|
|
{
|
|
BestPriority = Priority;
|
|
BestNode = NodeToDo;
|
|
BestPseudoReady = bPseudoReady;
|
|
bProgressMade = true;
|
|
}
|
|
}
|
|
if (bProgressMade)
|
|
{
|
|
if (!SubSort && GUBPNodes[BestNode].AgentSharingGroup != "")
|
|
{
|
|
foreach (var ChainNode in SortedAgentGroupChains[GUBPNodes[BestNode].AgentSharingGroup])
|
|
{
|
|
OrdereredToDo.Add(ChainNode);
|
|
NodesToDo.Remove(ChainNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OrdereredToDo.Add(BestNode);
|
|
NodesToDo.Remove(BestNode);
|
|
}
|
|
}
|
|
|
|
if (!bProgressMade && NodesToDo.Count > 0)
|
|
{
|
|
Log("Cycle in GUBP, could not resolve:");
|
|
foreach (var NodeToDo in NodesToDo)
|
|
{
|
|
string Deps = "";
|
|
if (!SubSort && GUBPNodes[NodeToDo].AgentSharingGroup != "")
|
|
{
|
|
foreach (var ChainNode in SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup])
|
|
{
|
|
foreach (var Dep in GUBPNodes[ChainNode].FullNamesOfDependencies)
|
|
{
|
|
if (!SortedAgentGroupChains[GUBPNodes[NodeToDo].AgentSharingGroup].Contains(Dep) && NodesToDo.Contains(Dep))
|
|
{
|
|
Deps = Deps + Dep + "[" + ChainNode + "->" + GUBPNodes[NodeToDo].AgentSharingGroup + "]" + " ";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
|
|
{
|
|
if (NodesToDo.Contains(Dep))
|
|
{
|
|
Deps = Deps + Dep + " ";
|
|
}
|
|
}
|
|
foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
|
|
{
|
|
if (NodesToDo.Contains(Dep))
|
|
{
|
|
Deps = Deps + Dep + " ";
|
|
}
|
|
}
|
|
Log(" {0} deps: {1}", NodeToDo, Deps);
|
|
}
|
|
throw new AutomationException("Cycle in GUBP");
|
|
}
|
|
}
|
|
if (!SubSort)
|
|
{
|
|
var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
|
|
Log("Took {0}s to sort {1} nodes", BuildDuration / 1000, OrdereredToDo.Count);
|
|
}
|
|
|
|
return OrdereredToDo;
|
|
}
|
|
|
|
string GetJobStepPath(string Dep)
|
|
{
|
|
if (Dep != "Noop" && GUBPNodes[Dep].AgentSharingGroup != "")
|
|
{
|
|
return "jobSteps[" + GUBPNodes[Dep].AgentSharingGroup + "]/jobSteps[" + Dep + "]";
|
|
}
|
|
return "jobSteps[" + Dep + "]";
|
|
}
|
|
string GetJobStep(string ParentPath, string Dep)
|
|
{
|
|
return ParentPath + "/" + GetJobStepPath(Dep);
|
|
}
|
|
|
|
void UpdateNodeHistory(string Node, string CLString)
|
|
{
|
|
if (GUBPNodes[Node].RunInEC() && !GUBPNodes[Node].TriggerNode() && CLString != "")
|
|
{
|
|
string GameNameIfAny = GUBPNodes[Node].GameNameIfAnyForTempStorage();
|
|
string NodeStoreWildCard = StoreName.Replace(CLString, "*") + "-" + GUBPNodes[Node].GetFullName();
|
|
var History = new NodeHistory();
|
|
|
|
History.AllStarted = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + StartedTempStorageSuffix, false, true, GameNameIfAny));
|
|
History.AllSucceeded = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + SucceededTempStorageSuffix, false, true, GameNameIfAny));
|
|
History.AllFailed = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + FailedTempStorageSuffix, false, true, GameNameIfAny));
|
|
|
|
if (History.AllFailed.Count > 0)
|
|
{
|
|
History.LastFailed = History.AllFailed[History.AllFailed.Count - 1];
|
|
}
|
|
if (History.AllSucceeded.Count > 0)
|
|
{
|
|
History.LastSucceeded = History.AllSucceeded[History.AllSucceeded.Count - 1];
|
|
|
|
foreach (var Failed in History.AllFailed)
|
|
{
|
|
if (Failed > History.LastSucceeded)
|
|
{
|
|
History.Failed.Add(Failed);
|
|
History.FailedString = GUBPNode.MergeSpaceStrings(History.FailedString, String.Format("{0}", Failed));
|
|
}
|
|
}
|
|
foreach (var Started in History.AllStarted)
|
|
{
|
|
if (Started > History.LastSucceeded && !History.Failed.Contains(Started))
|
|
{
|
|
History.InProgress.Add(Started);
|
|
History.InProgressString = GUBPNode.MergeSpaceStrings(History.InProgressString, String.Format("{0}", Started));
|
|
}
|
|
}
|
|
}
|
|
if (GUBPNodesHistory.ContainsKey(Node))
|
|
{
|
|
GUBPNodesHistory.Remove(Node);
|
|
}
|
|
GUBPNodesHistory.Add(Node, History);
|
|
}
|
|
}
|
|
|
|
bool HashSetEqual(HashSet<string> A, HashSet<string> B)
|
|
{
|
|
if (A.Count != B.Count)
|
|
{
|
|
return false;
|
|
}
|
|
foreach (var Elem in A)
|
|
{
|
|
if (!B.Contains(Elem))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
foreach (var Elem in B)
|
|
{
|
|
if (!A.Contains(Elem))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int FindLastNonDuplicateFail(string NodeToDo, string CLString)
|
|
{
|
|
var History = GUBPNodesHistory[NodeToDo];
|
|
int Result = P4Env.Changelist;
|
|
|
|
string GameNameIfAny = GUBPNodes[NodeToDo].GameNameIfAnyForTempStorage();
|
|
string NodeStore = StoreName + "-" + GUBPNodes[NodeToDo].GetFullName() + FailedTempStorageSuffix;
|
|
|
|
var BackwardsFails = new List<int>(History.AllFailed);
|
|
BackwardsFails.Add(P4Env.Changelist);
|
|
BackwardsFails.Sort();
|
|
BackwardsFails.Reverse();
|
|
HashSet<string> CurrentErrors = null;
|
|
foreach (var CL in BackwardsFails)
|
|
{
|
|
if (CL > P4Env.Changelist)
|
|
{
|
|
continue;
|
|
}
|
|
if (CL <= History.LastSucceeded)
|
|
{
|
|
break;
|
|
}
|
|
var ThisNodeStore = NodeStore.Replace(CLString, String.Format("{0}", CL));
|
|
DeleteLocalTempStorage(CmdEnv, ThisNodeStore, true); // these all clash locally, which is fine we just retrieve them from shared
|
|
|
|
List<string> Files = null;
|
|
try
|
|
{
|
|
bool WasLocal;
|
|
Files = RetrieveFromTempStorage(CmdEnv, ThisNodeStore, out WasLocal, GameNameIfAny); // this will fail on our CL if we didn't fail or we are just setting up the branch
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
if (Files == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (Files.Count != 1)
|
|
{
|
|
throw new AutomationException("Unexpected number of files for fail record {0}", Files.Count);
|
|
}
|
|
string ErrorFile = Files[0];
|
|
var ThisErrors = ECJobPropsUtils.ErrorsFromProps(ErrorFile);
|
|
if (CurrentErrors == null)
|
|
{
|
|
CurrentErrors = ThisErrors;
|
|
}
|
|
else
|
|
{
|
|
if (CurrentErrors.Count == 0 || !HashSetEqual(CurrentErrors, ThisErrors))
|
|
{
|
|
break;
|
|
}
|
|
Result = CL;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
List<string> GetECPropsForNode(string NodeToDo, string CLString, out string EMails, bool OnlyLateUpdates = false)
|
|
{
|
|
var StartTime = DateTime.UtcNow;
|
|
|
|
var ECProps = new List<string>();
|
|
EMails = "";
|
|
string FailCauserEMails = "";
|
|
string EMailNote = "";
|
|
bool SendSuccessForGreenAfterRed = false;
|
|
int NumPeople = 0;
|
|
if (GUBPNodesHistory.ContainsKey(NodeToDo))
|
|
{
|
|
var History = GUBPNodesHistory[NodeToDo];
|
|
|
|
ECProps.Add(string.Format("LastGreen/{0}={1}", NodeToDo, History.LastSucceeded));
|
|
ECProps.Add(string.Format("RedsSince/{0}={1}", NodeToDo, History.FailedString));
|
|
ECProps.Add(string.Format("InProgress/{0}={1}", NodeToDo, History.InProgressString));
|
|
|
|
|
|
if (History.LastSucceeded > 0 && History.LastSucceeded < P4Env.Changelist)
|
|
{
|
|
int LastNonDuplicateFail = P4Env.Changelist;
|
|
try
|
|
{
|
|
if (OnlyLateUpdates)
|
|
{
|
|
LastNonDuplicateFail = FindLastNonDuplicateFail(NodeToDo, CLString);
|
|
if (LastNonDuplicateFail < P4Env.Changelist)
|
|
{
|
|
Log("*** Red-after-red spam reduction, changed CL {0} to CL {1} because the errors didn't change.", P4Env.Changelist, LastNonDuplicateFail);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
LastNonDuplicateFail = P4Env.Changelist;
|
|
Log(System.Diagnostics.TraceEventType.Warning, "Failed to FindLastNonDuplicateFail.");
|
|
Log(System.Diagnostics.TraceEventType.Warning, LogUtils.FormatException(Ex));
|
|
}
|
|
|
|
var ChangeRecords = GetChanges(History.LastSucceeded, LastNonDuplicateFail, History.LastSucceeded);
|
|
foreach (var Record in ChangeRecords)
|
|
{
|
|
FailCauserEMails = GUBPNode.MergeSpaceStrings(FailCauserEMails, Record.UserEmail);
|
|
}
|
|
if (!String.IsNullOrEmpty(FailCauserEMails))
|
|
{
|
|
NumPeople++;
|
|
foreach (var AChar in FailCauserEMails.ToCharArray())
|
|
{
|
|
if (AChar == ' ')
|
|
{
|
|
NumPeople++;
|
|
}
|
|
}
|
|
if (NumPeople > 50)
|
|
{
|
|
EMailNote = String.Format("This step has been broken for more than 50 changes. It last succeeded at CL {0}. ", History.LastSucceeded);
|
|
}
|
|
}
|
|
}
|
|
else if (History.LastSucceeded <= 0)
|
|
{
|
|
EMailNote = String.Format("This step has been broken for more than a few days, so there is no record of it ever succeeding. ");
|
|
}
|
|
if (EMailNote != "" && !String.IsNullOrEmpty(History.FailedString))
|
|
{
|
|
EMailNote += String.Format("It has failed at CLs {0}. ", History.FailedString);
|
|
}
|
|
if (EMailNote != "" && !String.IsNullOrEmpty(History.InProgressString))
|
|
{
|
|
EMailNote += String.Format("These CLs are being built right now {0}. ", History.InProgressString);
|
|
}
|
|
if (History.LastSucceeded > 0 && History.LastSucceeded < P4Env.Changelist && History.LastFailed > History.LastSucceeded && History.LastFailed < P4Env.Changelist)
|
|
{
|
|
SendSuccessForGreenAfterRed = ParseParam("CIS");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ECProps.Add(string.Format("LastGreen/{0}=0", NodeToDo));
|
|
ECProps.Add(string.Format("RedsSince/{0}=", NodeToDo));
|
|
ECProps.Add(string.Format("InProgress/{0}=", NodeToDo));
|
|
}
|
|
|
|
ECProps.Add(string.Format("FailCausers/{0}={1}", NodeToDo, FailCauserEMails));
|
|
ECProps.Add(string.Format("EmailNotes/{0}={1}", NodeToDo, EMailNote));
|
|
|
|
{
|
|
var AdditonalEmails = "";
|
|
|
|
string Causers = "";
|
|
if (ParseParam("CIS") && !GUBPNodes[NodeToDo].SendSuccessEmail() && !GUBPNodes[NodeToDo].TriggerNode())
|
|
{
|
|
Causers = FailCauserEMails;
|
|
}
|
|
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 (GUBPNodes[NodeToDo].SendSuccessEmail() || SendSuccessForGreenAfterRed)
|
|
{
|
|
ECProps.Add("SendSuccessEmail/" + NodeToDo + "=1");
|
|
}
|
|
else
|
|
{
|
|
ECProps.Add("SendSuccessEmail/" + NodeToDo + "=0");
|
|
}
|
|
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;
|
|
Log("Took {0}s to get P4 history for node {1}", BuildDuration / 1000, NodeToDo);
|
|
|
|
return ECProps;
|
|
}
|
|
|
|
void UpdateECProps(string NodeToDo, string CLString)
|
|
{
|
|
try
|
|
{
|
|
Log("Updating node props for node {0}", NodeToDo);
|
|
string EMails;
|
|
var Props = GetECPropsForNode(NodeToDo, CLString, out EMails, true);
|
|
foreach (var Prop in Props)
|
|
{
|
|
var Parts = Prop.Split("=".ToCharArray());
|
|
RunECTool(String.Format("setProperty \"/myWorkflow/{0}\" \"{1}\"", Parts[0], Parts[1]), true);
|
|
}
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
Log(System.Diagnostics.TraceEventType.Warning, "Failed to UpdateECProps.");
|
|
Log(System.Diagnostics.TraceEventType.Warning, LogUtils.FormatException(Ex));
|
|
}
|
|
}
|
|
|
|
|
|
[Help("Runs one, several or all of the GUBP nodes")]
|
|
[Help(typeof(UE4Build))]
|
|
[Help("NoMac", "Toggle to exclude the Mac host platform, default is Win64+Mac+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("{0} does not look like a CL");
|
|
}
|
|
}
|
|
bPreflightBuild = true;
|
|
}
|
|
|
|
ECProject = ParseParamValue("ECProject");
|
|
if (ECProject == null)
|
|
{
|
|
ECProject = "";
|
|
}
|
|
HostPlatforms = new List<UnrealTargetPlatform>();
|
|
if (!ParseParam("NoPC"))
|
|
{
|
|
HostPlatforms.Add(UnrealTargetPlatform.Win64);
|
|
}
|
|
var BranchForOptions = "";
|
|
if (P4Enabled)
|
|
{
|
|
BranchForOptions = P4Env.BuildRootP4;
|
|
}
|
|
BranchOptions = GetBranchOptions(BranchForOptions);
|
|
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);
|
|
}
|
|
|
|
bBuildRocket = ParseParam("BuildRocket");
|
|
string Sample = ParseParamValue("Sample");
|
|
if (Sample != null)
|
|
{
|
|
Samples = new List<string>(Sample.Split('+'));
|
|
bBuildOnlySamples = true;
|
|
}
|
|
else
|
|
{
|
|
bBuildOnlySamples = ParseParam("OnlySamples");
|
|
}
|
|
bForceIncrementalCompile = ParseParam("ForceIncrementalCompile");
|
|
bool bNoAutomatedTesting = ParseParam("NoAutomatedTesting") || BranchOptions.bNoAutomatedTesting;
|
|
StoreName = ParseParamValue("Store");
|
|
string StoreSuffix = ParseParamValue("StoreSuffix", "");
|
|
|
|
if (bBuildRocket)
|
|
{
|
|
StoreSuffix = StoreSuffix + "-Rkt";
|
|
if (ParseParam("UsePostedRocket"))
|
|
{
|
|
bPostedRocketBuild = true;
|
|
RocketBuild = ParseParamValue("RocketBuild");
|
|
if (RocketBuild == null)
|
|
{
|
|
RocketBuild = FEngineVersionSupport.FromVersionFile(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine\Source\Runtime\Launch\Resources\Version.h")).ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
Log("************************* bBuildRocket: {0}", bBuildRocket);
|
|
|
|
|
|
GUBPNodes = new Dictionary<string, GUBPNode>();
|
|
|
|
Branch = new BranchInfo(HostPlatforms);
|
|
|
|
if (IsBuildMachine || ParseParam("AllPlatforms"))
|
|
{
|
|
ActivePlatforms = new List<UnrealTargetPlatform>();
|
|
foreach (var GameProj in Branch.CodeProjects)
|
|
{
|
|
foreach (var Kind in BranchInfo.MonolithicKinds)
|
|
{
|
|
if (GameProj.Properties.Targets.ContainsKey(Kind))
|
|
{
|
|
var Target = GameProj.Properties.Targets[Kind];
|
|
foreach (var HostPlatform in HostPlatforms)
|
|
{
|
|
var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
|
|
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);
|
|
}
|
|
if (bBuildRocket)
|
|
{
|
|
var FilteredActivePlatforms = new List<UnrealTargetPlatform>();
|
|
foreach (var Plat in ActivePlatforms)
|
|
{
|
|
if (
|
|
(Plat != UnrealTargetPlatform.PS4 || ParseParam("WithPS4")) &&
|
|
Plat != UnrealTargetPlatform.WinRT &&
|
|
Plat != UnrealTargetPlatform.WinRT_ARM &&
|
|
Plat != UnrealTargetPlatform.XboxOne
|
|
)
|
|
{
|
|
FilteredActivePlatforms.Add(Plat);
|
|
}
|
|
}
|
|
ActivePlatforms = FilteredActivePlatforms;
|
|
}
|
|
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));
|
|
AddNode(new RootEditorNode(HostPlatform));
|
|
if (bBuildRocket)
|
|
{
|
|
AddNode(new RootEditorHeadersNode(HostPlatform));
|
|
}
|
|
AddNode(new ToolsNode(HostPlatform));
|
|
AddNode(new InternalToolsNode(HostPlatform));
|
|
if (HostPlatform == UnrealTargetPlatform.Win64)
|
|
{
|
|
AddNode(new ToolsCrossCompileNode(HostPlatform));
|
|
}
|
|
foreach (var ProgramTarget in Branch.BaseEngineProject.Properties.Programs)
|
|
{
|
|
bool bInternalOnly;
|
|
bool SeparateNode;
|
|
bool CrossCompile;
|
|
|
|
if (ProgramTarget.Rules.GUBP_AlwaysBuildWithTools(HostPlatform, GUBP.bBuildRocket, 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, GUBP.bBuildRocket, 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)
|
|
{
|
|
throw new AutomationException("{0} was listed as a codeless project by GUBP_NonCodeProjects_BaseEditorTypeOnly, however it is a code based project.", Codeless.Key);
|
|
}
|
|
else
|
|
{
|
|
NonCodeProjectNames.Add(Codeless.Key, Codeless.Value);
|
|
}
|
|
}
|
|
|
|
var TempNonCodeFormalBuilds = Target.Rules.GUBP_GetNonCodeFormalBuilds_BaseEditorTypeOnly();
|
|
var HostMonos = GetMonolithicPlatformsForUProject(HostPlatform, Branch.BaseEngineProject, true);
|
|
|
|
foreach (var Codeless in TempNonCodeFormalBuilds)
|
|
{
|
|
if (NonCodeProjectNames.ContainsKey(Codeless.Key))
|
|
{
|
|
var PlatList = Codeless.Value;
|
|
var NewPlatList = new List<TargetRules.GUBPFormalBuild>();
|
|
foreach (var PlatPair in PlatList)
|
|
{
|
|
if (HostMonos.Contains(PlatPair.TargetPlatform))
|
|
{
|
|
NewPlatList.Add(PlatPair);
|
|
}
|
|
}
|
|
if (NewPlatList.Count > 0)
|
|
{
|
|
NonCodeFormalBuilds.Add(Codeless.Key, NewPlatList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log(System.Diagnostics.TraceEventType.Information, "{0} was listed as a codeless formal build GUBP_GetNonCodeFormalBuilds_BaseEditorTypeOnly, however it does not exist in this branch.", Codeless.Key);
|
|
}
|
|
}
|
|
}
|
|
|
|
DoASharedPromotable = NumSharedCode > 0 || NonCodeProjectNames.Count > 0 || NonCodeFormalBuilds.Count > 0;
|
|
|
|
if (!DoASharedPromotable && bBuildRocket)
|
|
{
|
|
throw new AutomationException("we were asked to make a rocket build, but this branch does not have a shared promotable.");
|
|
}
|
|
|
|
if (DoASharedPromotable)
|
|
{
|
|
AddNode(new NonUnityTestNode(HostPlatform));
|
|
|
|
var AgentSharingGroup = "Shared_EditorTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
|
|
|
|
var Options = Branch.BaseEngineProject.Options(HostPlatform);
|
|
|
|
if (!Options.bIsPromotable || Options.bSeparateGamePromotion)
|
|
{
|
|
throw new AutomationException("We assume that if we have shared promotable, the base engine is in it.");
|
|
}
|
|
|
|
|
|
if (HostPlatform == UnrealTargetPlatform.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)))
|
|
{
|
|
AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, Branch.BaseEngineProject, Plat));
|
|
if (bBuildRocket)
|
|
{
|
|
AddNode(new GameMonolithicHeadersNode(this, HostPlatform, 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}));
|
|
// we don't want this delayed
|
|
// this would normally wait for the testing phase, we just want to build it right away
|
|
RemovePseudodependencyFromNode(
|
|
CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform),
|
|
WaitForTestShared.StaticGetFullName());
|
|
RemovePseudodependencyFromNode(
|
|
CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform),
|
|
CookNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, CookedPlatform));
|
|
string BuildAgentSharingGroup = "";
|
|
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) continue; //temp hack till mac automated testing works
|
|
var ThisMonoGameTestNodes = new List<string>();
|
|
foreach (var Test in GameTests)
|
|
{
|
|
var TestName = Test.Key + "_" + Plat.ToString();
|
|
ThisMonoGameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, TestName, Test.Value, CookedAgentSharingGroup, false, RequiredPlatforms)));
|
|
}
|
|
if (ThisMonoGameTestNodes.Count > 0)
|
|
{
|
|
GameTestNodes.Add(AddNode(new GameAggregateNode(this, HostPlatform, NonCodeProject, "CookedTests_" + Plat.ToString() + "_" + Kind.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform), ThisMonoGameTestNodes, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if false
|
|
//for now, non-code projects don't do client or server.
|
|
foreach (var ServerPlatform in ServerPlatforms)
|
|
{
|
|
var ServerTarget = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Server];
|
|
foreach (var GamePlatform in GamePlatforms)
|
|
{
|
|
var Target = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Game];
|
|
|
|
foreach (var NonCodeProject in Branch.NonCodeProjects)
|
|
{
|
|
if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) || !NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) ||
|
|
!NonCodeProjectNames[NonCodeProject.GameName].Contains(ServerPlatform) || !NonCodeProjectNames[NonCodeProject.GameName].Contains(GamePlatform) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var ClientServerTests = Target.Rules.GUBP_GetClientServerTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), ServerPlatform, GamePlatform);
|
|
var RequiredPlatforms = new List<UnrealTargetPlatform> { ServerPlatform };
|
|
if (ServerPlatform != GamePlatform)
|
|
{
|
|
RequiredPlatforms.Add(GamePlatform);
|
|
}
|
|
foreach (var Test in ClientServerTests)
|
|
{
|
|
GameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, Test.Key + "_" + GamePlatform.ToString() + "_" + ServerPlatform.ToString(), Test.Value, false, RequiredPlatforms, true)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (GameTestNodes.Count > 0)
|
|
{
|
|
AddNode(new GameAggregateNode(this, HostPlatform, Branch.BaseEngineProject, "AllCookedTests", GameTestNodes));
|
|
}
|
|
}
|
|
|
|
foreach (var CodeProj in Branch.CodeProjects)
|
|
{
|
|
var Options = CodeProj.Options(HostPlatform);
|
|
|
|
if (!Options.bIsPromotable && !Options.bTestWithShared)
|
|
{
|
|
continue; // we skip things that aren't promotable and aren't tested
|
|
}
|
|
var AgentShareName = CodeProj.GameName;
|
|
if (!Options.bSeparateGamePromotion)
|
|
{
|
|
AgentShareName = "Shared";
|
|
}
|
|
|
|
AddNode(new EditorGameNode(this, HostPlatform, CodeProj));
|
|
if (!bNoAutomatedTesting && HostPlatform == UnrealTargetPlatform.Win64) //temp hack till automated testing works on other platforms than Win64
|
|
{
|
|
var EditorTests = CodeProj.Properties.Targets[TargetRules.TargetType.Editor].Rules.GUBP_GetEditorTests_EditorTypeOnly(HostPlatform);
|
|
var EditorTestNodes = new List<string>();
|
|
string AgentSharingGroup = "";
|
|
if (EditorTests.Count > 1)
|
|
{
|
|
AgentSharingGroup = AgentShareName + "_EditorTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
|
|
}
|
|
foreach (var Test in EditorTests)
|
|
{
|
|
EditorTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, Test.Key, Test.Value, AgentSharingGroup)));
|
|
}
|
|
if (EditorTestNodes.Count > 0)
|
|
{
|
|
AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "AllEditorTests", EditorTestNodes, 0.0f));
|
|
}
|
|
}
|
|
|
|
var CookedAgentSharingGroup = AgentShareName + "_CookedTests" + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform);
|
|
//var 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)))
|
|
{
|
|
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)
|
|
{
|
|
// we don't want this delayed
|
|
// this would normally wait for the testing phase, we just want to build it right away
|
|
RemovePseudodependencyFromNode(
|
|
CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform),
|
|
WaitForTestShared.StaticGetFullName());
|
|
RemovePseudodependencyFromNode(
|
|
CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform),
|
|
CookNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, CookedPlatform));
|
|
string BuildAgentSharingGroup = "";
|
|
|
|
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) continue; //temp hack till mac automated testing works
|
|
var GameTests = Target.Rules.GUBP_GetGameTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), Plat);
|
|
var RequiredPlatforms = new List<UnrealTargetPlatform> { Plat };
|
|
var ThisMonoGameTestNodes = new List<string>();
|
|
|
|
foreach (var Test in GameTests)
|
|
{
|
|
var TestNodeName = Test.Key + "_" + Plat.ToString();
|
|
ThisMonoGameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, TestNodeName, Test.Value, CookedAgentSharingGroup, false, RequiredPlatforms)));
|
|
}
|
|
if (ThisMonoGameTestNodes.Count > 0)
|
|
{
|
|
GameTestNodes.Add(AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "CookedTests_" + Plat.ToString() + "_" + Kind.ToString() + HostPlatformNode.StaticGetHostPlatformSuffix(HostPlatform), ThisMonoGameTestNodes, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!bNoAutomatedTesting)
|
|
{
|
|
foreach (var ServerPlatform in ServerPlatforms)
|
|
{
|
|
foreach (var GamePlatform in GamePlatforms)
|
|
{
|
|
if (HostPlatform == UnrealTargetPlatform.Mac) continue; //temp hack till mac automated testing works
|
|
var Target = CodeProj.Properties.Targets[TargetRules.TargetType.Game];
|
|
var ClientServerTests = Target.Rules.GUBP_GetClientServerTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), ServerPlatform, GamePlatform);
|
|
var RequiredPlatforms = new List<UnrealTargetPlatform> { ServerPlatform };
|
|
if (ServerPlatform != GamePlatform)
|
|
{
|
|
RequiredPlatforms.Add(GamePlatform);
|
|
}
|
|
foreach (var Test in ClientServerTests)
|
|
{
|
|
var TestNodeName = Test.Key + "_" + GamePlatform.ToString() + "_" + ServerPlatform.ToString();
|
|
GameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, TestNodeName, Test.Value, CookedAgentSharingGroup, false, RequiredPlatforms)));
|
|
}
|
|
}
|
|
}
|
|
if (GameTestNodes.Count > 0)
|
|
{
|
|
AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "AllCookedTests", GameTestNodes));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int NumSharedAllHosts = 0;
|
|
foreach (var CodeProj in Branch.CodeProjects)
|
|
{
|
|
if (CodeProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
|
|
{
|
|
bool AnySeparate = false;
|
|
var PromotedHosts = new List<UnrealTargetPlatform>();
|
|
|
|
foreach (var HostPlatform in HostPlatforms)
|
|
{
|
|
var Options = CodeProj.Options(HostPlatform);
|
|
AnySeparate = AnySeparate || Options.bSeparateGamePromotion;
|
|
if (Options.bIsPromotable)
|
|
{
|
|
if (!Options.bSeparateGamePromotion)
|
|
{
|
|
NumSharedAllHosts++;
|
|
}
|
|
PromotedHosts.Add(HostPlatform);
|
|
}
|
|
}
|
|
if (PromotedHosts.Count > 0)
|
|
{
|
|
AddNode(new GameAggregatePromotableNode(this, PromotedHosts, CodeProj, 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 WaitForTestShared(this));
|
|
AddNode(new WaitForSharedPromotionUserInput(this, true));
|
|
AddNode(new SharedLabelPromotableNode(this, true));
|
|
}
|
|
foreach (var HostPlatform in HostPlatforms)
|
|
{
|
|
AddCustomNodes(HostPlatform);
|
|
}
|
|
|
|
if (HasNode(ToolsForCompileNode.StaticGetFullName(UnrealTargetPlatform.Win64)))
|
|
{
|
|
if (HasNode(GamePlatformMonolithicsNode.StaticGetFullName(UnrealTargetPlatform.Mac, Branch.BaseEngineProject, UnrealTargetPlatform.IOS)) && HasNode(ToolsNode.StaticGetFullName(UnrealTargetPlatform.Win64)))
|
|
{
|
|
//AddNode(new IOSOnPCTestNode(this)); - Disable IOSOnPCTest until a1011 crash is fixed
|
|
}
|
|
AddNode(new VSExpressTestNode(this));
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
foreach (var NodeToDo in GUBPNodes)
|
|
{
|
|
ComputeDependentCISFrequencyQuantumShift(NodeToDo.Key);
|
|
}
|
|
|
|
if (bCleanLocalTempStorage) // shared temp storage can never be wiped
|
|
{
|
|
DeleteLocalTempStorageManifests(CmdEnv);
|
|
}
|
|
|
|
GUBPNodesControllingTrigger = new Dictionary<string, string>();
|
|
GUBPNodesControllingTriggerDotName = new Dictionary<string, string>();
|
|
|
|
var FullNodeList = new Dictionary<string, string>();
|
|
var FullNodeListSortKey = new Dictionary<string, int>();
|
|
var FullNodeDirectDependencies = new Dictionary<string, string>();
|
|
var FullNodeDependedOnBy = new Dictionary<string, string>();
|
|
var FullNodeDependentPromotions = new Dictionary<string, string>();
|
|
var SeparatePromotables = new List<string>();
|
|
{
|
|
Log("******* {0} GUBP Nodes", GUBPNodes.Count);
|
|
var SortedNodes = TopologicalSort(new HashSet<string>(GUBPNodes.Keys), LocalOnly: true, DoNotConsiderCompletion: true);
|
|
int Count = 0;
|
|
foreach (var Node in SortedNodes)
|
|
{
|
|
string Note = GetControllingTriggerDotName(Node);
|
|
if (Note == "")
|
|
{
|
|
Note = CISFrequencyQuantumShiftString(Node);
|
|
}
|
|
if (Note == "")
|
|
{
|
|
Note = "always";
|
|
}
|
|
if (GUBPNodes[Node].RunInEC())
|
|
{
|
|
var Deps = GetECDependencies(Node);
|
|
string All = "";
|
|
foreach (var Dep in Deps)
|
|
{
|
|
if (All != "")
|
|
{
|
|
All += " ";
|
|
}
|
|
All += Dep;
|
|
}
|
|
Log(" {0}: {1} {2}", Node, Note, All);
|
|
FullNodeList.Add(Node, Note);
|
|
FullNodeDirectDependencies.Add(Node, All);
|
|
FullNodeListSortKey.Add(Node, Count);
|
|
Count++;
|
|
}
|
|
else
|
|
{
|
|
Log(" {0}: {1} [Aggregate]", Node, Note);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bOnlyNode = false;
|
|
bool bRelatedToNode = false;
|
|
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) && bBuildRocket)
|
|
{
|
|
// rocket is the shared promotable plus some other stuff and nothing else
|
|
bRelatedToNode = true;
|
|
NodeSpec = "";
|
|
if(!bBuildOnlySamples)
|
|
{
|
|
NodeSpec = "Rocket_Aggregate+";
|
|
}
|
|
if (!ParseParam("nosamples"))
|
|
{
|
|
NodeSpec = NodeSpec + "Sample_Aggregate";
|
|
}
|
|
if (!ParseParam("RocketValidate"))
|
|
{
|
|
//build a full promotable NodeSpec = SharedAggregatePromotableNode.StaticGetFullName() + "+" + NodeSpec;
|
|
}
|
|
else if (!bFake)
|
|
{
|
|
NodeSpec = "Rocket_MakeBuild+" + NodeSpec;
|
|
}
|
|
}
|
|
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(!bOnlyNode)
|
|
{
|
|
foreach(var NodeToDo in NodesToDo)
|
|
{
|
|
if(GUBPNodes[NodeToDo].IsSeparatePromotable())
|
|
{
|
|
SeparatePromotables.Add(GUBPNodes[NodeToDo].GetFullName());
|
|
List<string> Dependencies = new List<string>();
|
|
Dependencies = GetECDependencies(NodeToDo);
|
|
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))
|
|
{
|
|
GUBPNodes[Dep].DependentPromotions.Add(NodeToDo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach(var NodeToDo in NodesToDo)
|
|
{
|
|
var Deps = GUBPNodes[NodeToDo].DependentPromotions;
|
|
string All = "";
|
|
foreach (var Dep in Deps)
|
|
{
|
|
if (All != "")
|
|
{
|
|
All += " ";
|
|
}
|
|
All += Dep;
|
|
}
|
|
FullNodeDependentPromotions.Add(NodeToDo, All);
|
|
}
|
|
}
|
|
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();
|
|
|
|
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>();
|
|
|
|
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(SharedLabelPromotableNode.StaticGetFullName(false)) && !bGraphSubset)
|
|
{
|
|
RemovePseudodependencyFromNode(NodeToDo, SharedLabelPromotableNode.StaticGetFullName(false));
|
|
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;
|
|
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 (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);
|
|
ECProps.Add(String.Format("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);
|
|
}
|
|
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
|
|
{
|
|
if(GUBPNodes[NodeToDo].IsRocketSample() && bPostedRocketBuild)
|
|
{
|
|
|
|
Log("***** Retrieving Build {0} for {1}", RocketBuild, GUBPNodes[NodeToDo].NodeHostPlatform());
|
|
RetrieveFromPermanentStorage(CmdEnv, "Rocket\\Automated", RocketBuild, GUBPNodes[NodeToDo].NodeHostPlatform());
|
|
}
|
|
Log("***** Building GUBP Node {0} for {1}", NodeToDo, NodeStoreName);
|
|
GUBPNodes[NodeToDo].DoBuild(this);
|
|
}
|
|
if (!GUBPNodes[NodeToDo].IsAggregate())
|
|
{
|
|
StoreToTempStorage(CmdEnv, NodeStoreName, GUBPNodes[NodeToDo].BuildProducts, !bSaveSharedTempStorage, GameNameIfAny, StorageRootIfAny);
|
|
|
|
if (ParseParam("StompCheck"))
|
|
{
|
|
foreach (var Dep in GUBPNodes[NodeToDo].AllDependencies)
|
|
{
|
|
try
|
|
{
|
|
bool WasLocal;
|
|
RetrieveFromTempStorage(CmdEnv, NodeStoreName, out WasLocal, GameNameIfAny, StorageRootIfAny);
|
|
if (!WasLocal)
|
|
{
|
|
throw new AutomationException("Retrieve was not local?");
|
|
}
|
|
|
|
}
|
|
catch(Exception Ex)
|
|
{
|
|
throw new AutomationException("Node {0} stomped Node {1} Ex: {2}", NodeToDo, Dep, LogUtils.FormatException(Ex));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
if (SaveSuccessRecords)
|
|
{
|
|
UpdateNodeHistory(NodeToDo, CLString);
|
|
SaveStatus(NodeToDo, FailedTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny, ParseParamValue("MyJobStepId"));
|
|
UpdateECProps(NodeToDo, CLString);
|
|
}
|
|
|
|
Log("{0}", ExceptionToString(Ex));
|
|
|
|
|
|
if (GUBPNodesHistory.ContainsKey(NodeToDo))
|
|
{
|
|
var History = GUBPNodesHistory[NodeToDo];
|
|
Log("Changes since last green *********************************");
|
|
Log("");
|
|
Log("");
|
|
Log("");
|
|
PrintDetailedChanges(History);
|
|
Log("End changes since last green");
|
|
}
|
|
|
|
string FailInfo = "";
|
|
FailInfo += "********************************* Main log file";
|
|
FailInfo += Environment.NewLine + Environment.NewLine;
|
|
FailInfo += LogUtils.GetLogTail();
|
|
FailInfo += Environment.NewLine + Environment.NewLine + Environment.NewLine;
|
|
|
|
|
|
|
|
string OtherLog = "See logfile for details: '";
|
|
if (FailInfo.Contains(OtherLog))
|
|
{
|
|
string LogFile = FailInfo.Substring(FailInfo.IndexOf(OtherLog) + OtherLog.Length);
|
|
if (LogFile.Contains("'"))
|
|
{
|
|
LogFile = CombinePaths(CmdEnv.LogFolder, LogFile.Substring(0, LogFile.IndexOf("'")));
|
|
if (FileExists_NoExceptions(LogFile))
|
|
{
|
|
FailInfo += "********************************* Sub log file " + LogFile;
|
|
FailInfo += Environment.NewLine + Environment.NewLine;
|
|
|
|
FailInfo += LogUtils.GetLogTail(LogFile);
|
|
FailInfo += Environment.NewLine + Environment.NewLine + Environment.NewLine;
|
|
}
|
|
}
|
|
}
|
|
|
|
string Filename = CombinePaths(CmdEnv.LogFolder, "LogTailsAndChanges.log");
|
|
WriteAllText(Filename, FailInfo);
|
|
|
|
throw(Ex);
|
|
}
|
|
if (SaveSuccessRecords)
|
|
{
|
|
UpdateNodeHistory(NodeToDo, CLString);
|
|
SaveStatus(NodeToDo, SucceededTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny);
|
|
UpdateECProps(NodeToDo, CLString);
|
|
}
|
|
}
|
|
foreach (var Product in GUBPNodes[NodeToDo].BuildProducts)
|
|
{
|
|
if (BuildProductToNodeMap.ContainsKey(Product))
|
|
{
|
|
throw new AutomationException("Overlapping build product: {0} and {1} both produce {2}", BuildProductToNodeMap[Product], NodeToDo, Product);
|
|
}
|
|
BuildProductToNodeMap.Add(Product, NodeToDo);
|
|
}
|
|
}
|
|
|
|
PrintRunTime();
|
|
}
|
|
string StartedTempStorageSuffix = "_Started";
|
|
string FailedTempStorageSuffix = "_Failed";
|
|
string SucceededTempStorageSuffix = "_Succeeded";
|
|
}
|