示例#1
0
		public bool CopyBuild(DirectoryReference InstallPath)
		{
			CommandUtils.LogInformation("Installing shared cooked build from manifest: {0} to {1}", Manifest.FullName, InstallPath.FullName);

			DirectoryReference PlatformInstallPath = DirectoryReference.Combine(InstallPath, Platform.ToString());

			FileReference PreviousManifest = FileReference.Combine(PlatformInstallPath, ".build", "Current.manifest");

			FileReference BPTI = FileReference.Combine(CommandUtils.RootDirectory, "Engine", "Binaries", "Win64", "NotForLicensees", "BuildPatchToolInstaller.exe");
			if (!FileReference.Exists(BPTI))
			{
				CommandUtils.LogInformation("Could not locate BuildPatchToolInstaller.exe");
				return false;
			}

			bool PreviousManifestExists = FileReference.Exists(PreviousManifest);
			if (!PreviousManifestExists && DirectoryReference.Exists(PlatformInstallPath))
			{
				DirectoryReference.Delete(PlatformInstallPath, true);
			}

			IProcessResult Result = CommandUtils.Run(BPTI.FullName, string.Format("-Manifest={0} -OutputDir={1} -stdout -GenericConsoleOutput", Manifest.FullName, PlatformInstallPath.FullName), null, CommandUtils.ERunOptions.Default);
			if (Result.ExitCode != 0)
			{
				CommandUtils.LogWarning("Failed to install manifest {0} to {1}", Manifest.FullName, PlatformInstallPath.FullName);
				return false;
			}

			FileReference SyncedBuildFile = new FileReference(CommandUtils.CombinePaths(PlatformInstallPath.FullName, SyncedBuildFileName));
			FileReference.WriteAllLines(SyncedBuildFile, new string[] { CL.ToString(), Manifest.FullName });

			return true;
		}
        /// <summary>
        /// Runs external program and writes the output to a logfile.
        /// </summary>
        /// <param name="App">Executable to run</param>
        /// <param name="CommandLine">Commandline to pass on to the executable</param>
        /// <param name="Logfile">Full path to the logfile, where the application output should be written to.</param>
        /// <param name="Input">Optional Input for the program (will be provided as stdin)</param>
        /// <param name="Options">Defines the options how to run. See ERunOptions.</param>
        /// <param name="FilterCallback">Callback to filter log spew before output.</param>
        public static string RunAndLog(string App, string CommandLine, string Logfile = null, int MaxSuccessCode = 0, string Input = null, ERunOptions Options = ERunOptions.Default, Dictionary <string, string> EnvVars = null, ProcessResult.SpewFilterCallbackType SpewFilterCallback = null)
        {
            IProcessResult Result = Run(App, CommandLine, Input, Options, EnvVars, SpewFilterCallback);

            if (Result.Output.Length > 0 && Logfile != null)
            {
                WriteToFile(Logfile, Result.Output);
            }
            else if (Logfile == null)
            {
                Logfile = "[No logfile specified]";
            }
            else
            {
                Logfile = "[None!, no output produced]";
            }

            if (Result.ExitCode > MaxSuccessCode || Result.ExitCode < 0)
            {
                throw new CommandFailedException((ExitCode)Result.ExitCode, String.Format("Command failed (Result:{3}): {0} {1}. See logfile for details: '{2}' ",
                                                                                          App, CommandLine, Path.GetFileName(Logfile), Result.ExitCode));
            }
            if (Result.Output.Length > 0)
            {
                return(Result.Output);
            }
            return("");
        }
示例#3
0
    int XcodeBuild(string Arguments)
    {
        string         XcodeBuildPath = "/usr/bin/xcodebuild";
        IProcessResult Result         = CommandUtils.Run(XcodeBuildPath, Arguments);

        return(Result.ExitCode);
    }
示例#4
0
        /// <summary>
        /// Attempts to detect source control server address from environment variables.
        /// </summary>
        /// <returns>Source control server address.</returns>
        private static string DetectP4Port()
        {
            string P4Port = null;

            // If it's not set, spawn Perforce to get the current server port setting
            IProcessResult Result = CommandUtils.Run(HostPlatform.Current.P4Exe, "set P4PORT", null, CommandUtils.ERunOptions.NoLoggingOfRunCommand);

            if (Result.ExitCode == 0)
            {
                const string KeyName = "P4PORT=";
                if (Result.Output.StartsWith(KeyName))
                {
                    int LastIdx = Result.Output.IndexOfAny(new char[] { ' ', '\n', '\r' });
                    if (LastIdx == -1)
                    {
                        LastIdx = Result.Output.Length;
                    }
                    P4Port = Result.Output.Substring(KeyName.Length, LastIdx - KeyName.Length);
                }
            }

            // Otherwise fallback to the uebp variables, or the default
            if (String.IsNullOrEmpty(P4Port))
            {
                Log.TraceWarning("P4PORT is not set. Using perforce:1666");
                P4Port = "perforce:1666";
            }

            return(P4Port);
        }
