ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MSBuild 在 .NET Core 上的构建、测试与调试实战指南

MSBuild 在 .NET Core 上的构建、测试与调试实战指南
  • 构建工具
  • 开发工具
  • CLI

【免费下载链接】msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.

项目地址:https://gitcode.com/gh_mirrors/ms/msbuild
点击查看免费下载

本指南以 documentation/wiki/Building-Testing-and-Debugging-on-.Net-Core-MSBuild.md 为主体,系统讲解如何在 Windows 与 Unix 平台上从源码构建 .NET Core 版 MSBuild、运行单元测试、在不编译源码的情况下获取官方二进制,以及利用MSBUILDDEBUGONSTART系列环境变量对 MSBuild 主进程、TaskHost 子进程和 MSBuild Server 进行断点调试。阅读本文后,你将掌握build.cmd/build.sh的正确用法、-msbuildEngine参数的作用、仓库内测试的组织方式,以及一套可直接照抄的调试工作流。

支持平台与前置条件

官方 wiki 记载,MSBuild 可在Windows、OS X 10.13、Ubuntu 14.04 与 Ubuntu 16.04上成功构建。注意,当前仓库根目录的 global.json 已固定要求sdk.version = 11.0.100-rc.1.26420.103(同时允许预发布版本),并声明tools.dotnet使用同一版本、tools.vs指向 Visual Studio 18.6,测试运行器为Microsoft.Testing.Platform。因此在动手前,请确保本机 .NET SDK 版本满足 global.json 的要求,否则构建脚本会提示 "The .NET SDK could not be found, please run a command-line build with ./build.cmd"。

Windows 上构建 .NET Core 版 MSBuild

使用 dotnet 引擎构建

在 Windows 上构建 .NET Core 版 MSBuild 的命令是:

build.cmd -msbuildEngine dotnet

-msbuildEngine用于指定以哪一套引擎来驱动构建流程。从 eng/build.ps1 的实现看,该参数有两个取值:

  • dotnet:使用引导(bootstrap)产出的core\dotnet.exe msbuild作为构建工具;
  • vs:使用引导产出的net472\MSBuild\Current\Bin\MSBuild.exe(即 .NET Framework 版 MSBuild)。

若调用时未显式指定该参数,脚本会读取 global.json:存在tools.vs则默认vs,否则回退到tools.dotnet;两者都没有时直接报错退出。

build.cmd 背后的调用链

build.cmd 本身是一个薄包装:它实际调用的是powershell -ExecutionPolicy ByPass -NoProfile -Command "& '%~dp0eng\build.ps1'" -restore -build,即先执行 restore 再执行 build。值得注意的一个细节是,build.cmd 开头会检查MSBUILDDEBUGONSTART环境变量——只要它被设置且非 0,脚本就会将其清除并提示改用MSBUILDDEBUGONSTART_HARD。这是因为 MSBuild 的构建流程本身也会读取MSBUILDDEBUGONSTART,为避免"构建时意外弹出调试器",普通构建会主动屏蔽它;如果你确实要调试构建过程本身,应改用MSBUILDDEBUGONSTART_HARD。

eng/build.ps1 还暴露了更多开关:-configuration/-c(默认Debug)、-verbosity/-v、-test/-t、-ci、-binaryLog/-bl、-binaryLogName、-excludeCIBinarylog/-nobl、-stage2与-stage2Argument,未声明参数会原样转发给 Arcade 构建。在调试构建(stage 2)之前,脚本还会设置MSBUILDDONOTLAUNCHDEBUGGER=true,确保调试位(debug bits)快速失败而不是挂起等待调试器附着。

Unix 上构建 .NET Core 版 MSBuild

最简单的方式

直接安装最新版 .NET SDK(可从官方下载页获取)。安装最新 SDK 即可满足本仓库构建所需的全部前置条件,无需再手动装其他依赖。

手动安装依赖包(OS X 与 Ubuntu)

