简体   繁体   English

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

[英]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.

相关问题 如何使用命令行中的C#7代码构建.csproj(msbuild) - How to build .csproj with C# 7 code from command line (msbuild) 如何使用msbuild从命令行编译我的c#解决方案 - How to compile my c# solution with msbuild from command line C# 控制台应用程序:有没有办法检测 exe 是否从命令行运行? - C# Console Application: Is there a way to detect whether the exe was run from a command line or not? 如何在命令行中从msbuild配置csc? - How configure csc from msbuild in command line? 在某些计算机上从命令行执行C#msbuild执行会跳过签名阶段WP8 - c# msbuild execution from command line on some computersskips the signing phase WP8 具有C#6.0代码的MSBuild命令行无法构建 - MSBuild command line with C# 6.0 code fails to build 从命令行运行7zip的C#应用​​程序-如何从打开命令行窗口停止7zip? - C# application running 7zip from command line - How to stop 7zip from opening command line windows? 从C#应用程序将命令行参数传递给IronPython? - Passing command line parameters to IronPython from C# application? C#控制台应用程序不会从命令行读取,因为它应该 - C# Console Application Does NOT read from the command line, as it should 通过c#从正在运行的应用程序运行命令行 - Run a command line from a running application through c#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM