Property Patterns

作者 dotnet0608d8924cd3MIT5.5K 个星标收录于 2026年10月8日更新于 2026年10月8日仓库今天更新

Diagnose and fix concrete MSBuild property defects in projects and existing shared-file hierarchies. USE FOR: conditions, defaults, append versus overwrite, last-write-wins values, OS/TFM checks, portable paths, normalization, and reviews centered on those property defects. Property defects in Directory.Build.* remain in scope, including overwritten values across parent/child imports and conditions that run before TargetFramework is set, even when the fix changes import order or moves a property group to .targets. DO NOT USE FOR: placement-only or import-only requests with no concrete property defect, such as discovering shared files or choosing which file owns a target or customization (use directory-build-organization); item operations; target structure; broad reviews without a concrete property defect; non-MSBuild work.

AI 生成的概览

用于诊断和修复项目及共享构建文件中具体 MSBuild 属性缺陷的参考模式。

功能
该技能提供规范的 MSBuild 属性模式,涵盖条件默认值、嵌套条件组、分号拼接、路径规范化、目标框架与操作系统检测、防护属性、功能开关、回退链以及求值顺序。它用于诊断和修复具体属性缺陷,例如值被覆盖、缺少条件以及路径不可移植等问题,适用于项目文件和共享文件层次结构。它产出的是修正后的属性定义和指导,而不是文件或脚本。
适用场景
当项目或共享构建文件存在具体的 MSBuild 属性缺陷时使用,例如默认值无法被覆盖、追加列表时覆盖了原有值,或路径破坏可移植性。它也适用于围绕此类属性缺陷的审查,包括 Directory.Build 文件中的缺陷。不适用于仅涉及放置位置或导入的问题、项操作或目标结构。
运行要求
不需要脚本或工具,仅为说明性内容。工作对象是 MSBuild 项目文件和共享构建文件,因此隐含需要基于 MSBuild 的项目环境。

MSBuild Property Patterns

Canonical property definition and manipulation patterns from the MSBuild repository.

Conditional Defaults — The Foundational Pattern

Set a property only if not already set, allowing callers to override:

xml
<PropertyGroup>  <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>  <Platform Condition="'$(Platform)' == ''">AnyCPU</Platform>  <BuildInParallel Condition="'$(BuildInParallel)' == ''">true</BuildInParallel></PropertyGroup>

Rules

  • Always quote both sides: '$(Prop)' == ''
  • In .props: creates overridable defaults. In .targets: creates fallbacks.
  • Properties without the condition cannot be overridden by earlier imports.

Nested Conditional Groups

Group related properties under a shared condition:

xml
<PropertyGroup Condition="$(TargetFramework.StartsWith('net4'))">  <DefineConstants>$(DefineConstants);FEATURE_APARTMENT_STATE</DefineConstants>  <DefineConstants>$(DefineConstants);FEATURE_APM</DefineConstants>  <FeatureAppDomain>true</FeatureAppDomain></PropertyGroup>
<PropertyGroup Condition="'$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)'))' == '.NETCoreApp'">  <NetCoreBuild>true</NetCoreBuild>  <DefineConstants>$(DefineConstants);RUNTIME_TYPE_NETCORE</DefineConstants></PropertyGroup>

Use the outer Condition on PropertyGroup to avoid repeating the same condition on every property.

Warning: $(TargetFramework) is empty in .props files for single-targeting projects until the project body is evaluated. Place TargetFramework-conditioned property groups in .targets files (or the project file itself), where the value is always available.

Composition — Semicolon Concatenation

Properties that hold lists use semicolons. Always include the existing value when appending:

xml
<PropertyGroup>  <DefineConstants>$(DefineConstants);MY_FEATURE</DefineConstants>  <NoWarn>$(NoWarn);NU5131;IDE0005</NoWarn>  <LibraryTargetFrameworks>$(FullFrameworkTFM);$(LatestDotNetCoreForMSBuild);netstandard2.0</LibraryTargetFrameworks></PropertyGroup>

Path Normalization and Trailing Slashes