示例#5
0
        public override void ExecuteBuild()
        {
            string Version = ParseParamValue("Version");

            if (Version == null)
            {
                throw new AutomationException("Missing -Version=... parameter");
            }

            IProcessResult Result = Run("xcodebuild", "-version");

            if (Result.ExitCode != 0)
            {
                throw new AutomationException("Unable to query version number from xcodebuild (exit code={0})", Result.ExitCode);
            }

            Match Match = Regex.Match(Result.Output, "^Xcode ([0-9.]+)", RegexOptions.Multiline);

            if (!Match.Success)
            {
                throw new AutomationException("Missing version number from xcodebuild output:\n{0}", Result.Output);
            }

            if (Match.Groups[1].Value != Version)
            {
                LogWarning("Installed Xcode version is {0} - expected {1}", Match.Groups[1].Value, Version);
            }
        }
		public IAppInstance Run(IAppInstall App)
		{
			LuminAppInstall LuminInstall = App as LuminAppInstall;

			if (LuminInstall == null)
			{
				throw new Exception("AppInstance is of incorrect type!");
			}

			string QualifiedPackageName = GetQualifiedProjectName(LuminInstall.Name);

			// wake the device - we can install while its asleep but not run
			PowerOn();

			// kill any currently running instance:
			KillRunningProcess(QualifiedPackageName);
			
			Log.Info("Launching {0} on '{1}' ", QualifiedPackageName, ToString());
			Log.Verbose("\t{0}", LuminInstall.CommandLine);

			// Clear the device's log in preparation for the test..
			RunMldbDeviceCommand("log -c");

			// start the app on device!
			string CommandLine = "launch --auto-net-privs " + QualifiedPackageName + " -i \"" + LuminInstall.CommandLine + "\"";
			IProcessResult Process = RunMldbDeviceCommand(CommandLine, false, true);

			return new LuminAppInstance(this, LuminInstall, Process);
		}
        public IAppInstance Run(IAppInstall App)
        {
            MacAppInstall MacInstall = App as MacAppInstall;

            if (MacInstall == null)
            {
                throw new AutomationException("Invalid install type!");
            }

            IProcessResult Result = null;

            lock (Globals.MainLock)
            {
                string NewWorkingDir = string.IsNullOrEmpty(MacInstall.WorkingDirectory) ? MacInstall.LocalPath : MacInstall.WorkingDirectory;
                string OldWD         = Environment.CurrentDirectory;
                Environment.CurrentDirectory = NewWorkingDir;

                Log.Info("Launching {0} on {1}", App.Name, ToString());
                Log.Verbose("\t{0}", MacInstall.CommandArguments);

                Result = CommandUtils.Run(MacInstall.ExecutablePath, MacInstall.CommandArguments, Options: MacInstall.RunOptions);

                if (Result.HasExited && Result.ExitCode != 0)
                {
                    throw new AutomationException("Failed to launch {0}. Error {1}", MacInstall.ExecutablePath, Result.ExitCode);
                }

                Environment.CurrentDirectory = OldWD;
            }

            return(new MacAppInstance(MacInstall, Result));
        }
		/// <summary>
		/// Updates cached activity log by running a shell command returning the full log from device (possibly over wifi)
		/// The result is cached and updated at ActivityLogDelta frequency
		/// </summary>
		private void UpdateCachedLog(bool ForceUpdate = false)
		{
			if (!ForceUpdate && (ActivityLogTime == DateTime.MinValue || ((DateTime.UtcNow - ActivityLogTime) < ActivityLogDelta)))
			{
				return;
			}

			if (Install.LuminDevice != null && Install.LuminDevice.Disposed)
			{
				Log.Warning("Attempting to cache log using disposed Lumin device");
				return;
			}

			string GetLogCommand = string.Format("log -d");
			IProcessResult LogQuery = Install.LuminDevice.RunMldbDeviceCommand(GetLogCommand, true, false);

			if (LogQuery.ExitCode != 0)
			{
				Log.VeryVerbose("Unable to query activity stdout on device {0}", Install.LuminDevice.Name);
			}
			else
			{
				ActivityLogCached = LogQuery.Output;
			}

			ActivityLogTime = DateTime.UtcNow;

			// the activity has exited, mark final log sentinel 
			if (ActivityExited)
			{
				ActivityLogTime = DateTime.MinValue;
			}

		}
        /// <summary>
        /// Checks on device whether the activity is running, this is an expensive shell with output operation
        /// the result is cached, with checks at ActivityCheckDelta seconds
        /// </summary>
        private bool IsActivityRunning()
        {
            if (ActivityExited)
            {
                return(true);
            }

            if ((DateTime.UtcNow - ActivityCheckTime) < ActivityCheckDelta)
            {
                return(false);
            }

            ActivityCheckTime = DateTime.UtcNow;

            // get activities filtered by our package name
            IProcessResult ActivityQuery = AndroidDevice.RunAdbDeviceCommand("shell dumpsys activity -p " + Install.AndroidPackageName + " a");

            // We have exited if our activity doesn't appear in the activity query or is not the focused activity.
            bool bActivityPresent      = ActivityQuery.Output.Contains(Install.AndroidPackageName);
            bool bActivityInForeground = ActivityQuery.Output.Contains("mResumedActivity");
            bool bHasExited            = !bActivityPresent || !bActivityInForeground;

            if (bHasExited)
            {
                ActivityExited = true;
                Log.VeryVerbose("{0}: process exited, Activity running={1}, Activity in foreground={2} ", ToString(), bActivityPresent.ToString(), bActivityInForeground.ToString());
            }

            return(bHasExited);
        }
示例#10
0
        // IPA handling using ditto command, which is capable of handling IPA's > 4GB/Zip64
        internal static bool ExecuteIPADittoCommand(String Arguments, out String Output, String ShouldExist = "")
        {
            using (new ScopedSuspendECErrorParsing())
            {
                IProcessResult Result = ExecuteCommand("ditto", Arguments);
                Output = Result.Output;

                if (Result.ExitCode != 0)
                {
                    if (!String.IsNullOrEmpty(ShouldExist))
                    {
                        if (!File.Exists(ShouldExist) && !Directory.Exists(ShouldExist))
                        {
                            Log.Error(String.Format("ditto encountered an error or warning procesing IPA, {0} missing", ShouldExist));
                            return(false);
                        }
                    }

                    Log.Error(String.Format("ditto encountered an issue procesing IPA"));
                    return(false);
                }
            }

            return(true);
        }
