mcpField
D
directory-build-organization

directory-build-organization

github.com/dotnet开发5.8k3.9k 次安装1 天前更新
代码仓库

关于 directory-build-organization

为维护 .NET 构建配置的开发者提供组织 Directory.Build 文件和集中管理 NuGet 版本的指导。内容重点说明 .props 与 .targets 的导入时机、适用范围及多层目录配置。

主要功能

  1. 01区分 .props 与 .targets 的用途
  2. 02规划 Directory.Packages.props 集中版本
  3. 03组织多层目录构建配置
  4. 04识别 TargetFramework 条件陷阱
  5. 05用预处理输出检查最终配置

适用场景

  1. 01整理多个项目重复的构建设置
  2. 02将 NuGet 版本集中管理
  3. 03规划 src 和 test 的分层配置
  4. 04排查属性导入顺序或条件未生效

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dotnet/skills --skill directory-build-organization

安装后在 Claude Code 中输入/directory-build-organization确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
common-patterns.md
multi-level-examples.md
targetframework-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 和 dotnet CLI
  • 非常适合统一 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。

工作流:组织构建基础设施

  1. 审查所有 .csproj 文件 — 汇总解决方案中的每个 <PropertyGroup>、<ItemGroup> 和自定义 <Target>。记录哪些设置重复,哪些是项目专属的。
  2. 创建根目录下的 Directory.Build.props — 将共享属性默认值(LangVersion、Nullable、TreatWarningsAsErrors、元数据)移至此处。这些设置会在项目文件之前导入,因此项目可以覆盖它们。
  3. 创建根目录下的 Directory.Build.targets — 将自定义构建目标、构建后验证,以及依赖 SDK 定义值的属性(例如单目标项目的 OutputPath、TargetFramework)移至此处。这些文件会在 SDK 之后导入,因此所有属性均可用。
  4. 创建 Directory.Packages.props — 启用中央包管理(ManagePackageVersionsCentrally),列出所有 PackageVersion 条目,并从 .csproj 文件中的 PackageReference 项移除 Version=。
  5. 设置多级层次结构 — 在 src/ 和 test/ 文件夹中创建内部 Directory.Build.props 文件,设置不同的配置。使用 GetPathOfFileAbove 串联到父文件。
  6. 简化 .csproj 文件 — 移除所有集中管理的属性、版本属性和重复目标。每个项目只应保留其独有的内容。
  7. 验证 — 针对找到的 .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

这会将所有导入展开并内联显示,方便你准确查看每个属性的设置位置以及最终评估值。

评论

支持 Markdown