MSBuild 总结
背景介绍
最近又要开始搞 Visual Studio 的项目了,可能还要把之前一些老构建流程的项目迁移过来,所以在看看 MSBuild 的相关资料,整理一下学习笔记。
这次的话会写得简单一些,因为有些基础概念已经都掌握了。
官方文档见 MSBuild - MSBuild
基本概念
- Property:约等于 Variable 的概念
- Item:会展示在 IDE 中的文件
- Target:执行构建工作流的节点
- Task:实际执行的动作,一个 Target 可以包含多个 Tasks
Property
用 $(<name>)
来使用。
主要是要知道有些内建的 Properties:MSBuild Reserved and Well-known Properties - MSBuild
然后是环境变量也能当成 Property 用:How to: Use Environment Variables in a Build - MSBuild
可以用一些特定的函数对 Property 进行计算:Property Functions - MSBuild
Item
用 @(<name>)
来使用 Item,用 %(<name>)
来使用其 Metadata。
Metadata 是 Attach 到 Item 上的属性,有一些内建的 Metadata 可用:MSBuild Well-known Item Metadata - MSBuild
可以用一些特定的函数对 Metadata 进行计算:Item Functions - MSBuild
可以用 MSBuild Transforms - MSBuild 这样一种语法对 Item 列表基于 Metadata 来进行一些变换(类似于 Mapping 操作)。
Target
默认的 Targets 有这些 MSBuild Targets - MSBuild
如何确定 Target 的执行顺序见 Target Build Order - MSBuild
这里比较容易引起困惑的是 DependsOnTargets 和 AfterTargets,他们的区别见 DependsOnTargets Vs AfterTargets · Issue #2994 · MicrosoftDocs/visualstudio-docs
<Target Name="x" DependsOnTargets="y" /> means: |
空的 proj
就没有 Target,但是一般我们都用 vcxproj
什么的,通过 SDK 就给注入了一堆 target。
Task
内置的 Task 有这些 MSBuild Task Reference - MSBuild(定义于 Microsoft.Common.CurrentVersion.Targets)
.targets file |
Description |
---|---|
Microsoft.Common.targets | Defines the steps in the standard build process for Visual Basic and C# projects. Imported by the Microsoft.CSharp.targets and Microsoft.VisualBasic.targets files, which include the following statement: <Import Project="Microsoft.Common.targets" /> |
Microsoft.CSharp.targets | Defines the steps in the standard build process for Visual C# projects. Imported by Visual C# project files ( .csproj ), which include the following statement: <Import Project="$(MSBuildToolsSpeed)\Microsoft.CSharp.targets" /> |
Microsoft.VisualBasic.targets | Defines the steps in the standard build process for Visual Basic projects. Imported by Visual Basic project files ( .vbproj ), which include the following statement: <Import Project="$(MSBuildToolsSpeed)\Microsoft.VisualBasic.targets" /> |
C++ 的好像是 Microsoft.Cpp.targets
看起来好像跟 Microsoft.Commons.targets
没关系(看这里的可能更靠谱 C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Microsoft.Common.tasks
)。
如果需要自定义 Task 可以参考 Task Writing - MSBuild 和 Create a custom task - MSBuild。如果只需要嵌入非常简单的小段代码,可以考虑 MSBuild Inline Tasks - MSBuild。
Directory.Build.props
递归引用的小技巧
https://docs.microsoft.com/zh-cn/visualstudio/msbuild/how-to-use-project-sdk?view=vs-2022
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove('$(MSBuildThisFileDirectory)..', 'Directory.Build.props'))\Directory.Build.props" /> |
MSBuildSdks
https://github.com/microsoft/MSBuildSdks
https://docs.microsoft.com/zh-cn/visualstudio/msbuild/how-to-use-project-sdk?view=vs-2022
这里面搞了几个比较有用的扩展:
- Microsoft.Build.Traversal 一般用来搞一个
dirs.proj
,递归的引用子目录中的其他项目。这样方便在一个特别大的仓库中组织几百个项目。最终可以用 SlnGen 工具生成sln
文件使用 IDE 进行开发。这么搞的原因是大型sln
文件性能比较差。 - Microsoft.Build.CentralPackageVersions 中心化配置依赖版本,这样就能将整个仓库中所有项目使用的依赖版本对齐了,互相引用和链接的时候也不会出什么依赖版本不一致的问题。
- Microsoft.Build.NoTargets Build 过程为空,这样方便在编译时期处罚一些自定义的任务,比如说
robocopy
什么的。 - Microsoft.Build.Artifacts 把项目产物放到一个集中的地方,方便比如说 Azure DevOps Pipeline 编译完了之后把所有产物收集起来(Azure Artifacts)。
其他
XML 文件 Schema
The schema link in an MSBuild project file is not required in Visual Studio 2017 and later. If present, it should be
http://schemas.microsoft.com/developer/msbuild/2003
regardless of the version of Visual Studio.
但是看现在 SDK 的项目自动都把 Schema 抹掉了,也不知道是为啥。
一般把扩展拆成 .props 和 .targets 两个文件
说这个主要是写 nuget 包的时候会用到。
When using explicit imports, you can import from a
.props
or.targets
file at any point. Here is the widely used convention:
.props
files are imported early in the import order..targets
files are imported late in the build order.
根根据项目类型自定义配置
<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.vbproj'"> |
从 proj
文件生成 sln
文件
https://microsoft.github.io/slngen/
编译 .NET Framework 目标工程
可以用这个 https://github.com/Microsoft/dotnet/tree/master/releases/reference-assemblies 而不安装 .NET Framework SDK
C++ SDK
https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/