示例#11
0
        /// <summary>
        /// Detects the current code changelist the workspace is synced to.
        /// </summary>
        /// <param name="ClientRootPath">Workspace path.</param>
        /// <returns>Changelist number as a string.</returns>
        private static string DetectCurrentCodeCL(P4Connection Connection, string ClientRootPath)
        {
            CommandUtils.LogVerbose("uebp_CodeCL not set, detecting last code CL...");

            // Retrieve the current changelist
            StringBuilder P4Cmd = new StringBuilder("changes -m 1");

            string[] CodeExtensions = { ".cs", ".h", ".cpp", ".inl", ".usf", ".ush", ".uproject", ".uplugin" };
            foreach (string CodeExtension in CodeExtensions)
            {
                P4Cmd.AppendFormat(" \"{0}/...{1}#have\"", CommandUtils.CombinePaths(PathSeparator.Depot, ClientRootPath), CodeExtension);
            }

            IProcessResult P4Result = Connection.P4(P4Cmd.ToString(), AllowSpew: false);

            // Loop through all the lines of the output. Even though we requested one result, we'll get one for each search pattern.
            int CL = 0;

            foreach (string Line in P4Result.Output.Split('\n'))
            {
                string[] Tokens = Line.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (Tokens.Length >= 2)
                {
                    int LineCL = Int32.Parse(Tokens[1]);
                    CL = Math.Max(CL, LineCL);
                }
            }
            return(CL.ToString());
        }
    public override void ExecuteBuild()
    {
        LogInformation("************************* ShooterGameRun");
        string MapToRun = ParseParamValue("map", "/game/maps/sanctuary");

        PushDir(CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/"));
        //		string GameServerExe = CombinePaths(CmdEnv.LocalRoot, @"ShooterGame/Binaries/Win64/ShooterGameServer.exe");
        //		string GameExe = CombinePaths(CmdEnv.LocalRoot, @"ShooterGame/Binaries/Win64/ShooterGame.exe");
        string EditorExe = CombinePaths(CmdEnv.LocalRoot, @"Engine/Binaries/Win64/UE4Editor.exe");

        string ServerLogFile = CombinePaths(CmdEnv.LogFolder, "Server.log");

        string ServerApp = EditorExe;
        string OtherArgs = String.Format("{0} -server -abslog={1} -log", MapToRun, ServerLogFile);
        string StartArgs = "ShooterGame ";

        string ServerCmdLine = StartArgs + OtherArgs;

        IProcessResult ServerProcess = Run(ServerApp, ServerCmdLine, null, ERunOptions.AllowSpew | ERunOptions.NoWaitForExit | ERunOptions.AppMustExist | ERunOptions.NoStdOutRedirect);

        LogFileReaderProcess(ServerLogFile, ServerProcess, (string Output) =>
        {
            if (String.IsNullOrEmpty(Output) == false)
            {
                Console.Write(Output);
            }
            return(true);            // keep reading
        });
    }
        /// <summary>
        /// Checks whether version of deployed bundle matches local IPA
        /// </summary>
        bool CheckDeployedIPA(IOSBuild Build)
        {
            try
            {
                Log.Verbose("Checking deployed IPA hash");

                string         CommandLine = String.Format("--bundle_id {0} --download={1} --to {2}", Build.PackageName, "/Documents/IPAHash.txt", LocalCachePath);
                IProcessResult Result      = ExecuteIOSDeployCommand(CommandLine, 120);

                if (Result.ExitCode != 0)
                {
                    return(false);
                }

                string Hash       = File.ReadAllText(LocalCachePath + "/Documents/IPAHash.txt").Trim();
                string StoredHash = File.ReadAllText(IPAHashFilename).Trim();

                if (Hash == StoredHash)
                {
                    Log.Verbose("Deployed app hash matched cached IPA hash");
                    return(true);
                }
            }
            catch (Exception Ex)
            {
                if (!Ex.Message.Contains("is denied"))
                {
                    Log.Verbose("Unable to pull cached IPA cache from device, cached file may not exist: {0}", Ex.Message);
                }
            }

            Log.Verbose("Deployed app hash doesn't match, IPA will be installed");
            return(false);
        }
        /// <summary>
        /// Starts compiling the provided project file and returns the process. Caller should check HasExited
        /// or call WaitForExit() to get results
        /// </summary>
        /// <param name="ProjectFile"></param>
        /// <returns></returns>
        private static bool CompileAutomationProject(FileReference ProjectFile, Dictionary <string, string> Properties)
        {
            if (!ProjectFile.HasExtension(".csproj"))
            {
                throw new AutomationException(String.Format("Unable to build Project {0}. Not a valid .csproj file.", ProjectFile));
            }
            if (!FileReference.Exists(ProjectFile))
            {
                throw new AutomationException(String.Format("Unable to build Project {0}. Project file not found.", ProjectFile));
            }

            string CmdLine = String.Format("\"{0}\" /verbosity:quiet /nologo /target:Build {1} /p:TreatWarningsAsErrors=false /p:NoWarn=\"612,618,672,1591\" /p:BuildProjectReferences=true",
                                           ProjectFile, GetMsBuildPropertyArguments(Properties));

            // Compile the project
            IProcessResult Result = CommandUtils.Run(CommandUtils.CmdEnv.MsBuildExe, CmdLine);

            if (Result.ExitCode != 0)
            {
                throw new AutomationException(String.Format("Failed to build \"{0}\":{1}{2}", ProjectFile, Environment.NewLine, Result.Output));
            }
            else
            {
                // Remove .Automation.csproj and copy to target dir
                Log.TraceVerbose("Successfully compiled {0}", ProjectFile);
            }
            return(Result.ExitCode == 0);
        }
示例#15
0
        /// <summary>
        /// Runs UBT with the specified commandline. Automatically creates a logfile. When
        /// no LogName is specified, the executable name is used as logfile base name.
        /// </summary>
        /// <param name="Env">Environment to use.</param>
        /// <param name="CommandLine">Commandline to pass on to UBT.</param>
        /// <param name="LogName">Optional logfile name.</param>
        public static void RunUBT(CommandEnvironment Env, string UBTExecutable, string CommandLine)
        {
            if (!FileExists(UBTExecutable))
            {
                throw new AutomationException("Unable to find UBT executable: " + UBTExecutable);
            }

            string BaseLogName = String.Format("UBT-{0}", String.Join("-", SharedUtils.ParseCommandLine(CommandLine).Where(x => !x.Contains('/') && !x.Contains('\\') && !x.StartsWith("-"))));
            string LogName;

            for (int Attempt = 1;; Attempt++)
            {
                LogName = String.Format("{0}.txt", (Attempt == 1)? BaseLogName : String.Format("{0}_{1}", BaseLogName, Attempt));

                FileReference LogLocation = FileReference.Combine(new DirectoryReference(Env.LogFolder), LogName);
                if (!FileReference.Exists(LogLocation))
                {
                    CommandLine += String.Format(" -log=\"{0}\"", LogLocation);
                    break;
                }

                if (Attempt >= 50)
                {
                    throw new AutomationException("Unable to find name for UBT log file after {0} attempts", Attempt);
                }
            }

            IProcessResult Result = Run(UBTExecutable, CommandLine, Options: ERunOptions.AllowSpew | ERunOptions.NoStdOutCapture);

            if (Result.ExitCode != 0)
            {
                throw new AutomationException((ExitCode)Result.ExitCode, "UnrealBuildTool failed. See log for more details. ({0})", CommandUtils.CombinePaths(Env.FinalLogFolder, LogName));
            }
        }
        public IAppInstance Run(IAppInstall App)
        {
            AndroidAppInstall DroidAppInstall = App as AndroidAppInstall;

            if (DroidAppInstall == null)
            {
                throw new Exception("AppInstance is of incorrect type!");
            }

            // wake the device - we can install while its asleep but not run
            PowerOn();

            // kill any currently running instance:
            KillRunningProcess(DroidAppInstall.AndroidPackageName);

            string LaunchActivity = AndroidPlatform.GetLaunchableActivityName();

            Log.Info("Launching {0} on '{1}' ", DroidAppInstall.AndroidPackageName + "/" + LaunchActivity, ToString());
            Log.Verbose("\t{0}", DroidAppInstall.CommandLine);

            // Clear the device's logcat in preparation for the test..
            RunAdbDeviceCommand("logcat --clear");

            // start the app on device!
            string         CommandLine = "shell am start -W -S -n " + DroidAppInstall.AndroidPackageName + "/" + LaunchActivity;
            IProcessResult Process     = RunAdbDeviceCommand(CommandLine, false, true);

            return(new AndroidAppInstance(this, DroidAppInstall, Process));
        }
示例#17
0
        private async Task ProcessRequest(HttpListenerContext context)
        {
            IProcessResult <ContentResult> response = default;
            var textWriter = new StreamWriter(context.Response.OutputStream);

            try
            {
                response = await handleClientRequest
                           .Invoke(context.Request, textWriter);

                if (!response.Successful)
                {
                    throw response.Exception;
                }

                await textWriter.FlushAsync();

                context.Response.StatusCode        = response.Result.StatusCode;
                context.Response.StatusDescription = response.Result.StatusDescription;
                context.Response.ContentType       = response.Result.ContentType;
                context.Response.Close();
            }
            catch (Exception exception)
            {
                var result = await(handleClientException?.Invoke(exception, textWriter)
                                   ?? Task.FromResult <ContentResult>(default));
        /// <summary>
        /// Runs an ADB command at the global scope
        /// </summary>
        /// <param name="Args"></param>
        /// <param name="Wait"></param>
        /// <returns></returns>
        public static IProcessResult RunAdbGlobalCommand(string Args, bool Wait = true, bool bShouldLogCommand = false)
        {
            CommandUtils.ERunOptions RunOptions = CommandUtils.ERunOptions.AppMustExist | CommandUtils.ERunOptions.NoWaitForExit;

            if (Log.IsVeryVerbose)
            {
                RunOptions |= CommandUtils.ERunOptions.AllowSpew;
            }
            else
            {
                RunOptions |= CommandUtils.ERunOptions.NoLoggingOfRunCommand;
            }

            if (bShouldLogCommand)
            {
                Log.Verbose("Running ADB Command: adb {0}", Args);
            }

            IProcessResult Process = AndroidPlatform.RunAdbCommand(null, null, Args, null, RunOptions);

            if (Wait)
            {
                Process.WaitForExit();
            }

            return(Process);
        }
    public override IProcessResult RunClient(ERunOptions ClientRunFlags, string ClientApp, string ClientCmdLine, ProjectParams Params)
    {
        // look for browser
        string BrowserPath = Params.Devices[0].Replace("HTML5@", "");

        // open the webpage
        Int32 ServerPort = 8000;

        var ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Params.RawProjectPath.FullName), CombinePaths(CmdEnv.LocalRoot, "Engine"));

        ConfigCache.GetInt32("/Script/HTML5PlatformEditor.HTML5TargetSettings", "DeployServerPort", out ServerPort);
        string WorkingDirectory = Path.GetDirectoryName(ClientApp);
        string url = Path.GetFileName(ClientApp) + ".html";
        // Are we running via cook on the fly server?
        // find our http url - This is awkward because RunClient doesn't have real information that NFS is running or not.
        bool IsCookOnTheFly = false;

        // 9/24/2014 @fixme - All this is convoluted, clean up.
        // looks like cookonthefly commandline stopped adding protocol or the port :/ hard coding to DEFAULT_TCP_FILE_SERVING_PORT+1 (DEFAULT_HTTP_FILE_SERVING_PORT)
        // This will fail if the NFS server is started with a different port - we need to modify driver .cs script to pass in IP/Port data correctly.

        if (ClientCmdLine.Contains("filehostip"))
        {
            IsCookOnTheFly = true;
            url            = "http://127.0.0.1:41898/" + url;
        }

        if (IsCookOnTheFly)
        {
            url += "?cookonthefly=true";
        }
        else
        {
            url = String.Format("http://localhost:{0}/{1}", ServerPort, url);
        }

        // Check HTML5LaunchHelper source for command line args

        var LowerBrowserPath = BrowserPath.ToLower();
        var ProfileDirectory = Path.Combine(Utils.GetUserSettingDirectory().FullName, "UE4_HTML5", "user");

        string BrowserCommandline = url;

        if (LowerBrowserPath.Contains("chrome"))
        {
            BrowserCommandline += "  " + String.Format("--user-data-dir=\\\"{0}\\\" --enable-logging --no-first-run", Path.Combine(ProfileDirectory, "chrome"));
        }
        else if (LowerBrowserPath.Contains("firefox"))
        {
            BrowserCommandline += "  " + String.Format("-no-remote -profile \\\"{0}\\\"", Path.Combine(ProfileDirectory, "firefox"));
        }

        string LauncherArguments = string.Format(" -Browser=\"{0}\" + -BrowserCommandLine=\"{1}\" -ServerPort=\"{2}\" -ServerRoot=\"{3}\" ", new object[] { BrowserPath, BrowserCommandline, ServerPort, WorkingDirectory });

        var            LaunchHelperPath = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/HTML5LaunchHelper.exe");
        IProcessResult BrowserProcess   = Run(LaunchHelperPath, LauncherArguments, null, ClientRunFlags | ERunOptions.NoWaitForExit);

        return(BrowserProcess);
    }
        public IProcessResult ExecuteIOSDeployCommand(String CommandLine, int WaitTime = 60, bool WarnOnTimeout = true, bool UseDeviceID = true)
        {
            if (UseDeviceID && !IsDefaultDevice)
            {
                CommandLine = String.Format("--id {0} {1}", DeviceName, CommandLine);
            }

            String IOSDeployPath = Path.Combine(Globals.UE4RootDir, "Engine/Extras/ThirdPartyNotUE/ios-deploy/bin/ios-deploy");

            if (!File.Exists(IOSDeployPath))
            {
                throw new AutomationException("Unable to run ios-deploy binary at {0}", IOSDeployPath);
            }

            CommandUtils.ERunOptions RunOptions = CommandUtils.ERunOptions.NoWaitForExit;

            if (Log.IsVeryVerbose)
            {
                RunOptions |= CommandUtils.ERunOptions.AllowSpew;
            }
            else
            {
                RunOptions |= CommandUtils.ERunOptions.NoLoggingOfRunCommand;
            }

            Log.Verbose("ios-deploy executing '{0}'", CommandLine);

            IProcessResult Result = CommandUtils.Run(IOSDeployPath, CommandLine, Options: RunOptions);

            if (WaitTime > 0)
            {
                DateTime StartTime = DateTime.Now;

                Result.ProcessObject.WaitForExit(WaitTime * 1000);

                if (Result.HasExited == false)
                {
                    if ((DateTime.Now - StartTime).TotalSeconds >= WaitTime)
                    {
                        string Message = String.Format("IOSDeployPath timeout after {0} secs: {1}, killing process", WaitTime, CommandLine);

                        if (WarnOnTimeout)
                        {
                            Log.Warning(Message);
                        }
                        else
                        {
                            Log.Info(Message);
                        }

                        Result.ProcessObject.Kill();
                        // wait up to 15 seconds for process exit
                        Result.ProcessObject.WaitForExit(15000);
                    }
                }
            }

            return(Result);
        }
    public static void Deploy(ProjectParams Params)
    {
        Params.ValidateAndLog();
        if (!Params.Deploy)
        {
            return;
        }

        Log("********** DEPLOY COMMAND STARTED **********");

        if (!Params.NoClient)
        {
            var DeployContextList = CreateDeploymentContext(Params, false, false);
            foreach (var SC in DeployContextList)
            {
                if (SC.StageTargetPlatform.DeployViaUFE)
                {
                    string ClientCmdLine = "-run=Deploy ";
                    ClientCmdLine += "-Device=" + string.Join("+", Params.Devices) + " ";
                    ClientCmdLine += "-Targetplatform=" + SC.StageTargetPlatform.PlatformType.ToString() + " ";
                    ClientCmdLine += "-SourceDir=\"" + CombinePaths(Params.BaseStageDirectory, SC.StageTargetPlatform.PlatformType.ToString()) + "\" ";
                    string ClientApp = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/Win64/UnrealFrontend.exe");

                    Log("Deploying via UFE:");
                    Log("\tClientCmdLine: " + ClientCmdLine + "");

                    //@todo UAT: Consolidate running of external applications like UFE (See 'RunProjectCommand' for other instances)
                    PushDir(Path.GetDirectoryName(ClientApp));
                    // Always start client process and don't wait for exit.
                    IProcessResult ClientProcess = Run(ClientApp, ClientCmdLine, null, ERunOptions.NoWaitForExit);
                    PopDir();
                    if (ClientProcess != null)
                    {
                        do
                        {
                            Thread.Sleep(100);
                        }while (ClientProcess.HasExited == false);
                    }
                }
                else
                {
                    SC.StageTargetPlatform.Deploy(Params, SC);
                }
            }
        }
        if (Params.DedicatedServer)
        {
            ProjectParams ServerParams = new ProjectParams(Params);
            ServerParams.Devices = new ParamList <string>(ServerParams.ServerDevice);
            var DeployContextList = CreateDeploymentContext(ServerParams, true, false);
            foreach (var SC in DeployContextList)
            {
                SC.StageTargetPlatform.Deploy(ServerParams, SC);
            }
        }

        Log("********** DEPLOY COMMAND COMPLETED **********");
    }
示例#22
0
        /// <summary>
        /// Execute the task.
        /// </summary>
        /// <param name="Job">Information about the current job</param>
        /// <param name="BuildProducts">Set of build products produced by this node.</param>
        /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param>
        public override void Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet)
        {
            IProcessResult Result = CommandUtils.Run(Parameters.Exe, Parameters.Arguments);

            if (Result.ExitCode < 0 || Result.ExitCode >= Parameters.ErrorLevel)
            {
                throw new AutomationException("{0} terminated with an exit code indicating an error ({1})", Path.GetFileName(Parameters.Exe), Result.ExitCode);
            }
        }
	public IOSClientProcess(IProcessResult inChildProcess, string inDeviceID)
	{
		childProcess = inChildProcess;
		
		// Startup another thread that collect device console logs
		processConsoleLogs = true;
		consoleLogWorker = new Thread(() => ProcessConsoleOutput(inDeviceID));
		consoleLogWorker.Start();
	}
