如何从命令行或C#应用程序检测msbuild的状态

我正在用C#编写一个checkout,build和deployment应用程序,并且需要知道检测我对msbuild.exe调用是否成功的最佳方法。 我试图使用该过程中的错误代码,但我不确定这是否始终准确。

有没有办法(通过下面的代码)我可以判断msbuild.exe是否成功完成?

 try { Process msbProcess = new Process(); msbProcess.StartInfo.FileName = this.MSBuildPath; msbProcess.StartInfo.Arguments = msbArguments; msbProcess.Start(); msbProcess.WaitForExit(); if (msbProcess.ExitCode != 0) { // } else { // } msbProcess.Close(); } catch (Exception ex) { // } 

据我所知,MSBuild在遇到错误时返回退出代码> 0。 如果它没有遇到任何错误,它将返回退出代码0.我从未见过它以代码<0退出。

我在批处理文件中使用它:

 msbuild  if errorlevel 1 goto errorDone 

在用这种方式四年的时间里,我从来没有理由质疑这种方法的正确性。

MSDN论坛上的几个问题问同样的问题。 例如: http : //social.msdn.microsoft.com/forums/en-US/msbuild/thread/a4ae6b2b-9b1f-4e59-86b4-370f44d73a85 。 标准响应实际上是“如果errorlevel为0,那么就没有错误。”

对不起,如果我对派对来说有点太晚了……但问题发布后近7年我想看到一个完整的答案。 我使用下面的代码做了一些测试,结果如下:


分析

当至少发生一次构建错误时, msbuild.exe返回1 ,并在构建成功完成时返回0 。 目前,程序没有考虑警告,这意味着成功构建警告会导致msbuild.exe仍然返回0

其他错误如:尝试构建不存在的项目,或提供不正确的参数(如/myInvalidArgument ),也会导致msbuild.exe返回1


源代码

以下C#代码是一个完整的实现,用于通过msbuild.exe触发msbuild.exe来构建您喜欢的项目。 在编译项目之前,不要忘记设置任何必要的环境设置。

您的BuildControl类:

 using System; namespace Example { public sealed class BuildControl { // ... public bool BuildStuff() { MsBuilder builder = new MsBuilder(@"C:\...\project.csproj", "Release", "x86") { Target = "Rebuild", // for rebuilding instead of just building }; bool success = builder.Build(out string buildOutput); Console.WriteLine(buildOutput); return success; } // ... } } 

MsBuilder类:通过从命令行调用MsBuild.exe来构建东西:

 using System; using System.Collections.Generic; using System.IO; using System.Text; namespace Example { public sealed class MsBuilder { public string ProjectPath { get; } public string LogPath { get; set; } public string Configuration { get; } public string Platform { get; } public int MaxCpuCount { get; set; } = 1; public string Target { get; set; } = "Build"; public string MsBuildPath { get; set; } = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MsBuild.exe"; public string BuildOutput { get; private set; } public MsBuilder(string projectPath, string configuration, string platform) { ProjectPath = !string.IsNullOrWhiteSpace(projectPath) ? projectPath : throw new ArgumentNullException(nameof(projectPath)); if (!File.Exists(ProjectPath)) throw new FileNotFoundException(projectPath); Configuration = !string.IsNullOrWhiteSpace(configuration) ? configuration : throw new ArgumentNullException(nameof(configuration)); Platform = !string.IsNullOrWhiteSpace(platform) ? platform : throw new ArgumentNullException(nameof(platform)); LogPath = Path.Combine(Path.GetDirectoryName(ProjectPath), $"{Path.GetFileName(ProjectPath)}.{Configuration}-{Platform}.msbuild.log"); } public bool Build(out string buildOutput) { List arguments = new List() { $"/nologo", $"\"{ProjectPath}\"", $"/p:Configuration={Configuration}", $"/p:Platform={Platform}", $"/t:{Target}", $"/maxcpucount:{(MaxCpuCount > 0 ? MaxCpuCount : 1)}", $"/fileLoggerParameters:LogFile=\"{LogPath}\";Append;Verbosity=diagnostic;Encoding=UTF-8", }; using (CommandLineProcess cmd = new CommandLineProcess(MsBuildPath, string.Join(" ", arguments))) { StringBuilder sb = new StringBuilder(); sb.AppendLine($"Build started: Project: '{ProjectPath}', Configuration: {Configuration}, Platform: {Platform}"); // Call MsBuild: int exitCode = cmd.Run(out string processOutput, out string processError); // Check result: sb.AppendLine(processOutput); if (exitCode == 0) { sb.AppendLine("Build completed successfully!"); buildOutput = sb.ToString(); return true; } else { if (!string.IsNullOrWhiteSpace(processError)) sb.AppendLine($"MSBUILD PROCESS ERROR: {processError}"); sb.AppendLine("Build failed!"); buildOutput = sb.ToString(); return false; } } } } } 

CommandLineProcess类 – 启动命令行进程并等待直到完成。 捕获所有标准输出/错误,并且不为该过程启动单独的窗口:

 using System; using System.Diagnostics; using System.IO; namespace Example { public sealed class CommandLineProcess : IDisposable { public string Path { get; } public string Arguments { get; } public bool IsRunning { get; private set; } public int? ExitCode { get; private set; } private Process Process; private readonly object Locker = new object(); public CommandLineProcess(string path, string arguments) { Path = path ?? throw new ArgumentNullException(nameof(path)); if (!File.Exists(path)) throw new ArgumentException($"Executable not found: {path}"); Arguments = arguments; } public int Run(out string output, out string err) { lock (Locker) { if (IsRunning) throw new Exception("The process is already running"); Process = new Process() { EnableRaisingEvents = true, StartInfo = new ProcessStartInfo() { FileName = Path, Arguments = Arguments, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, }, }; if (!Process.Start()) throw new Exception("Process could not be started"); output = Process.StandardOutput.ReadToEnd(); err = Process.StandardError.ReadToEnd(); Process.WaitForExit(); try { Process.Refresh(); } catch { } return (ExitCode = Process.ExitCode).Value; } } public void Kill() { lock (Locker) { try { Process?.Kill(); } catch { } IsRunning = false; Process = null; } } public void Dispose() { try { Process?.Dispose(); } catch { } } } } 

PS :我正在使用Visual Studio 2017 / .NET 4.7.2