directory-build-organization
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOrganizing Build Infrastructure with Directory.Build Files
使用Directory.Build文件组织构建基础设施
Directory.Build.props vs Directory.Build.targets
Directory.Build.props 与 Directory.Build.targets 的区别
Understanding which file to use is critical. They differ in when they are imported during evaluation:
Evaluation order:
Directory.Build.props → SDK .props → YourProject.csproj → SDK .targets → Directory.Build.targetsUse | Use |
|---|---|
| Setting property defaults | Custom build targets |
| Common item definitions | Late-bound property overrides |
| Properties projects can override | Post-build steps |
| Assembly/package metadata | Conditional logic on final values |
| Analyzer PackageReferences | Targets that depend on SDK-defined properties |
Rule of thumb: Properties and items go in . Custom targets and late-bound logic go in .
.props.targetsBecause is imported before the project file, the project can override any value set there. Because is imported after everything, it gets the final say—but projects cannot override values.
.props.targets.targets了解应该使用哪个文件至关重要。它们的区别在于评估期间的导入时机:
评估顺序:
Directory.Build.props → SDK .props → YourProject.csproj → SDK .targets → Directory.Build.targets适用于 | 适用于 |
|---|---|
| 设置属性默认值 | 自定义构建目标 |
| 通用项定义 | 后期绑定属性重写 |
| 项目可重写的属性 | 构建后步骤 |
| 程序集/包元数据 | 基于最终值的条件逻辑 |
| 分析器 PackageReferences | 依赖于SDK定义属性的目标 |
经验法则: 属性和项放在 中。自定义目标和后期绑定逻辑放在 中。
.props.targets由于 在项目文件之前导入,因此项目可以覆盖其中设置的任何值。由于 在所有内容之后导入,它拥有最终决定权——但项目无法覆盖 中的值。
.props.targets.targets⚠️ Critical: TargetFramework Availability in .props vs .targets
⚠️ 关键注意事项:.props 与 .targets 中的 TargetFramework 可用性
Property conditions on in files silently fail for single-targeting projects — the property is empty during evaluation. Move TFM-conditional properties to instead. ItemGroup and Target conditions are not affected.
$(TargetFramework).props.props.targetsSee targetframework-props-pitfall.md for the full explanation.
在 文件中对 设置的属性条件在单目标项目中会静默失败——在 评估期间该属性为空。请将TFM相关的条件属性移至 中。ItemGroup和Target的条件不受影响。
.props$(TargetFramework).props.targets有关完整说明,请参阅 targetframework-props-pitfall.md。
Directory.Build.props
Directory.Build.props
Good candidates: language settings, assembly/package metadata, build warnings, code analysis, common analyzers.
xml
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<Company>Contoso</Company>
<Authors>Contoso Engineering</Authors>
</PropertyGroup>
</Project>Do NOT put here: project-specific TFMs, project-specific PackageReferences, targets/build logic, or properties depending on SDK-defined values (not available during evaluation).
.props适用场景:语言设置、程序集/包元数据、构建警告、代码分析、通用分析器。
xml
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<Company>Contoso</Company>
<Authors>Contoso Engineering</Authors>
</PropertyGroup>
</Project>请勿在此放置: 项目特定的TFM、项目特定的PackageReferences、目标/构建逻辑,或依赖于SDK定义值的属性(在 评估期间不可用)。
.propsDirectory.Build.targets
Directory.Build.targets
Good candidates: custom build targets, late-bound property overrides (values depending on SDK properties), post-build validation.
xml
<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 depends on OutputPath, which is set by the SDK -->
<DocumentationFile Condition="'$(IsPackable)' == 'true'">$(OutputPath)$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
</Project>适用场景:自定义构建目标、后期绑定属性重写(依赖于SDK属性的值)、构建后验证。
xml
<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 depends on OutputPath, which is set by the SDK -->
<DocumentationFile Condition="'$(IsPackable)' == 'true'">$(OutputPath)$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
</Project>Directory.Packages.props (Central Package Management)
Directory.Packages.props(集中包管理)
Central Package Management (CPM) provides a single source of truth for all NuGet package versions. See https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management for details.
Enable CPM in at the repo root:
Directory.Packages.propsxml
<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 applies to ALL projects — great for analyzers -->
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
</ItemGroup>
</Project>集中包管理(CPM)为所有NuGet包版本提供单一事实来源。有关详细信息,请参阅 https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management。
在代码库根目录的 中启用CPM:
Directory.Packages.propsxml
<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 applies to ALL projects — great for analyzers -->
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
</ItemGroup>
</Project>Directory.Build.rsp
Directory.Build.rsp
Contains default MSBuild CLI arguments applied to all builds under the directory tree.
Example :
Directory.Build.rsp/maxcpucount
/nodeReuse:false
/consoleLoggerParameters:Summary;ForceNoAlign
/warnAsMessage:MSB3277- Works with both and
msbuildCLI in modern .NET versionsdotnet - Great for enforcing consistent CI and local build flags
- Each argument goes on its own line
包含应用于目录树下所有构建的默认MSBuild CLI参数。
Directory.Build.rsp/maxcpucount
/nodeReuse:false
/consoleLoggerParameters:Summary;ForceNoAlign
/warnAsMessage:MSB3277- 在现代.NET版本中可与 和
msbuildCLI配合使用dotnet - 非常适合强制执行一致的CI和本地构建标志
- 每个参数单独占一行
Multi-level Directory.Build Files
多层级Directory.Build文件
MSBuild only auto-imports the first (or ) it finds walking up from the project directory. To chain multiple levels, explicitly import the parent at the top of the inner file. See multi-level-examples for full file examples.
Directory.Build.props.targetsxml
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"
Condition="Exists('$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))')" />
<!-- Inner-level overrides go here -->
</Project>Example layout:
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)MSBuild在从项目目录向上遍历的过程中只会自动导入第一个找到的 (或 )。要链接多个层级,请在内部文件的顶部显式导入父文件。有关完整文件示例,请参阅 multi-level-examples。
Directory.Build.props.targetsxml
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"
Condition="Exists('$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))')" />
<!-- Inner-level overrides go here -->
</Project>示例布局:
repo/
Directory.Build.props ← 代码库全局设置(语言版本、公司信息、分析器)
Directory.Build.targets ← 代码库全局目标
Directory.Packages.props ← 集中包版本
src/
Directory.Build.props ← 源码目录特定设置(导入代码库级设置,设置IsPackable=true)
test/
Directory.Build.props ← 测试目录特定设置(导入代码库级设置,设置IsPackable=false,添加测试包)Artifact Output Layout (.NET 8+)
产物输出布局(.NET 8+)
Set in to automatically produce project-name-separated , , and directories under a single folder, avoiding bin/obj clashes by default. See common-patterns for the directory layout and additional patterns (conditional settings by project type, post-pack validation).
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>Directory.Build.propsbin/obj/publish/artifacts/在 中设置 ,可自动在单个 文件夹下生成按项目名称分隔的 、 和 目录,默认避免bin/obj目录冲突。有关目录布局和其他模式(按项目类型设置条件、打包后验证),请参阅 common-patterns。
Directory.Build.props<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>artifacts/bin/obj/publish/Workflow: Organizing Build Infrastructure
工作流:组织构建基础设施
- Audit all files — Catalog every
.csproj,<PropertyGroup>, and custom<ItemGroup>across the solution. Note which settings repeat and which are project-specific.<Target> - Create root — Move shared property defaults (LangVersion, Nullable, TreatWarningsAsErrors, metadata) here. These are imported before the project file so projects can override them.
Directory.Build.props - Create root — Move custom build targets, post-build validation, and any properties that depend on SDK-defined values (e.g.,
Directory.Build.targets,OutputPathfor single-targeting projects) here. These are imported after the SDK so all properties are available.TargetFramework - Create — Enable Central Package Management (
Directory.Packages.props), list allManagePackageVersionsCentrallyentries, and removePackageVersionfromVersion=items inPackageReferencefiles..csproj - Set up multi-level hierarchy — Create inner files for
Directory.Build.propsandsrc/folders with distinct settings. Usetest/to chain to the parent.GetPathOfFileAbove - Simplify files — Remove all centralized properties, version attributes, and duplicated targets. Each project should only contain what is unique to it.
.csproj - Validate — Run and verify no regressions. Use
dotnet restore && dotnet buildto inspect the final merged view if needed.dotnet msbuild -pp:output.xml
- 审核所有 文件 —— 记录解决方案中所有的
.csproj、<PropertyGroup>和自定义<ItemGroup>。记录哪些设置是重复的,哪些是项目特定的。<Target> - 创建根目录 —— 将共享属性默认值(LangVersion、Nullable、TreatWarningsAsErrors、元数据)移至此处。这些设置会在项目文件之前导入,因此项目可以覆盖它们。
Directory.Build.props - 创建根目录 —— 将自定义构建目标、构建后验证以及所有依赖于SDK定义值的属性(例如
Directory.Build.targets、单目标项目的OutputPath)移至此处。这些设置会在SDK之后导入,因此所有属性都已可用。TargetFramework - 创建 —— 启用集中包管理(
Directory.Packages.props),列出所有ManagePackageVersionsCentrally条目,并从PackageVersion文件的.csproj项中移除PackageReference属性。Version= - 设置多层级结构 —— 为 和
src/文件夹创建内部test/文件,配置不同的设置。使用Directory.Build.props链接到父文件。GetPathOfFileAbove - 简化 文件 —— 移除所有集中管理的属性、版本属性和重复的目标。每个项目应仅包含其特有的设置。
.csproj - 验证 —— 运行 并验证没有回归问题。如有需要,可使用
dotnet restore && dotnet build检查最终合并视图。dotnet msbuild -pp:output.xml
Troubleshooting
故障排除
| Problem | Cause | Fix |
|---|---|---|
| File name casing wrong (exact match required on Linux/macOS) | Verify exact casing: |
Properties from | Project sets the same property after the import | Move the property to |
| Multi-level import doesn't work | Missing | Add the |
Properties using SDK values are empty in | SDK properties aren't defined yet during | Move to |
| File not at repo root or not named exactly | Must be named |
Property condition on | | Move property to |
Diagnosis: Use the preprocessed project output to see all imports and final property values:
bash
dotnet msbuild -pp:output.xml MyProject.csprojThis expands all imports inline so you can see exactly where each property is set and what the final evaluated value is.
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 文件名大小写错误(在Linux/macOS下需要完全匹配) | 验证文件名完全匹配: |
| 项目在导入后设置了相同的属性 | 将该属性移至 |
| 多层级导入不生效 | 内部文件中缺少 | 在内部文件顶部添加 |
| 在 | 移至 |
| 文件不在代码库根目录或文件名不正确 | 必须命名为 |
| 在 | 将属性移至 |
诊断方法: 使用预处理项目输出来查看所有导入和最终属性值:
bash
dotnet msbuild -pp:output.xml MyProject.csproj此命令会将所有导入展开为内联内容,以便你准确查看每个属性的设置位置和最终评估值。