You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Added a NET_CORE define to allow us to have changes side by side. The AWS S3 changes are required due to us requiring to upgrade the S3 assembly version to get net core support (which made all methods async). The ACL checks for files are not available in the system libraries of net core, as such the api is a bit different. AutomationToolLauncher now just spawns a subprocess when used in netcore, as netcore does not support custom AppDomains and shadow copying. We will generally need to revisit this for netcore as this whole feature of building the source for UAT in UAT is not really possible. To enable this set environment variable "UE_USE_DOTNET=1", note that with netcore all applications change their output path so this will likely break a bit of tooling when enabled. #rb ben.marsh [CL 14572339 by Joakim Lindqvist in ue5-main branch]
196 lines
7.7 KiB
C#
196 lines
7.7 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
// This software is provided "as-is," without any express or implied warranty.
|
|
// In no event shall the author, nor Epic Games, Inc. be held liable for any damages arising from the use of this software.
|
|
// This software will not be supported.
|
|
// Use at your own risk.
|
|
using System;
|
|
using System.Threading;
|
|
using System.Diagnostics;
|
|
using UnrealBuildTool;
|
|
using System.Reflection;
|
|
using Tools.DotNETCommon;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace AutomationTool
|
|
{
|
|
public class Program
|
|
{
|
|
/// <summary>
|
|
/// Keep a persistent reference to the delegate for handling Ctrl-C events. Since it's passed to non-managed code, we have to prevent it from being garbage collected.
|
|
/// </summary>
|
|
static ProcessManager.CtrlHandlerDelegate CtrlHandlerDelegateInstance = CtrlHandler;
|
|
|
|
public static int Main(string[] Arguments)
|
|
{
|
|
// Ensure UTF8Output flag is respected, since we are initializing logging early in the program.
|
|
if (CommandUtils.ParseParam(Arguments, "-Utf8output"))
|
|
{
|
|
Console.OutputEncoding = new System.Text.UTF8Encoding(false, false);
|
|
}
|
|
|
|
// Parse the log level argument
|
|
if(CommandUtils.ParseParam(Arguments, "-Verbose"))
|
|
{
|
|
Log.OutputLevel = LogEventType.Verbose;
|
|
}
|
|
if(CommandUtils.ParseParam(Arguments, "-VeryVerbose"))
|
|
{
|
|
Log.OutputLevel = LogEventType.VeryVerbose;
|
|
}
|
|
|
|
// Initialize the log system, buffering the output until we can create the log file
|
|
StartupTraceListener StartupListener = new StartupTraceListener();
|
|
Trace.Listeners.Add(StartupListener);
|
|
|
|
// Configure log timestamps
|
|
Log.IncludeTimestamps = CommandUtils.ParseParam(Arguments, "-Timestamps");
|
|
|
|
// Enter the main program section
|
|
ExitCode ReturnCode = ExitCode.Success;
|
|
try
|
|
{
|
|
// Set the working directory to the UE4 root
|
|
#if NET_CORE
|
|
// net core projects our output to a subfolder in the binaries path e.g. Engine/Binaries/Dotnet/AutomationTool
|
|
Environment.CurrentDirectory = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetOriginalLocation()), "..", "..", "..", ".."));
|
|
#else
|
|
Environment.CurrentDirectory = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetOriginalLocation()), "..", "..", ".."));
|
|
#endif
|
|
|
|
|
|
// Ensure we can resolve any external assemblies as necessary.
|
|
string PathToBinariesDotNET = Path.GetDirectoryName(Assembly.GetEntryAssembly().GetOriginalLocation());
|
|
AssemblyUtils.InstallAssemblyResolver(PathToBinariesDotNET);
|
|
AssemblyUtils.InstallRecursiveAssemblyResolver(PathToBinariesDotNET);
|
|
|
|
// Ensure that any third-party libraries marked as CopyLocal=false have their folders added as well (if the resolver can't be done locally where the class is used)
|
|
#if !NET_CORE
|
|
// for net core we just copy the dependent assemblies to their output location
|
|
string PathToBinariesThirdParty = Path.Combine(PathToBinariesDotNET, "..", "ThirdParty");
|
|
AssemblyUtils.InstallRecursiveAssemblyResolver(Path.Combine(PathToBinariesThirdParty, "Google"));
|
|
AssemblyUtils.InstallRecursiveAssemblyResolver(Path.Combine(PathToBinariesThirdParty, "AWSSDK"));
|
|
#endif
|
|
// Initialize the host platform layer
|
|
HostPlatform.Initialize();
|
|
|
|
// Log the operating environment. Since we usually compile to AnyCPU, we may be executed using different system paths under WOW64.
|
|
Log.TraceVerbose("{2}: Running on {0} as a {1}-bit process.", HostPlatform.Current.GetType().Name, Environment.Is64BitProcess ? 64 : 32, DateTime.UtcNow.ToString("o"));
|
|
|
|
// Log if we're running from the launcher
|
|
string ExecutingAssemblyLocation = Assembly.GetExecutingAssembly().Location;
|
|
if (string.Compare(ExecutingAssemblyLocation, Assembly.GetEntryAssembly().GetOriginalLocation(), StringComparison.OrdinalIgnoreCase) != 0)
|
|
{
|
|
Log.TraceVerbose("Executed from AutomationToolLauncher ({0})", ExecutingAssemblyLocation);
|
|
}
|
|
Log.TraceVerbose("CWD={0}", Environment.CurrentDirectory);
|
|
|
|
// Hook up exit callbacks
|
|
AppDomain Domain = AppDomain.CurrentDomain;
|
|
Domain.ProcessExit += Domain_ProcessExit;
|
|
Domain.DomainUnload += Domain_ProcessExit;
|
|
HostPlatform.Current.SetConsoleCtrlHandler(CtrlHandlerDelegateInstance);
|
|
|
|
// Log the application version
|
|
FileVersionInfo Version = AssemblyUtils.ExecutableVersion;
|
|
Log.TraceVerbose("{0} ver. {1}", Version.ProductName, Version.ProductVersion);
|
|
|
|
// Don't allow simultaneous execution of AT (in the same branch)
|
|
ReturnCode = InternalUtils.RunSingleInstance(Arguments, () => MainProc(Arguments, StartupListener));
|
|
}
|
|
catch (AutomationException Ex)
|
|
{
|
|
// Output the message in the desired format
|
|
if(Ex.OutputFormat == AutomationExceptionOutputFormat.Silent)
|
|
{
|
|
Log.TraceLog("{0}", ExceptionUtils.FormatExceptionDetails(Ex));
|
|
}
|
|
else if(Ex.OutputFormat == AutomationExceptionOutputFormat.Minimal)
|
|
{
|
|
Log.TraceInformation("{0}", Ex.ToString().Replace("\n", "\n "));
|
|
Log.TraceLog("{0}", ExceptionUtils.FormatExceptionDetails(Ex));
|
|
}
|
|
else
|
|
{
|
|
Log.WriteException(Ex, LogUtils.FinalLogFileName);
|
|
}
|
|
|
|
// Take the exit code from the exception
|
|
ReturnCode = Ex.ErrorCode;
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
// Use a default exit code
|
|
Log.WriteException(Ex, LogUtils.FinalLogFileName);
|
|
ReturnCode = ExitCode.Error_Unknown;
|
|
}
|
|
finally
|
|
{
|
|
// In all cases, do necessary shut down stuff, but don't let any additional exceptions leak out while trying to shut down.
|
|
|
|
// Make sure there's no directories on the stack.
|
|
NoThrow(() => CommandUtils.ClearDirStack(), "Clear Dir Stack");
|
|
|
|
// Try to kill process before app domain exits to leave the other KillAll call to extreme edge cases
|
|
NoThrow(() => { if (ShouldKillProcesses && !Utils.IsRunningOnMono) ProcessManager.KillAll(); }, "Kill All Processes");
|
|
|
|
// Write the exit code
|
|
Log.TraceInformation("AutomationTool exiting with ExitCode={0} ({1})", (int)ReturnCode, ReturnCode);
|
|
|
|
// Can't use NoThrow here because the code logs exceptions. We're shutting down logging!
|
|
Trace.Close();
|
|
}
|
|
return (int)ReturnCode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wraps an action in an exception block.
|
|
/// Ensures individual actions can be performed and exceptions won't prevent further actions from being executed.
|
|
/// Useful for shutdown code where shutdown may be in several stages and it's important that all stages get a chance to run.
|
|
/// </summary>
|
|
/// <param name="Action"></param>
|
|
private static void NoThrow(System.Action Action, string ActionDesc)
|
|
{
|
|
try
|
|
{
|
|
Action();
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
Log.TraceError("Exception performing nothrow action \"{0}\": {1}", ActionDesc, LogUtils.FormatException(Ex));
|
|
}
|
|
}
|
|
|
|
static bool CtrlHandler(CtrlTypes EventType)
|
|
{
|
|
Domain_ProcessExit(null, null);
|
|
if (EventType == CtrlTypes.CTRL_C_EVENT)
|
|
{
|
|
// Force exit
|
|
Environment.Exit(3);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void Domain_ProcessExit(object sender, EventArgs e)
|
|
{
|
|
// Kill all spawned processes (Console instead of Log because logging is closed at this time anyway)
|
|
if (ShouldKillProcesses && !Utils.IsRunningOnMono)
|
|
{
|
|
ProcessManager.KillAll();
|
|
}
|
|
Trace.Close();
|
|
}
|
|
|
|
static ExitCode MainProc(string[] Arguments, StartupTraceListener StartupListener)
|
|
{
|
|
ExitCode Result = Automation.Process(Arguments, StartupListener);
|
|
ShouldKillProcesses = Automation.ShouldKillProcesses;
|
|
return Result;
|
|
}
|
|
|
|
static bool ShouldKillProcesses = true;
|
|
}
|
|
}
|