示例#24
0
    private static void RunInternal(ProjectParams Params, string ServerLogFile, string ClientLogFile)
    {
        // Setup server process if required.
        if (Params.DedicatedServer && !Params.SkipServer)
        {
            if (Params.ServerTargetPlatforms.Count > 0)
            {
                TargetPlatformDescriptor ServerPlatformDesc = Params.ServerTargetPlatforms[0];
                ServerProcess = RunDedicatedServer(Params, ServerLogFile, Params.RunCommandline);
                // With dedicated server, the client connects to local host to load a map.
                if (ServerPlatformDesc.Type == UnrealTargetPlatform.Linux)
                {
                    Params.MapToRun = Params.ServerDeviceAddress;
                }
                else
                {
                    Params.MapToRun = "127.0.0.1";
                }
            }
            else
            {
                throw new AutomationException("Failed to run, server target platform not specified");
            }
        }
        else if (Params.FileServer && !Params.SkipServer)
        {
            ServerProcess = RunFileServer(Params, ServerLogFile, Params.RunCommandline);
        }

        if (ServerProcess != null)
        {
            Log("Waiting a few seconds for the server to start...");
            Thread.Sleep(5000);
        }

        if (!Params.NoClient)
        {
            Log("Starting Client....");

            var SC = CreateDeploymentContext(Params, false);

            ERunOptions ClientRunFlags;
            string      ClientApp;
            string      ClientCmdLine;
            SetupClientParams(SC, Params, ClientLogFile, out ClientRunFlags, out ClientApp, out ClientCmdLine);

            // Run the client.
            if (ServerProcess != null)
            {
                RunClientWithServer(SC, ServerLogFile, ServerProcess, ClientApp, ClientCmdLine, ClientRunFlags, ClientLogFile, Params);
            }
            else
            {
                RunStandaloneClient(SC, ClientLogFile, ClientRunFlags, ClientApp, ClientCmdLine, Params);
            }
        }
    }
示例#25
0
    IEnumerable <string> EnumerateBuildFolders(string Target, string Configuration, string Platform, bool DebugRun)
    {
        FileReference OutputFile = FileReference.Combine(CommandUtils.EngineDirectory, "Intermediate", "Build", "ThirdParty.json");

        IProcessResult Result = Run(UE4Build.GetUBTExecutable(), String.Format("{0} {1} {2} -jsonexport=\"{3}\" -skipbuild", Target, Configuration, Platform, OutputFile.FullName), Options: DebugRun ? ERunOptions.Default : ERunOptions.NoLoggingOfRunCommand);

        if (Result.ExitCode != 0)
        {
            throw new AutomationException("Failed to run UBT");
        }

        JsonObject Object = JsonObject.Read(OutputFile);

        // local function that takes a RuntimeDependency path and resolves it (replacing Env vars that we support)
        Func <string, DirectoryReference> ResolveRuntimeDependencyFolder = (string DependencyPath) =>
        {
            return(new DirectoryReference(Path.GetDirectoryName(
                                              // Regex to replace the env vars we support $(EngineDir|ProjectDir), ignoring case
                                              Regex.Replace(DependencyPath, @"\$\((?<Type>Engine|Project)Dir\)", M =>
                                                            M.Groups["Type"].Value.Equals("Engine", StringComparison.InvariantCultureIgnoreCase)
                                                ? CommandUtils.EngineDirectory.FullName
                                                : new FileReference(Object.GetStringField("ProjectFile")).Directory.FullName,
                                                            RegexOptions.IgnoreCase))));
        };

        JsonObject Modules = Object.GetObjectField("Modules");

        // Create a set of directories used for each binary
        List <DirectoryReference> DirectoriesToScan = Modules
                                                      // get all directories that the module uses (source folder and any runtime dependencies)
                                                      .KeyNames.Select(KeyName => Modules.GetObjectField(KeyName))
                                                      .SelectMany(Module => Module
                                                                  // resolve any runtime dependency folders and add them.
                                                                  .GetObjectArrayField("RuntimeDependencies").Select(Dependency => ResolveRuntimeDependencyFolder(Dependency.GetStringField("Path")))
                                                      // Add on the module source directory
                                                                  .Concat(new[] { new DirectoryReference(Module.GetStringField("Directory")) }))
                                                      // remove any duplicate folders since some modules may be from the same plugin
                                                      .Distinct()
                                                      // Project to a list as we need to do an O(n^2) operation below.
                                                      .ToList();

        List <string> FinalDirs = DirectoriesToScan.Where(RemovalCandidate =>
                                                          // O(n^2) search to remove subfolders of any we are already searching.
                                                          // look for directories that aren't subdirectories of any other directory in the list.
                                                          !DirectoriesToScan.Any(DirectoryToScan =>
                                                          // != check because this inner loop will eventually check against itself
                                                                                 RemovalCandidate != DirectoryToScan &&
                                                                                 RemovalCandidate.IsUnderDirectory(DirectoryToScan)))
                                  // grab the full name
                                  .Select(Dir => Dir.FullName)
                                  // sort the final output
                                  .OrderBy(Dir => Dir)
                                  // log the folders
                                  .ToList();

        return(FinalDirs);
    }
        private static string GetBundleIdentifier(string SourceIPA)
        {
            // Get a list of files in the IPA
            IProcessResult Result = ExecuteCommand("unzip", String.Format("-Z1 {0}", SourceIPA));

            if (Result.ExitCode != 0)
            {
                Log.Warning(String.Format("Unable to list files for IPA {0}", SourceIPA));
                return(null);
            }

            string[] Filenames = Regex.Split(Result.Output, "\r\n|\r|\n");
            string   PList     = Filenames.Where(F => F.ToLower().Contains("info.plist")).FirstOrDefault();

            if (String.IsNullOrEmpty(PList))
            {
                Log.Warning(String.Format("Unable to find plist for IPA {0}", SourceIPA));
                return(null);
            }

            // Get the plist info
            Result = ExecuteCommand("unzip", String.Format("-p '{0}' '{1}'", SourceIPA, PList));

            if (Result.ExitCode != 0)
            {
                Log.Warning(String.Format("Unable to extract plist data for IPA {0}", SourceIPA));
                return(null);
            }

            string PlistInfo = Result.Output;

            // todo: plist parsing, could be better
            string PackageName = null;
            string KeyString   = "<key>CFBundleIdentifier</key>";
            int    KeyIndex    = PlistInfo.IndexOf(KeyString);

            if (KeyIndex > 0)
            {
                int StartIdx = PlistInfo.IndexOf("<string>", KeyIndex + KeyString.Length) + "<string>".Length;
                int EndIdx   = PlistInfo.IndexOf("</string>", StartIdx);
                if (StartIdx > 0 && EndIdx > StartIdx)
                {
                    PackageName = PlistInfo.Substring(StartIdx, EndIdx - StartIdx);
                }
            }

            if (String.IsNullOrEmpty(PackageName))
            {
                Log.Warning(String.Format("Unable to find CFBundleIdentifier in plist info for IPA {0}", SourceIPA));
                return(null);
            }

            Log.Verbose("Found bundle id: {0}", PackageName);

            return(PackageName);
        }