若选择手动配置环境,请参考 .NET Core 官方前置条件文档,并特别注意OpenSSL:

  • MSBuild 在构建过程中会调用 .NET CLI,而 CLI 需要/usr/lib中存在较新版本的 OpenSSL 库;
  • OS X 上可用 brew 安装:brew install openssl;
  • Ubuntu 上可用 apt-get 安装:apt-get install openssl;
  • 也可以选择从源码编译安装。

如果使用的是其他包管理器,且构建时出现Unable to load DLL 'System.Security.Cryptography.Native'错误,通常说明dotnet在错误的路径下查找该库——请检查 OpenSSL 是否安装到了/usr/lib下。

构建命令

./build.sh

build.sh 同样是薄包装,它执行的是eng/build.sh --restore --build "$@"。若构建过程中遇到问题,可参考仓库 wiki 中名为Something's wrong in my build的排查文档。

常用参数

eng/build.sh 支持以下参数(大小写不敏感解析):

参数简写说明
--configuration-c构建配置,默认Debug
--test-t构建后运行仓库内全部测试
--ci—CI 模式
--binaryLog-bl生成二进制日志
--excludeCIBinarylog-nobl排除 CI 二进制日志
--stage2/--stage2Argument—触发多阶段(stage1 + stage2)构建

其余未知参数会作为属性原样转发给 Arcade 构建脚本。

运行单元测试

Unix:一条命令跑全部测试

./build.sh --test

该命令会构建仓库并运行全部测试。

Windows:按测试项目逐个运行

官方文档给出的命令行测试流程如下(先完成一次完整构建,再进入目标测试项目目录,最后用过滤器运行单个测试):

build.cmd :: 先完整构建 cd src\Build.UnitTests :: 进入包含测试 csproj 的目录(按实际项目调整) dotnet test --filter "FullyQualifiedName~TESTNAME" :: 运行单个测试

--filter "FullyQualifiedName~TESTNAME"采用模糊匹配,~表示包含关系,TESTNAME可替换为具体的测试方法名或类名片段。仓库中可选的测试项目包括src/Build.UnitTests、src/Build.OM.UnitTests、src/Framework.UnitTests、src/Tasks.UnitTests、src/MSBuild.UnitTests、src/BuildCheck.UnitTests、src/Utilities.UnitTests等。

在 Visual Studio 中运行测试

先在Developer Command Prompt for VS 2026(或把devenv加入PATH)中执行:

build.cmd :: 先完整构建 devenv MSBuild.slnx

然后使用测试资源管理器(Test Explorer)运行测试。需要注意:Visual Studio 中必须勾选"Use previews of the .NET SDK (requires restart)"(使用 .NET SDK 预览版,需要重启),否则可能因 SDK 版本不匹配而无法加载测试项目。

单元测试在 Windows 上的运行细节(含远程测试环境)可参考 .NET Framework 开发指南的 Running Unit Tests 一节:build.cmd -test可构建并运行全部测试;部分测试会创建符号链接,需要开启 Windows 开发者模式或以管理员身份运行;若要模拟 CI,可执行build.cmd -pack -sign -publish -ci。

如上图所示,通过仓库根目录的 testenvironments.json 已预置 WSL 与 Linux Docker 两种远程测试环境,在测试资源管理器中可直接选择使用。

不编译源码直接获取 .NET Core MSBuild

如果只是想使用 .NET Core 版 MSBuild 而无需修改源码,最直接的方式是安装 .NET Core SDK——MSBuild 随 SDK 一起分发。安装后有两种使用方式:

  1. 通过dotnet build命令间接调用;
  2. 手动调用 dotnet 发行目录中的MSBuild.dll:
dotnet path\to\MSBuild.dll yourproject.proj

这样拿到的即是最新发布的 .NET Core 版 MSBuild。

调试 MSBuild

利用仓库二进制进行构建

要使用仓库刚构建出的二进制来构建其他项目,需要先产生 "bootstrap" 目录(默认构建流程即会生成)。在 Windows 上可把artifacts\bin\bootstrap\net472\MSBuild\Current\Bin\MSBuild.exe指向目标项目文件;在 .NET Core 引擎下,引导产物则是artifacts\bin\bootstrap\core\dotnet.exe(见 eng/build.ps1)。如需将本地构建产物覆盖到 Visual Studio 的 MSBuild 目录以做联调,可使用 scripts/Deploy-MSBuild.ps1。

