@cratis/pi 7.102.0 → 7.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
- package/package/corpus/hooks/README.md +11 -1
- package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
- package/package/corpus/rules/profiles.md +10 -1
- package/package/corpus/rules/pull-requests.md +2 -0
- package/package/corpus/rules/secrets.md +4 -0
- package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
- package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
- package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
- package/package/corpus/skills/binlog-generation/LICENSE +23 -0
- package/package/corpus/skills/binlog-generation/NOTICE +22 -0
- package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
- package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
- package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
- package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
- package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
- package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
- package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
- package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
- package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
- package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
- package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
- package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
- package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
- package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
- package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
- package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
- package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
- package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
- package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
- package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
- package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
- package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
- package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
- package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
- package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
- package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
- package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
- package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
- package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
- package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
- package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
- package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
- package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
- package/package/corpus/skills/dump-collect/LICENSE +23 -0
- package/package/corpus/skills/dump-collect/NOTICE +40 -0
- package/package/corpus/skills/dump-collect/SKILL.md +97 -0
- package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
- package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
- package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
- package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
- package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
- package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
- package/package/corpus/skills/extension-points/LICENSE +23 -0
- package/package/corpus/skills/extension-points/NOTICE +18 -0
- package/package/corpus/skills/extension-points/SKILL.md +248 -0
- package/package/corpus/skills/filter-syntax/LICENSE +23 -0
- package/package/corpus/skills/filter-syntax/NOTICE +17 -0
- package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
- package/package/corpus/skills/including-generated-files/LICENSE +23 -0
- package/package/corpus/skills/including-generated-files/NOTICE +17 -0
- package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
- package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
- package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
- package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
- package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
- package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
- package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
- package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
- package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
- package/package/corpus/skills/msbuild/LICENSE +23 -0
- package/package/corpus/skills/msbuild/NOTICE +55 -0
- package/package/corpus/skills/msbuild/SKILL.md +117 -0
- package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
- package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
- package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
- package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
- package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
- package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
- package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
- package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
- package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
- package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
- package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
- package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
- package/package/corpus/skills/orleans/LICENSE +21 -0
- package/package/corpus/skills/orleans/NOTICE +64 -0
- package/package/corpus/skills/orleans/SKILL.md +140 -0
- package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
- package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
- package/package/corpus/skills/orleans/references/examples.md +66 -0
- package/package/corpus/skills/orleans/references/grain-api.md +784 -0
- package/package/corpus/skills/orleans/references/grains.md +75 -0
- package/package/corpus/skills/orleans/references/hosting.md +71 -0
- package/package/corpus/skills/orleans/references/implementation.md +591 -0
- package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
- package/package/corpus/skills/orleans/references/patterns.md +811 -0
- package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
- package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
- package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
- package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
- package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
- package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
- package/package/corpus/skills/platform-detection/LICENSE +23 -0
- package/package/corpus/skills/platform-detection/NOTICE +17 -0
- package/package/corpus/skills/platform-detection/SKILL.md +230 -0
- package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
- package/package/corpus/skills/profiling/LICENSE +21 -0
- package/package/corpus/skills/profiling/NOTICE +50 -0
- package/package/corpus/skills/profiling/SKILL.md +144 -0
- package/package/corpus/skills/profiling/references/commands.md +439 -0
- package/package/corpus/skills/profiling/references/patterns.md +586 -0
- package/package/corpus/skills/profiling/references/profiling.md +190 -0
- package/package/corpus/skills/run-tests/LICENSE +23 -0
- package/package/corpus/skills/run-tests/NOTICE +19 -0
- package/package/corpus/skills/run-tests/SKILL.md +305 -0
- package/package/corpus/skills/target-authoring/LICENSE +23 -0
- package/package/corpus/skills/target-authoring/NOTICE +17 -0
- package/package/corpus/skills/target-authoring/SKILL.md +165 -0
- package/package/corpus/skills/vectorization/LICENSE +23 -0
- package/package/corpus/skills/vectorization/NOTICE +17 -0
- package/package/corpus/skills/vectorization/SKILL.md +189 -0
- package/package/profile-catalog.json +85 -1
- package/package.json +1 -1
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: extension-points
|
|
3
|
+
description: 'Owns MSBuild import and hook discovery: CustomBefore and CustomAfter hooks, ordered wildcard and NuGet auto-imports, control properties, build and buildTransitive packed layout, package ID and file-name matching, per-TFM forwarders, and tracing why assets or hooks are missing, broken, or replaced. Use when imports or hooks do not run, run in the wrong order, or are overwritten. Not for a verdict on whether one Import needs an Exists guard (use the msbuild skill''s antipatterns reference), target Inputs/Outputs or incremental skipping (use target-authoring), or props-versus-targets placement (use directory-build-organization).'
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- Modified by Cratis from dotnet's original (MIT): frontmatter, applicability, navigation and the changes listed in NOTICE. Upstream: https://github.com/dotnet/skills (plugins/dotnet-msbuild/skills/extension-points/SKILL.md; see NOTICE) -->
|
|
8
|
+
|
|
9
|
+
# MSBuild Extension Points
|
|
10
|
+
|
|
11
|
+
Cratis rules in `.cratis/ai/rules/` (including `csharp.md`, `specs.csharp.md`, and `orleans.md`) and the `cratis-*` skills win where they differ.
|
|
12
|
+
|
|
13
|
+
How the MSBuild pipeline provides hooks for SDKs, NuGet packages, repos, and users to inject custom logic.
|
|
14
|
+
|
|
15
|
+
## CustomBefore / CustomAfter Hooks
|
|
16
|
+
|
|
17
|
+
Every major `.targets` file defines import hooks:
|
|
18
|
+
|
|
19
|
+
```xml
|
|
20
|
+
<PropertyGroup>
|
|
21
|
+
<CustomBeforeMicrosoftCommonTargets Condition="'$(CustomBeforeMicrosoftCommonTargets)' == ''">
|
|
22
|
+
$(MSBuildExtensionsPath)\v$(MSBuildToolsVersion)\Custom.Before.Microsoft.Common.targets
|
|
23
|
+
</CustomBeforeMicrosoftCommonTargets>
|
|
24
|
+
</PropertyGroup>
|
|
25
|
+
|
|
26
|
+
<Import Project="$(CustomBeforeMicrosoftCommonTargets)"
|
|
27
|
+
Condition="'$(CustomBeforeMicrosoftCommonTargets)' != '' and Exists('$(CustomBeforeMicrosoftCommonTargets)')"/>
|
|
28
|
+
<!-- ... core targets ... -->
|
|
29
|
+
<Import Project="$(CustomAfterMicrosoftCommonTargets)"
|
|
30
|
+
Condition="'$(CustomAfterMicrosoftCommonTargets)' != '' and Exists('$(CustomAfterMicrosoftCommonTargets)')"/>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Rules
|
|
34
|
+
|
|
35
|
+
- Default path includes version (`v$(MSBuildToolsVersion)`) for side-by-side installations.
|
|
36
|
+
- Always check `Exists()`. The file may not be present on every machine.
|
|
37
|
+
- **Append** to the property (don't overwrite) to chain multiple hooks:
|
|
38
|
+
|
|
39
|
+
```xml
|
|
40
|
+
<PropertyGroup>
|
|
41
|
+
<CustomBeforeMicrosoftCommonTargets>
|
|
42
|
+
$(CustomBeforeMicrosoftCommonTargets);$(MSBuildThisFileDirectory)MyExtension.targets
|
|
43
|
+
</CustomBeforeMicrosoftCommonTargets>
|
|
44
|
+
</PropertyGroup>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Wildcard Import Directories
|
|
48
|
+
|
|
49
|
+
MSBuild imports all files in extension directories, sorted alphabetically:
|
|
50
|
+
|
|
51
|
+
```xml
|
|
52
|
+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.props\ImportBefore\*"
|
|
53
|
+
Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == 'true'
|
|
54
|
+
and Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.props\ImportBefore')" />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Key paths
|
|
58
|
+
|
|
59
|
+
| Property | Resolves to | Scope |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `$(MSBuildUserExtensionsPath)` | `%APPDATA%\Microsoft\MSBuild` | Per-user |
|
|
62
|
+
| `$(MSBuildExtensionsPath)` | MSBuild install directory | Machine-wide |
|
|
63
|
+
| `$(MSBuildProjectExtensionsPath)` | `obj/` directory | Per-project (NuGet) |
|
|
64
|
+
|
|
65
|
+
Name files with numeric prefixes for ordering: `01-first.props`, `02-second.props`.
|
|
66
|
+
|
|
67
|
+
## Import Gating — Control Properties
|
|
68
|
+
|
|
69
|
+
Every wildcard import is gated by a boolean property:
|
|
70
|
+
|
|
71
|
+
```xml
|
|
72
|
+
<PropertyGroup>
|
|
73
|
+
<ImportByWildcardBeforeMicrosoftCommonProps
|
|
74
|
+
Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportByWildcardBeforeMicrosoftCommonProps>
|
|
75
|
+
<ImportDirectoryBuildProps
|
|
76
|
+
Condition="'$(ImportDirectoryBuildProps)' == ''">true</ImportDirectoryBuildProps>
|
|
77
|
+
</PropertyGroup>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Available control properties
|
|
81
|
+
|
|
82
|
+
| Property | What it disables |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `ImportDirectoryBuildProps` | Directory.Build.props auto-discovery |
|
|
85
|
+
| `ImportDirectoryBuildTargets` | Directory.Build.targets auto-discovery |
|
|
86
|
+
| `ImportProjectExtensionProps` | NuGet-generated `*.props` in obj/ |
|
|
87
|
+
| `ImportProjectExtensionTargets` | NuGet-generated `*.targets` in obj/ |
|
|
88
|
+
| `ImportByWildcardBefore*` | Machine-level ImportBefore extensions |
|
|
89
|
+
| `ImportByWildcardAfter*` | Machine-level ImportAfter extensions |
|
|
90
|
+
|
|
91
|
+
## NuGet Package Build Extension Layout
|
|
92
|
+
|
|
93
|
+
NuGet packages inject build logic via `build/` or `buildTransitive/` folders:
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
MyPackage/
|
|
97
|
+
build/
|
|
98
|
+
MyPackage.props ← imported via *.props wildcard
|
|
99
|
+
MyPackage.targets ← imported via *.targets wildcard
|
|
100
|
+
buildTransitive/
|
|
101
|
+
MyPackage.props ← imported by transitive consumers
|
|
102
|
+
MyPackage.targets
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Rules
|
|
106
|
+
|
|
107
|
+
- File names **must match the package ID** exactly.
|
|
108
|
+
- `build/` affects direct consumers only. `buildTransitive/` affects the entire dependency chain.
|
|
109
|
+
- Props are imported early (before the project), targets are imported late (after the project).
|
|
110
|
+
|
|
111
|
+
### Forwarding chain: `buildTransitive/` → `build/` → shared
|
|
112
|
+
|
|
113
|
+
Forward `buildTransitive/*.props` and `buildTransitive/*.targets` through their sibling `build/*.props` / `build/*.targets` files (chain `buildTransitive → build → shared`) instead of importing `buildMultiTargeting/` directly. This keeps `build/` as the single source of truth with a clear ownership chain, so transitive consumers stay in sync with direct consumers instead of the two layouts drifting apart.
|
|
114
|
+
|
|
115
|
+
When `build/` is packed **per-TFM** (`build/<tfm>/`, via `TfmSpecificPackageFile`, a per-TFM `<PackagePath>`, or SDK conventions) while `buildMultiTargeting/` is not, a `buildTransitive/<tfm>/` forwarder **must include the TFM segment** — dropping it resolves to a non-existent package-root `build/MyPackage.props` and fails transitive consumers with **`MSB4019`**. Derive the segment from the file's own folder, never `$(TargetFramework)` (NuGet nearest-match can serve a `net10.0` consumer the `net9.0` folder, so `$(TargetFramework)` may name a folder that was never restored):
|
|
116
|
+
|
|
117
|
+
```xml
|
|
118
|
+
<!-- buildTransitive/<tfm>/MyPackage.props -->
|
|
119
|
+
<Import Project="$(MSBuildThisFileDirectory)..\..\build\$([System.IO.Path]::GetFileName($([System.IO.Path]::GetDirectoryName('$(MSBuildThisFileDirectory)'))))\MyPackage.props" />
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Source Tree vs Packed Layout
|
|
123
|
+
|
|
124
|
+
When reviewing a NuGet build-extension package, the **source layout** in the repository can legitimately differ from the **packed layout** inside the produced `.nupkg`. This is a common source of false-positive "import points at a missing file" findings.
|
|
125
|
+
|
|
126
|
+
Three packaging mechanisms reshape the layout at pack time:
|
|
127
|
+
|
|
128
|
+
1. **`.nuspec` `<file src=… target=…>` mappings** — copy a single source file into multiple per-TFM targets:
|
|
129
|
+
|
|
130
|
+
```xml
|
|
131
|
+
<!-- Source tree has ONE shared file:
|
|
132
|
+
buildTransitive\common\MyAdapter.props
|
|
133
|
+
Pack rewrites it to per-TFM targets inside the .nupkg:
|
|
134
|
+
buildTransitive\net462\MyAdapter.props
|
|
135
|
+
buildTransitive\net8.0\MyAdapter.props
|
|
136
|
+
buildTransitive\net9.0\MyAdapter.props -->
|
|
137
|
+
<files>
|
|
138
|
+
<file src="buildTransitive\common\MyAdapter.props" target="buildTransitive\net462\MyAdapter.props" />
|
|
139
|
+
<file src="buildTransitive\common\MyAdapter.props" target="buildTransitive\net8.0\MyAdapter.props" />
|
|
140
|
+
<file src="buildTransitive\common\MyAdapter.props" target="buildTransitive\net9.0\MyAdapter.props" />
|
|
141
|
+
</files>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
In the `<file>` element, a `target` ending in `\` is treated as a folder (filename preserved from `src`); a `target` ending in a filename renames the file.
|
|
145
|
+
|
|
146
|
+
2. **`.csproj` `<PackagePath>` metadata** on `<None Update=…>` or `<Content Include=…>` items — same effect via SDK pack. Use one item per destination to keep the mapping unambiguous:
|
|
147
|
+
|
|
148
|
+
```xml
|
|
149
|
+
<ItemGroup>
|
|
150
|
+
<None Include="buildTransitive\common\MyAdapter.props" Pack="true" PackagePath="buildTransitive\net8.0\MyAdapter.props" />
|
|
151
|
+
<None Include="buildTransitive\common\MyAdapter.props" Pack="true" PackagePath="buildTransitive\net9.0\MyAdapter.props" />
|
|
152
|
+
</ItemGroup>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
NuGet/SDK pack also accepts a semicolon-separated list (`PackagePath="buildTransitive\net8.0\;buildTransitive\net9.0\"`) to fan one source out to multiple destinations, but the multi-item form above is harder to misread.
|
|
156
|
+
|
|
157
|
+
3. **SDK conventions** — `IncludeBuildOutput`, `BuildOutputTargetFolder`, `IncludeContentInPack` automatically place built outputs under `lib/<tfm>/` or `build/<tfm>/`.
|
|
158
|
+
|
|
159
|
+
### Implication for reviewers
|
|
160
|
+
|
|
161
|
+
A forwarder like the following inside a packed `build/net462/` folder is **not** a "missing-file" bug, even if the source tree has no `buildTransitive/net462/` directory:
|
|
162
|
+
|
|
163
|
+
```xml
|
|
164
|
+
<!-- In packed build/net462/MyAdapter.props -->
|
|
165
|
+
<Project>
|
|
166
|
+
<Import Project="$(MSBuildThisFileDirectory)..\..\buildTransitive\net462\MyAdapter.props" />
|
|
167
|
+
</Project>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Before flagging an unguarded `<Import>` inside a `build/<tfm>/` or `buildTransitive/<tfm>/` folder:
|
|
171
|
+
|
|
172
|
+
1. Look for `*.nuspec` in the project directory and its immediate parent directory (do not walk further up). Read every `<file target=…>` whose `target` matches the imported path.
|
|
173
|
+
2. Read the `.csproj` for `<PackagePath>` metadata on `<None>`/`<Content>` items.
|
|
174
|
+
3. Only flag the import if the target path is missing from **both** the source tree *and* the projected package layout.
|
|
175
|
+
|
|
176
|
+
See also msbuild antipatterns AP-13 ("NuGet package forwarders" exception).
|
|
177
|
+
|
|
178
|
+
## Import Guard Pattern
|
|
179
|
+
|
|
180
|
+
The `.targets` file ensures `.props` was imported using a guard property:
|
|
181
|
+
|
|
182
|
+
```xml
|
|
183
|
+
<!-- End of Microsoft.Common.props -->
|
|
184
|
+
<PropertyGroup>
|
|
185
|
+
<MicrosoftCommonPropsHasBeenImported>true</MicrosoftCommonPropsHasBeenImported>
|
|
186
|
+
</PropertyGroup>
|
|
187
|
+
|
|
188
|
+
<!-- Top of Microsoft.Common.CurrentVersion.targets -->
|
|
189
|
+
<Import Project="Microsoft.Common.props"
|
|
190
|
+
Condition="'$(MicrosoftCommonPropsHasBeenImported)' != 'true'" />
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This handles projects that only import `.targets`.
|
|
194
|
+
|
|
195
|
+
## Directory.Build Discovery
|
|
196
|
+
|
|
197
|
+
MSBuild walks up the directory tree to find the nearest `Directory.Build.props`:
|
|
198
|
+
|
|
199
|
+
```xml
|
|
200
|
+
<_DirectoryBuildPropsBasePath>
|
|
201
|
+
$([MSBuild]::GetDirectoryNameOfFileAbove('$(MSBuildProjectDirectory)', 'Directory.Build.props'))
|
|
202
|
+
</_DirectoryBuildPropsBasePath>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Only the **nearest** file is discovered. Nested hierarchies must explicitly import parents:
|
|
206
|
+
|
|
207
|
+
```xml
|
|
208
|
+
<!-- src/Directory.Build.props -->
|
|
209
|
+
<PropertyGroup>
|
|
210
|
+
<_ParentPropsPath>$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))</_ParentPropsPath>
|
|
211
|
+
</PropertyGroup>
|
|
212
|
+
<Import Project="$(_ParentPropsPath)" Condition="'$(_ParentPropsPath)' != ''" />
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Creating Your Own Extension Point
|
|
216
|
+
|
|
217
|
+
```xml
|
|
218
|
+
<!-- MySDK.targets -->
|
|
219
|
+
<Project>
|
|
220
|
+
<Import Project="MySDK.props" Condition="'$(MySDKPropsImported)' != 'true'" />
|
|
221
|
+
|
|
222
|
+
<PropertyGroup>
|
|
223
|
+
<CustomBeforeMySDK Condition="'$(CustomBeforeMySDK)' == ''">$(MSBuildProjectDirectory)\MySDK.Before.targets</CustomBeforeMySDK>
|
|
224
|
+
<CustomAfterMySDK Condition="'$(CustomAfterMySDK)' == ''">$(MSBuildProjectDirectory)\MySDK.After.targets</CustomAfterMySDK>
|
|
225
|
+
</PropertyGroup>
|
|
226
|
+
|
|
227
|
+
<Import Project="$(CustomBeforeMySDK)" Condition="Exists('$(CustomBeforeMySDK)')" />
|
|
228
|
+
|
|
229
|
+
<PropertyGroup>
|
|
230
|
+
<MySDKBuildDependsOn>BeforeMySDKBuild;CoreMySDKBuild;AfterMySDKBuild</MySDKBuildDependsOn>
|
|
231
|
+
</PropertyGroup>
|
|
232
|
+
<Target Name="MySDKBuild" DependsOnTargets="$(MySDKBuildDependsOn)" />
|
|
233
|
+
<Target Name="BeforeMySDKBuild" />
|
|
234
|
+
<Target Name="AfterMySDKBuild" />
|
|
235
|
+
<Target Name="CoreMySDKBuild">
|
|
236
|
+
<!-- implementation -->
|
|
237
|
+
</Target>
|
|
238
|
+
|
|
239
|
+
<Import Project="$(CustomAfterMySDK)" Condition="Exists('$(CustomAfterMySDK)')" />
|
|
240
|
+
</Project>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Common Pitfalls
|
|
244
|
+
|
|
245
|
+
- **Missing `Exists()` on optional imports** causes build failures when files are absent. **Exception**: imports inside published `build/<tfm>/` and `buildTransitive/<tfm>/` folders of a NuGet package are a package contract — the target is guaranteed by the packed layout (see "Source Tree vs Packed Layout" above). Don't guard them and don't flag them.
|
|
246
|
+
- **Overwriting Custom* properties** drops prior hooks. Append with `;` separator.
|
|
247
|
+
- **NuGet package file names not matching package ID** silently skips the import.
|
|
248
|
+
- **Nested Directory.Build.props** without parent import loses repo-root settings.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) .NET Foundation and Contributors
|
|
4
|
+
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Test Filter Syntax Reference
|
|
2
|
+
Copyright (c) .NET Foundation and Contributors
|
|
3
|
+
Licensed under the MIT License. See LICENSE.
|
|
4
|
+
|
|
5
|
+
Upstream source: https://github.com/dotnet/skills/blob/a6bf43ab61967c30919334986515163ab24d7635/plugins/dotnet-test/skills/filter-syntax/SKILL.md
|
|
6
|
+
|
|
7
|
+
Changes made by Cratis:
|
|
8
|
+
|
|
9
|
+
LICENSE:
|
|
10
|
+
- Copy upstream MIT license and copyright verbatim.
|
|
11
|
+
|
|
12
|
+
SKILL.md:
|
|
13
|
+
- Normalize frontmatter serialization, add MIT license where absent; move user-invocable, disable-model-invocation under metadata as strings.
|
|
14
|
+
- Add marked modification/source comment and applicability/Cratis precedence line with the skill-specific safety, ownership or compatibility caveat.
|
|
15
|
+
- Rewrite the description to the Cratis shape (what it does, Use when, Not for) and drop references to skills that are not part of this corpus.
|
|
16
|
+
|
|
17
|
+
NOTICE: Add this attribution, change list, and any download/execution disclosure.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: filter-syntax
|
|
3
|
+
description: 'Reference-only filter syntax for VSTest and Microsoft Testing Platform (MTP) with MSTest, NUnit, xUnit v3, and TUnit. Use when run-tests needs to create or translate a test filter after the platform and framework are known. Not for unfiltered runs or platform detection (use platform-detection); loaded by run-tests, never invoked directly.'
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
user-invocable: 'false'
|
|
7
|
+
disable-model-invocation: 'true'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<!-- Modified by Cratis from dotnet's original (MIT): frontmatter, applicability, navigation and the changes listed in NOTICE. Upstream: https://github.com/dotnet/skills (plugins/dotnet-test/skills/filter-syntax/SKILL.md; see NOTICE) -->
|
|
11
|
+
|
|
12
|
+
# Test Filter Syntax Reference
|
|
13
|
+
|
|
14
|
+
Cratis rules in `.cratis/ai/rules/` (including `csharp.md`, `specs.csharp.md`, and `orleans.md`) and the `cratis-*` skills win where they differ. Specification authoring belongs to **cratis-specifications-csharp**; Cratis core repositories run xUnit v2 on VSTest.
|
|
15
|
+
|
|
16
|
+
Filter syntax depends on the **platform** and **test framework**.
|
|
17
|
+
|
|
18
|
+
## VSTest filters (MSTest, xUnit v2, NUnit on VSTest)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
dotnet test --filter <EXPRESSION>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Expression syntax: `<Property><Operator><Value>[|&<Expression>]`
|
|
25
|
+
|
|
26
|
+
**Operators:**
|
|
27
|
+
|
|
28
|
+
| Operator | Meaning |
|
|
29
|
+
|----------|---------|
|
|
30
|
+
| `=` | Exact match |
|
|
31
|
+
| `!=` | Not exact match |
|
|
32
|
+
| `~` | Contains |
|
|
33
|
+
| `!~` | Does not contain |
|
|
34
|
+
|
|
35
|
+
**Combinators:** `|` (OR), `&` (AND). Parentheses for grouping: `(A|B)&C`
|
|
36
|
+
|
|
37
|
+
**Supported properties by framework:**
|
|
38
|
+
|
|
39
|
+
| Framework | Properties |
|
|
40
|
+
|-----------|-----------|
|
|
41
|
+
| MSTest | `FullyQualifiedName`, `Name`, `ClassName`, `Priority`, `TestCategory` |
|
|
42
|
+
| xUnit | `FullyQualifiedName`, `DisplayName`, `Traits` |
|
|
43
|
+
| NUnit | `FullyQualifiedName`, `Name`, `Priority`, `TestCategory` |
|
|
44
|
+
|
|
45
|
+
An expression without an operator is treated as `FullyQualifiedName~<value>`.
|
|
46
|
+
|
|
47
|
+
**Examples (VSTest):**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Run tests whose name contains "LoginTest"
|
|
51
|
+
dotnet test --filter "Name~LoginTest"
|
|
52
|
+
|
|
53
|
+
# Run a specific test class
|
|
54
|
+
dotnet test --filter "ClassName=MyNamespace.MyTestClass"
|
|
55
|
+
|
|
56
|
+
# Run tests in a category
|
|
57
|
+
dotnet test --filter "TestCategory=Integration"
|
|
58
|
+
|
|
59
|
+
# Exclude a category
|
|
60
|
+
dotnet test --filter "TestCategory!=Slow"
|
|
61
|
+
|
|
62
|
+
# Combine: class AND category
|
|
63
|
+
dotnet test --filter "ClassName=MyNamespace.MyTestClass&TestCategory=Unit"
|
|
64
|
+
|
|
65
|
+
# Either of two classes
|
|
66
|
+
dotnet test --filter "ClassName=MyNamespace.ClassA|ClassName=MyNamespace.ClassB"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## MTP filters — MSTest and NUnit
|
|
70
|
+
|
|
71
|
+
MSTest and NUnit on MTP use the **same `--filter` syntax** as VSTest (same properties, operators, and combinators). The only difference is how the flag is passed:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# .NET SDK 8/9 (after --)
|
|
75
|
+
dotnet test -- --filter "Name~LoginTest"
|
|
76
|
+
|
|
77
|
+
# .NET SDK 10+ (direct)
|
|
78
|
+
dotnet test --filter "Name~LoginTest"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## MTP filters — xUnit (v3)
|
|
82
|
+
|
|
83
|
+
xUnit v3 on MTP uses **framework-specific filter flags** instead of the generic `--filter` expression:
|
|
84
|
+
|
|
85
|
+
| Flag | Description |
|
|
86
|
+
|------|-------------|
|
|
87
|
+
| `--filter-class "name"` | Run all tests in a given class |
|
|
88
|
+
| `--filter-not-class "name"` | Exclude all tests in a given class |
|
|
89
|
+
| `--filter-method "name"` | Run a specific test method |
|
|
90
|
+
| `--filter-not-method "name"` | Exclude a specific test method |
|
|
91
|
+
| `--filter-namespace "name"` | Run all tests in a namespace |
|
|
92
|
+
| `--filter-not-namespace "name"` | Exclude all tests in a namespace |
|
|
93
|
+
| `--filter-trait "name=value"` | Run tests with a matching trait |
|
|
94
|
+
| `--filter-not-trait "name=value"` | Exclude tests with a matching trait |
|
|
95
|
+
|
|
96
|
+
Multiple values can be specified with a single flag: `--filter-class Foo Bar`.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# .NET SDK 8/9
|
|
100
|
+
dotnet test -- --filter-class "MyNamespace.LoginTests"
|
|
101
|
+
|
|
102
|
+
# .NET SDK 10+
|
|
103
|
+
dotnet test --filter-class "MyNamespace.LoginTests"
|
|
104
|
+
|
|
105
|
+
# Combine: namespace + trait
|
|
106
|
+
dotnet test --filter-namespace "MyApp.Tests.Integration" --filter-trait "Category=Smoke"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### xUnit v3 query filter language
|
|
110
|
+
|
|
111
|
+
For complex expressions, use `--filter-query` with a path-segment syntax:
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
/<assemblyFilter>/<namespaceFilter>/<classFilter>/<methodFilter>[traitName=traitValue]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Each segment matches against: assembly name, namespace, class name, method name. Use `*` for "match all" in any segment. Documentation: <https://xunit.net/docs/query-filter-language>
|
|
118
|
+
|
|
119
|
+
```shell
|
|
120
|
+
# xUnit.net v3 MTP — using query language (assembly/namespace/class/method[trait])
|
|
121
|
+
dotnet test -- --filter-query "/*/*/*IntegrationTests*/*[Category=Smoke]"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## MTP filters — TUnit
|
|
125
|
+
|
|
126
|
+
TUnit uses `--treenode-filter` with a path-based syntax:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
--treenode-filter "/<Assembly>/<Namespace>/<ClassName>/<TestName>"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Wildcards (`*`) are supported in any segment. Filter operators can be appended to test names for property-based filtering.
|
|
133
|
+
|
|
134
|
+
| Operator | Meaning |
|
|
135
|
+
|----------|---------|
|
|
136
|
+
| `*` | Wildcard match |
|
|
137
|
+
| `=` | Exact property match (e.g., `[Category=Unit]`) |
|
|
138
|
+
| `!=` | Exclude property value |
|
|
139
|
+
| `&` | AND (combine conditions) |
|
|
140
|
+
| `\|` | OR (within a segment, requires parentheses) |
|
|
141
|
+
|
|
142
|
+
**Examples (TUnit):**
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# All tests in a class
|
|
146
|
+
dotnet run --treenode-filter "/*/*/LoginTests/*"
|
|
147
|
+
|
|
148
|
+
# A specific test
|
|
149
|
+
dotnet run --treenode-filter "/*/*/*/AcceptCookiesTest"
|
|
150
|
+
|
|
151
|
+
# By namespace prefix (wildcard)
|
|
152
|
+
dotnet run --treenode-filter "/*/MyProject.Tests.Api*/*/*"
|
|
153
|
+
|
|
154
|
+
# By custom property
|
|
155
|
+
dotnet run --treenode-filter "/*/*/*/*[Category=Smoke]"
|
|
156
|
+
|
|
157
|
+
# Exclude by property
|
|
158
|
+
dotnet run --treenode-filter "/*/*/*/*[Category!=Slow]"
|
|
159
|
+
|
|
160
|
+
# OR across classes
|
|
161
|
+
dotnet run --treenode-filter "/*/*/(LoginTests)|(SignupTests)/*"
|
|
162
|
+
|
|
163
|
+
# Combined: namespace + property
|
|
164
|
+
dotnet run --treenode-filter "/*/MyProject.Tests.Integration/*/*/*[Priority=Critical]"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## VSTest → MTP filter translation (for migration)
|
|
168
|
+
|
|
169
|
+
**MSTest, NUnit, and xUnit.net v2 (with `YTest.MTP.XUnit2`)**: The VSTest `--filter` syntax is identical on both VSTest and MTP. No changes needed.
|
|
170
|
+
|
|
171
|
+
**xUnit.net v3 (native MTP)**: xUnit.net v3 does NOT support the VSTest `--filter` syntax on MTP. Translate filters using xUnit.net v3's native options:
|
|
172
|
+
|
|
173
|
+
| VSTest `--filter` syntax | xUnit.net v3 MTP equivalent | Notes |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `FullyQualifiedName~ClassName` | `--filter-class *ClassName*` | Wildcards required for substring match |
|
|
176
|
+
| `FullyQualifiedName=Ns.Class.Method` | `--filter-method Ns.Class.Method` | Exact match on fully qualified method |
|
|
177
|
+
| `Name=MethodName` | `--filter-method *MethodName*` | Wildcards for substring match |
|
|
178
|
+
| `Category=Value` (trait) | `--filter-trait "Category=Value"` | Filter by trait name/value pair |
|
|
179
|
+
| Complex expressions | `--filter-query "expr"` | Uses xUnit.net query filter language (see above) |
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) .NET Foundation and Contributors
|
|
4
|
+
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Including Generated Files Into Your Build
|
|
2
|
+
Copyright (c) .NET Foundation and Contributors
|
|
3
|
+
Licensed under the MIT License. See LICENSE.
|
|
4
|
+
|
|
5
|
+
Upstream source: https://github.com/dotnet/skills/blob/a6bf43ab61967c30919334986515163ab24d7635/plugins/dotnet-msbuild/skills/including-generated-files/SKILL.md
|
|
6
|
+
|
|
7
|
+
Changes made by Cratis:
|
|
8
|
+
|
|
9
|
+
LICENSE:
|
|
10
|
+
- Copy upstream MIT license and copyright verbatim.
|
|
11
|
+
|
|
12
|
+
SKILL.md:
|
|
13
|
+
- Normalize frontmatter serialization, add MIT license where absent; preserve name and description.
|
|
14
|
+
- Add marked modification/source comment and applicability/Cratis precedence line.
|
|
15
|
+
- Rewrite the description to the Cratis shape (what it does, Use when, Not for) and drop references to skills that are not part of this corpus.
|
|
16
|
+
|
|
17
|
+
NOTICE: Add this attribution, change list, and any download/execution disclosure.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: including-generated-files
|
|
3
|
+
description: 'Owns MSBuild generated-artifact integration: a target that creates source, Content, None, or another physical file that is missing from compilation or output, target timing, evaluation-time glob misses, IntermediateOutputPath placement, and FileWrites clean tracking. Use when a generated file is missing from the build even if the prompt does not name the producing task. Not for general Include/Remove/Update semantics, item metadata or batching, duplicate declarations, Roslyn source-generator internals, or non-MSBuild systems.'
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- Modified by Cratis from dotnet's original (MIT): frontmatter, applicability, navigation and the changes listed in NOTICE. Upstream: https://github.com/dotnet/skills (plugins/dotnet-msbuild/skills/including-generated-files/SKILL.md; see NOTICE) -->
|
|
8
|
+
|
|
9
|
+
# Including Generated Files Into Your Build
|
|
10
|
+
|
|
11
|
+
Cratis rules in `.cratis/ai/rules/` (including `csharp.md`, `specs.csharp.md`, and `orleans.md`) and the `cratis-*` skills win where they differ.
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
Files generated during the build are generally ignored by the build process. This leads to confusing results such as:
|
|
16
|
+
- Generated files not being included in the output directory
|
|
17
|
+
- Generated source files not being compiled
|
|
18
|
+
- Globs not capturing files created during the build
|
|
19
|
+
|
|
20
|
+
This happens because of how MSBuild's build phases work.
|
|
21
|
+
|
|
22
|
+
## Quick Takeaway
|
|
23
|
+
|
|
24
|
+
For code files generated during the build - we need to add those to `Compile` and `FileWrites` item groups within the target generating the file(s):
|
|
25
|
+
|
|
26
|
+
```xml
|
|
27
|
+
<ItemGroup>
|
|
28
|
+
<Compile Include="$(GeneratedFilePath)" />
|
|
29
|
+
<FileWrites Include="$(GeneratedFilePath)" />
|
|
30
|
+
</ItemGroup>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The target generating the file(s) should be hooked before CoreCompile and BeforeCompile targets - `BeforeTargets="CoreCompile;BeforeCompile"`
|
|
34
|
+
|
|
35
|
+
## Why Generated Files Are Ignored
|
|
36
|
+
|
|
37
|
+
For detailed explanation, see [How MSBuild Builds Projects](https://docs.microsoft.com/visualstudio/msbuild/build-process-overview).
|
|
38
|
+
|
|
39
|
+
### Evaluation Phase
|
|
40
|
+
|
|
41
|
+
MSBuild reads your project, imports everything, creates Properties, expands globs for Items **outside of Targets**, and sets up the build process.
|
|
42
|
+
|
|
43
|
+
### Execution Phase
|
|
44
|
+
|
|
45
|
+
MSBuild runs Targets & Tasks with the provided Properties & Items to perform the build.
|
|
46
|
+
|
|
47
|
+
**Key Takeaway:** Files generated during execution don't exist during evaluation, therefore they aren't found. This particularly affects files that are globbed by default, such as source files (`.cs`).
|
|
48
|
+
|
|
49
|
+
## Solution: Manually Add Generated Files
|
|
50
|
+
|
|
51
|
+
When files are generated during the build, manually add them into the build process. The approach depends on the type of file being generated.
|
|
52
|
+
|
|
53
|
+
### Use `$(IntermediateOutputPath)` for Generated File Location
|
|
54
|
+
|
|
55
|
+
Always use `$(IntermediateOutputPath)` as the base directory for generated files. **Do not** hardcode `obj\` or construct the intermediary path manually (e.g., `obj\$(Configuration)\$(TargetFramework)\`). The intermediate output path can be redirected to a different location in some build configurations (e.g., shared output directories, CI environments). Using `$(IntermediateOutputPath)` ensures your target works correctly regardless of the actual path.
|
|
56
|
+
|
|
57
|
+
### Always Add Generated Files to `FileWrites`
|
|
58
|
+
|
|
59
|
+
Every generated file should be added to the `FileWrites` item group. This ensures that MSBuild's `Clean` target properly removes your generated files. Without this, generated files will accumulate as stale artifacts across builds.
|
|
60
|
+
|
|
61
|
+
```xml
|
|
62
|
+
<ItemGroup>
|
|
63
|
+
<FileWrites Include="$(IntermediateOutputPath)my-generated-file.xyz" />
|
|
64
|
+
</ItemGroup>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Basic Pattern (Non-Code Files)
|
|
68
|
+
|
|
69
|
+
For generated files that need to be copied to output (config files, data files, etc.), add them to `Content` or `None` items before `BeforeBuild`:
|
|
70
|
+
|
|
71
|
+
```xml
|
|
72
|
+
<Target Name="IncludeGeneratedFiles" BeforeTargets="BeforeBuild">
|
|
73
|
+
|
|
74
|
+
<!-- Your logic that generates files goes here -->
|
|
75
|
+
|
|
76
|
+
<ItemGroup>
|
|
77
|
+
<None Include="$(IntermediateOutputPath)my-generated-file.xyz" CopyToOutputDirectory="PreserveNewest"/>
|
|
78
|
+
|
|
79
|
+
<!-- Capture all files of a certain type with a glob -->
|
|
80
|
+
<None Include="$(IntermediateOutputPath)generated\*.xyz" CopyToOutputDirectory="PreserveNewest"/>
|
|
81
|
+
|
|
82
|
+
<!-- Register generated files for proper cleanup -->
|
|
83
|
+
<FileWrites Include="$(IntermediateOutputPath)my-generated-file.xyz" />
|
|
84
|
+
<FileWrites Include="$(IntermediateOutputPath)generated\*.xyz" />
|
|
85
|
+
</ItemGroup>
|
|
86
|
+
</Target>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### For Generated Source Files (Code That Needs Compilation)
|
|
90
|
+
|
|
91
|
+
If you're generating `.cs` files that need to be compiled, use **`BeforeTargets="CoreCompile;BeforeCompile"`**. This is the correct timing for adding `Compile` items — it runs late enough that the file generation has occurred, but before the compiler runs. Using `BeforeBuild` is too early for some scenarios and may not work reliably with all SDK features.
|
|
92
|
+
|
|
93
|
+
```xml
|
|
94
|
+
<Target Name="IncludeGeneratedSourceFiles" BeforeTargets="CoreCompile;BeforeCompile">
|
|
95
|
+
<PropertyGroup>
|
|
96
|
+
<GeneratedCodeDir>$(IntermediateOutputPath)Generated\</GeneratedCodeDir>
|
|
97
|
+
<GeneratedFilePath>$(GeneratedCodeDir)MyGeneratedFile.cs</GeneratedFilePath>
|
|
98
|
+
</PropertyGroup>
|
|
99
|
+
|
|
100
|
+
<MakeDir Directories="$(GeneratedCodeDir)" />
|
|
101
|
+
|
|
102
|
+
<!-- Your logic that generates the .cs file goes here -->
|
|
103
|
+
|
|
104
|
+
<ItemGroup>
|
|
105
|
+
<Compile Include="$(GeneratedFilePath)" />
|
|
106
|
+
<FileWrites Include="$(GeneratedFilePath)" />
|
|
107
|
+
</ItemGroup>
|
|
108
|
+
</Target>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Note: Specifying both `CoreCompile` and `BeforeCompile` ensures the target runs before whichever target comes first, providing robust ordering regardless of customizations in the build.
|
|
112
|
+
|
|
113
|
+
## Target Timing
|
|
114
|
+
|
|
115
|
+
Choose the `BeforeTargets` value based on the type of file being generated:
|
|
116
|
+
|
|
117
|
+
- **`BeforeTargets="BeforeBuild"`** — For non-code files added to `None` or `Content`. Runs early enough for copy-to-output scenarios.
|
|
118
|
+
- **`BeforeTargets="CoreCompile;BeforeCompile"`** — For generated source files added to `Compile`. Ensures the file is included before the compiler runs.
|
|
119
|
+
- **`BeforeTargets="AssignTargetPaths"`** — The "final stop" before `None` and `Content` items (among others) are transformed into new items. Use as a fallback if `BeforeBuild` is too early.
|
|
120
|
+
|
|
121
|
+
## Globbing Behavior
|
|
122
|
+
|
|
123
|
+
Globs behave according to **when** the glob took place:
|
|
124
|
+
|
|
125
|
+
| Glob Location | Files Captured |
|
|
126
|
+
|---------------|----------------|
|
|
127
|
+
| Outside of a target | Only files visible during Evaluation phase (before build starts) |
|
|
128
|
+
| Inside of a target | Files visible when the target runs (can capture generated files if timed correctly) |
|
|
129
|
+
|
|
130
|
+
This is why the solution places the `<ItemGroup>` inside a `<Target>` - the glob runs during execution when the generated files exist.
|
|
131
|
+
|
|
132
|
+
## Relevant Links
|
|
133
|
+
|
|
134
|
+
- [How MSBuild Builds Projects](https://docs.microsoft.com/visualstudio/msbuild/build-process-overview)
|
|
135
|
+
- [Evaluation Phase](https://docs.microsoft.com/visualstudio/msbuild/build-process-overview#evaluation-phase)
|
|
136
|
+
- [Execution Phase](https://docs.microsoft.com/visualstudio/msbuild/build-process-overview#execution-phase)
|
|
137
|
+
- [Common Item Types](https://docs.microsoft.com/visualstudio/msbuild/common-msbuild-project-items)
|
|
138
|
+
- [How the SDK imports items by default](https://github.com/dotnet/sdk/blob/main/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.DefaultItems.props)
|
|
139
|
+
- [Official docs: Handle generated files](https://learn.microsoft.com/visualstudio/msbuild/customize-your-build#handle-generated-files)
|