示例#27
0
        /// <summary>
        /// Run the client application on the platform
        /// </summary>
        /// <param name="ClientRunFlags"></param>
        /// <param name="ClientApp"></param>
        /// <param name="ClientCmdLine"></param>
        public virtual IProcessResult RunClient(ERunOptions ClientRunFlags, string ClientApp, string ClientCmdLine, ProjectParams Params)
        {
            PushDir(Path.GetDirectoryName(ClientApp));
            // Always start client process and don't wait for exit.
            IProcessResult ClientProcess = Run(ClientApp, ClientCmdLine, null, ClientRunFlags | ERunOptions.NoWaitForExit);

            PopDir();

            return(ClientProcess);
        }
示例#28
0
        protected bool RunEditorAndWaitForMapLoad(bool bIsWarming)
        {
            string ProjectArg = ProjectFile != null?ProjectFile.ToString() : "";

            string EditorPath = HostPlatform.Current.GetUE4ExePath("UE4Editor.exe");
            string LogArg     = string.Format("-log={0}.log", MakeValidFileName(GetFullTaskName()).Replace(" ", "_"));
            string Arguments  = string.Format("{0} {1} -execcmds=\"automation runtest System.Maps.PIE;Quit\" -stdout -FullStdOutLogOutput -unattended {2}", ProjectArg, EditorArgs, LogArg);

            if (TaskOptions.HasFlag(DDCTaskOptions.NoSharedDDC))
            {
                Arguments += (" -ddc=noshared");
            }

            if (!bIsWarming && TaskOptions.HasFlag(DDCTaskOptions.NoShaderDDC))
            {
                Arguments += (" -noshaderddc");
            }

            if (TaskOptions.HasFlag(DDCTaskOptions.NoXGE))
            {
                Arguments += (" -noxgeshadercompile");
            }

            var RunOptions = CommandUtils.ERunOptions.AllowSpew | CommandUtils.ERunOptions.NoWaitForExit;

            var SpewDelegate = new ProcessResult.SpewFilterCallbackType(EndOnMapCheckFilter);

            //TestCompleted = false;
            LastStdOutTime = DateTime.Now;
            CurrentProcess = CommandUtils.Run(EditorPath, Arguments, Options: RunOptions, SpewFilterCallback: SpewDelegate);

            DateTime StartTime = DateTime.Now;

            int MaxWaitMins = 90;

            while (!CurrentProcess.HasExited)
            {
                Thread.Sleep(5 * 1000);

                lock (CurrentProcess)
                {
                    if ((LastStdOutTime - StartTime).TotalMinutes >= MaxWaitMins)
                    {
                        Log.TraceError("Gave up waiting for task after {0} minutes of no output", MaxWaitMins);
                        CurrentProcess.ProcessObject.Kill();
                    }
                }
            }

            int ExitCode = CurrentProcess.ExitCode;

            CurrentProcess = null;

            return(ExitCode == 0);
        }
示例#29
0
        /// <summary>
        /// Runs UBT with the specified commandline. Automatically creates a logfile. When
        /// no LogName is specified, the executable name is used as logfile base name.
        /// </summary>
        /// <param name="Env">Environment to use.</param>
        /// <param name="CommandLine">Commandline to pass on to UBT.</param>
        /// <param name="LogName">Optional logfile name.</param>
        public static void RunUBT(CommandEnvironment Env, string UBTExecutable, string CommandLine)
        {
            if (!FileExists(UBTExecutable))
            {
                throw new AutomationException("Unable to find UBT executable: " + UBTExecutable);
            }

            if (GlobalCommandLine.VS2015)
            {
                CommandLine += " -2015";
            }
            if (!IsBuildMachine && UnrealBuildTool.BuildHostPlatform.Current.Platform == UnrealBuildTool.UnrealTargetPlatform.Mac)
            {
                CommandLine += " -nocreatestub";
            }
            CommandLine += " -NoHotReload";
            if (bJunkDeleted || GlobalCommandLine.IgnoreJunk)
            {
                // UBT has already deleted junk files, make sure it doesn't do it again
                CommandLine += " -ignorejunk";
            }
            else
            {
                // UBT will delete junk on first run
                bJunkDeleted = true;
            }

            string BaseLogName = String.Format("UBT-{0}", String.Join("-", SharedUtils.ParseCommandLine(CommandLine).Where(x => !x.Contains('/') && !x.Contains('\\') && !x.StartsWith("-"))));
            string LogName;

            for (int Attempt = 1;; Attempt++)
            {
                LogName = String.Format("{0}.txt", (Attempt == 1)? BaseLogName : String.Format("{0}_{1}", BaseLogName, Attempt));

                FileReference LogLocation = FileReference.Combine(new DirectoryReference(Env.LogFolder), LogName);
                if (!FileReference.Exists(LogLocation))
                {
                    CommandLine += String.Format(" -log=\"{0}\"", LogLocation);
                    break;
                }

                if (Attempt >= 50)
                {
                    throw new AutomationException("Unable to find name for UBT log file after {0} attempts", Attempt);
                }
            }

            IProcessResult Result = Run(UBTExecutable, CommandLine, Options: ERunOptions.AllowSpew | ERunOptions.NoStdOutCapture);

            if (Result.ExitCode != 0)
            {
                throw new AutomationException((ExitCode)Result.ExitCode, "UnrealBuildTool failed. See log for more details. ({0})", CommandUtils.CombinePaths(Env.FinalLogFolder, LogName));
            }
        }
        private string GetUrlComponent(string code)
        {
            IProcessResult processResult = jarRunner.RunJarWithInput(code, "-encodeurl", "-pipe");

            if (processResult.ExitCode != 0)
            {
                string message = UTF8.GetString(processResult.Error);
                throw new RenderingException(code, message);
            }

            return(UTF8.GetString(processResult.Output));
        }
        public LocalAppProcess(IProcessResult InProcess, string InCommandLine, string InProcessLogFile = null)
        {
            this.CommandLine    = InCommandLine;
            this.ProcessResult  = InProcess;
            this.ProcessLogFile = InProcessLogFile;

            // start reader thread if logging to a file
            if (!string.IsNullOrEmpty(InProcessLogFile))
            {
                new System.Threading.Thread(LogFileReaderThread).Start();
            }
        }