启动断点:MSBUILDDEBUGONSTART

设置环境变量MSBUILDDEBUGONSTART可以控制 MSBuild 启动时的调试器行为,取值含义如下:

取值行为
1对所有进程启动调试器(包括主 MSBuild 进程与 TaskHost 子进程)
2对所有进程等待手动附加调试器
3仅对主 MSBuild 进程启动调试器,跳过 TaskHost 子进程

例如,设置MSBUILDDEBUGONSTART=2,进程启动后会打印等待信息并阻塞在Console.ReadLine()上,此时手动将调试器附加到对应进程 ID 即可。

从源码可以印证其实现:src/MSBuild/XMake.cs 中的DebuggerLaunchCheck()对MSBUILDDEBUGONSTART做 switch 分发——1(以及服务端节点下的4)调用Debugger.Launch();3会先判断FrameworkDebugUtils.IsInTaskHostNode(),在 TaskHost 子进程中直接跳过;2打印 "Waiting for debugger to attach (... PID ...)" 并等待回车。与之对应,TaskHost 侧的实现位于 src/MSBuildTaskHost/OutOfProcTaskHost.cs。另外注意:1、3依赖Debugger.Launch(),仅 Windows 可用;而2的"等待附加"方式在 Windows 与非 Windows 平台都可用(参见 MSBuild-Environment-Variables.md)。

调试 MSBuild Server

启用 Server 模式后,可使用另外两个取值:MSBUILDDEBUGONSTART=4(在 Server 中启动调试器,Windows)与5(等待手动附加)。这两个模式会跳过转发客户端(forwarding client)和其他子节点,并在连接建立之后、求值(evaluation)开始之前暂停——无论是新建还是复用的 Server 均适用。

在 PowerShell 中的完整示例:

$env:MSBUILDUSESERVER = '1' $env:MSBUILDDEBUGONSTART = '5' dotnet msbuild .\project.proj

流程说明:

  1. 附加到终端中打印的 Server PID 并恢复执行;
  2. 与模式2不同,模式5不需要按回车键——因为客户端已连接、Server 的输出已被重定向,它无法读取客户端的 stdin,所以实现上改为轮询Debugger.IsAttached(见 XMake.cs);
  3. 无需修改连接或握手逻辑,因为客户端已经处于连接状态;
  4. 既有的取消/回退行为保持不变:若想放弃等待,可手动恢复或停止 Server;
  5. 调试结束后,记得取消设置MSBUILDDEBUGONSTART。

需要说明的是,模式4/5不覆盖 Server 的启动过程与握手阶段的调试。

更多调试手段

  • 调试引擎内部:设置MSBuildDebugEngine=1可让 MSBuild 输出全部可能的调试日志(如调度器状态、节点通信),并会在BuildManager.BeginBuild处注入二进制日志,对 Visual Studio 这类会过滤构建事件消息的宿主场景尤其有用;日志默认输出到当前目录的MSBuild_Logs目录,可用MSBUILDDEBUGPATH指定目录;
  • 在 API 宿主内打断点:设置MSBuildDebugBuildManagerOnStart=1(Debugger.Launch)或2(等待附加)可在BuildManager.BeginBuild处中断,适合调试通过 API 调用 MSBuild 的场景;可用MSBuildDebugProcessName按进程名子串过滤触发断点的进程(如设为devenv);
  • 定向 dump:MSBUILDDEBUGSCHEDULER=1配合MSBUILDDEBUGPATH可 dump 调度器状态;MSBUILDDEBUGCOMM=1可 dump 节点通信数据。

以上断点与日志类环境变量的完整清单可参考 MSBuild-Environment-Variables.md,更多针对 .NET Framework 版的调试选项见 Building-Testing-and-Debugging-on-Full-Framework-MSBuild.md。

  • 构建工具
  • 开发工具
  • CLI

【免费下载链接】msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.

项目地址:https://gitcode.com/gh_mirrors/ms/msbuild
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进