2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 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
{
2014-04-02 18:09:23 -04:00
internal LocalP4Environment ( P4Connection Connection , CommandEnvironment CmdEnv )
: base ( Connection , CmdEnv )
2014-03-14 14:13:41 -04:00
{
}
/// <summary>
/// Initializes the environment. Tries to autodetect all source control settings.
/// </summary>
/// <param name="CompilationEnv">Compilation environment</param>
2014-04-02 18:09:23 -04:00
protected override void InitEnvironment ( P4Connection Connection , CommandEnvironment CmdEnv )
2014-03-14 14:13:41 -04:00
{
var HostName = Environment . MachineName . ToLower ( ) ;
var P4PortEnv = Environment . GetEnvironmentVariable ( "P4PORT" ) ;
if ( String . IsNullOrEmpty ( P4PortEnv ) )
{
P4PortEnv = DetectP4Port ( ) ;
}
var UserName = CommandUtils . GetEnvVar ( EnvVarNames . User ) ;
if ( String . IsNullOrEmpty ( UserName ) )
{
2014-04-02 18:09:23 -04:00
UserName = DetectUserName ( Connection ) ;
2014-03-14 14:13:41 -04:00
}
var CommandLineClient = CommandUtils . GetEnvVar ( EnvVarNames . Client ) ;
P4ClientInfo ThisClient = null ;
if ( String . IsNullOrEmpty ( CommandLineClient ) = = false )
{
2014-04-02 18:09:23 -04:00
ThisClient = Connection . GetClientInfo ( CommandLineClient ) ;
2014-03-14 14:13:41 -04:00
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
{
2014-04-02 18:09:23 -04:00
ThisClient = DetectClient ( Connection , UserName , HostName , CmdEnv . UATExe ) ;
2014-03-14 14:13:41 -04:00
}
Log . TraceInformation ( "Using user {0} clientspec {1} {2}" , UserName , ThisClient . Name , ThisClient . RootPath ) ;
Environment . SetEnvironmentVariable ( "P4CLIENT" , ThisClient . Name ) ;
string BuildRootPath ;
string ClientRootPath ;
2014-04-02 18:09:23 -04:00
DetectRootPaths ( Connection , CmdEnv . LocalRoot , ThisClient , out BuildRootPath , out ClientRootPath ) ;
2014-03-14 14:13:41 -04:00
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 ) ;
2014-04-23 20:07:00 -04:00
var CLString = CommandUtils . GetEnvVar ( EnvVarNames . Changelist , null ) ;
2014-04-25 13:15:17 -04:00
if ( String . IsNullOrEmpty ( CLString ) & & CommandUtils . P4CLRequired )
2014-04-02 18:09:23 -04:00
{
2014-04-23 17:27:31 -04:00
CLString = DetectCurrentCL ( Connection , ClientRootPath ) ;
2014-04-02 18:09:23 -04:00
}
2014-04-23 20:07:00 -04:00
if ( ! String . IsNullOrEmpty ( CLString ) )
{
CommandUtils . ConditionallySetEnvVar ( EnvVarNames . Changelist , CLString ) ;
}
2014-03-14 14:13:41 -04:00
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 ( ) ;
2014-04-02 18:09:23 -04:00
base . InitEnvironment ( Connection , CmdEnv ) ;
2014-03-14 14:13:41 -04:00
}
/// <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" ) ;
}
2014-04-23 18:28:28 -04:00
BuildRoot = CommandUtils . EscapePath ( BuildRoot ) ;
2014-03-14 14:13:41 -04:00
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>
2014-04-02 18:09:23 -04:00
private static string DetectCurrentCL ( P4Connection Connection , string ClientRootPath )
2014-03-14 14:13:41 -04:00
{
2014-04-23 20:07:00 -04:00
CommandUtils . Log ( "uebp_CL not set, detecting 'have' CL..." ) ;
2014-03-14 14:13:41 -04:00
// Retrieve the current changelist
2014-04-02 18:09:23 -04:00
var P4Result = Connection . P4 ( "changes -m 1 " + CommandUtils . CombinePaths ( PathSeparator . Depot , ClientRootPath , "/...#have" ) , AllowSpew : false ) ;
2014-03-14 14:13:41 -04:00
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>
2014-04-02 18:09:23 -04:00
private static void DetectRootPaths ( P4Connection Connection , string LocalRootPath , P4ClientInfo ThisClient , out string BuildRootPath , out string ClientRootPath )
2014-03-14 14:13:41 -04:00
{
// Figure out the build root
string KnownFilePathFromRoot = CommandEnvironment . KnownFileRelativeToRoot ;
2014-04-02 18:09:23 -04:00
string KnownLocalPath = CommandUtils . MakePathSafeToUseWithCommandLine ( CommandUtils . CombinePaths ( PathSeparator . Slash , LocalRootPath , KnownFilePathFromRoot ) ) ;
ProcessResult P4Result = Connection . P4 ( String . Format ( "files -m 1 {0}" , KnownLocalPath ) , AllowSpew : false ) ;
2014-03-14 14:13:41 -04:00
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 = = 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>
2014-04-02 18:09:23 -04:00
private static P4ClientInfo DetectClient ( P4Connection Connection , string UserName , string HostName , string UATLocation )
2014-03-14 14:13:41 -04:00
{
2014-04-23 20:07:00 -04:00
CommandUtils . Log ( "uebp_CLIENT not set, detecting current client..." ) ;
2014-03-14 14:13:41 -04:00
var MatchingClients = new List < P4ClientInfo > ( ) ;
2014-04-23 20:07:00 -04:00
P4ClientInfo [ ] P4Clients = Connection . GetClientsForUser ( UserName , UATLocation ) ;
2014-03-14 14:13:41 -04:00
foreach ( var Client in P4Clients )
{
2014-05-05 12:11:00 -04:00
if ( ! String . IsNullOrEmpty ( Client . Host ) & & String . Compare ( Client . Host , HostName , true ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
2014-05-05 12:11:00 -04:00
Log . TraceInformation ( "Rejecting client because of different Host {0} \"{1}\" != \"{2}\"" , Client . Name , Client . Host , HostName ) ;
2014-03-14 14:13:41 -04:00
continue ;
}
2014-04-23 20:07:00 -04:00
MatchingClients . Add ( Client ) ;
2014-03-14 14:13:41 -04:00
}
P4ClientInfo ClientToUse = null ;
if ( MatchingClients . Count = = 0 )
{
throw new AutomationException ( "No matching clientspecs found!" ) ;
}
else if ( MatchingClients . Count = = 1 )
{
ClientToUse = MatchingClients [ 0 ] ;
}
else
{
2014-05-05 12:11:00 -04:00
// 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 ) ;
}
2014-03-14 14:13:41 -04:00
}
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>
2014-04-02 18:09:23 -04:00
private static string DetectUserName ( P4Connection Connection )
2014-03-14 14:13:41 -04:00
{
var UserName = String . Empty ;
2014-04-02 18:09:23 -04:00
var P4Result = Connection . P4 ( "info" , AllowSpew : false ) ;
2014-03-14 14:13:41 -04:00
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
2014-04-02 18:09:23 -04:00
var Tags = Connection . ParseTaggedP4Output ( P4Result . Output ) ;
2014-03-14 14:13:41 -04:00
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 ( )
{
// Try to read the P4PORT environment and check if it is set correctly
var 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 ;
}
}
}