2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-06-04 11:01:22 -04:00
# include "UnrealSync.h"
# include "P4Env.h"
# include "ProcessHelper.h"
2014-06-17 14:06:07 -04:00
# include "Regex.h"
2015-04-09 04:40:26 -04:00
# include "Map.h"
# include "JsonSerializer.h"
# include "JsonObject.h"
# include "JsonReader.h"
# include "SEditableTextBox.h"
# include "SGridPanel.h"
DECLARE_LOG_CATEGORY_CLASS ( LogP4Env , Log , All ) ;
2014-07-18 14:17:23 -04:00
/**
* Parses param from command line .
*
* @ param Value Output value .
* @ param CommandLine Command line to parse .
* @ param ParamName Param name to parse .
*
* @ returns True if found , false otherwise .
*/
2015-04-09 04:40:26 -04:00
bool GetCommandLineParam ( FString & Value , const TCHAR * CommandLine , EP4ParamType Type )
2014-06-04 11:01:22 -04:00
{
2015-04-09 04:40:26 -04:00
return FParse : : Value ( CommandLine , * ( FP4Env : : GetParamName ( Type ) + " = " ) , Value ) ;
2014-07-18 14:17:23 -04:00
}
2014-06-04 11:01:22 -04:00
2015-04-09 04:40:26 -04:00
/**
* Cache class for file settings .
*/
class FSettingsCache
{
public :
/**
* Instance getter .
*/
static FSettingsCache & Get ( )
{
static FSettingsCache Cache ;
return Cache ;
}
/**
* Gets setting of given type .
*
* @ param Value Output parameter . If succeeded this parameter will be overwritten with setting value .
* @ param Type Type to look for .
*
* @ returns True if setting was found . False otherwise .
*/
bool GetSetting ( FString & Value , EP4ParamType Type )
{
auto * ParamPtr = Settings . Find ( FP4Env : : GetParamName ( Type ) ) ;
if ( ParamPtr = = nullptr )
{
return false ;
}
Value = * ParamPtr ;
return true ;
}
/**
* Sets setting of given type to given value .
*
* @ param Type Type of the setting .
* @ param Value Value of the setting .
*/
void SetSetting ( EP4ParamType Type , const FString & Value )
{
auto * ParamPtr = Settings . Find ( FP4Env : : GetParamName ( Type ) ) ;
if ( ParamPtr ! = nullptr )
{
* ParamPtr = Value ;
}
Settings . Add ( FP4Env : : GetParamName ( Type ) , Value ) ;
}
/**
* Saves setting to file .
*/
void Save ( )
{
TSharedRef < FJsonObject > Object ( new FJsonObject ( ) ) ;
for ( const auto & Setting : Settings )
{
if ( ! Setting . Value . IsEmpty ( ) )
{
Object - > SetStringField ( Setting . Key , Setting . Value ) ;
}
}
FString Buffer ;
TSharedRef < TJsonWriter < > > Writer = TJsonWriterFactory < > : : Create ( & Buffer ) ;
if ( ! FJsonSerializer : : Serialize ( Object , Writer ) )
{
UE_LOG ( LogP4Env , Error , TEXT ( " Failed serializing settings using JSON. " ) ) ;
return ;
}
auto bNeedsWrite = true ;
if ( FPaths : : FileExists ( GetSettingsFileName ( ) ) )
{
// Read the file to a string.
FString FileContents ;
bNeedsWrite = ! ( FFileHelper : : LoadFileToString ( FileContents , * GetSettingsFileName ( ) ) & & FileContents = = Buffer ) ;
}
if ( bNeedsWrite & & ! FFileHelper : : SaveStringToFile ( Buffer , * GetSettingsFileName ( ) ) )
{
UE_LOG ( LogP4Env , Error , TEXT ( " Failed writing settings to file: \" %s \" . " ) , * GetSettingsFileName ( ) ) ;
return ;
}
}
private :
/**
* Constructor .
*
* Creates the cache from value read from settings file if it exists .
*/
FSettingsCache ( )
{
// Read the file to a string.
FString FileContents ;
if ( ! FFileHelper : : LoadFileToString ( FileContents , * GetSettingsFileName ( ) ) )
{
UE_LOG ( LogP4Env , Log , TEXT ( " Couldn't find settings file \" %s \" . " ) , * GetSettingsFileName ( ) ) ;
return ;
}
// Deserialize a JSON object from the string
TSharedPtr < FJsonObject > Object ;
TSharedRef < TJsonReader < > > Reader = TJsonReaderFactory < > : : Create ( FileContents ) ;
if ( ! FJsonSerializer : : Deserialize ( Reader , Object ) | | ! Object . IsValid ( ) )
{
UE_LOG ( LogP4Env , Error , TEXT ( " Settings file JSON parsing failed: \" %s \" . " ) , * Reader - > GetErrorMessage ( ) ) ;
return ;
}
for ( const auto & Value : Object - > Values )
{
FString Text ;
if ( ! Value . Value - > TryGetString ( Text ) )
{
Settings . Empty ( ) ;
break ;
}
Settings . Add ( Value . Key , Text ) ;
}
}
/**
* Gets settings file name . For now it ' s hardcoded .
*
* @ returns Settings file name .
*/
static const FString & GetSettingsFileName ( )
{
const static FString HardCodedSettingsFileName = " UnrealSync.settings " ;
return HardCodedSettingsFileName ;
}
/** Value map of settings. */
TMap < FString , FString > Settings ;
} ;
2014-07-18 14:17:23 -04:00
/**
* Gets param from environment variables .
*
* @ param Value Output value .
* @ param ParamName Param name to parse .
*
* @ returns True if found , false otherwise .
*/
bool GetEnvParam ( FString & Value , const FString & ParamName )
{
2014-06-04 11:01:22 -04:00
TCHAR Buf [ 512 ] ;
FPlatformMisc : : GetEnvironmentVariable ( * ParamName , Buf , 512 ) ;
2014-07-18 14:17:23 -04:00
if ( FCString : : Strlen ( Buf ) > 0 )
{
Value = Buf ;
return true ;
}
return false ;
2014-06-04 11:01:22 -04:00
}
2014-07-18 14:17:23 -04:00
FP4Env : : FP4Env ( )
2014-06-04 11:01:22 -04:00
{
}
const FString & FP4Env : : GetClient ( ) const
{
return Client ;
}
const FString & FP4Env : : GetPort ( ) const
{
return Port ;
}
const FString & FP4Env : : GetUser ( ) const
{
return User ;
}
/**
* Gets known path in the depot ( arbitrarily chosen ) .
*
* @ returns Known path .
*/
FString GetKnownPath ( )
{
return FPaths : : ConvertRelativePathToFull (
FPaths : : Combine ( * FPaths : : EngineConfigDir ( ) , TEXT ( " BaseEngine.ini " ) ) ) ;
}
/**
* Gets computer host name .
*
* @ returns Host name .
*/
FString GetHostName ( )
{
return FPlatformProcess : : ComputerName ( ) ;
}
const FString & FP4Env : : GetBranch ( ) const
{
return Branch ;
}
FP4Env & FP4Env : : Get ( )
{
return * Env ;
}
/**
2014-07-18 14:17:23 -04:00
* Param detection iterator interface .
2014-06-04 11:01:22 -04:00
*/
2014-07-18 14:17:23 -04:00
class IP4EnvParamDetectionIterator
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
public :
/**
* Try to auto - detect next param proposition .
*
* @ returns True if found . False otherwise .
*/
virtual bool MoveNext ( ) = 0 ;
2014-06-05 12:11:12 -04:00
2014-07-18 14:17:23 -04:00
/**
* Gets currently found param proposition .
*
* @ returns Found param .
*/
virtual const FString & GetCurrent ( ) const = 0 ;
2014-06-05 12:11:12 -04:00
2014-07-18 14:17:23 -04:00
/**
* Creates auto - detection iterator for given type .
*
* @ param Type Type of iterator to create .
* @ param CommandLine Command line to parse by iterator .
* @ param Env Current P4 environment state .
*
* @ returns Shared pointer to created iterator .
*/
2015-04-09 04:40:26 -04:00
static TSharedPtr < IP4EnvParamDetectionIterator > Create ( EP4ParamType Type , const TCHAR * CommandLine , const FP4Env & Env ) ;
2014-07-18 14:17:23 -04:00
} ;
/**
* Base class for param detection iterators .
*/
class FP4EnvParamDetectionIteratorBase : public IP4EnvParamDetectionIterator
{
public :
/**
* Constructor
*
* @ param Type Type of the param to auto - detect .
* @ param CommandLine Command line to check for param .
*/
2015-04-09 04:40:26 -04:00
FP4EnvParamDetectionIteratorBase ( EP4ParamType Type , const TCHAR * CommandLine )
2014-07-18 14:17:23 -04:00
: Type ( Type )
2014-06-05 12:11:12 -04:00
{
2015-04-09 04:40:26 -04:00
if ( GetCommandLineParam ( HardParam , CommandLine , Type ) )
2014-06-05 12:11:12 -04:00
{
2015-04-09 04:40:26 -04:00
OverriddenSetting ( Type , HardParam , true ) ;
}
if ( FSettingsCache : : Get ( ) . GetSetting ( HardParam , Type ) )
{
OverriddenSetting ( Type , HardParam , false ) ;
2014-06-05 12:11:12 -04:00
}
}
2014-07-18 14:17:23 -04:00
/**
* Try to auto - detect next param proposition .
*
* @ returns True if found . False otherwise .
*/
virtual bool MoveNext ( ) override
{
if ( bFinished )
{
2015-04-09 04:40:26 -04:00
if ( ! HardParam . IsEmpty ( ) )
2014-07-18 14:17:23 -04:00
{
2015-04-09 04:40:26 -04:00
Current = HardParam ;
HardParam . Empty ( ) ;
2014-07-18 14:17:23 -04:00
return true ;
}
else
{
return false ;
}
}
if ( ! bEnvChecked )
{
bEnvChecked = true ;
if ( GetEnvParam ( Current , FP4Env : : GetParamName ( Type ) ) )
{
return true ;
}
}
return FindNext ( Current ) ;
}
/**
* Gets currently found param proposition .
*
* @ returns Found param .
*/
virtual const FString & GetCurrent ( ) const override
{
return Current ;
}
protected :
/**
* Find next param .
*
* @ param Output Output parameter .
*
* @ returns True if found . False otherwise .
*/
virtual bool FindNext ( FString & Output ) = 0 ;
/**
* Mark this iterator as finished .
*/
void Finish ( )
{
bFinished = true ;
}
2015-04-09 04:40:26 -04:00
/**
* Method reports in the log that given param is overridden ( from settings or command line ) .
*
* @ param Type Param type to report on .
* @ param Value Value of this param .
* @ param CommandLine Boolean value that tells if this override comes from command line . If false it comes from settings file .
*/
void OverriddenSetting ( EP4ParamType Type , const FString & Value , bool CommandLine )
{
FString Source = CommandLine ? TEXT ( " command line " ) : TEXT ( " settings file " ) ;
UE_LOG ( LogP4Env , Log , TEXT ( " Setting %s overridden from %s and set to value \" %s \" . " ) , * FP4Env : : GetParamName ( Type ) , * Source , * Value ) ;
Finish ( ) ;
}
2014-07-18 14:17:23 -04:00
private :
/** Currently found param proposition. */
FString Current ;
2015-04-09 04:40:26 -04:00
/** Found hard param i.e. it can't be replaced by any autodetected ones. */
FString HardParam ;
2014-07-18 14:17:23 -04:00
/** Type of the param. */
2015-04-09 04:40:26 -04:00
EP4ParamType Type ;
2014-07-18 14:17:23 -04:00
/** Is this iterator finished. */
bool bFinished = false ;
/** Tells if environment variable have been checked already by this iterator. */
bool bEnvChecked = false ;
} ;
/**
* P4 path param auto - detection iterator .
*/
class FP4PathDetectionIterator : public FP4EnvParamDetectionIteratorBase
{
public :
/**
* Constructor .
*
* @ param CommandLine Command line to parse .
*/
FP4PathDetectionIterator ( const TCHAR * CommandLine )
2014-07-24 08:44:41 -04:00
: Step ( 0 ) , FP4EnvParamDetectionIteratorBase ( EP4ParamType : : Path , CommandLine )
2014-07-18 14:17:23 -04:00
{
2015-04-09 04:40:26 -04:00
// Tries to detect in standard environment paths.
static const FString WhereCommand = " where " ; // TODO Mac: I think 'where' command equivalent on Mac is 'whereis'
static const FString P4ExecutableName = " p4.exe " ;
2015-05-07 15:29:49 -04:00
TArray < FString > LocationsToLook ;
LocationsToLook . Add ( FPaths : : Combine ( TEXT ( " C: \\ Program Files \\ Perforce " ) , * P4ExecutableName ) ) ;
LocationsToLook . Add ( FPaths : : Combine ( TEXT ( " C: \\ Program Files (x86) \\ Perforce " ) , * P4ExecutableName ) ) ;
2015-04-09 04:40:26 -04:00
FString WhereOutput ;
if ( RunProcessOutput ( WhereCommand , P4ExecutableName , WhereOutput ) )
{
2015-05-07 15:29:49 -04:00
TArray < FString > Outputs ;
if ( WhereOutput . ParseIntoArrayLines ( Outputs , true ) )
{
for ( auto & Output : Outputs )
{
LocationsToLook . Add ( Output ) ;
}
}
2015-04-09 04:40:26 -04:00
}
2015-05-07 15:29:49 -04:00
for ( auto & LocationToLook : LocationsToLook )
2015-04-09 04:40:26 -04:00
{
2015-05-07 15:29:49 -04:00
FString LocationCandidate = FPaths : : ConvertRelativePathToFull ( LocationToLook ) ;
2015-04-09 04:40:26 -04:00
if ( FPaths : : FileExists ( LocationCandidate ) )
{
AddUniqueLocation ( LocationCandidate ) ;
}
}
2014-07-18 14:17:23 -04:00
}
/**
* Function that tries to detect P4 executable path .
*
* @ param Output Output param . Found P4 executable path .
*
* @ returns True if succeeded . False otherwise .
*/
virtual bool FindNext ( FString & Output ) override
{
2015-04-09 04:40:26 -04:00
if ( Step > = PossibleLocations . Num ( ) )
2014-07-18 14:17:23 -04:00
{
Finish ( ) ;
return false ;
}
2015-04-09 04:40:26 -04:00
Output = PossibleLocations [ Step + + ] ;
return true ;
2014-07-18 14:17:23 -04:00
}
private :
2015-04-09 04:40:26 -04:00
/**
* Adds unique location of P4 .
*
* @ param Path Location to add .
*/
void AddUniqueLocation ( const FString & Path )
{
auto NormalizedPath = PlatformNormalize ( Path ) ;
if ( ! PossibleLocations . Contains ( NormalizedPath ) )
{
PossibleLocations . Add ( NormalizedPath ) ;
}
}
/**
* Normalizes path in platform - specific way , i . e . lowers case on Windowsish
* systems and leaves it intact on * nix .
*/
FString PlatformNormalize ( const FString & Path )
{
# if PLATFORM_WINDOWS || PLATFORM_WINRT || PLATFORM_XBOXONE
return Path . ToLower ( ) ;
# else
return Path ;
# endif
}
2014-07-18 14:17:23 -04:00
/** Possible P4 installation locations. */
TArray < FString > PossibleLocations ;
/** Current step number. */
int32 Step ;
} ;
2014-06-05 12:11:12 -04:00
# include "XmlParser.h"
/**
* Tries to get last connection string from P4V config file .
*
* @ param Output Output param .
* @ param LastConnectionStringId Which string to output .
*
* @ returns True if succeeded , false otherwise .
*/
bool GetP4VLastConnectionStringElement ( FString & Output , int32 LastConnectionStringId )
{
class FLastConnectionStringCache
{
public :
FLastConnectionStringCache ( )
{
bFoundData = false ;
// TODO Mac: This path is going to be different on Mac.
FString AppSettingsXmlPath = FPaths : : ConvertRelativePathToFull (
FPaths : : Combine ( FPlatformProcess : : UserDir ( ) ,
TEXT ( " .. " ) , TEXT ( " .p4qt " ) ,
TEXT ( " ApplicationSettings.xml " )
)
) ;
if ( ! FPaths : : FileExists ( AppSettingsXmlPath ) )
{
return ;
}
TSharedPtr < FXmlFile > Doc = MakeShareable ( new FXmlFile ( AppSettingsXmlPath , EConstructMethod : : ConstructFromFile ) ) ;
for ( FXmlNode * PropertyListNode : Doc - > GetRootNode ( ) - > GetChildrenNodes ( ) )
{
if ( PropertyListNode - > GetTag ( ) ! = " PropertyList " | | PropertyListNode - > GetAttribute ( " varName " ) ! = " Connection " )
{
continue ;
}
for ( FXmlNode * VarNode : PropertyListNode - > GetChildrenNodes ( ) )
{
if ( VarNode - > GetTag ( ) ! = " String " | | VarNode - > GetAttribute ( " varName " ) ! = " LastConnection " )
{
continue ;
}
bFoundData = true ;
FString Content = VarNode - > GetContent ( ) ;
FString Current ;
FString Rest ;
while ( Content . Split ( " , " , & Current , & Rest ) )
{
Current . Trim ( ) ;
Current . TrimTrailing ( ) ;
Data . Add ( Current ) ;
Content = Rest ;
}
Rest . Trim ( ) ;
Rest . TrimTrailing ( ) ;
Data . Add ( Rest ) ;
}
}
}
bool bFoundData ;
TArray < FString > Data ;
} ;
static FLastConnectionStringCache Cache ;
if ( ! Cache . bFoundData | | Cache . Data . Num ( ) < = LastConnectionStringId )
{
return false ;
}
Output = Cache . Data [ LastConnectionStringId ] ;
return true ;
}
/**
2014-07-18 14:17:23 -04:00
* P4 port param auto - detection iterator .
2014-06-05 12:11:12 -04:00
*/
2014-07-18 14:17:23 -04:00
class FP4PortDetectionIterator : public FP4EnvParamDetectionIteratorBase
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
public :
/**
* Constructor .
*
* @ param CommandLine Command line to parse .
*/
FP4PortDetectionIterator ( const TCHAR * CommandLine )
2014-07-24 08:44:41 -04:00
: Step ( 0 ) , FP4EnvParamDetectionIteratorBase ( EP4ParamType : : Port , CommandLine )
2014-07-18 14:17:23 -04:00
{ }
/**
* Function that tries to detect P4 port .
*
* @ param Output Output param . Found P4 port .
*
* @ returns True if succeeded . False otherwise .
*/
virtual bool FindNext ( FString & Output ) override
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
if ( Step = = 0 )
{
+ + Step ;
if ( GetP4VLastConnectionStringElement ( Output , 0 ) )
{
return true ;
}
}
if ( Step = = 1 )
{
+ + Step ;
// Fallback to default port. If it's not valid auto-detection will fail later.
Output = " perforce:1666 " ;
return true ;
}
Finish ( ) ;
return false ;
2014-06-05 12:11:12 -04:00
}
2014-07-18 14:17:23 -04:00
private :
/** Current step number. */
int32 Step ;
} ;
2014-06-05 12:11:12 -04:00
/**
2014-07-18 14:17:23 -04:00
* P4 user param auto - detection iterator .
2014-06-05 12:11:12 -04:00
*/
2014-07-18 14:17:23 -04:00
class FP4UserDetectionIterator : public FP4EnvParamDetectionIteratorBase
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
public :
/**
* Constructor .
*
* @ param CommandLine Command line to parse .
* @ param Env Current P4 environment state .
*/
FP4UserDetectionIterator ( const TCHAR * CommandLine , const FP4Env & Env )
2014-07-24 08:44:41 -04:00
: Step ( 0 ) , FP4EnvParamDetectionIteratorBase ( EP4ParamType : : User , CommandLine ) , Env ( Env )
2014-06-05 12:11:12 -04:00
{
}
2014-07-18 14:17:23 -04:00
/**
* Function that tries to detect P4 user .
*
* @ param Output Output param . Found P4 user .
*
* @ returns True if succeeded . False otherwise .
*/
virtual bool FindNext ( FString & Output ) override
2014-06-05 12:14:47 -04:00
{
2014-07-18 14:17:23 -04:00
if ( Step = = 0 )
{
+ + Step ;
if ( GetP4VLastConnectionStringElement ( Output , 1 ) )
{
return true ;
}
}
if ( Step = = 1 )
{
+ + Step ;
FString InfoOutput ;
RunProcessOutput ( Env . GetPath ( ) , FString : : Printf ( TEXT ( " -p%s info " ) , * Env . GetPort ( ) ) , InfoOutput ) ;
const FRegexPattern UserNamePattern ( TEXT ( " User name: \\ s*([^ \\ t \\ n \\ r]+) \ \ s * " )) ;
FRegexMatcher Matcher ( UserNamePattern , InfoOutput ) ;
if ( Matcher . FindNext ( ) )
{
2015-04-27 02:46:47 -04:00
Output = Matcher . GetCaptureGroup ( 1 ) ;
2014-07-18 14:17:23 -04:00
return true ;
}
}
Finish ( ) ;
return false ;
2014-06-05 12:14:47 -04:00
}
2014-07-18 14:17:23 -04:00
private :
/** Current P4 environment state. */
const FP4Env & Env ;
/** Current step number. */
int32 Step ;
} ;
2014-06-05 12:11:12 -04:00
# include "Internationalization/Regex.h"
/**
2014-07-18 14:17:23 -04:00
* P4 client param auto - detection iterator .
2014-06-05 12:11:12 -04:00
*/
2014-07-18 14:17:23 -04:00
class FP4ClientDetectionIterator : public FP4EnvParamDetectionIteratorBase
2014-06-04 11:01:22 -04:00
{
2014-07-18 14:17:23 -04:00
public :
/**
* Constructor .
*
* @ param CommandLine Command line to parse .
* @ param Env Current P4 environment state .
*/
FP4ClientDetectionIterator ( const TCHAR * CommandLine , const FP4Env & Env )
: FP4EnvParamDetectionIteratorBase ( EP4ParamType : : Client , CommandLine ) , Env ( Env )
2014-06-04 11:01:22 -04:00
{
2015-04-09 04:40:26 -04:00
auto P4CommandLine = FString : : Printf ( TEXT ( " -p%s clients -u%s " ) , * Env . GetPort ( ) , * Env . GetUser ( ) ) ;
if ( ! RunProcessOutput ( Env . GetPath ( ) , P4CommandLine , P4ClientsOutput ) )
2014-07-18 14:17:23 -04:00
{
2015-04-09 04:40:26 -04:00
UE_LOG ( LogP4Env , Log , TEXT ( " Failed to get client list. Used settings: " ) LINE_TERMINATOR
TEXT ( " \t Path: %s " ) LINE_TERMINATOR
TEXT ( " \t Port: %s " ) LINE_TERMINATOR
TEXT ( " \t User: %s " ) , * Env . GetPath ( ) , * Env . GetPort ( ) , * Env . GetUser ( ) )
2014-07-18 14:17:23 -04:00
Finish ( ) ;
return ;
}
static const FRegexPattern ClientsPattern ( TEXT ( " Client ([^ ]+) \ \ d { 4 } / \ \ d { 2 } / \ \ d { 2 } root ( . + ) ' . * ' " ));
Matcher = MakeShareable ( new FRegexMatcher ( ClientsPattern , P4ClientsOutput ) ) ;
2014-06-04 11:01:22 -04:00
}
2014-07-18 14:17:23 -04:00
/**
* Function that tries to detect P4 client .
*
* @ param Output Output param . Found P4 client .
*
* @ returns True if succeeded . False otherwise .
*/
virtual bool FindNext ( FString & Output ) override
2014-06-04 11:01:22 -04:00
{
2014-07-18 14:17:23 -04:00
static const FString KnownPath = GetKnownPath ( ) ;
static const FString HostName = GetHostName ( ) ;
2014-06-04 11:01:22 -04:00
2014-07-18 14:17:23 -04:00
while ( Matcher - > FindNext ( ) )
2014-06-04 11:01:22 -04:00
{
2015-04-27 02:46:47 -04:00
auto ClientName = Matcher - > GetCaptureGroup ( 1 ) ;
auto Root = Matcher - > GetCaptureGroup ( 2 ) ;
2014-06-04 11:01:22 -04:00
2014-07-18 14:17:23 -04:00
if ( KnownPath . StartsWith ( FPaths : : ConvertRelativePathToFull ( Root ) ) )
2014-06-04 11:01:22 -04:00
{
2014-07-18 14:17:23 -04:00
FString InfoOutput ;
2014-10-30 10:03:43 -04:00
if ( ! RunProcessOutput ( Env . GetPath ( ) , FString : : Printf ( TEXT ( " -p%s -u%s client -o %s " ) , * Env . GetPort ( ) , * Env . GetUser ( ) , * ClientName ) , InfoOutput ) )
2014-06-04 11:01:22 -04:00
{
2014-07-18 14:17:23 -04:00
continue ;
}
2014-10-30 10:03:43 -04:00
const FRegexPattern InfoPattern ( TEXT ( " Host: \\ s*([^ \\ r \\ n \\ t ]+) \ \ s * " )) ;
2014-07-18 14:17:23 -04:00
FRegexMatcher InfoMatcher ( InfoPattern , InfoOutput ) ;
2014-10-30 10:03:43 -04:00
while ( InfoMatcher . FindNext ( ) )
2014-07-18 14:17:23 -04:00
{
2015-04-27 02:46:47 -04:00
if ( InfoMatcher . GetCaptureGroup ( 1 ) . Equals ( HostName , ESearchCase : : IgnoreCase ) )
2014-07-18 14:17:23 -04:00
{
Output = ClientName ;
return true ;
}
2014-06-04 11:01:22 -04:00
}
}
}
2014-07-18 14:17:23 -04:00
Finish ( ) ;
return false ;
2014-06-04 11:01:22 -04:00
}
2014-07-18 14:17:23 -04:00
private :
/** Current regex matcher that parses next client. */
TSharedPtr < FRegexMatcher > Matcher ;
/** Current P4 environment state. */
const FP4Env & Env ;
/** Output of the p4 clients command. */
FString P4ClientsOutput ;
} ;
2014-06-04 11:01:22 -04:00
/**
2014-07-18 14:17:23 -04:00
* Gets branch detected for this app from current P4 environment state .
2014-06-04 11:01:22 -04:00
*
2014-07-18 14:17:23 -04:00
* @ param Output Found branch prefix .
2015-01-20 17:25:03 -05:00
* @ param Env Current P4 environment state .
2014-06-04 11:01:22 -04:00
*
2014-07-18 14:17:23 -04:00
* @ returns True if found , false otherwise .
2014-06-04 11:01:22 -04:00
*/
2014-07-18 14:17:23 -04:00
bool GetCurrentBranch ( FString & Output , const FP4Env & Env )
2014-06-04 11:01:22 -04:00
{
2014-07-18 14:17:23 -04:00
FString FilesOutput ;
2014-06-05 12:11:12 -04:00
if ( ! RunProcessOutput ( Env . GetPath ( ) , FString : : Printf ( TEXT ( " -p%s -u%s -c%s files %s " ) ,
2014-07-18 14:17:23 -04:00
* Env . GetPort ( ) , * Env . GetUser ( ) , * Env . GetClient ( ) , * GetKnownPath ( ) ) , FilesOutput ) )
2014-06-04 11:01:22 -04:00
{
2015-04-09 04:40:26 -04:00
UE_LOG ( LogP4Env , Log ,
TEXT ( " Failed to verify branch. Details below: " ) LINE_TERMINATOR
TEXT ( " Note that you can't use this tool for cross syncing, i.e. you need to use " ) LINE_TERMINATOR
TEXT ( " UnrealSync from the same path as the branch being synced. In other word, if P4 " ) LINE_TERMINATOR
TEXT ( " with given settings can't recognize currently running executable the " ) LINE_TERMINATOR
TEXT ( " verification will fail. Used settings: " ) LINE_TERMINATOR
TEXT ( " \t Path: %s " ) LINE_TERMINATOR
TEXT ( " \t Port: %s " ) LINE_TERMINATOR
TEXT ( " \t User: %s " ) LINE_TERMINATOR
TEXT ( " \t Client: %s " ) , * Env . GetPath ( ) , * Env . GetPort ( ) , * Env . GetUser ( ) , * Env . GetClient ( ) )
2014-06-04 11:01:22 -04:00
return false ;
}
FString Rest ;
2014-07-18 14:17:23 -04:00
if ( ! FilesOutput . Split ( " /Engine/ " , & Output , & Rest ) )
2014-06-04 11:01:22 -04:00
{
return false ;
}
return true ;
}
2014-07-18 14:17:23 -04:00
/**
* P4 client param auto - detection iterator .
*/
class FP4BranchDetectionIterator : public FP4EnvParamDetectionIteratorBase
{
public :
/**
* Constructor .
*
* @ param CommandLine Command line to parse .
* @ param Env Current P4 environment state .
*/
FP4BranchDetectionIterator ( const TCHAR * CommandLine , const FP4Env & Env )
: FP4EnvParamDetectionIteratorBase ( EP4ParamType : : Branch , CommandLine ) , Env ( Env )
{
}
/**
* Function that tries to detect P4 branch .
*
* @ param Output Output param . Found P4 branch .
*
* @ returns True if succeeded . False otherwise .
*/
virtual bool FindNext ( FString & Output ) override
{
Finish ( ) ;
return GetCurrentBranch ( Output , Env ) ;
}
private :
/** Current P4 environment state. */
const FP4Env & Env ;
} ;
2014-06-05 12:11:12 -04:00
bool FP4Env : : Init ( const TCHAR * CommandLine )
2014-06-04 11:01:22 -04:00
{
2014-07-18 14:17:23 -04:00
TSharedPtr < FP4Env > Env = MakeShareable ( new FP4Env ( ) ) ;
2014-06-04 11:01:22 -04:00
2014-07-18 14:17:23 -04:00
if ( ! Env - > AutoDetectMissingParams ( CommandLine ) )
{
return false ;
}
FString CurrentBranch ;
if ( ! GetCurrentBranch ( CurrentBranch , * Env ) | | CurrentBranch ! = Env - > GetBranch ( ) )
2014-06-04 11:01:22 -04:00
{
return false ;
}
2014-06-05 12:11:12 -04:00
FP4Env : : Env = Env ;
2014-06-04 11:01:22 -04:00
2014-06-05 12:11:12 -04:00
return true ;
2014-06-04 11:01:22 -04:00
}
bool FP4Env : : RunP4Progress ( const FString & CommandLine , const FOnP4MadeProgress & OnP4MadeProgress )
{
2014-06-05 12:11:12 -04:00
return RunProcessProgress ( Get ( ) . GetPath ( ) , FString : : Printf ( TEXT ( " -p%s -c%s -u%s %s " ) , * Get ( ) . GetPort ( ) , * Get ( ) . GetClient ( ) , * Get ( ) . GetUser ( ) , * CommandLine ) , OnP4MadeProgress ) ;
2014-06-04 11:01:22 -04:00
}
bool FP4Env : : RunP4Output ( const FString & CommandLine , FString & Output )
{
class FOutputCollector
{
public :
bool Progress ( const FString & Chunk )
{
Output + = Chunk ;
return true ;
}
FString Output ;
} ;
FOutputCollector OC ;
if ( ! RunP4Progress ( CommandLine , FOnP4MadeProgress : : CreateRaw ( & OC , & FOutputCollector : : Progress ) ) )
{
return false ;
}
Output = OC . Output ;
return true ;
}
bool FP4Env : : RunP4 ( const FString & CommandLine )
{
return RunP4Progress ( CommandLine , nullptr ) ;
}
2014-06-05 12:11:12 -04:00
void FP4Env : : SerializeParams ( const FSerializationTask & SerializationTask )
{
SerializationTask . ExecuteIfBound ( Path , EP4ParamType : : Path ) ;
SerializationTask . ExecuteIfBound ( Port , EP4ParamType : : Port ) ;
SerializationTask . ExecuteIfBound ( User , EP4ParamType : : User ) ;
SerializationTask . ExecuteIfBound ( Client , EP4ParamType : : Client ) ;
SerializationTask . ExecuteIfBound ( Branch , EP4ParamType : : Branch ) ;
}
2015-04-09 04:40:26 -04:00
FString FP4Env : : GetParamName ( EP4ParamType Type )
2014-06-05 12:11:12 -04:00
{
switch ( Type )
{
case EP4ParamType : : Path :
return " P4PATH " ;
case EP4ParamType : : Port :
return " P4PORT " ;
case EP4ParamType : : User :
return " P4USER " ;
case EP4ParamType : : Client :
return " P4CLIENT " ;
case EP4ParamType : : Branch :
return " P4BRANCH " ;
}
checkNoEntry ( ) ;
return " " ;
}
2014-07-18 14:17:23 -04:00
bool FP4Env : : AutoDetectMissingParams ( const TCHAR * CommandLine )
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
TArray < TSharedPtr < IP4EnvParamDetectionIterator > > ParamDetectionIteratorsStack ;
2015-04-09 04:40:26 -04:00
auto Type = EP4ParamType : : Path ;
2014-07-18 14:17:23 -04:00
ParamDetectionIteratorsStack . Add ( IP4EnvParamDetectionIterator : : Create ( Type , CommandLine , * this ) ) ;
2014-10-30 10:03:43 -04:00
int32 IterationCountdown = 20 ;
2014-07-18 14:17:23 -04:00
while ( ParamDetectionIteratorsStack . Num ( ) > 0 )
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
if ( ParamDetectionIteratorsStack . Last ( ) - > MoveNext ( ) )
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
SetParam ( Type , ParamDetectionIteratorsStack . Last ( ) - > GetCurrent ( ) ) ;
2014-06-05 12:11:12 -04:00
2014-07-18 14:17:23 -04:00
if ( Type ! = EP4ParamType : : Branch )
2014-06-05 12:11:12 -04:00
{
2015-04-09 04:40:26 -04:00
Type = ( EP4ParamType ) ( ( int ) Type + 1 ) ;
2014-07-18 14:17:23 -04:00
ParamDetectionIteratorsStack . Add ( IP4EnvParamDetectionIterator : : Create ( Type , CommandLine , * this ) ) ;
continue ;
2014-06-05 12:11:12 -04:00
}
2014-07-18 14:17:23 -04:00
else
2014-06-05 12:11:12 -04:00
{
2014-07-18 14:17:23 -04:00
return true ;
2014-06-05 12:11:12 -04:00
}
}
2014-07-18 14:17:23 -04:00
else
2014-06-05 12:11:12 -04:00
{
2015-04-09 04:40:26 -04:00
Type = ( EP4ParamType ) ( ( int ) Type - 1 ) ;
2014-07-18 14:17:23 -04:00
ParamDetectionIteratorsStack . RemoveAt ( ParamDetectionIteratorsStack . Num ( ) - 1 ) ;
2014-10-30 10:03:43 -04:00
- - IterationCountdown ;
if ( ! IterationCountdown )
{
break ;
}
2014-06-05 12:11:12 -04:00
}
2014-07-18 14:17:23 -04:00
}
2014-06-05 12:11:12 -04:00
2014-07-18 14:17:23 -04:00
return false ;
2014-06-05 12:11:12 -04:00
}
FString FP4Env : : GetCommandLine ( )
{
class CommandLineOutput
{
public :
2015-04-09 04:40:26 -04:00
void AppendParam ( FString & FieldReference , EP4ParamType Type )
2014-06-05 12:11:12 -04:00
{
Output + = " - " + GetParamName ( Type ) + " = \" " + FieldReference + " \" " ;
}
FString Output ;
} ;
CommandLineOutput Output ;
SerializeParams ( FSerializationTask : : CreateRaw ( & Output , & CommandLineOutput : : AppendParam ) ) ;
return Output . Output ;
}
const FString & FP4Env : : GetPath ( ) const
{
return Path ;
}
2015-04-09 04:40:26 -04:00
void FP4Env : : SetParam ( EP4ParamType Type , const FString & Value )
2014-07-18 14:17:23 -04:00
{
switch ( Type )
{
case EP4ParamType : : Path :
Path = Value ;
break ;
case EP4ParamType : : Port :
Port = Value ;
break ;
case EP4ParamType : : User :
User = Value ;
break ;
case EP4ParamType : : Client :
Client = Value ;
break ;
case EP4ParamType : : Branch :
Branch = Value ;
break ;
default :
// Unimplemented param type?
checkNoEntry ( ) ;
}
}
2015-04-09 04:40:26 -04:00
bool FP4Env : : CheckIfFileNeedsUpdate ( const FString & FilePath )
{
FString Output ;
if ( ! RunP4Output ( FString : : Printf ( TEXT ( " fstat %s " ) , * FilePath ) , Output ) )
{
UE_LOG ( LogP4Env , Error , TEXT ( " Checking if file \" %s \" needs update, failed. " ) , * FilePath ) ;
return false ;
}
static const FRegexPattern HeadRevPattern ( TEXT ( " \\ . \\ . \\ . headRev ( \\ d+) " )) ;
static const FRegexPattern HaveRevPattern ( TEXT ( " \\ . \\ . \\ . haveRev ( \\ d+) " )) ;
static const FRegexPattern ChangePattern ( TEXT ( " \\ . \\ . \\ . change ([^ \\ n]+) " )) ;
FRegexMatcher ChangeMatcher ( ChangePattern , Output ) ;
if ( ChangeMatcher . FindNext ( ) )
{
UE_LOG ( LogP4Env , Error , TEXT ( " File \" %s \" is checked out, can't update. " ) , * FilePath ) ;
return false ;
}
FRegexMatcher HeadRevMatcher ( HeadRevPattern , Output ) ;
FRegexMatcher HaveRevMatcher ( HaveRevPattern , Output ) ;
if ( ! HeadRevMatcher . FindNext ( ) | | ! HaveRevMatcher . FindNext ( ) )
{
UE_LOG ( LogP4Env , Error , TEXT ( " Can't determine head or have revision for file \" %s \" . " ) , * FilePath ) ;
return false ;
}
2015-04-27 02:46:47 -04:00
int32 HeadRev = FPlatformString : : Atoi ( * HeadRevMatcher . GetCaptureGroup ( 1 ) ) ;
int32 HaveRev = FPlatformString : : Atoi ( * HaveRevMatcher . GetCaptureGroup ( 1 ) ) ;
2015-04-09 04:40:26 -04:00
return HaveRev < HeadRev ;
}
bool FP4Env : : IsValid ( )
{
return Env . IsValid ( ) ;
}
const FString & FP4Env : : GetParamByType ( EP4ParamType Type ) const
{
switch ( Type )
{
case EP4ParamType : : Path :
return GetPath ( ) ;
case EP4ParamType : : Port :
return GetPort ( ) ;
case EP4ParamType : : User :
return GetUser ( ) ;
case EP4ParamType : : Client :
return GetClient ( ) ;
case EP4ParamType : : Branch :
return GetBranch ( ) ;
default :
// Unimplemented param type?
checkNoEntry ( ) ;
throw 0 ; // Won't get here, used to suppress "not all control paths return a value"
}
}
TSharedPtr < IP4EnvParamDetectionIterator > IP4EnvParamDetectionIterator : : Create ( EP4ParamType Type , const TCHAR * CommandLine , const FP4Env & Env )
2014-07-18 14:17:23 -04:00
{
switch ( Type )
{
case EP4ParamType : : Path :
return MakeShareable ( new FP4PathDetectionIterator ( CommandLine ) ) ;
case EP4ParamType : : Port :
return MakeShareable ( new FP4PortDetectionIterator ( CommandLine ) ) ;
case EP4ParamType : : User :
return MakeShareable ( new FP4UserDetectionIterator ( CommandLine , Env ) ) ;
case EP4ParamType : : Client :
return MakeShareable ( new FP4ClientDetectionIterator ( CommandLine , Env ) ) ;
case EP4ParamType : : Branch :
return MakeShareable ( new FP4BranchDetectionIterator ( CommandLine , Env ) ) ;
default :
// Unimplemented param type?
checkNoEntry ( ) ;
return nullptr ;
}
}
2014-06-04 11:01:22 -04:00
/* Static variable initialization. */
2015-04-09 04:40:26 -04:00
TSharedPtr < FP4Env > FP4Env : : Env ;
/**
* Grid panel widget with labelled options .
*/
class SOptionsPanel : public SGridPanel
{
public :
SLATE_BEGIN_ARGS ( SOptionsPanel ) { }
SLATE_END_ARGS ( )
/**
* Method that constructs this widget given Slate construction arguments .
*
* @ param InArgs Slate construction arguments .
*/
void Construct ( const FArguments & InArgs )
{
SetColumnFill ( 1 , 1.0f ) ;
}
/**
* Inner class forward declaration .
*/
class SOption ;
/**
* Adds option widget to the grid .
*
* @ param Option Option to add .
*/
void Add ( TSharedPtr < SOption > Option ) ;
private :
/** Collection of all options in this grid. */
TArray < TSharedPtr < SOption > > Options ;
} ;
/**
* Widget and label provider for any option added .
*/
class SOptionsPanel : : SOption : public TSharedFromThis < SOption >
{
friend void SOptionsPanel : : Add ( TSharedPtr < SOption > Option ) ;
public :
/**
* Constructor
*/
SOption ( ) { }
protected :
/**
* Must be overridden . Provides a label for this option .
*
* @ returns Label for this option .
*/
virtual FText GetLabel ( ) const = 0 ;
/**
* Creates and returns widget for this option . If not overridden it creates editable text box .
*
* @ returns Widget for this option .
*/
virtual TSharedRef < SWidget > CreateWidget ( ) { return SNew ( SEditableTextBox ) ; }
} ;
void SOptionsPanel : : Add ( TSharedPtr < SOption > Option )
{
auto Id = Options . Add ( Option ) ;
AddSlot ( 0 , Id ) . Padding ( 1.0f )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
+ SHorizontalBox : : Slot ( ) . AutoWidth ( ) . VAlign ( VAlign_Center )
[
SNew ( STextBlock ) . Text ( Option . Get ( ) , & SOption : : GetLabel )
]
] ;
AddSlot ( 1 , Id ) . Padding ( 1.0f )
[
Option - > CreateWidget ( )
] ;
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SP4EnvTabWidget : : Construct ( const FArguments & InArgs )
{
/**
* P4 option widget .
*/
class SP4Option : public SOptionsPanel : : SOption
{
public :
/**
* Constructor
*
* @ param Reference to FP4Option object this object relates to .
*/
SP4Option ( TSharedRef < FP4Option > Option )
: Option ( Option )
{
}
protected :
/**
* Creates and returns widget for this option .
*
* @ returns Widget for this option .
*/
virtual TSharedRef < SWidget > CreateWidget ( ) override
{
return SNew ( SEditableTextBox )
. OnTextCommitted ( this , & SP4Option : : OnTextCommitted )
. Text ( this , & SP4Option : : GetText )
. ToolTipText ( FText : : Format ( LOCTEXT ( " P4OptionToolTip " , " This is a setting for {0}. If you see greyed text, then it means that this setting was autodetected. " ) , FText : : FromString ( FP4Env : : GetParamName ( Option - > GetType ( ) ) ) ) )
. HintText ( this , & SP4Option : : GetHintText ) ;
}
/**
* On text committed event handler .
*
* @ param CommittedText Committed text .
* @ param Type Commit type .
*/
void OnTextCommitted ( const FText & CommittedText , ETextCommit : : Type Type )
{
Option - > GetText ( ) = CommittedText ;
}
/**
* SEditableTextBox text provider .
*
* @ returns Text that should be displayed in SEditableTextBox for this option .
*/
FText GetText ( ) const
{
return Option - > GetText ( ) ;
}
/**
* SEditableTextBox hint text provider ( greyed text ) .
*
* @ returns Hint text that should be displayed in SEditableTextBox for this option .
*/
FText GetHintText ( ) const
{
if ( FP4Env : : IsValid ( ) )
{
auto Env = FP4Env : : Get ( ) ;
return FText : : FromString ( Env . GetParamByType ( Option - > GetType ( ) ) ) ;
}
return FText ( ) ;
}
/**
* Provides a label for this option .
*
* @ returns Label for this option .
*/
virtual FText GetLabel ( ) const override
{
switch ( Option - > GetType ( ) )
{
case EP4ParamType : : Branch :
return LOCTEXT ( " P4Option_Branch " , " Branch name " ) ;
case EP4ParamType : : Client :
return LOCTEXT ( " P4Option_Client " , " Workspace name " ) ;
case EP4ParamType : : Path :
return LOCTEXT ( " P4Option_Path " , " Path to P4 executable " ) ;
case EP4ParamType : : Port :
return LOCTEXT ( " P4Option_Port " , " P4 server address " ) ;
case EP4ParamType : : User :
return LOCTEXT ( " P4Option_User " , " P4 user name " ) ;
default :
checkNoEntry ( ) ;
throw 0 ; // Won't get here, used to suppress "not all control paths return a value"
}
}
private :
/** Option reference. */
TSharedRef < FP4Option > Option ;
} ;
Options . Reserve ( 4 ) ;
Options . Add ( MakeShareable ( new FP4Option ( EP4ParamType : : Path ) ) ) ;
Options . Add ( MakeShareable ( new FP4Option ( EP4ParamType : : Port ) ) ) ;
Options . Add ( MakeShareable ( new FP4Option ( EP4ParamType : : User ) ) ) ;
Options . Add ( MakeShareable ( new FP4Option ( EP4ParamType : : Client ) ) ) ;
auto OptionsPanel = SNew ( SOptionsPanel ) ;
for ( auto Option : Options )
{
OptionsPanel - > Add ( MakeShareable ( new SP4Option ( Option ) ) ) ;
}
this - > ChildSlot
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( ) . HAlign ( HAlign_Center ) . AutoHeight ( ) . Padding ( 10.0f )
[
SNew ( STextBlock )
. Font ( FSlateFontInfo ( FPaths : : EngineContentDir ( ) / TEXT ( " Slate/Fonts/Roboto-Regular.ttf " ) , 14 ) )
. Text ( LOCTEXT ( " P4Settings " , " Perforce settings " ) )
]
+ SVerticalBox : : Slot ( )
[
OptionsPanel
]
+ SVerticalBox : : Slot ( ) . AutoHeight ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( ) . VAlign ( VAlign_Center )
[
SNew ( STextBlock ) . Text (
! FP4Env : : IsValid ( )
? LOCTEXT ( " P4AutodetectFailed " , " P4 environment detection failed. See the log for more details. " )
: LOCTEXT ( " P4AutodetectSucceeded " , " P4 environment detection succeeded. " )
)
]
+ SHorizontalBox : : Slot ( ) . AutoWidth ( )
[
SNew ( SButton )
. OnClicked ( this , & SP4EnvTabWidget : : OnSaveAndRestartButtonClick )
[
SNew ( STextBlock ) . Text ( LOCTEXT ( " SaveAndRestart " , " Save and restart " ) )
]
]
+ SHorizontalBox : : Slot ( ) . AutoWidth ( )
[
SNew ( SButton )
. OnClicked ( this , & SP4EnvTabWidget : : OnCloseButtonClick )
[
SNew ( STextBlock ) . Text ( LOCTEXT ( " Close " , " Close " ) )
]
]
]
] ;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
FReply SP4EnvTabWidget : : OnSaveAndRestartButtonClick ( )
{
auto & Settings = FSettingsCache : : Get ( ) ;
for ( auto Option : Options )
{
Settings . SetSetting ( Option - > GetType ( ) , Option - > GetText ( ) . ToString ( ) ) ;
}
Settings . Save ( ) ;
FUnrealSync : : RunDetachedUS ( FPlatformProcess : : ExecutableName ( false ) , true , true , false ) ;
FPlatformMisc : : RequestExit ( false ) ;
return FReply : : Handled ( ) ;
}
FReply SP4EnvTabWidget : : OnCloseButtonClick ( )
{
FPlatformMisc : : RequestExit ( false ) ;
return FReply : : Handled ( ) ;
}
SP4EnvTabWidget : : FP4Option : : FP4Option ( EP4ParamType Type )
: Type ( Type )
{
FString Param ;
if ( FSettingsCache : : Get ( ) . GetSetting ( Param , Type ) )
{
Text = FText : : FromString ( Param ) ;
}
}