You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
========================== MAJOR FEATURES + CHANGES ========================== Change 2959679 on 2016/04/28 by Ben.Marsh UGS: Show the original author for changes with a #ROBOMERGE-AUTHOR tag. Change 2959695 on 2016/04/28 by Ben.Marsh UGS: Only filter out changes from by buildmachine that contain the string "CIS Counter". Change 2960798 on 2016/04/29 by Ben.Marsh Remove C++ version of ParallelExecutor. Now implemented in C# as part of UAT. Change 2960928 on 2016/04/29 by Ben.Marsh UGS: Change filter for buildmachine changes to only include rebuilt lightmaps. Change 2963214 on 2016/05/02 by Ben.Marsh BuildGraph: Allow specifying optional dependencies for a node, indicating that the build products from an upstream node are desired, but should not block the node from running. Change 2964454 on 2016/05/03 by Ben.Marsh Change PostBuildInfoTool to PostBadgeStatus, and add position-independent argument parsing. Change 2964533 on 2016/05/03 by Ben.Marsh BuildGraph: Add the ability to generate summary badges from BuildGraph scripts, which can be pushed into a separate database for consumption by UGS. Change 2964852 on 2016/05/03 by Ben.Marsh BuildGraph: Add a task which can submit a set of files to Perforce, optionally creating and using a different workspace to do so. Change 2966856 on 2016/05/04 by Ben.Marsh EC: Allow specifying a filter for the changes considered when looking for the most recent change. Allows filtering out content changes for UGS builds, code-only builds, etc... Change 2966867 on 2016/05/04 by Ben.Marsh EC: Restore code to always set time in CIS state; we never want large builds to trigger off their defined interval. Change 2967504 on 2016/05/05 by Ben.Marsh UAT: Make sure the intermediate directory exists before writing out the list of changes in StreamCopyDescription. Change 2967778 on 2016/05/05 by Ben.Marsh UAT: Detect the P4 environment by querying Perforce for the setting of P4PORT, rather than assuming it's set in an environment variable. Windows stores this setting in the registry rather than the environment, but it's also valid to be set via P4CONFIG. Change 2967815 on 2016/05/05 by Ben.Marsh EC: Copy the initial resource pool setting from the stream settings into an EC property Change 2967873 on 2016/05/05 by Ben.Marsh EC: Allow stream settings to be stored directly in /GUBP_V5/Streams/ rather than having to be in a child property sheet. Change 2969294 on 2016/05/06 by Ben.Marsh EC: Extend ConformResources command to allow updating the pools that resources are assigned to, and to limit the number of machines which are syncing at once. Also added new EC procedure to allow specifying these arguments. Change 2969371 on 2016/05/06 by Ben.Marsh EC: Allow overriding the stream and workspace identifier synced by the builders. Overriding the stream allows syncing a narrower view of files (eg. Dev-Main vs Main), and overriding the workspace identifier allows sharing a workspace between two streams. Change 2970623 on 2016/05/09 by Ben.Marsh UAT: Prevent Ctrl-C handler delegate from being garbage collected and failing to be triggered. Change 2970627 on 2016/05/09 by Ben.Marsh UAT: Don't limit the list of valid target platforms specified on the command line to just those that we have initialized. Ignoring the platform if the SDK is not installed is never what the user wants. Change 2972140 on 2016/05/10 by Ben.Marsh Change 'Engine, Tools and Monolithics' to include QAGame and Template editors, but exclude everything downstream of a trigger. #lockdown Nick.Penwarden [CL 2972146 by Ben Marsh in Main branch]
340 lines
12 KiB
C#
340 lines
12 KiB
C#
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using UnrealBuildTool;
|
|
|
|
namespace AutomationTool
|
|
{
|
|
/// <summary>
|
|
/// Local P4 environment settings
|
|
/// </summary>
|
|
class LocalP4Environment : P4Environment
|
|
{
|
|
internal LocalP4Environment(P4Connection Connection, CommandEnvironment CmdEnv)
|
|
: base(Connection, CmdEnv)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the environment. Tries to autodetect all source control settings.
|
|
/// </summary>
|
|
/// <param name="CompilationEnv">Compilation environment</param>
|
|
protected override void InitEnvironment(P4Connection Connection, CommandEnvironment CmdEnv)
|
|
{
|
|
var HostName = Environment.MachineName.ToLower();
|
|
var P4PortEnv = DetectP4Port();
|
|
|
|
var UserName = CommandUtils.GetEnvVar(EnvVarNames.User);
|
|
if (String.IsNullOrEmpty(UserName))
|
|
{
|
|
UserName = DetectUserName(Connection);
|
|
}
|
|
|
|
var CommandLineClient = CommandUtils.GetEnvVar(EnvVarNames.Client);
|
|
P4ClientInfo ThisClient = null;
|
|
if (String.IsNullOrEmpty(CommandLineClient) == false)
|
|
{
|
|
ThisClient = Connection.GetClientInfo(CommandLineClient);
|
|
if (ThisClient == null)
|
|
{
|
|
throw new AutomationException("Unable to find client {0}", CommandLineClient);
|
|
}
|
|
if (String.Compare(ThisClient.Owner, UserName, true) != 0)
|
|
{
|
|
throw new AutomationException("Client specified with {0}={1} has a different owner then the detected user name (has: {2}, expected: {3})",
|
|
EnvVarNames.Client, CommandLineClient, ThisClient.Owner, UserName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ThisClient = DetectClient(Connection, UserName, HostName, CmdEnv.UATExe);
|
|
}
|
|
|
|
Log.TraceInformation("Using user {0} clientspec {1} {2}", UserName, ThisClient.Name, ThisClient.RootPath);
|
|
Environment.SetEnvironmentVariable("P4CLIENT", ThisClient.Name);
|
|
|
|
string BuildRootPath;
|
|
string ClientRootPath;
|
|
DetectRootPaths(Connection, CmdEnv.LocalRoot, ThisClient, out BuildRootPath, out ClientRootPath);
|
|
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.P4Port, P4PortEnv);
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.User, UserName);
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.Client, ThisClient.Name);
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.BuildRootP4, BuildRootPath);
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.ClientRoot, ClientRootPath);
|
|
|
|
var CLString = CommandUtils.GetEnvVar(EnvVarNames.Changelist, null);
|
|
if (String.IsNullOrEmpty(CLString) && CommandUtils.P4CLRequired)
|
|
{
|
|
CLString = DetectCurrentCL(Connection, ClientRootPath);
|
|
}
|
|
if (!String.IsNullOrEmpty(CLString))
|
|
{
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.Changelist, CLString);
|
|
}
|
|
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.LabelToSync, "");
|
|
CommandUtils.ConditionallySetEnvVar("P4USER", UserName);
|
|
CommandUtils.ConditionallySetEnvVar("P4CLIENT", ThisClient.Name);
|
|
|
|
var P4Password = Environment.GetEnvironmentVariable(EnvVarNames.P4Password);
|
|
if (!String.IsNullOrEmpty(P4Password))
|
|
{
|
|
CommandUtils.ConditionallySetEnvVar("P4PASSWD", P4Password);
|
|
}
|
|
|
|
SetBuildRootEscaped();
|
|
|
|
base.InitEnvironment(Connection, CmdEnv);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the escaped build root environment variable. If the build root is not set, UAT's location UE4 root will be used.
|
|
/// </summary>
|
|
private void SetBuildRootEscaped()
|
|
{
|
|
var BuildRoot = CommandUtils.GetEnvVar(EnvVarNames.BuildRootP4);
|
|
if (String.IsNullOrEmpty(BuildRoot))
|
|
{
|
|
throw new AutomationException("Build root is empty");
|
|
}
|
|
BuildRoot = CommandUtils.EscapePath(BuildRoot);
|
|
CommandUtils.ConditionallySetEnvVar(EnvVarNames.BuildRootEscaped, BuildRoot);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects the current changelist the workspace is synced to.
|
|
/// </summary>
|
|
/// <param name="ClientRootPath">Workspace path.</param>
|
|
/// <returns>Changelist number as a string.</returns>
|
|
private static string DetectCurrentCL(P4Connection Connection, string ClientRootPath)
|
|
{
|
|
CommandUtils.LogVerbose("uebp_CL not set, detecting 'have' CL...");
|
|
|
|
// Retrieve the current changelist
|
|
var P4Result = Connection.P4("changes -m 1 " + CommandUtils.CombinePaths(PathSeparator.Depot, ClientRootPath, "/...#have"), AllowSpew: false);
|
|
var CLTokens = P4Result.Output.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
var CLString = CLTokens[1];
|
|
var CL = Int32.Parse(CLString);
|
|
if (CLString != CL.ToString())
|
|
{
|
|
throw new AutomationException("Failed to retrieve current changelist.");
|
|
}
|
|
return CLString;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects root paths for the specified client.
|
|
/// </summary>
|
|
/// <param name="UATLocation">AutomationTool.exe location</param>
|
|
/// <param name="ThisClient">Client to detect the root paths for</param>
|
|
/// <param name="BuildRootPath">Build root</param>
|
|
/// <param name="LocalRootPath">Local root</param>
|
|
/// <param name="ClientRootPath">Client root</param>
|
|
private static void DetectRootPaths(P4Connection Connection, string LocalRootPath, P4ClientInfo ThisClient, out string BuildRootPath, out string ClientRootPath)
|
|
{
|
|
if(!String.IsNullOrEmpty(ThisClient.Stream))
|
|
{
|
|
BuildRootPath = ThisClient.Stream;
|
|
ClientRootPath = String.Format("//{0}", ThisClient.Name);
|
|
}
|
|
else
|
|
{
|
|
// Figure out the build root
|
|
string KnownFilePathFromRoot = CommandEnvironment.KnownFileRelativeToRoot;
|
|
string KnownLocalPath = CommandUtils.MakePathSafeToUseWithCommandLine(CommandUtils.CombinePaths(PathSeparator.Slash, LocalRootPath, KnownFilePathFromRoot));
|
|
ProcessResult P4Result = Connection.P4(String.Format("files -m 1 {0}", KnownLocalPath), AllowSpew: false);
|
|
|
|
string KnownFileDepotMapping = P4Result.Output;
|
|
|
|
// Get the build root
|
|
Log.TraceVerbose("Looking for {0} in {1}", KnownFilePathFromRoot, KnownFileDepotMapping);
|
|
int EndIdx = KnownFileDepotMapping.IndexOf(KnownFilePathFromRoot, StringComparison.CurrentCultureIgnoreCase);
|
|
if (EndIdx < 0)
|
|
{
|
|
EndIdx = KnownFileDepotMapping.IndexOf(CommandUtils.ConvertSeparators(PathSeparator.Slash, KnownFilePathFromRoot), StringComparison.CurrentCultureIgnoreCase);
|
|
}
|
|
// Get the root path without the trailing path separator
|
|
BuildRootPath = KnownFileDepotMapping.Substring(0, EndIdx - 1);
|
|
|
|
// Get the client root
|
|
if (LocalRootPath.StartsWith(CommandUtils.CombinePaths(PathSeparator.Slash, ThisClient.RootPath, "/"), StringComparison.InvariantCultureIgnoreCase) || LocalRootPath == CommandUtils.CombinePaths(PathSeparator.Slash, ThisClient.RootPath))
|
|
{
|
|
ClientRootPath = CommandUtils.CombinePaths(PathSeparator.Depot, String.Format("//{0}/", ThisClient.Name), LocalRootPath.Substring(ThisClient.RootPath.Length));
|
|
}
|
|
else
|
|
{
|
|
throw new AutomationException("LocalRootPath ({0}) does not start with the client root path ({1})", LocalRootPath, ThisClient.RootPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects a workspace given the current user name, host name and depot path.
|
|
/// </summary>
|
|
/// <param name="UserName">User name</param>
|
|
/// <param name="HostName">Host</param>
|
|
/// <param name="UATLocation">Path to UAT exe, this will be checked agains the root path.</param>
|
|
/// <returns>Client to use.</returns>
|
|
private static P4ClientInfo DetectClient(P4Connection Connection, string UserName, string HostName, string UATLocation)
|
|
{
|
|
CommandUtils.LogVerbose("uebp_CLIENT not set, detecting current client...");
|
|
|
|
var MatchingClients = new List<P4ClientInfo>();
|
|
P4ClientInfo[] P4Clients = Connection.GetClientsForUser(UserName, UATLocation);
|
|
foreach (var Client in P4Clients)
|
|
{
|
|
if (!String.IsNullOrEmpty(Client.Host) && String.Compare(Client.Host, HostName, true) != 0)
|
|
{
|
|
Log.TraceInformation("Rejecting client because of different Host {0} \"{1}\" != \"{2}\"", Client.Name, Client.Host, HostName);
|
|
continue;
|
|
}
|
|
|
|
MatchingClients.Add(Client);
|
|
}
|
|
|
|
P4ClientInfo ClientToUse = null;
|
|
if (MatchingClients.Count == 0)
|
|
{
|
|
throw new AutomationException("No matching clientspecs found!");
|
|
}
|
|
else if (MatchingClients.Count == 1)
|
|
{
|
|
ClientToUse = MatchingClients[0];
|
|
}
|
|
else
|
|
{
|
|
// We may have empty host clients here, so pick the first non-empty one if possible
|
|
foreach (var Client in MatchingClients)
|
|
{
|
|
if (!String.IsNullOrEmpty(Client.Host) && String.Compare(Client.Host, HostName, true) == 0)
|
|
{
|
|
ClientToUse = Client;
|
|
break;
|
|
}
|
|
}
|
|
if (ClientToUse == null)
|
|
{
|
|
Log.TraceWarning("{0} clients found that match the current host and root path. The most recently accessed client will be used.", MatchingClients.Count);
|
|
ClientToUse = GetMostRecentClient(MatchingClients);
|
|
}
|
|
}
|
|
|
|
return ClientToUse;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given a list of clients with the same owner and root path, tries to find the most recently accessed one.
|
|
/// </summary>
|
|
/// <param name="Clients">List of clients with the same owner and path.</param>
|
|
/// <returns>The most recent client from the list.</returns>
|
|
private static P4ClientInfo GetMostRecentClient(List<P4ClientInfo> Clients)
|
|
{
|
|
Log.TraceVerbose("Detecting the most recent client.");
|
|
P4ClientInfo MostRecentClient = null;
|
|
var MostRecentAccessTime = DateTime.MinValue;
|
|
foreach (var ClientInfo in Clients)
|
|
{
|
|
if (ClientInfo.Access.Ticks > MostRecentAccessTime.Ticks)
|
|
{
|
|
MostRecentAccessTime = ClientInfo.Access;
|
|
MostRecentClient = ClientInfo;
|
|
}
|
|
}
|
|
if (MostRecentClient == null)
|
|
{
|
|
throw new AutomationException("Failed to determine the most recent client in {0}", Clients[0].RootPath);
|
|
}
|
|
return MostRecentClient;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects current user name.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private static string DetectUserName(P4Connection Connection)
|
|
{
|
|
var UserName = String.Empty;
|
|
var P4Result = Connection.P4("info", AllowSpew: false);
|
|
if (P4Result.ExitCode != 0)
|
|
{
|
|
throw new AutomationException("Perforce command failed: {0}. Please make sure your P4PORT or {1} is set properly.", P4Result.Output, EnvVarNames.P4Port);
|
|
}
|
|
|
|
// Retrieve the P4 user name
|
|
var Tags = Connection.ParseTaggedP4Output(P4Result.Output);
|
|
Tags.TryGetValue("User name", out UserName);
|
|
|
|
if (String.IsNullOrEmpty(UserName))
|
|
{
|
|
UserName = Environment.GetEnvironmentVariable(EnvVarNames.User);
|
|
|
|
if (!String.IsNullOrEmpty(UserName))
|
|
{
|
|
Log.TraceWarning("Unable to retrieve perforce user name. Trying to fall back to {0} which is set to {1}.", EnvVarNames.User, UserName);
|
|
}
|
|
else
|
|
{
|
|
throw new AutomationException("Failed to retrieve user name.");
|
|
}
|
|
}
|
|
|
|
Environment.SetEnvironmentVariable("P4USER", UserName);
|
|
|
|
return UserName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to detect source control server address from environment variables.
|
|
/// </summary>
|
|
/// <returns>Source control server address.</returns>
|
|
private static string DetectP4Port()
|
|
{
|
|
string P4PortEnv = Environment.GetEnvironmentVariable("P4PORT");
|
|
if(String.IsNullOrEmpty(P4PortEnv))
|
|
{
|
|
// If it's not set, spawn Perforce to get the current server port setting
|
|
ProcessResult Result = CommandUtils.Run(HostPlatform.Current.P4Exe, "set P4PORT", null, CommandUtils.ERunOptions.NoLoggingOfRunCommand);
|
|
if (Result.ExitCode == 0)
|
|
{
|
|
const string KeyName = "P4PORT=";
|
|
if (Result.Output.StartsWith(KeyName))
|
|
{
|
|
int LastIdx = Result.Output.IndexOfAny(new char[] { ' ', '\n' });
|
|
if (LastIdx == -1)
|
|
{
|
|
LastIdx = Result.Output.Length;
|
|
}
|
|
P4PortEnv = Result.Output.Substring(KeyName.Length, LastIdx - KeyName.Length);
|
|
}
|
|
}
|
|
|
|
// Otherwise fallback to the uebp variables, or the default
|
|
if(String.IsNullOrEmpty(P4PortEnv))
|
|
{
|
|
// Try to read the P4PORT environment and check if it is set correctly
|
|
P4PortEnv = Environment.GetEnvironmentVariable(EnvVarNames.P4Port);
|
|
|
|
// If not, try to fallback to Mapping.P4Port and set this as P4PORT before continueing
|
|
if (!String.IsNullOrEmpty(P4PortEnv))
|
|
{
|
|
Log.TraceWarning("P4PORT is not set. Falling back to {0} which is set to {1}.", EnvVarNames.P4Port, P4PortEnv);
|
|
}
|
|
else
|
|
{
|
|
// If that fails as well, we just give it a shot with perforce:1666 and hope that this works
|
|
Log.TraceWarning("P4PORT is not set. Trying to fallback to perforce:1666");
|
|
P4PortEnv = "perforce:1666";
|
|
}
|
|
}
|
|
|
|
Environment.SetEnvironmentVariable("P4PORT", P4PortEnv);
|
|
}
|
|
return P4PortEnv;
|
|
}
|
|
}
|
|
}
|