Property Patterns

dotnet/skills/plugins/dotnet-msbuild/skills/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 屬性模式,涵蓋條件式預設值、巢狀條件群組、分號串接、路徑正規化、目標 Framework 與作業系統偵測、防護屬性、功能閘控、後援鏈以及評估順序。它用來診斷並修正具體的屬性缺陷,例如值被覆寫、缺少條件,以及路徑無法攜行等問題,適用於專案檔與共用檔案階層。它產出的是修正後的屬性定義與指引,而非檔案或指令碼。
適用情境
當專案或共用建置檔案有具體的 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 從公開儲存庫中收錄這些內容。

檢舉或申請下架