如何为不同的解决方案分别引入 Build.props?
可以在共用的 Directory.Build.props 中,根据 $(SolutionName) 设置属性,或按条件导入各自的 .props 文件。共享项目会按当前解决方案的构建上下文读取相应配置。
这种方式要求项目在构建时能获取解决方案上下文。直接构建 .csproj 时,应使用默认值,或通过命令行显式传入属性。
为什么两个解决方案都会受影响
MSBuild 从项目所在目录向上查找 Directory.Build.props,找到后便导入。查找路径由项目在磁盘上的位置决定,与解决方案文件的位置无关。因此,两个解决方案中的 Project2.csproj 会找到同一个 Directory.Build.props。Microsoft Learn:按目录自定义构建
MSBuild 不会根据 MySolution.props、MySolution.Debug.props 这样的文件名自动区分解决方案,导入时需要明确写出条件。
只有少量属性时,直接按名称设置
假设需要以下配置:
- 构建
MySolution.slnx时,UseNuGet为true。 - 构建
MySolution.Debug.slnx时,UseNuGet为false。 - 单独构建项目时,默认使用
false。
在根目录的 Directory.Build.props 中写入:
<Project>
<PropertyGroup>
<UseNuGet Condition="'$(UseNuGet)' == ''">false</UseNuGet>
</PropertyGroup>
<PropertyGroup Condition="'$(SolutionName)' == 'MySolution'">
<UseNuGet>true</UseNuGet>
</PropertyGroup>
<PropertyGroup Condition="'$(SolutionName)' == 'MySolution.Debug'">
<UseNuGet>false</UseNuGet>
</PropertyGroup>
</Project>
$(SolutionName) 是不含扩展名的解决方案名称,因此条件中分别使用 MySolution 和 MySolution.Debug。构建解决方案时,包括 SolutionName 在内的上下文属性会传给项目。MSBuild 源码:解决方案属性传递
按此配置,Project2.csproj 通过不同解决方案构建时,会获得不同的 UseNuGet 值。Project1.csproj 通过 MySolution.Debug.slnx 构建时也会获得 false。
文件名中的 .Debug 只是解决方案名称的一部分,不会自动选择项目的 Debug 配置。构建配置仍由 $(Configuration) 和解决方案中的配置映射决定。
配置较多时,分别导入 .props 文件
可以使用以下目录结构:
根目录/
├── MySolution.slnx
├── MySolution.Debug.slnx
├── Directory.Build.props
├── build/
│ ├── MySolution.props
│ └── MySolution.Debug.props
├── Project1/
│ └── Project1.csproj
└── Project2/
└── Project2.csproj
在 Directory.Build.props 中保留默认值,并按解决方案名称导入对应文件:
<Project>
<PropertyGroup>
<UseNuGet Condition="'$(UseNuGet)' == ''">false</UseNuGet>
</PropertyGroup>
<Import
Project="$(MSBuildThisFileDirectory)build/MySolution.props"
Condition="'$(SolutionName)' == 'MySolution'" />
<Import
Project="$(MSBuildThisFileDirectory)build/MySolution.Debug.props"
Condition="'$(SolutionName)' == 'MySolution.Debug'" />
</Project>
build/MySolution.props:
<Project>
<PropertyGroup>
<UseNuGet>true</UseNuGet>
</PropertyGroup>
</Project>
build/MySolution.Debug.props:
<Project>
<PropertyGroup>
<UseNuGet>false</UseNuGet>
</PropertyGroup>
</Project>
$(MSBuildThisFileDirectory) 指向当前导入文件所在的目录。用它定位 build 文件夹,可以避免相对路径随项目位置变化。
两种写法选一种即可。
单独构建项目时,显式传入属性
直接构建共享项目时,项目本身无法确定该采用哪个解决方案的配置。这个例子只有一个开关,可以直接传入:
dotnet build Project2/Project2.csproj -p:UseNuGet=true
构建整个解决方案时,也可以统一指定:
dotnet build MySolution.slnx -p:UseNuGet=true
dotnet build MySolution.Debug.slnx -p:UseNuGet=false
通过命令行 -p 传入的是全局属性,通常会传递给子项目,且优先于项目文件中的普通属性赋值;显式声明为局部属性等特殊情况除外。Microsoft Learn:MSBuild 属性
运行这些解决方案构建命令,要求所用的 .NET SDK/MSBuild 支持 .slnx 格式。
需要注意的边界
UseNuGet在这里是自定义属性,只有项目、导入文件或构建目标读取它时才会影响行为。将它设为false不会自动关闭 NuGet 还原或移除PackageReference。Directory.Build.props导入较早,后续的.csproj或其他导入文件仍可能覆盖其中的普通属性。如果属性值不符合预期,应检查后续赋值。- 如果项目目录下还有更近的
Directory.Build.props,默认查找会停在那里,需要由该文件显式导入上层配置。位于根目录之外的项目也需要另行安排导入。 - 如果同一个共享项目的依赖会随属性变化,还原和构建时应使用一致的属性。并行构建两种依赖配置时,还应考虑隔离中间文件和输出目录。
Directory.Solution.props 定制的是解决方案构建层。把这些项目属性直接移入其中,普通属性赋值不会自动传递给所有项目,而且这类解决方案构建扩展不适用于 Visual Studio 内部构建。Microsoft Learn:自定义解决方案构建
备注:内容仅供参考。
