Skip to content
代码片段 群组 项目
MSBuildProcessManager.cs 4.3 KB
更新 更旧
  • 了解如何忽略特定修订
  • // Copyright (c) .NET Foundation. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
    
    using System;
    using System.Diagnostics;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
    {
        internal static class MSBuildProcessManager
        {
    
            public static Task<MSBuildResult> RunProcessAsync(
                ProjectDirectory project,
                string arguments,
                TimeSpan? timeout = null,
                MSBuildProcessKind msBuildProcessKind = MSBuildProcessKind.Dotnet)
    
                timeout = timeout ?? TimeSpan.FromSeconds(120);
    
                var processStartInfo = new ProcessStartInfo()
    
                    WorkingDirectory = project.DirectoryPath,
                    UseShellExecute = false,
                    RedirectStandardError = true,
                    RedirectStandardOutput = true,
                };
    
                if (msBuildProcessKind == MSBuildProcessKind.Desktop)
                {
                    if (string.IsNullOrEmpty(BuildVariables.MSBuildPath))
    
    Pranav K's avatar
    Pranav K 已提交
                        throw new ArgumentException("Unable to locate MSBuild.exe to run desktop tests. " +
                            "MSBuild.exe is located using state created as part of running build[cmd|sh] at the root of the repository. Run build /t:Prepeare to set this up if this hasn't been done.");
    
                    }
    
                    processStartInfo.FileName = BuildVariables.MSBuildPath;
                    processStartInfo.Arguments = arguments;
                }
                else
                {
                    processStartInfo.FileName = "dotnet";
                    processStartInfo.Arguments = $"msbuild {arguments}";
                }
    
                var process = new Process()
                {
                    StartInfo = processStartInfo,
    
                    EnableRaisingEvents = true,
                };
    
                var output = new StringBuilder();
    
                process.ErrorDataReceived += Process_ErrorDataReceived;
                process.OutputDataReceived += Process_OutputDataReceived;
    
                process.Start();
                process.BeginErrorReadLine();
                process.BeginOutputReadLine();
    
                var timeoutTask = Task.Delay(timeout.Value).ContinueWith((t) =>
                {
                    // Don't timeout during debug sessions
                    while (Debugger.IsAttached)
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(1));
                    }
    
                    if (process.HasExited)
                    {
                        // This will happen on success, the 'real' task has already completed so this value will
                        // never be visible.
                        return (MSBuildResult)null;
                    }
    
                    // This is a timeout.
                    process.Kill();
    
                    throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}. Output: {output.ToString()}");
    
                var waitTask = Task.Run(() =>
    
                    // We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
                    // this code used Process.Exited, which could result in us missing some output due to the ordering of
                    // events.
                    //
                    // See the remarks here: https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx
                    if (!process.WaitForExit(Int32.MaxValue))
                    {
                        // unreachable - the timeoutTask will kill the process before this happens.
                        throw new TimeoutException();
                    }
    
                    process.WaitForExit();
    
                    var result = new MSBuildResult(project, process.StartInfo.FileName, process.StartInfo.Arguments, process.ExitCode, output.ToString());
    
                    return result;
                });
    
                return Task.WhenAny<MSBuildResult>(waitTask, timeoutTask).Unwrap();
    
    
                void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
                {
                    output.AppendLine(e.Data);
                }
    
                void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
                {
                    output.AppendLine(e.Data);
                }
            }
        }
    }