// 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 System.Reflection; using EpicGames.Core; using System.IO; using System.Collections.Generic; using System.Text; using UnrealBuildBase; namespace AutomationToolDriver { public class Program { // Do not add [STAThread] here. It will cause deadlocks in platform automation code. public static int Main(string[] Arguments) { // Wait for a debugger to be attached if (ParseParam(Arguments, "-WaitForDebugger")) { Console.WriteLine("Waiting for debugger to be attached..."); while (Debugger.IsAttached == false) { Thread.Sleep(100); } Debugger.Break(); } Stopwatch Timer = Stopwatch.StartNew(); // Ensure UTF8Output flag is respected, since we are initializing logging early in the program. if (ParseParam(Arguments, "-Utf8output")) { Console.OutputEncoding = new System.Text.UTF8Encoding(false, false); } // Parse the log level argument if(ParseParam(Arguments, "-Verbose")) { Log.OutputLevel = LogEventType.Verbose; } if(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 = ParseParam(Arguments, "-Timestamps"); // Enter the main program section ExitCode ReturnCode = ExitCode.Success; try { // Set the working directory to the UE4 root Environment.CurrentDirectory = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetOriginalLocation()), "..", "..", "..", "..")); // Ensure we can resolve any external assemblies as necessary. string PathToBinariesDotNET = Path.GetDirectoryName(Assembly.GetEntryAssembly().GetOriginalLocation()); AssemblyUtils.InstallAssemblyResolver(PathToBinariesDotNET); AssemblyUtils.InstallRecursiveAssemblyResolver(PathToBinariesDotNET); // 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.", RuntimePlatform.Current.ToString(), 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); // Log the application version FileVersionInfo Version = AssemblyUtils.ExecutableVersion; Log.TraceVerbose("{0} ver. {1}", Version.ProductName, Version.ProductVersion); bool bWaitForUATMutex = ParseParam(Arguments, "-WaitForUATMutex"); // Don't allow simultaneous execution of AT (in the same branch) ReturnCode = ProcessSingleton.RunSingleInstance(() => MainProc(Arguments, StartupListener), bWaitForUATMutex); } catch (Exception Ex) { Log.TraceError("Exception occurred between AutomationToolDriver.Main() and Automation.Process()" + ExceptionUtils.FormatException(Ex)); } finally { // Write the exit code Log.TraceInformation("AutomationTool executed for {0}", Timer.Elapsed.ToString("h'h 'm'm 's's'")); 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; } static ExitCode MainProc(string[] Arguments, StartupTraceListener StartupListener) { ExitCode Result = AutomationTool.Automation.Process(Arguments, StartupListener); return Result; } // Code duplicated from CommandUtils.cs /// /// Parses the argument list for a parameter and returns whether it is defined or not. /// /// Argument list. /// Param to check for. /// True if param was found, false otherwise. public static bool ParseParam(string[] ArgList, string Param) { string ValueParam = Param; if (!ValueParam.EndsWith("=")) { ValueParam += "="; } foreach (string ArgStr in ArgList) { if (ArgStr.Equals(Param, StringComparison.InvariantCultureIgnoreCase) || ArgStr.StartsWith(ValueParam, StringComparison.InvariantCultureIgnoreCase)) { return true; } } return false; } } }