private static Process StartProcess(ShellProcessArgs args) { var startInfo = new ProcessStartInfo() { FileName = args.Executable, Arguments = string.Join(" ", args.Arguments), WorkingDirectory = args.WorkingDirectory?.FullName ?? new DirectoryInfo(".").FullName, RedirectStandardInput = true, RedirectStandardOutput = true, StandardOutputEncoding = Encoding.UTF8, RedirectStandardError = true, StandardErrorEncoding = Encoding.UTF8, CreateNoWindow = true, UseShellExecute = false }; if (args.EnvironmentVariables != null) { foreach (var pair in args.EnvironmentVariables) { startInfo.EnvironmentVariables[pair.Key] = pair.Value; } } var process = new Process { StartInfo = startInfo }; if (args.OutputDataReceived != null) { process.OutputDataReceived += args.OutputDataReceived; } if (args.ErrorDataReceived != null) { process.ErrorDataReceived += args.ErrorDataReceived; } process.Start(); if (args.OutputDataReceived != null) { process.BeginOutputReadLine(); } if (args.ErrorDataReceived != null) { process.BeginErrorReadLine(); } return(process); }
public static ShellProcessOutput Run(ShellProcessArgs shellArgs) { Assert.IsNotNull(shellArgs); Assert.IsFalse(string.IsNullOrEmpty(shellArgs.Executable)); var command = string.Join(" ", shellArgs.Executable.AsArray().Concat(shellArgs.Arguments)); try { var runOutput = new ShellProcessOutput(); var hasErrors = false; var output = new StringBuilder(); var logOutput = new StringBuilder(); var errorOutput = new StringBuilder(); // Setup shell command if (shellArgs.ExtraPaths != null) { var extraPaths = string.Join(k_PathSeparator.ToString(), shellArgs.ExtraPaths.Select(p => p.DoubleQuoted())); #if UNITY_EDITOR_WIN command = $"SET PATH={extraPaths}{k_PathSeparator}%PATH%{Environment.NewLine}{command}"; #else command = $"export PATH={extraPaths}{k_PathSeparator}$PATH{Environment.NewLine}{command}"; #endif } LogProcessData($"TINY SHELL> {shellArgs.WorkingDirectory?.FullName ?? Application.RootDirectory.FullName}", logOutput); LogProcessData(command, logOutput); // Setup temporary command file var tmpCommandFile = Path.GetTempPath() + Guid.NewGuid().ToString(); #if UNITY_EDITOR_WIN tmpCommandFile += ".bat"; #else tmpCommandFile += ".sh"; #endif File.WriteAllText(tmpCommandFile, command); // Prepare data received handlers DataReceivedEventHandler outputReceived = (sender, e) => { LogProcessData(e.Data, output); logOutput.AppendLine(e.Data); }; DataReceivedEventHandler errorReceived = (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { errorOutput.AppendLine(e.Data); hasErrors = true; } LogProcessData(e.Data, output); logOutput.AppendLine(e.Data); }; // Run command in shell and wait for exit try { using (var process = StartProcess(new ShellProcessArgs() { #if UNITY_EDITOR_WIN Executable = "cmd.exe", Arguments = new string[] { "/Q", "/C", tmpCommandFile.DoubleQuoted() }, #else Executable = "bash", Arguments = tmpCommandFile.DoubleQuoted().AsArray(), #endif WorkingDirectory = shellArgs.WorkingDirectory, OutputDataReceived = outputReceived, ErrorDataReceived = errorReceived })) { var processUpdate = WaitForProcess(process, shellArgs.MaxIdleTimeInMilliseconds); while (processUpdate.MoveNext()) { } var exitCode = runOutput.ExitCode = processUpdate.Current == ProcessStatus.Killed ? -1 : process.ExitCode; runOutput.Command = command; runOutput.CommandOutput = output; runOutput.FullOutput = logOutput.ToString(); runOutput.ErrorOutput = errorOutput.ToString(); LogProcessData($"Process exited with code '{exitCode}'", logOutput); hasErrors |= (exitCode != 0); } } finally { File.Delete(tmpCommandFile); } if (hasErrors && shellArgs.ThrowOnError) { throw new Exception(errorOutput.ToString()); } runOutput.Succeeded = !hasErrors; return(runOutput); } catch (Exception) { //TinyEditorAnalytics.SendException(nameof(Shell.Run), e); throw; } }
public static Process RunAsync(ShellProcessArgs args) { return(StartProcess(args)); }
public static IEnumerator <ProgressInfo> Run(string arguments, StringBuilder command, StringBuilder output, DirectoryInfo workingDirectory = null) { var beeExe = Path.GetFullPath("Packages/com.unity.tiny/DotsPlayer/bee~/bee.exe"); var executable = beeExe; arguments = "--no-colors " + arguments; #if !UNITY_EDITOR_WIN arguments = executable.DoubleQuoted() + " " + arguments; executable = Application.MonoDirectory.GetFile("mono").FullName; #endif command.Append(executable); command.Append(" "); command.Append(arguments); var progressInfo = new ProgressInfo() { Progress = 0.0f, Info = null }; void ProgressHandler(object sender, DataReceivedEventArgs args) { if (args.Data != null) { lock (output) { output.AppendLine(args.Data); } } var msg = args.Data; if (string.IsNullOrWhiteSpace(msg)) { return; } progressInfo.FullInfo = msg; var match = BeeProgressRegex.Match(msg); if (match.Success) { var num = match.Groups[1].Value; var den = match.Groups[2].Value; if (int.TryParse(num, out var numInt) && int.TryParse(den, out var denInt)) { progressInfo.Progress = (float)numInt / denInt; } progressInfo.Info = match.Groups[3].Value; } else { progressInfo.Progress = float.MinValue; progressInfo.Info = null; } } var config = new ShellProcessArgs() { Executable = executable, Arguments = arguments.AsEnumerable(), WorkingDirectory = workingDirectory, #if !UNITY_EDITOR_WIN // bee requires external programs to perform build actions EnvironmentVariables = new Dictionary <string, string>() { { "PATH", string.Join(":", Application.MonoDirectory.FullName, "/bin", "/usr/bin", "/usr/local/bin") } }, #else EnvironmentVariables = null, #endif OutputDataReceived = ProgressHandler, ErrorDataReceived = ProgressHandler }; var bee = Shell.RunAsync(config); yield return(progressInfo); const int maxBuildTimeInMs = 30 * 60 * 1000; // 30 minutes var statusEnum = Shell.WaitForProcess(bee, maxBuildTimeInMs); while (statusEnum.MoveNext()) { yield return(progressInfo); } progressInfo.Progress = 1.0f; progressInfo.IsDone = true; progressInfo.ExitCode = bee.ExitCode; progressInfo.Info = "Build completed"; yield return(progressInfo); }