自定义生成 - Visual Studio | Microsoft Docs
使用标准生成进程(导入 Microsoft.Common.props 和 Microsoft.Common.targets)的 MSBuild 项目有多个可用于自定义生成过程的扩展性挂钩 。
向项目的命令行 MSBuild 调用添加参数
MSBuild 响应文件。
Directory.Build.props 和 Directory.Build.targets
另外,还必须在 .props 文件中定义属性,然后在解决方案的每个项目中显式导入该 .props 文件。
Directory.Build.props 是用户定义文件,对目录下的项目提供自定义选项。
备注
请确保 Directory.Build.props 文件名的大小写完全匹配,否则将不会在生成流程中检测到它。
此 GitHub 问题。
Directory.Build.props 示例
例如,如果想要使所有项目都可以访问新的 Roslyn /deterministic 功能(属性 $(Deterministic) 在 Roslyn CoreCompile 目标中公开了此功能),可以执行以下操作。
-
在存储库根目录中创建一个名为 Directory.Build.props 的新文件。
-
将以下 XML 添加到此文件。
复制<Project> <PropertyGroup> <Deterministic>true</Deterministic> </PropertyGroup> </Project> -
项目现有的 Microsoft.Common.props 和 Microsoft.Common.targets 导入会找到该文件并将其导入。
搜索范围
例如,如果 $(MSBuildProjectFullPath) 为 c:\users\username\code\test\case1,MSBuild 将从该位置开始搜索,然后向上搜索目录结构,直到找到 Directory.Build.props 文件,如以下目录结构中所示。
c:\users\username\code\test\case1
c:\users\username\code\test
c:\users\username\code
c:\users\username
c:\users
c:\
解决方案文件的位置与 Directory.Build.props 无关。
导入顺序
因此,请避免引用尚未定义的属性(否则计算结果将为空)。
Directory.Build.props 中设置的属性可能会在项目文件或导入文件中的其他位置被覆盖,因此,应考虑将 Directory.Build.props 中的设置指定为项目的默认值 。
因此,它可以覆盖大多数生成逻辑中定义的属性和目标,或者为所有项目设置属性,而不考虑各个项目的设置。
如何使用 MSBuild 项目 SDK。
备注
在下次调用 Msbuild.exe 或 Visual Studio 下次生成之后,修改才会生效。
用例:多级别合并
假设你具有此标准解决方案结构:
\
MySolution.sln
Directory.Build.props (1)
\src
Directory.Build.props (2-src)
\Project1
\Project2
\test
Directory.Build.props (2-test)
\Project1Tests
\Project2Tests
则可能需要具有所有项目 (1) 的通用属性、src 项目 (2-src) 的通用属性,以及 test 项目 (2-test) 的通用属性。
要继续扫描并合并到外部文件,请将此代码置于这两个内部文件中:
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
MSBuild 的常规方法汇总如下:
- 对于任何给定的项目,MSBuild 在解决方案结构中向上查找第一个 Directory.Build.props,将其与默认项合并,然后停止扫描
<Import...>(如上所示)“外部”文件- 如果“外部”文件本身不会再导入其上的内容,则扫描在此处停止
- 要控制扫描/合并过程,请使用
$(DirectoryBuildPropsPath)和$(ImportDirectoryBuildProps)
或再简单点:不能导入任何内容的第一个 Directory.Build.props 即为 MSBuild 停止的位置。
选择将属性添加到 .props 文件或 .targets 文件
MSBuild 依赖于导入顺序,属性(或 UsingTask 或目标)的最后一个定义是使用的定义。
下面介绍广泛使用的约定:
-
.props 文件在导入顺序的早期导入。
-
.targets 文件在生成顺序的后期导入。
导入强制执行(即,在文件的所有内容之前首先导入 Sdk.props,然后在文件的所有内容之后最后导入 Sdk.targets)。
在决定在何处放置属性后,使用以下通用原则:
-
对于许多属性,在何处定义它们并不重要,因为它们不会被覆盖,只能在执行时读取。
-
对于可能在单个项目中自定义的行为,请在 .props 文件中设置默认值。
-
通过读取可能自定义属性的值,避免在 .props 文件中设置依赖属性,因为在 MSBuild 读取用户项目之前不会进行自定义。
-
在 .targets 文件中设置依赖属性,因为它们将从单个项目中提取自定义项。
-
使用派生属性时务必小心;还可能需要覆盖派生属性。
-
在任何项目之前都要考虑所有属性,因此可以提取用户项目属性自定义项,这使用户的项目有机会
Remove或Update导入所引入的任何项目。 -
但是,如果 SDK 导入了 .targets 文件,请记住此方案使得覆盖目标更加困难,因为默认情况下用户的项目没有可以覆盖它的地方。
-
此原则可以更轻松地加载项目并了解正在执行的操作。
MSBuildProjectExtensionsPath
NuGet 用此机制来引用随包提供的生成逻辑,也就是说,在还原时,它会创建引用包内容的 {project}.nuget.g.props 文件。
可以通过在 Directory.Build.props 中或者在导入 Microsoft.Common.props 前将属性 ImportProjectExtensionProps 设为 false 来禁用此扩展性机制 。
备注
一些 NuGet 包需要生成逻辑来执行其功能,并且在禁用该功能时会呈现不可用。
.user 文件
对于计划签入源代码管理的长期更改,最好更改项目本身,以便将来的维护人员不必了解此扩展机制。
MSBuildExtensionsPath 和 MSBuildUserExtensionsPath
警告
尝试使用可以签入源代码管理系统并在基本代码的所有开发人员之间共享的配置。
按照惯例,许多核心生成逻辑文件
$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\{TargetFileName}\ImportBefore\*.targets
会在其内容前后各导入一次
$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\{TargetFileName}\ImportAfter\*.targets
这一约定使已安装的 SDK 可以增强常见项目类型的生成逻辑。
例如,将 ImportUserLocationsByWildcardBeforeMicrosoftCommonProps 设置为 false 会阻止导入 $(MSBuildUserExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.props\ImportBefore\*。
自定义解决方案生成
重要
自定义一个解决方案中所有项目的更好方法是在解决方案文件夹中使用 Directory.Build.props 和 Directory.build.targets 文件,如本文其他部分所述。
已生成的项目文件在定义任何目标前导入 before.{solutionname}.sln.targets,在导入目标后导入 after.{solutionname}.sln.targets ,其中包括安装到 $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportBefore 和 $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter 目录的目标。
例如,可以在包含以下内容的名为 after.MyCustomizedSolution.sln.targets 的相同目录中创建文件,从而定义在生成 MyCustomizedSolution.sln 后写自定义日志消息的新目标
<Project>
<Target Name="EmitCustomMessage" AfterTargets="Build">
<Message Importance="High" Text="The solution has completed the Build target" />
</Target>
</Project>
解决方案生成与项目生成分开进行,因此,此处的设置不会影响项目生成。
自定义所有 .NET 生成
可以通过使用特定的 MSBuild 属性并添加某些自定义 .targets 和 .props 文件,来影响特定项目类型的所有生成(如所有 C# 项目)。
若要影响通过安装 MSBuild 或 Visual Studio 控制的所有 C# 或 Visual Basic 的生成,请创建 Custom.Before.Microsoft.Common.Targets 或 Custom.After.Microsoft.Common.Targets文件(其目标将在 Microsoft.Common.targets 之前或之后运行),或创建 Custom.Before.Microsoft.Common.Props 或 Custom.After.Microsoft.Common.Props 文件 (将在 Microsoft.Common.props 之前或之后进行处理其属性)。
可以使用以下 MSBuild 属性指定这些文件的位置:
- CustomBeforeMicrosoftCommonProps
- CustomBeforeMicrosoftCommonTargets
- CustomAfterMicrosoftCommonProps
- CustomAfterMicrosoftCommonTargets
- CustomBeforeMicrosoftCSharpProps
- CustomBeforeMicrosoftVisualBasicProps
- CustomAfterMicrosoftCSharpProps
- CustomAfterMicrosoftVisualBasicProps
- CustomBeforeMicrosoftCSharpTargets
- CustomBeforeMicrosoftVisualBasicTargets
- CustomAfterMicrosoftCSharpTargets
- CustomAfterMicrosoftVisualBasicTargets
可以在 MSBuild 命令行中设置这些属性。
msbuild /p:CustomBeforeMicrosoftCommonTargets="C:\build\config\Custom.Before.Microsoft.Common.Targets" MyProject.csproj
凭借 Visual Studio 扩展性,你可以自定义生成系统,并提供安装和管理自定义项的机制。
如果需要让自定义目标仅在某些条件适用时执行,可使用其他文件位置,并(仅在需要时)通过在 MSBuild 命令行中设置相应的 MSBuild 属性设置该文件的路径。
警告
这可能会带来意想不到的后果,如果操作不正确,可能会导致 Visual Studio 无法在你的计算机上进行生成。
自定义 C++ 生成
.vcxproj 和 .props 文件结构) 。
但是,你可以使用以下属性来指定要在 Microsoft.Cpp.* 文件之前/之后自动导入的 .props 文件 :
- ForceImportAfterCppDefaultProps
- ForceImportBeforeCppProps
- ForceImportAfterCppProps
- ForceImportBeforeCppTargets
- ForceImportAfterCppTargets
要自定义所有 C++ 生成的默认属性值,请创建另一个 .props 文件(例如 MyProps.props),然后在指向该文件的 Directory.Build.props 中定义 ForceImportAfterCppProps 属性 :
$(MsbuildThisFileDirectory)\MyProps.props
MyProps.props 会自动导入到 Microsoft.Cpp.props 的最末尾 。
自定义所有 C++ 生成
不建议自定义 Visual Studio 安装,因为无法轻松跟踪此类自定义项,但如果要扩展 Visual Studio 以自定义特定平台的 C++ 生成,则可以为每个平台创建 .targets 文件,并将这些文件作为 Visual Studio 扩展的一部分放在适用于这些平台的相应导入文件夹中。
Win32 平台的 .targets 文件 (Microsoft.Cpp.Win32.targets) 包含以下 Import 元素:
<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportBefore\*.targets"
Condition="Exists('$(VCTargetsPath)\Platforms\Win32\ImportBefore')"
/>
同一文件的末尾附近有一个相似的元素:
<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportAfter\*.targets"
Condition="Exists('$(VCTargetsPath)\Platforms\Win32\ImportAfter')"
/>
*%ProgramFiles32%\MSBuild\Microsoft.Cpp\v{version}\Platforms* 中的其他目标平台也存在类似的导入元素。
如果需要,可以将多个 .targets 文件放在那里。
C++ 项目扩展性。
在命令行上指定自定义导入
对于要针对某个 C++ 项目的特定生成加入的自定义 .targets,请在命令行上设置 ForceImportBeforeCppTargets 和/或 ForceImportAfterCppTargets 属性。
msbuild /p:ForceImportBeforeCppTargets="C:\build\config\Custom.Before.Microsoft.Cpp.Targets" MyCppProject.vcxproj
之所以可行,是因为 MSBuild 始终读取环境并为所有环境变量创建(或覆盖)属性。