xml
<!-- Ensure trailing slash on directories --><PropertyGroup>  <OutDir Condition="'$(OutDir)' != '' and !HasTrailingSlash('$(OutDir)')">$(OutDir)\</OutDir></PropertyGroup>
<!-- Normalize paths for cross-platform --><PropertyGroup>  <TargetRefPath>$([MSBuild]::NormalizePath('$(TargetDir)', 'ref', '$(TargetFileName)'))</TargetRefPath></PropertyGroup>
<!-- Make relative path absolute --><PropertyGroup>  <MSBuildProjectExtensionsPath      Condition="'$([System.IO.Path]::IsPathRooted('$(MSBuildProjectExtensionsPath)'))' == 'false'">    $([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)'))  </MSBuildProjectExtensionsPath></PropertyGroup>

Preferred path functions

FunctionPurpose
$([MSBuild]::NormalizePath(...))Combine and normalize (cross-platform)
$([System.IO.Path]::Combine(...))Combine path segments
$([System.IO.Path]::IsPathRooted(...))Check if absolute
HasTrailingSlash(...)Check for trailing slash
$([MSBuild]::GetDirectoryNameOfFileAbove(...))Walk up directory tree
$(MSBuildThisFileDirectory)Directory of current file

Target Framework Detection Helpers

xml
<!-- Get TFM identifier --><PropertyGroup Condition="'$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)'))' == '.NETCoreApp'">  <NetCoreBuild>true</NetCoreBuild></PropertyGroup>
<!-- Check TFM compatibility --><PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net472'))">  <UseFrozenVersions>true</UseFrozenVersions></PropertyGroup>
<!-- OS detection --><PropertyGroup Condition="$([MSBuild]::IsOSPlatform('windows'))">  <DefineConstants>$(DefineConstants);TEST_ISWINDOWS</DefineConstants></PropertyGroup>

Guard Properties

Mark that a file has been imported to prevent double-imports:

xml
<!-- At the end of MySDK.props --><PropertyGroup>  <MySDKPropsImported>true</MySDKPropsImported></PropertyGroup>
<!-- At the top of MySDK.targets --><Import Project="MySDK.props" Condition="'$(MySDKPropsImported)' != 'true'" />

Feature Gating by MSBuild Version

xml
<PropertyGroup Condition="$([MSBuild]::AreFeaturesEnabled('17.10'))">  <UseNewBehavior>true</UseNewBehavior></PropertyGroup>

Fallback Chains

Set via primary source first, then fall back:

xml
<PropertyGroup>  <TlbExpPath>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToDotNetFrameworkSdkFile('tlbexp.exe'))</TlbExpPath>  <TlbExpPath Condition="'$(TlbExpPath)' == ''">$(_NetFxToolsDir)TlbExp.exe</TlbExpPath></PropertyGroup>

Last Write Wins — Evaluation Order

MSBuild evaluates properties top-to-bottom. The last assignment wins:

xml
<!-- File 1 (imported first) --><MyProp>value1</MyProp>        <!-- set to value1 --><!-- File 2 (imported second) --><MyProp>value2</MyProp>        <!-- overwritten to value2 --><!-- File 3 (imported third) --><MyProp Condition="'$(MyProp)' == ''">value3</MyProp>  <!-- NOT set — already value2 -->

Properties in .targets (imported late) override properties in .props (imported early) and the project file.

Common Pitfalls

  • Unquoted conditions ($(X)==true) fail when the property is empty. Always quote both sides.
  • Overwriting DefineConstants (<DefineConstants>MY_CONST</DefineConstants>) drops all prior constants. Always append with $(DefineConstants);.
  • Hardcoded absolute paths break portability. Use $(MSBuildThisFileDirectory) or $([MSBuild]::NormalizePath(...)).
  • Missing Condition on defaults makes properties non-overridable. Add Condition="'$(Prop)' == ''" for values meant to be defaults.

来源与署名

来源:dotnet/skills位于plugins/dotnet-msbuild/skills/property-patterns提交0608d89

许可证: MIT

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架