[英]How to detect the status of msbuild from command line or C# Application
I am writing up a checkout, build and deployment application in C#, and need to know the best way to detect whether my call to msbuild.exe
has succeeded or not.我正在用 C# 编写结帐、构建和部署应用程序,并且需要知道检测我对msbuild.exe
调用是否成功的最佳方法。 I have tried to use the error code from the process, but I am not sure whether this is always accurate.我尝试使用过程中的错误代码,但我不确定这是否总是准确的。
Is there a way (through the code below) that I can tell whether msbuild.exe
completed successfully?有没有办法(通过下面的代码)可以判断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)
{
//
}
As far as I've been able to determine, MSBuild returns an exit code greater then zero when it encounters an error.据我所知,MSBuild 在遇到错误时返回大于零的退出代码。 If it doesn't encounter any errors, it returns exit code 0. I've never seen it exit with code lower than 0.如果它没有遇到任何错误,它会返回退出代码 0。我从未见过它以低于 0 的代码退出。
I use it in a batch file:我在批处理文件中使用它:
msbuild <args>
if errorlevel 1 goto errorDone
In four years of using it this way, I've never had reason to question the correctness of this approach.在以这种方式使用它的四年中,我从来没有理由质疑这种方法的正确性。
Several questions on the MSDN forums ask the same thing . MSDN 论坛上的几个问题提出了同样的问题。
The standard response is, in effect, "if errorlevel is 0, then there was no error".实际上,标准响应是“如果错误级别为 0,则没有错误”。
Sorry if I'm a little bit too late for the party... but nearly 7 years after the question was posted I wanted to see a complete answer for it.对不起,如果我参加聚会有点太晚了……但是在问题发布近 7 年后,我想看到一个完整的答案。 I did some tests using the code below, and here are the conclusions:我使用下面的代码做了一些测试,以下是结论:
Analysis分析
msbuild.exe
returns 1
when at least one build error occurs, and returns 0
when the build is successfully completed. msbuild.exe
在至少发生一个构建错误时返回1
,并在构建成功完成时返回0
。 At present, the program does not take warnings into account, which means a successful build with warnings causes msbuild.exe
to still return 0
.目前,该程序没有考虑警告,这意味着带有警告的成功构建会导致msbuild.exe
仍然返回0
。
Other errors like: trying to build a project that does not exist, or providing an incorrect argument (like /myInvalidArgument
), will also cause msbuild.exe
to return 1
.其他错误,例如:尝试构建不存在的项目,或提供不正确的参数(如/myInvalidArgument
),也会导致msbuild.exe
返回1
。
Source Code源代码
The following C# code is a complete implementation for building your favorite projects by firing msbuild.exe
from a command line.以下 C# 代码是通过msbuild.exe
触发msbuild.exe
来构建您最喜欢的项目的完整实现。 Don't forget to setup any necessary environment settings before compiling your projects.在编译项目之前不要忘记设置任何必要的环境设置。
Your BuildControl class:您的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 class: Builds stuff by calling MsBuild.exe from command line: 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<string> arguments = new List<string>()
{
$"/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 class - Starts a command line process and waits until it finishes. CommandLineProcess类 - 启动命令行进程并等待它完成。 All standard output/error is captured, and no separate window is started for the process:捕获所有标准输出/错误,并且不会为该进程启动单独的窗口:
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 : I'm using Visual Studio 2017 / .NET 4.7.2 PS :我使用的是 Visual Studio 2017/.NET 4.7.2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.