2020-05-18 18:03:04 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2020-05-05 18:50:52 -04:00
using System ;
using System.Xml.Serialization ;
using System.IO ;
using UnrealBuildTool ;
using AutomationTool ;
2020-12-21 23:07:37 -04:00
using EpicGames.Core ;
2020-05-05 18:50:52 -04:00
using System.Diagnostics ;
2020-05-07 17:05:50 -04:00
using System.ComponentModel ;
2020-05-05 18:50:52 -04:00
namespace Turnkey
{
2020-07-29 14:40:30 -04:00
[XmlRoot(ElementName = "Source")]
2020-05-05 18:50:52 -04:00
public class CopyAndRun
{
// @todo turnkey: for some reason the TypeConverter stuff setup in UnrealTargetPlatform isn't kicking in to convert from string to UTP, so do it a manual way
[XmlElement("HostPlatform")]
public string PlatformString = null ;
[XmlIgnore]
public UnrealTargetPlatform ? Platform = null ;
public string Copy = null ;
public string CommandPath = null ;
public string CommandLine = null ;
/// <summary>
/// Needs a parameterless constructor for Xml deserialization
/// </summary>
CopyAndRun ( )
{ }
/// <summary>
/// Create a one-of local object with just a Copy operation
/// </summary>
/// <param name="CopyOperation"></param>
public CopyAndRun ( string CopyOperation )
{
Copy = TurnkeyUtils . ExpandVariables ( CopyOperation ) ;
}
public CopyAndRun ( CopyAndRun Other )
{
PlatformString = Other . PlatformString ;
Platform = Other . Platform ;
Copy = Other . Copy ;
CommandPath = Other . CommandPath ;
CommandLine = Other . CommandLine ;
}
internal void PostDeserialize ( )
{
if ( ! string . IsNullOrEmpty ( PlatformString ) )
{
Platform = UnrealTargetPlatform . Parse ( PlatformString ) ;
}
// perform early expansion, important for $(ThisManifestDir) which is valid only during deserialization
// but don't use any other variables yet, because UAT could have bad values in Environment
Copy = TurnkeyUtils . ExpandVariables ( Copy , bUseOnlyTurnkeyVariables : true ) ;
CommandPath = TurnkeyUtils . ExpandVariables ( CommandPath , bUseOnlyTurnkeyVariables : true ) ;
CommandLine = TurnkeyUtils . ExpandVariables ( CommandLine , bUseOnlyTurnkeyVariables : true ) ;
}
2021-02-08 17:07:27 -04:00
public static int RunExternalCommand ( string CommandPath , string CommandLine , ITurnkeyContext TurnkeyContext , bool bUnattended , bool bRequiresPrivilegeElevation , bool bCreateWindow )
2020-07-29 14:40:30 -04:00
{
string FixedCommandPath = TurnkeyUtils . ExpandVariables ( CommandPath ) ;
string FixedCommandLine = TurnkeyUtils . ExpandVariables ( CommandLine ) ;
FixedCommandPath = FixedCommandPath . Replace ( Path . AltDirectorySeparatorChar , Path . DirectorySeparatorChar ) ;
string PreviousCWD = Environment . CurrentDirectory ;
// if a directory was included in the command path, then run from there
string CommandWorkingDir = Path . GetDirectoryName ( FixedCommandPath ) ;
// if we run UseShellExecute with true, StartInfo.WorkingDirectory won't set the directory as expected
if ( ! string . IsNullOrEmpty ( CommandWorkingDir ) )
{
Environment . CurrentDirectory = CommandWorkingDir ;
}
TurnkeyUtils . StartTrackingExternalEnvVarChanges ( ) ;
// run installer as administrator, as some need it
Process InstallProcess = new Process ( ) ;
2021-02-08 17:07:27 -04:00
InstallProcess . StartInfo . UseShellExecute = bCreateWindow ;
2020-07-29 14:40:30 -04:00
InstallProcess . StartInfo . FileName = FixedCommandPath ;
InstallProcess . StartInfo . Arguments = FixedCommandLine ;
2021-02-08 17:07:27 -04:00
InstallProcess . StartInfo . WindowStyle = bCreateWindow ? ProcessWindowStyle . Normal : ProcessWindowStyle . Hidden ;
2020-07-29 14:40:30 -04:00
InstallProcess . OutputDataReceived + = ( Sender , Args ) = > { if ( Args ! = null & & Args . Data ! = null ) TurnkeyUtils . Log ( Args . Data . TrimEnd ( ) ) ; } ;
InstallProcess . ErrorDataReceived + = ( Sender , Args ) = > { if ( Args ! = null & & Args . Data ! = null ) TurnkeyUtils . Log ( "Error: {0}" , Args . Data . TrimEnd ( ) ) ; } ;
//installers may require administrator access to succeed. so run as an admmin.
// run in a loop in case of a failure
bool bDone = false ;
int ExitCode = - 1 ;
while ( ! bDone )
{
try
{
2021-02-08 17:07:27 -04:00
if ( bRequiresPrivilegeElevation & & InstallProcess . StartInfo . Verb ! = "runas" )
2021-01-22 15:26:45 -04:00
{
TurnkeyUtils . Log ( "The installer {0} requires elevated permissions, trying with Admin privileges (output may be hidden)" , FixedCommandPath ) ;
InstallProcess . StartInfo . UseShellExecute = true ;
InstallProcess . StartInfo . Verb = "runas" ;
InstallProcess . StartInfo . WindowStyle = ProcessWindowStyle . Normal ;
}
2020-07-29 14:40:30 -04:00
InstallProcess . Start ( ) ;
InstallProcess . WaitForExit ( ) ;
ExitCode = InstallProcess . ExitCode ;
}
catch ( Exception Ex )
{
// native error in a Win32Exception, of 740, means the process needs elevation, so we need to runas. However,
// this will not allow capturing stdout, so run with window as Normal
if ( InstallProcess . StartInfo . UseShellExecute = = false & & Ex is Win32Exception & & ( ( Win32Exception ) Ex ) . NativeErrorCode = = 740 )
{
2021-02-08 17:07:27 -04:00
bRequiresPrivilegeElevation = true ;
2020-07-29 14:40:30 -04:00
continue ;
}
2021-02-08 17:07:27 -04:00
TurnkeyContext . ReportError ( $"Error: {FixedCommandPath} caused an exception: {Ex.Message}" ) ;
2020-07-29 14:40:30 -04:00
ExitCode = - 1 ;
}
if ( ExitCode ! = 0 )
{
TurnkeyUtils . Log ( "" ) ;
2021-02-08 17:07:27 -04:00
TurnkeyContext . ReportError ( $"Command {FixedCommandPath} {FixedCommandLine} failed [Exit code {ExitCode}, working dir = {CommandWorkingDir}]" ) ;
2020-07-29 14:40:30 -04:00
TurnkeyUtils . Log ( "" ) ;
2021-02-08 17:07:27 -04:00
if ( ! bUnattended )
{
2021-06-24 08:55:13 -04:00
bool bResponse = TurnkeyUtils . GetUserConfirmation ( "Do you want to attempt again?" , false ) ;
if ( bResponse = = false )
2021-02-08 17:07:27 -04:00
{
bDone = true ;
}
}
else
2020-07-29 14:40:30 -04:00
{
bDone = true ;
}
}
else
{
bDone = true ;
}
}
TurnkeyUtils . EndTrackingExternalEnvVarChanges ( ) ;
Environment . CurrentDirectory = PreviousCWD ;
2021-02-08 17:07:27 -04:00
return ExitCode ;
2020-07-29 14:40:30 -04:00
}
2020-05-05 18:50:52 -04:00
}
}