关于 directory-build-organization
为维护 .NET 构建配置的开发者提供组织 Directory.Build 文件和集中管理 NuGet 版本的指导。内容重点说明 .props 与 .targets 的导入时机、适用范围及多层目录配置。
主要功能
- 01区分 .props 与 .targets 的用途
- 02规划 Directory.Packages.props 集中版本
- 03组织多层目录构建配置
- 04识别 TargetFramework 条件陷阱
- 05用预处理输出检查最终配置
适用场景
- 01整理多个项目重复的构建设置
- 02将 NuGet 版本集中管理
- 03规划 src 和 test 的分层配置
- 04排查属性导入顺序或条件未生效
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill directory-build-organization安装后在 Claude Code 中输入/directory-build-organization确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/common-patterns.mdmulti-level-examples.mdtargetframework-props-pitfall.md详细介绍
使用 Directory.Build 文件组织构建基础设施
意图控制
- 对于“是否应该”“应该如何组织”“推荐”“建议”或审查请求,请检查实际项目文件,并且只返回建议的布局。除非用户明确要求应用、移动、集中管理或清理配置,否则不要创建或编辑文件。
- 在总结之前,确认每个项目特有的属性、项或目标由哪个项目保留。指出实际文件;不要根据同级文件名推断。
- 只有在该路径下发现实际的
.sln、.slnx、.proj或项目文件后,才推荐验证命令。如果仓库根目录没有入口文件,请提供针对已发现项目的命令,或说明未找到入口文件。 - 每条建议都必须说明:
Microsoft.Common.props会从每个项目所在目录向上搜索,并自动导入最近的Directory.Build.props,且导入较早;之后,项目中的值可以覆盖共享默认值。若推荐Directory.Build.targets,还必须说明等效的Microsoft.Common.targets会在较晚阶段导入。
Directory.Build.props 与 Directory.Build.targets
了解应该使用哪个文件至关重要。两者在评估过程中导入的时机不同:
评估顺序:
Microsoft.Common.props imports Directory.Build.props early
→ project body and package/project imports evaluate
→ Microsoft.Common.targets imports Directory.Build.targets late
.props 适用于 | .targets 适用于 |
|---|---|
| 设置属性默认值 | 自定义构建目标 |
| 公共项定义 | 后期绑定的属性覆盖 |
| 项目可覆盖的属性 | 构建后步骤 |
| 程序集/包元数据 | 基于最终值的条件逻辑 |
| 分析器 PackageReferences | 依赖 SDK 定义属性的目标 |
经验法则: 属性和项放在 .props 中。自定义目标和后期绑定逻辑放在 .targets 中。
由于 .props 在项目文件之前导入,项目可以覆盖其中设置的任何值。由于 .targets 在所有内容之后导入,它最终具有决定权,但项目无法覆盖 .targets 中的值。
⚠️ 重要:.props 与 .targets 中的 TargetFramework 可用性
在 .props 文件中对 $(TargetFramework) 使用属性条件,对于单目标项目会静默失效——在评估 .props 时,该属性为空。请改为将基于 TFM 的条件属性移至 .targets。项组和目标条件不受影响。
完整说明请参阅 targetframework-props-pitfall.md。
Directory.Build.props
适用内容:语言设置、程序集/包元数据、构建警告、代码分析、通用分析器。
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<Company>Contoso</Company>
<Authors>Contoso Engineering</Authors>
</PropertyGroup>
</Project>
不要放在这里: 项目特有的 TFM、项目特有的 PackageReferences、目标/构建逻辑,或依赖 SDK 定义值的属性(这些值在评估 .props 时不可用)。
Directory.Build.targets
适用内容:自定义构建目标、后期绑定的属性覆盖(依赖 SDK 属性的值)、构建后验证。
<Project>
<Target Name="ValidateProjectSettings" BeforeTargets="Build">
<Error Text="All libraries must target netstandard2.0 or higher"
Condition="'$(OutputType)' == 'Library' AND '$(TargetFramework)' == 'net472'" />
</Target>
<PropertyGroup>
<DocumentationFile Condition="'$(IsPackable)' == 'true'">$(OutputPath)$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
</Project>
Directory.Packages.props(中央包管理)
中央包管理(CPM)为所有 NuGet 包版本提供统一的事实来源。详情请参阅 https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management。
在仓库根目录的 Directory.Packages.props 中启用 CPM:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="xunit" Version="2.9.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
</ItemGroup>
</Project>
Directory.Build.rsp
包含应用于目录树下所有构建的默认 MSBuild CLI 参数。
Directory.Build.rsp 示例:
/maxcpucount
/nodeReuse:false
/consoleLoggerParameters:Summary;ForceNoAlign
/warnAsMessage:MSB3277
- 可用于现代 .NET 版本中的
msbuild和dotnetCLI - 非常适合统一 CI 和本地构建标志
- 每个参数单独占一行
多级 Directory.Build 文件
MSBuild 只会自动导入从项目目录向上查找时找到的第一个 Directory.Build.props(或 .targets)。要串联多个层级,请在内部文件的顶部显式导入父文件。完整文件示例请参见 multi-level-examples。
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"
Condition="Exists('$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))')" />
</Project>
示例目录结构:
repo/
Directory.Build.props ← repo-wide (lang version, company info, analyzers)
Directory.Build.targets ← repo-wide targets
Directory.Packages.props ← central package versions
src/
Directory.Build.props ← src-specific (imports repo-level, sets IsPackable=true)
test/
Directory.Build.props ← test-specific (imports repo-level, sets IsPackable=false, adds test packages)
构建产物输出布局(.NET 8+)
在 Directory.Build.props 中设置 <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>,即可自动在统一的 artifacts/ 文件夹下生成按项目名称区分的 bin/、obj/ 和 publish/ 目录,默认避免 bin/obj 冲突。目录布局和其他模式(按项目类型进行条件设置、打包后验证)请参见 common-patterns。
工作流:组织构建基础设施
- 审查所有
.csproj文件 — 汇总解决方案中的每个<PropertyGroup>、<ItemGroup>和自定义<Target>。记录哪些设置重复,哪些是项目专属的。 - 创建根目录下的
Directory.Build.props— 将共享属性默认值(LangVersion、Nullable、TreatWarningsAsErrors、元数据)移至此处。这些设置会在项目文件之前导入,因此项目可以覆盖它们。 - 创建根目录下的
Directory.Build.targets— 将自定义构建目标、构建后验证,以及依赖 SDK 定义值的属性(例如单目标项目的OutputPath、TargetFramework)移至此处。这些文件会在 SDK 之后导入,因此所有属性均可用。 - 创建
Directory.Packages.props— 启用中央包管理(ManagePackageVersionsCentrally),列出所有PackageVersion条目,并从.csproj文件中的PackageReference项移除Version=。 - 设置多级层次结构 — 在
src/和test/文件夹中创建内部Directory.Build.props文件,设置不同的配置。使用GetPathOfFileAbove串联到父文件。 - 简化
.csproj文件 — 移除所有集中管理的属性、版本属性和重复目标。每个项目只应保留其独有的内容。 - 验证 — 针对找到的
.sln、.slnx、.proj或项目路径运行 restore/build,例如dotnet restore <entrypoint> && dotnet build <entrypoint>。如果不存在根入口点,则明确验证找到的每个项目,而不要声称进行了仓库根目录构建。使用dotnet msbuild <project> -pp:output.xml检查最终合并后的视图。
故障排除
| 问题 | 原因 | 修复方法 |
|---|---|---|
未加载 Directory.Build.props | 文件名大小写错误(在 Linux/macOS 上必须完全匹配) | 检查大小写是否完全一致:Directory.Build.props(D、B 大写) |
项目忽略 .props 中的属性 | 项目在导入后设置了相同属性 | 将属性移至 Directory.Build.targets,使其在项目之后设置 |
| 多级导入不起作用 | 内部文件中缺少 GetPathOfFileAbove 导入 | 在内部文件顶部添加 <Import> 元素(参见“多级”部分) |
.props 中使用 SDK 值的属性为空 | 在评估 .props 时,SDK 属性尚未定义 | 将其移至在 SDK 之后导入的 .targets |
找不到 Directory.Packages.props | 文件不在仓库根目录,或文件名不完全匹配 | 文件名必须为 Directory.Packages.props,且位于项目目录或其上级目录中 |
.props 中基于 $(TargetFramework) 的属性条件不匹配 | 对于单目标项目,在评估 .props 时尚未设置 TargetFramework | 将属性移至 .targets,或改用 ItemGroup/Target 条件(它们会较晚评估) |
诊断: 使用预处理后的项目输出查看所有导入和最终属性值:
dotnet msbuild -pp:output.xml MyProject.csproj
这会将所有导入展开并内联显示,方便你准确查看每个属性的设置位置以及最终评估值。