Visual Studio远不止是一个编辑器和调试器,它还是微软跨平台构建工具的载体。MSBuild就是那些被遗忘的英雄之一,它是所有开发流程的核心工具,但却隐藏在开发环境的表象之下。

MSBuild是代码、库和SDK被整理并交付给.NET及其他编译器的地方。如果你在用C#、C++或任何其他微软语言编程,并且在Visual Studio或Visual Studio Code中工作——或者与任何其他代码编辑器、IDE一起使用独立的构建工具,或者作为GitHub构建流水线的一部分——你都会在不知不觉中使用MSBuild。甚至像Windows应用开发CLI这样的工具也依赖于MSBuild。

简而言之,MSBuild很容易被忽视。但这对那个已经连续交付了18个主要版本(以及其间众多次要版本)微软构建工具的团队来说是不公平的。微软内部很少有团队能说自己把开发从专有变为开放,从单一平台扩展到跨平台,并沿途扩展到多种处理器架构。我自己也难免有这个毛病,往往只关注语言、编辑器和编译器,却忽略了将这一切整合在一起、把代码交付为可运行应用程序的“管道系统”。

如果你有Unix背景,那么你会熟悉像make和cmake这样使用脚本从源代码构建二进制文件的工具。在微软的世界里,尽管你仍然可以将make与gcc及其他编译器一起使用,但你更可能使用的是MSBuild,即通常所说的Microsoft Build Engine。

MSBuild旨在使用由Visual Studio或其他开发工具生成的项目文件中的指令,来编排将源代码构建为可运行二进制文件的过程。你可以在IDE内完成这一操作,也可以选择下载命令行构建工具。这些工具受到与Visual Studio相同的许可协议约束,不过在处理开源软件时可以免费使用。(与任何同时涉及专有软件和开源软件的应用程序一样,仔细阅读许可协议以确保合规非常重要。)

微软将MSBuild作为.NET项目的一部分在GitHub上进行开放开发,尽管它的设计远不止服务于.NET。当然,你可以自行编译MSBuild,包括在任何支持核心.NET运行时和库的Unix平台上。你也可以提交自己的拉取请求,贡献新功能或修复错误。现有代码包括可用于向构建流水线添加自定义任务或日志记录的辅助类,如果你正在开发CI/CD工具并希望添加对MSBuild的支持,这会很有用。

你可以将MSBuild作为Visual Studio构建工具的一部分安装,或者通过.NET CLI安装,其中dotnet build命令会启动.NET版本的工具。它的下载体积相对较小,因此适合通过脚本启动,或作为自定义构建系统的一部分集成到你自己的流水线中。

自定义构建系统确实需要一些工作,但可以利用高级功能,例如并行编译大型应用程序的各个部分,以及添加你自己的预处理和后处理步骤,或将输出保存到自定义位置。Azure Pipelines和GitHub Actions等工具正是通过这种方式使用MSBuild,将代码从一个仓库中提取出来,并将测试构建和发布版本交付到特定位置。

如果你有源代码和项目文件,就可以开始通过命令行使用MSBuild。由于.NET CLI为MSBuild功能提供了各种别名,同样的命令行开关也可以用于dotnet build和dotnet publish这样的工具(但不适用于dotnet run)。

MSBuild的命令行开关允许你调整操作。它们还允许你生成有用的输出,帮助你理解构建的运行方式或应用程序由哪些组件构成。后一种选项允许你为代码构建依赖关系图,为应用程序中使用的任何开源库或其他组件构建软件物料清单(SBOM)提供起点。随着越来越多的监管机构要求提供SBOM以确保安全,这一功能无疑将变得越来越有用。

其他功能还包括根据你所使用的硬件调整MSBuild的能力,例如通过控制使用的进程数量。默认情况下,MSBuild以单进程方式工作,但通过使用正确的开关,你可以选择特定的进程数量,或强制MSBuild使用所有可用资源。在使用云托管系统进行构建时,锁定并控制其优先级可以帮助管理成本,通过管理用于GitHub运行器等的虚拟机资源。

微软一直在将其项目文件语法过渡到XML,并使用引用多个项目的解决方案文件。项目文件包含MSBuild从源代码构建项目所需的所有输入和目标信息。这一过程包括定义构建环境,以及根据各个项目文件构建内存中的结构。

这有助于定义目标构建顺序,处理依赖关系并适当地执行构建,这样对解决方案中另一个项目有依赖关系的代码将等待这些依赖项构建完成后再构建。正确排序至关重要,这就是为什么MSBuild在做任何事情之前会先评估构建结构并整理出一个堆栈。转向使用图来管理这一依赖关系映射有助于加快构建速度,在跨独立核心运行的并行节点上调度各种任务并进行编排。

项目文件将保存依赖项列表,以及特定语言的要求。后者使MSBuild能够管理C#和C++构建所需的过程,其中一个是构建在.NET运行时上运行的字节码,另一个是编译为本机二进制文件。

由于这些文件是XML格式,它们可以进行自定义。但这需要谨慎进行。微软提供了从零开始构建自己的项目文件的说明,如果你正在将代码从一个环境移植到另一个环境,而没有原始项目文件,这个过程会很有用。如果你在没有SDK的情况下工作,且不想承担像.NET这样的平台所带来的开销,也可以使用这种方法。

更常见的做法是使用MSBuild向构建过程添加自定义任务。一种可能是为构建添加代码生成功能,获取文本文件并使用模板将其转换为代码。当你需要生成代码以处理特定输入数据时,这种方法会很有用,可以根据需要硬编码到例如IoT硬件控制应用程序中的数据动态修改模板。

在构建CI/CD流水线时,理解(并自定义)MSBuild变得至关重要。在这里,你需要在Visual Studio之外运行构建引擎,将其嵌入到可按需触发的容器或虚拟机中,与必要的编译器一起安装,并使用命令行工具来调整性能。其中一个选项是在配置文件中提供资源限制,这样同一个构建脚本就可以在不同环境中运行而不会出现问题。

通过对构建服务进行脚本化,你可以适当地针对不同的构建类型进行定位和优先级排序,将构件放在一个位置用于自动化测试,另一个位置用于验收测试,为预发布自动化部署,并以所需格式交付到生产环境。测试构建可能配置为使用比生产环境更少的CPU,因为它们可能会更频繁地运行,成本需要更多控制。

在Visual Studio中按F5构建可执行文件,掩盖了这个工具其实非常灵活强大的一面——如果你愿意深入细节并调整默认构建设置,它能帮助大规模软件开发变得更快、更经济。对于使用基于云的CI/CD工具的大型项目而言,深入研究MSBuild可能是值得付出的努力。

Q&A

Q1:MSBuild是什么?

A:MSBuild是微软的构建引擎,用于根据项目文件中的指令,将源代码编排构建为可运行的二进制文件,是Visual Studio及相关开发工具背后的核心构建平台。

Q2:MSBuild只能用于.NET项目吗?

A:不是。MSBuild虽然是.NET项目的一部分并在GitHub上开源开发,但它的设计目标远不止.NET,也支持C++等其他语言的构建流程。

Q3:MSBuild如何用于CI/CD流水线?

A:可以在Visual Studio之外运行MSBuild,将其嵌入容器或虚拟机中按需触发,配合必要的编译器安装,并通过命令行工具和配置文件调整性能与资源限制,从而适配不同的构建环境。

InfoWorld