示例#32
0
		/// <summary>
		/// Allow platform specific clean-up or detection after client has run
		/// </summary>
		/// <param name="ClientRunFlags"></param>
		public virtual void PostRunClient(IProcessResult Result, ProjectParams Params)
		{
			// do nothing in the default case
		}
		/// <summary>
		/// Keeps reading a log file as it's being written to by another process until it exits.
		/// </summary>
		/// <param name="LogFilename">Name of the log file.</param>
		/// <param name="LogProcess">Process that writes to the log file.</param>
		/// <param name="OnLogRead">Callback used to process the recently read log contents.</param>
		protected static void LogFileReaderProcess(string LogFilename, IProcessResult LogProcess, ProcessLog OnLogRead = null)
		{
			while (!FileExists(LogFilename) && !LogProcess.HasExited)
			{
				Log("Waiting for logging process to start...");
				Thread.Sleep(2000);
			}
			Thread.Sleep(1000);

			using (FileStream ProcessLog = File.Open(LogFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
			{
				StreamReader LogReader = new StreamReader(ProcessLog);
				bool bKeepReading = true;

				// Read until the process has exited.
				while (!LogProcess.HasExited && bKeepReading)
				{
					while (!LogReader.EndOfStream && bKeepReading)
					{
						string Output = LogReader.ReadToEnd();
						if (Output != null && OnLogRead != null)
						{
							bKeepReading = OnLogRead(Output);
						}
					}

					while (LogReader.EndOfStream && !LogProcess.HasExited && bKeepReading)
					{
						Thread.Sleep(250);
						// Tick the callback so that it can respond to external events
						if (OnLogRead != null)
						{
							bKeepReading = OnLogRead(null);
						}
					}
				}
			}
		}
        public override IServiceResult ParseResult(VersionControlArguments args, IProcessResult processResult)
        {
            var serializer = new VaultResultSerializer();

            var vaultResult = serializer.Deserialize(processResult.StandardOutput);

            if (!vaultResult.Success)
            {
                return new ServiceResult(ServiceResultCode.Error);
            }

            var result = new ServiceResult(ServiceResultCode.Success);

            switch (args.Operation)
            {
                case VersionControlOperation.GetLocalVersion:

                    // Find the file we were trying to get the version for
                    foreach (var file in vaultResult.Folder.Files)
                    {
                        if (args.SourcePath.EndsWith(file.Name))
                        {
                            result.ResultValue = file.Version;
                        }
                    }

                    break;

                default:
                    throw new ArgumentOutOfRangeException();
            }

            return result;
        }
 /// <summary>
 /// Parses the result of executing the VCS executable.
 /// </summary>
 /// <param name="args"></param>
 /// <param name="processResult"></param>
 /// <returns></returns>
 public abstract IServiceResult ParseResult(VersionControlArguments args, IProcessResult processResult);
	private static void RunInternal(ProjectParams Params, string ServerLogFile, string ClientLogFile)
	{
		// Setup server process if required.
		if (Params.DedicatedServer && !Params.SkipServer)
		{
			if (Params.ServerTargetPlatforms.Count > 0)
			{
				TargetPlatformDescriptor ServerPlatformDesc = Params.ServerTargetPlatforms[0];
				ServerProcess = RunDedicatedServer(Params, ServerLogFile, Params.RunCommandline);
				// With dedicated server, the client connects to local host to load a map.
				if (ServerPlatformDesc.Type == UnrealTargetPlatform.Linux)
				{
					Params.MapToRun = Params.ServerDeviceAddress;
				}
				else
				{
					Params.MapToRun = "127.0.0.1";
				}
			}
			else
			{
				throw new AutomationException("Failed to run, server target platform not specified");
			}
		}
		else if (Params.FileServer && !Params.SkipServer)
		{
			ServerProcess = RunFileServer(Params, ServerLogFile, Params.RunCommandline);
		}

		if (ServerProcess != null)
		{
			Log("Waiting a few seconds for the server to start...");
			Thread.Sleep(5000);
		}

		if (!Params.NoClient)
		{
			Log("Starting Client....");

			var SC = CreateDeploymentContext(Params, false);

			ERunOptions ClientRunFlags;
			string ClientApp;
			string ClientCmdLine;
			SetupClientParams(SC, Params, ClientLogFile, out ClientRunFlags, out ClientApp, out ClientCmdLine);

			// Run the client.
			if (ServerProcess != null)
			{
				RunClientWithServer(SC, ServerLogFile, ServerProcess, ClientApp, ClientCmdLine, ClientRunFlags, ClientLogFile, Params);
			}
			else
			{
				RunStandaloneClient(SC, ClientLogFile, ClientRunFlags, ClientApp, ClientCmdLine, Params);
			}
		}
	}
	private static void RunClientWithServer(List<DeploymentContext> DeployContextList, string ServerLogFile, IProcessResult ServerProcess, string ClientApp, string ClientCmdLine, ERunOptions ClientRunFlags, string ClientLogFile, ProjectParams Params)
	{
		IProcessResult ClientProcess = null;
		var OtherClients = new List<IProcessResult>();

		bool WelcomedCorrectly = false;
		int NumClients = Params.NumClients;
		string AllClientOutput = "";
		int LastAutoFailIndex = -1;

		if (Params.Unattended)
		{
			string LookFor = "Bringing up level for play took";
			if (Params.DedicatedServer)
			{
				LookFor = "Welcomed by server";
			}
			else if (Params.RunAutomationTest != "")
			{
				LookFor = "Automation Test Succeeded";
			}
			else if (Params.RunAutomationTests)
			{
				LookFor = "Automation Test Queue Empty";
			}
			{
				while (!FileExists(ServerLogFile) && !ServerProcess.HasExited)
				{
					Log("Waiting for logging process to start...");
					Thread.Sleep(2000);
				}
				Thread.Sleep(1000);

				string AllServerOutput = "";
				using (FileStream ProcessLog = File.Open(ServerLogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
				{
					StreamReader LogReader = new StreamReader(ProcessLog);
					bool bKeepReading = true;

					FileStream ClientProcessLog = null;
					StreamReader ClientLogReader = null;

					// Read until the process has exited.
					while (!ServerProcess.HasExited && bKeepReading)
					{
						while (!LogReader.EndOfStream && bKeepReading && ClientProcess == null)
						{
							string Output = LogReader.ReadToEnd();
							if (!String.IsNullOrEmpty(Output))
							{
								AllServerOutput += Output;
								if (ClientProcess == null &&
										(AllServerOutput.Contains("Game Engine Initialized") || AllServerOutput.Contains("Unreal Network File Server is ready")))
								{
									Log("Starting Client for unattended test....");
									ClientProcess = Run(ClientApp, ClientCmdLine + " -FORCELOGFLUSH -testexit=\"" + LookFor + "\"", null, ClientRunFlags | ERunOptions.NoWaitForExit);
									//@todo no testing is done on these
									if (NumClients > 1 && NumClients < 9)
									{
										for (int i = 1; i < NumClients; i++)
										{
											Log("Starting Extra Client....");
											OtherClients.Add(Run(ClientApp, ClientCmdLine, null, ClientRunFlags | ERunOptions.NoWaitForExit));
										}
									}
									while (!FileExists(ClientLogFile) && !ClientProcess.HasExited)
									{
										Log("Waiting for client logging process to start...{0}", ClientLogFile);
										Thread.Sleep(2000);
									}
									if (!ClientProcess.HasExited)
									{
										Thread.Sleep(2000);
										Log("Client logging process started...{0}", ClientLogFile);
										ClientProcessLog = File.Open(ClientLogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
										ClientLogReader = new StreamReader(ClientProcessLog);
									}
								}
								else if (ClientProcess == null && !ServerProcess.HasExited)
								{
									Log("Waiting for server to start....");
									Thread.Sleep(2000);
								}
								if (ClientProcess != null && ClientProcess.HasExited)
								{
									ServerProcess.StopProcess();
									throw new AutomationException("Client exited before we asked it to.");
								}
							}
						}
						if (ClientLogReader != null)
						{
							if (ClientProcess.HasExited)
							{
								ServerProcess.StopProcess();
								throw new AutomationException("Client exited or closed the log before we asked it to.");
							}
							while (!ClientProcess.HasExited && !ServerProcess.HasExited && bKeepReading)
							{
								while (!ClientLogReader.EndOfStream && bKeepReading && !ServerProcess.HasExited && !ClientProcess.HasExited)
								{
									string ClientOutput = ClientLogReader.ReadToEnd();
									if (!String.IsNullOrEmpty(ClientOutput))
									{
										AllClientOutput += ClientOutput;
										Console.Write(ClientOutput);

										if (AllClientOutput.LastIndexOf(LookFor) > AllClientOutput.IndexOf(LookFor))
										{
											if (Params.FakeClient)
											{
												Log("Welcomed by server or client loaded, lets wait ten minutes...");
												Thread.Sleep(60000 * 10);
											}
											else
											{
												Log("Welcomed by server or client loaded, lets wait 30 seconds...");
												Thread.Sleep(30000);
											}
											WelcomedCorrectly = true;
											bKeepReading = false;
										}
										else if (Params.RunAutomationTests)
										{
											int FailIndex = AllClientOutput.LastIndexOf("Automation Test Failed");
											int ParenIndex = AllClientOutput.LastIndexOf(")");
											if (FailIndex >= 0 && ParenIndex > FailIndex && FailIndex > LastAutoFailIndex)
											{
												string Tail = AllClientOutput.Substring(FailIndex);
												int CloseParenIndex = Tail.IndexOf(")");
												int OpenParenIndex = Tail.IndexOf("(");
												string Test = "";
												if (OpenParenIndex >= 0 && CloseParenIndex > OpenParenIndex)
												{
													Test = Tail.Substring(OpenParenIndex + 1, CloseParenIndex - OpenParenIndex - 1);
													LogError("Automated test failed ({0}).", Test);
													LastAutoFailIndex = FailIndex;
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		else
		{

			LogFileReaderProcess(ServerLogFile, ServerProcess, (string Output) =>
			{
				bool bKeepReading = true;
				if (ClientProcess == null && !String.IsNullOrEmpty(Output))
				{
					AllClientOutput += Output;
					if (ClientProcess == null && (AllClientOutput.Contains("Game Engine Initialized") || AllClientOutput.Contains("Unreal Network File Server is ready")))
					{
						Log("Starting Client....");
						var SC = DeployContextList[0];
						ClientProcess = SC.StageTargetPlatform.RunClient(ClientRunFlags | ERunOptions.NoWaitForExit, ClientApp, ClientCmdLine, Params);
//						ClientProcess = Run(ClientApp, ClientCmdLine, null, ClientRunFlags | ERunOptions.NoWaitForExit);
						if (NumClients > 1 && NumClients < 9)
						{
							for (int i = 1; i < NumClients; i++)
							{
								Log("Starting Extra Client....");
								IProcessResult NewClient = SC.StageTargetPlatform.RunClient(ClientRunFlags | ERunOptions.NoWaitForExit, ClientApp, ClientCmdLine, Params);
								OtherClients.Add(NewClient);
							}
						}
					}
				}
				else if (ClientProcess == null && !ServerProcess.HasExited)
				{
					Log("Waiting for server to start....");
					Thread.Sleep(2000);
				}

				if (String.IsNullOrEmpty(Output) == false)
				{
					Console.Write(Output);
				}

				if (ClientProcess != null && ClientProcess.HasExited)
				{

					Log("Client exited, stopping server....");
					if (!GlobalCommandLine.NoKill)
					{
						ServerProcess.StopProcess();
					}
					bKeepReading = false;
				}

				return bKeepReading; // Keep reading
			});
		}
		Log("Server exited....");
		if (ClientProcess != null && !ClientProcess.HasExited)
		{
			ClientProcess.StopProcess();
		}
		foreach (var OtherClient in OtherClients)
		{
			if (OtherClient != null && !OtherClient.HasExited)
			{
				OtherClient.StopProcess();
			}
		}
		if (Params.Unattended)
		{
			if (!WelcomedCorrectly)
			{
				throw new AutomationException("Server or client exited before we asked it to.");
			}
		}
	}
	public override void PostRunClient(IProcessResult Result, ProjectParams Params)
	{
		if (UnrealBuildTool.BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
		{
			string LaunchTracePath = Params.BaseStageDirectory + "/" + PlatformName + "/launch.trace";
			Console.WriteLine ("Deleting " + LaunchTracePath);
			if (Directory.Exists(LaunchTracePath))
			{
				Directory.Delete (LaunchTracePath, true);
			}

			switch (Result.ExitCode)
			{
				case 253:
                    throw new AutomationException(ExitCode.Error_DeviceNotSetupForDevelopment, "Launch Failure");
				case 255:
                    throw new AutomationException(ExitCode.Error_DeviceOSNewerThanSDK, "Launch Failure");
			}
		}
	}