簡體   English   中英

如何從命令行或 C# 應用程序檢測 msbuild 的狀態

[英]How to detect the status of msbuild from command line or C# Application

我正在用 C# 編寫結帳、構建和部署應用程序,並且需要知道檢測我對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 的代碼退出。

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

msbuild <args>
if errorlevel 1 goto errorDone

在以這種方式使用它的四年中,我從來沒有理由質疑這種方法的正確性。

MSDN 論壇上的幾個問題提出了同樣的問題

實際上,標准響應是“如果錯誤級別為 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<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類 - 啟動命令行進程並等待它完成。 捕獲所有標准輸出/錯誤,並且不會為該進程啟動單獨的窗口:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM