@cratis/pi 7.101.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/github-actions.instructions.md +17 -0
- 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/github-actions.md +17 -0
- 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/cratis-ci-pipeline-performance/LICENSE +2 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +201 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +158 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/caching.md +86 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +70 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/publish-pipelines.md +67 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/required-checks-and-path-filters.md +117 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md +65 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md +73 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/sources.md +33 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +82 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/ci-timings.sh +356 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1003.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1007.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1010.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/runs.json +102 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +244 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/docker-images.md +86 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +87 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/go-jvm-python-elixir.md +83 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +78 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/other-environments.md +60 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +97 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +778 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/npm-restore-check.cjs +109 -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 +87 -1
- package/package.json +3 -2
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# I/O, Serialization & General Patterns
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Use HttpCompletionOption.ResponseHeadersRead for Streaming
|
|
6
|
+
- Use Async FileStream Operations
|
|
7
|
+
- Use Memory\<byte\> Overloads for Stream.ReadAsync/WriteAsync
|
|
8
|
+
- Use Span-Based TryFormat for Number Formatting
|
|
9
|
+
- Use static readonly for Runtime Devirtualization
|
|
10
|
+
- Avoid Explicit Static Constructors — Use Field Initializers
|
|
11
|
+
- Detection
|
|
12
|
+
- Patterns Requiring Manual Review
|
|
13
|
+
|
|
14
|
+
### Use HttpCompletionOption.ResponseHeadersRead for Streaming
|
|
15
|
+
🟡 **DO** use `ResponseHeadersRead` when downloading large responses | .NET Core 3.0+
|
|
16
|
+
|
|
17
|
+
❌
|
|
18
|
+
```csharp
|
|
19
|
+
var response = await client.GetAsync(uri);
|
|
20
|
+
```
|
|
21
|
+
✅
|
|
22
|
+
```csharp
|
|
23
|
+
using var response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
|
|
24
|
+
using var stream = await response.Content.ReadAsStreamAsync();
|
|
25
|
+
await stream.CopyToAsync(destinationStream);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Impact: ~2x faster for large downloads (10MB+), dramatically reduced memory usage.**
|
|
29
|
+
|
|
30
|
+
### Use Async FileStream Operations
|
|
31
|
+
🟡 **DO** use `FileStream` with `useAsync: true` for scalable file I/O | .NET 6+
|
|
32
|
+
|
|
33
|
+
❌
|
|
34
|
+
```csharp
|
|
35
|
+
using var fs = new FileStream(path, FileMode.Open);
|
|
36
|
+
```
|
|
37
|
+
✅
|
|
38
|
+
```csharp
|
|
39
|
+
await using var fs = new FileStream(path, FileMode.Open, FileAccess.Read,
|
|
40
|
+
FileShare.Read, bufferSize: 4096, useAsync: true);
|
|
41
|
+
|
|
42
|
+
byte[] buffer = new byte[1024];
|
|
43
|
+
while (await fs.ReadAsync(buffer) != 0) { /* process */ }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Impact: Up to 3x faster async reads; allocation reduced from megabytes to hundreds of bytes.**
|
|
47
|
+
|
|
48
|
+
### Use Memory\<byte\> Overloads for Stream.ReadAsync/WriteAsync
|
|
49
|
+
🟡 **DO** use `Memory<byte>`-based stream overloads instead of `byte[]` overloads | .NET 5+
|
|
50
|
+
|
|
51
|
+
❌
|
|
52
|
+
```csharp
|
|
53
|
+
await stream.ReadAsync(buffer, 0, buffer.Length);
|
|
54
|
+
await stream.WriteAsync(buffer, 0, buffer.Length);
|
|
55
|
+
```
|
|
56
|
+
✅
|
|
57
|
+
```csharp
|
|
58
|
+
await stream.ReadAsync(buffer.AsMemory());
|
|
59
|
+
await stream.WriteAsync(buffer.AsMemory());
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Impact: Eliminates ~72 KB allocation per 1,000 read/write pairs on NetworkStream.**
|
|
63
|
+
|
|
64
|
+
### Use Span-Based TryFormat for Number Formatting
|
|
65
|
+
🟡 **DO** use `TryFormat` to format numbers into `Span<char>` buffers | .NET Core 2.1+
|
|
66
|
+
|
|
67
|
+
❌
|
|
68
|
+
```csharp
|
|
69
|
+
string formatted = value.ToString();
|
|
70
|
+
destination.Write(formatted);
|
|
71
|
+
```
|
|
72
|
+
✅
|
|
73
|
+
```csharp
|
|
74
|
+
Span<char> buffer = stackalloc char[20];
|
|
75
|
+
if (value.TryFormat(buffer, out int charsWritten))
|
|
76
|
+
destination.Write(buffer[..charsWritten]);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Impact: Int32.ToString() ~2x faster in .NET Core 2.1, Int32 parsing ~5x faster in .NET Core 3.0.**
|
|
80
|
+
|
|
81
|
+
### Use static readonly for Runtime Devirtualization
|
|
82
|
+
🟡 **DO** store implementations in `static readonly` fields for JIT devirtualization | .NET Core 3.0+
|
|
83
|
+
|
|
84
|
+
❌
|
|
85
|
+
```csharp
|
|
86
|
+
private static Base s_impl = new DerivedImpl();
|
|
87
|
+
s_impl.Process();
|
|
88
|
+
```
|
|
89
|
+
✅
|
|
90
|
+
```csharp
|
|
91
|
+
private static readonly Base s_impl = new DerivedImpl();
|
|
92
|
+
s_impl.Process();
|
|
93
|
+
|
|
94
|
+
private static readonly bool s_feature =
|
|
95
|
+
Environment.GetEnvironmentVariable("Feature") == "1";
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Impact: Virtual call eliminated entirely — can be inlined to zero overhead. Dead code elimination in tier 1.**
|
|
99
|
+
|
|
100
|
+
### Avoid Explicit Static Constructors — Use Field Initializers
|
|
101
|
+
🟡 **AVOID** explicit `static` constructors when field initializers suffice | .NET Core 3.0+
|
|
102
|
+
|
|
103
|
+
❌
|
|
104
|
+
```csharp
|
|
105
|
+
class Foo
|
|
106
|
+
{
|
|
107
|
+
static readonly int s_value;
|
|
108
|
+
static Foo() { s_value = ComputeValue(); }
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
✅
|
|
112
|
+
```csharp
|
|
113
|
+
class Foo
|
|
114
|
+
{
|
|
115
|
+
static readonly int s_value = ComputeValue();
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Impact: Enables better JIT optimization and reduces potential lock overhead on static method access.**
|
|
120
|
+
|
|
121
|
+
## Detection
|
|
122
|
+
|
|
123
|
+
Scan recipes for I/O and serialization anti-patterns. Run these and report exact counts.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# new HttpClient() (socket exhaustion risk)
|
|
127
|
+
grep -rn --include='*.cs' 'new HttpClient(' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
128
|
+
|
|
129
|
+
# new JsonSerializerOptions() not cached (592x slower in .NET 6)
|
|
130
|
+
grep -rn --include='*.cs' 'new JsonSerializerOptions' --exclude-dir=bin --exclude-dir=obj . | grep -v 'static\|readonly' | wc -l
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Patterns Requiring Manual Review
|
|
134
|
+
|
|
135
|
+
- **`JsonSerializer.Serialize/Deserialize` without source-gen context**: Can't determine from grep if a context parameter is passed
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Memory & String Patterns
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Use ReadOnlySpan\<byte\> for Constant Byte Data
|
|
6
|
+
- Use stackalloc for Small Temporary Buffers
|
|
7
|
+
- Use Span.TryWrite for Allocation-Free Interpolation
|
|
8
|
+
- Use Span.Split() for Zero-Allocation Splitting
|
|
9
|
+
- Use UTF8 String Literals (u8 suffix)
|
|
10
|
+
- Use ReadOnlySpan\<char\> Pattern Matching with switch
|
|
11
|
+
- Use params ReadOnlySpan\<T\> to Eliminate Array Allocations
|
|
12
|
+
- Avoid Chained String-Returning Operations
|
|
13
|
+
- Cache char.ToString() for Known Character Sets
|
|
14
|
+
- Detection
|
|
15
|
+
- Patterns Requiring Manual Review
|
|
16
|
+
|
|
17
|
+
### Use ReadOnlySpan\<byte\> for Constant Byte Data
|
|
18
|
+
🟡 **DO** assign constant byte arrays to `ReadOnlySpan<byte>` | .NET 5+
|
|
19
|
+
|
|
20
|
+
❌
|
|
21
|
+
```csharp
|
|
22
|
+
byte[] data = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F };
|
|
23
|
+
```
|
|
24
|
+
✅
|
|
25
|
+
```csharp
|
|
26
|
+
ReadOnlySpan<byte> data = [0x48, 0x65, 0x6C, 0x6C, 0x6F];
|
|
27
|
+
ReadOnlySpan<int> primes = [2, 3, 5, 7, 11, 13];
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Impact: ~100x faster access than static byte[] field, zero allocation.**
|
|
31
|
+
|
|
32
|
+
### Use stackalloc for Small Temporary Buffers
|
|
33
|
+
🟡 **DO** use `stackalloc` for small, fixed-size temporary buffers | .NET Core+
|
|
34
|
+
|
|
35
|
+
❌
|
|
36
|
+
```csharp
|
|
37
|
+
char[] buffer = new char[64];
|
|
38
|
+
guid.TryFormat(buffer, out int written);
|
|
39
|
+
```
|
|
40
|
+
✅
|
|
41
|
+
```csharp
|
|
42
|
+
Span<char> buffer = stackalloc char[64];
|
|
43
|
+
guid.TryFormat(buffer, out int written);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Impact: Zero heap allocation, no GC pressure, instant alloc/dealloc.**
|
|
47
|
+
|
|
48
|
+
### Use Span.TryWrite for Allocation-Free Interpolation
|
|
49
|
+
🟡 **DO** use `MemoryExtensions.TryWrite` to format into `Span<char>` buffers | .NET 6+
|
|
50
|
+
|
|
51
|
+
❌
|
|
52
|
+
```csharp
|
|
53
|
+
string formatted = $"Date: {dt:R}";
|
|
54
|
+
destination.Write(formatted);
|
|
55
|
+
```
|
|
56
|
+
✅
|
|
57
|
+
```csharp
|
|
58
|
+
Span<char> buffer = stackalloc char[64];
|
|
59
|
+
buffer.TryWrite($"Date: {dt:R}", out int charsWritten);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Impact: Zero heap allocation for formatting operations.**
|
|
63
|
+
|
|
64
|
+
### Use Span.Split() for Zero-Allocation Splitting
|
|
65
|
+
🟡 **DO** use `MemoryExtensions.Split` for allocation-free string splitting | .NET 9+
|
|
66
|
+
|
|
67
|
+
❌
|
|
68
|
+
```csharp
|
|
69
|
+
string[] parts = input.Split(',');
|
|
70
|
+
```
|
|
71
|
+
✅
|
|
72
|
+
```csharp
|
|
73
|
+
foreach (Range range in input.AsSpan().Split(','))
|
|
74
|
+
{
|
|
75
|
+
ReadOnlySpan<char> segment = input.AsSpan(range);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Impact: 208 bytes → 0 bytes per split, 2x faster.**
|
|
80
|
+
|
|
81
|
+
### Use UTF8 String Literals (u8 suffix)
|
|
82
|
+
🟡 **DO** use the `u8` suffix for compile-time UTF8 `ReadOnlySpan<byte>` | .NET 7+
|
|
83
|
+
|
|
84
|
+
❌
|
|
85
|
+
```csharp
|
|
86
|
+
byte[] header = Encoding.UTF8.GetBytes("Content-Type");
|
|
87
|
+
```
|
|
88
|
+
✅
|
|
89
|
+
```csharp
|
|
90
|
+
ReadOnlySpan<byte> header = "Content-Type"u8;
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Impact: 17ns → 0.006ns — eliminates runtime transcoding entirely.**
|
|
94
|
+
|
|
95
|
+
### Use ReadOnlySpan\<char\> Pattern Matching with switch
|
|
96
|
+
🟡 **DO** use `switch` on `ReadOnlySpan<char>` for allocation-free string matching | C# 11+
|
|
97
|
+
|
|
98
|
+
❌
|
|
99
|
+
```csharp
|
|
100
|
+
switch (attr.Value.Trim()) { case "preserve": /* ... */ break; }
|
|
101
|
+
```
|
|
102
|
+
✅
|
|
103
|
+
```csharp
|
|
104
|
+
switch (attr.Value.AsSpan().Trim())
|
|
105
|
+
{
|
|
106
|
+
case "preserve": return Preserve;
|
|
107
|
+
case "default": return Default;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Impact: Eliminates string allocation from Trim() in switch-based dispatch.**
|
|
112
|
+
|
|
113
|
+
### Use params ReadOnlySpan\<T\> to Eliminate Array Allocations
|
|
114
|
+
🟡 **DO** add `params ReadOnlySpan<T>` overloads to library methods | C# 13 / .NET 9+
|
|
115
|
+
|
|
116
|
+
❌
|
|
117
|
+
```csharp
|
|
118
|
+
public static void Log(params string[] messages) { /* ... */ }
|
|
119
|
+
Log("Starting", "Processing", "Done");
|
|
120
|
+
```
|
|
121
|
+
✅
|
|
122
|
+
```csharp
|
|
123
|
+
public static void Log(params ReadOnlySpan<string> messages) { /* ... */ }
|
|
124
|
+
Log("Starting", "Processing", "Done");
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Impact: Eliminates params array allocation. E.g., Path.Join with 5+ segments saves 64 bytes per call.**
|
|
128
|
+
|
|
129
|
+
### Avoid Chained String-Returning Operations
|
|
130
|
+
🟡 **AVOID** chains of 3+ string-returning method calls that each allocate intermediates | .NET Core+
|
|
131
|
+
|
|
132
|
+
**Pattern 1: Chained .Replace() calls**
|
|
133
|
+
|
|
134
|
+
❌
|
|
135
|
+
```csharp
|
|
136
|
+
string result = input.Replace("a", "b").Replace("c", "d").Replace("e", "f");
|
|
137
|
+
```
|
|
138
|
+
✅
|
|
139
|
+
```csharp
|
|
140
|
+
var sb = new StringBuilder(input.Length);
|
|
141
|
+
// single pass replacing all patterns
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Pattern 2: Chained Regex.Replace() calls**
|
|
145
|
+
|
|
146
|
+
❌
|
|
147
|
+
```csharp
|
|
148
|
+
public static string Underscore(this string input) =>
|
|
149
|
+
Regex3.Replace(Regex2.Replace(Regex1.Replace(input, "$1_$2"), "$1_$2"), "_").ToLower();
|
|
150
|
+
```
|
|
151
|
+
✅
|
|
152
|
+
```csharp
|
|
153
|
+
return string.Create(totalLength, state, (span, s) => { /* write directly */ });
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Pattern 3: += string concatenation in loops**
|
|
157
|
+
|
|
158
|
+
❌
|
|
159
|
+
```csharp
|
|
160
|
+
string result = "";
|
|
161
|
+
foreach (var part in parts)
|
|
162
|
+
result += separator + part;
|
|
163
|
+
```
|
|
164
|
+
✅
|
|
165
|
+
```csharp
|
|
166
|
+
var sb = new StringBuilder();
|
|
167
|
+
foreach (var part in parts)
|
|
168
|
+
sb.Append(separator).Append(part);
|
|
169
|
+
return sb.ToString();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Impact: Eliminates N-1 intermediate string allocations per chain. For `+=` in loops, eliminates O(n²) total allocation.**
|
|
173
|
+
|
|
174
|
+
### Cache char.ToString() for Known Character Sets
|
|
175
|
+
🟡 **DO** cache `char.ToString()` results when the set of characters is small and known | .NET Core+
|
|
176
|
+
|
|
177
|
+
❌
|
|
178
|
+
```csharp
|
|
179
|
+
return symbol.ToString();
|
|
180
|
+
|
|
181
|
+
foreach (var prefix in UnitPrefixes)
|
|
182
|
+
input = input.Replace(prefix.Value.Name, prefix.Key.ToString());
|
|
183
|
+
```
|
|
184
|
+
✅
|
|
185
|
+
```csharp
|
|
186
|
+
private static readonly FrozenDictionary<char, string> s_charStrings =
|
|
187
|
+
new Dictionary<char, string>
|
|
188
|
+
{
|
|
189
|
+
['k'] = "k", ['M'] = "M", ['G'] = "G",
|
|
190
|
+
}.ToFrozenDictionary();
|
|
191
|
+
|
|
192
|
+
return s_charStrings[symbol];
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Impact: Eliminates one string allocation per char.ToString() call. Significant when called in loops or on hot paths.**
|
|
196
|
+
|
|
197
|
+
## Detection
|
|
198
|
+
|
|
199
|
+
Scan recipes for memory and string anti-patterns. Run these and report exact counts.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# .ToLower()/.ToUpper() without culture parameter (allocates + culture-sensitive)
|
|
203
|
+
grep -rn --include='*.cs' -E '\.(ToLower|ToUpper)\(\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
204
|
+
|
|
205
|
+
# Chained .Replace( calls (3+ on one line — intermediate string allocations)
|
|
206
|
+
grep -rn --include='*.cs' '\.Replace(.*\.Replace(.*\.Replace(' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
207
|
+
|
|
208
|
+
# params in method signatures (array allocation per call)
|
|
209
|
+
grep -rn --include='*.cs' 'params ' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
210
|
+
|
|
211
|
+
# LINQ on strings — .All/.Any on IEnumerable<char> (replace with foreach loop)
|
|
212
|
+
grep -rn --include='*.cs' -E '\.(All|Any)\(char\.' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Patterns Requiring Manual Review
|
|
216
|
+
|
|
217
|
+
- **Boxing via string.Format**: Can't determine argument types from grep — needs type analysis
|
|
218
|
+
- **`+=` string concatenation in loops**: `+=` matches all types (int, list, event, string) — needs type context to confirm string
|
|
219
|
+
- **`char.ToString()`**: Requires knowing the variable type is `char` — not reliably greppable
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Regex Patterns
|
|
2
|
+
|
|
3
|
+
### Choose the Right Regex Engine Mode
|
|
4
|
+
🟡 **DO** use `[GeneratedRegex]` for all static regex patterns, but never remove `NonBacktracking` if present | .NET 7+
|
|
5
|
+
|
|
6
|
+
❌
|
|
7
|
+
```csharp
|
|
8
|
+
var r = new Regex(dynamicPattern, RegexOptions.Compiled);
|
|
9
|
+
```
|
|
10
|
+
✅
|
|
11
|
+
```csharp
|
|
12
|
+
[GeneratedRegex("pattern")]
|
|
13
|
+
private static partial Regex MyRegex();
|
|
14
|
+
|
|
15
|
+
var safe = new Regex(untrustedPattern, RegexOptions.NonBacktracking);
|
|
16
|
+
|
|
17
|
+
var oneOff = new Regex("pattern");
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Impact: Source generator is always beneficial for static patterns. NonBacktracking prevents O(2^N) worst case — never remove it if present.**
|
|
21
|
+
|
|
22
|
+
### Use IsMatch When You Only Need a Boolean Result
|
|
23
|
+
🟡 **DO** use `IsMatch` instead of `Match(...).Success` | .NET 7+
|
|
24
|
+
|
|
25
|
+
❌
|
|
26
|
+
```csharp
|
|
27
|
+
bool found = Regex.Match(input, pattern).Success;
|
|
28
|
+
```
|
|
29
|
+
✅
|
|
30
|
+
```csharp
|
|
31
|
+
bool found = Regex.IsMatch(input, pattern);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Impact: Avoids Match object allocation; with NonBacktracking, ~3x faster by skipping capture computation.**
|
|
35
|
+
|
|
36
|
+
### Use Regex.Count/EnumerateMatches Instead of Matches
|
|
37
|
+
🟡 **DO** use `Count()` and `EnumerateMatches()` for allocation-free match processing | .NET 7+
|
|
38
|
+
|
|
39
|
+
❌
|
|
40
|
+
```csharp
|
|
41
|
+
int count = 0;
|
|
42
|
+
Match m = regex.Match(text);
|
|
43
|
+
while (m.Success) { count++; m = m.NextMatch(); }
|
|
44
|
+
```
|
|
45
|
+
✅
|
|
46
|
+
```csharp
|
|
47
|
+
int count = regex.Count(text);
|
|
48
|
+
|
|
49
|
+
foreach (ValueMatch m in Regex.EnumerateMatches(text, @"\b\w+\b"))
|
|
50
|
+
{
|
|
51
|
+
ReadOnlySpan<char> word = text.AsSpan(m.Index, m.Length);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Impact: ~3x faster than Match/NextMatch with NonBacktracking. Zero allocations for both Count and EnumerateMatches.**
|
|
56
|
+
|
|
57
|
+
### Use Span-Based Regex APIs for Allocation-Free Matching
|
|
58
|
+
🟡 **DO** use `ReadOnlySpan<char>` overloads for regex matching on spans | .NET 7+
|
|
59
|
+
|
|
60
|
+
❌
|
|
61
|
+
```csharp
|
|
62
|
+
string sub = largeBuffer.Substring(start, length);
|
|
63
|
+
bool found = Regex.IsMatch(sub, pattern);
|
|
64
|
+
```
|
|
65
|
+
✅
|
|
66
|
+
```csharp
|
|
67
|
+
ReadOnlySpan<char> text = largeBuffer.AsSpan(start, length);
|
|
68
|
+
foreach (ValueMatch m in Regex.EnumerateMatches(text, @"\b\w+\b"))
|
|
69
|
+
{
|
|
70
|
+
ReadOnlySpan<char> word = text.Slice(m.Index, m.Length);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Impact: Eliminates string allocations when working with spans — particularly valuable in high-throughput parsing pipelines.**
|
|
75
|
+
|
|
76
|
+
## Detection
|
|
77
|
+
|
|
78
|
+
Scan recipes for regex anti-patterns. Run these and report exact counts.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Compiled regex count (startup cost budget — compare ratio to GeneratedRegex)
|
|
82
|
+
grep -rn --include='*.cs' 'RegexOptions.Compiled' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
83
|
+
|
|
84
|
+
# GeneratedRegex count (already optimized — verify the inverse)
|
|
85
|
+
grep -rn --include='*.cs' 'GeneratedRegex' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
86
|
+
|
|
87
|
+
# Uncached new Regex() calls (construction cost per call)
|
|
88
|
+
grep -rn --include='*.cs' 'new Regex(' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
When `RegexOptions.Compiled` appears inside a class constructor or field initializer of an instantiated class (not a static singleton), count how many instances of that class are created at startup to determine total compiled regex budget. For example, if a `Rule` class compiles a regex in its constructor and 122 rules are registered, that is 122 compiled regexes at startup.
|
|
92
|
+
|
|
93
|
+
### Patterns Requiring Manual Review
|
|
94
|
+
|
|
95
|
+
- **`new Regex(` uncached**: Field assignment may span multiple lines — grep on one line is unreliable. Verify that matched instances are stored in `static readonly` fields or `[GeneratedRegex]`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Structural Patterns
|
|
2
|
+
|
|
3
|
+
Patterns detected by the **absence** of a keyword or interface. These require codebase-wide counting scans, not single-file matching.
|
|
4
|
+
|
|
5
|
+
### Seal Classes for Devirtualization
|
|
6
|
+
🟡 **DO** seal all leaf classes (those not subclassed) | .NET Core 3.0+
|
|
7
|
+
|
|
8
|
+
Sealing lets the JIT devirtualize/inline virtual calls and use pointer comparison for type checks. Every non-abstract, non-static class that is not subclassed should be sealed. **Cratis qualification:** Never seal public types of a published framework/library package: doing so is a breaking change, and Cratis framework repositories treat public API changes as semver.
|
|
9
|
+
|
|
10
|
+
**Detection:** This is an absence pattern — scan for classes that are NOT sealed.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Count unsealed (non-abstract, non-static) classes
|
|
14
|
+
grep -rn --include='*.cs' -E '^\s*((public|internal|private|protected|file)\s+)?(partial\s+)?class ' --exclude-dir=bin --exclude-dir=obj . | grep -v 'sealed' | grep -v 'abstract' | grep -v 'static' | wc -l
|
|
15
|
+
|
|
16
|
+
# Count already-sealed classes (verify the inverse)
|
|
17
|
+
grep -rn --include='*.cs' 'sealed class' --exclude-dir=bin --exclude-dir=obj . | wc -l
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Exclusions:** Do not seal classes that are subclassed elsewhere in the codebase. Identifying base classes requires manual review — grep for `: ClassName` patterns and cross-reference, but expect false positives from interface implementations and generic constraints.
|
|
21
|
+
|
|
22
|
+
❌
|
|
23
|
+
```csharp
|
|
24
|
+
internal class MyHandler : Base
|
|
25
|
+
{ public override int Run() => 42; }
|
|
26
|
+
```
|
|
27
|
+
✅
|
|
28
|
+
```csharp
|
|
29
|
+
internal sealed class MyHandler : Base
|
|
30
|
+
{ public override int Run() => 42; }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Impact: Virtual calls up to 500x faster; type checks ~25x faster. Severity scales with count.**
|
|
34
|
+
|
|
35
|
+
**Scale-based severity:**
|
|
36
|
+
- 1-10 unsealed leaf classes → ℹ️ Info
|
|
37
|
+
- 11-50 unsealed leaf classes → 🟡 Moderate
|
|
38
|
+
- 50+ unsealed leaf classes → 🟡 Moderate (elevated priority)
|
|
@@ -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,22 @@
|
|
|
1
|
+
Generate Binary Logs
|
|
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/binlog-generation/SKILL.md
|
|
6
|
+
|
|
7
|
+
Changes made by Cratis:
|
|
8
|
+
|
|
9
|
+
LICENSE:
|
|
10
|
+
- Copy upstream MIT license and copyright verbatim.
|
|
11
|
+
|
|
12
|
+
SKILL.md:
|
|
13
|
+
- Remove the SDK version from the description; the 17.12 floor stays in the body.
|
|
14
|
+
- Normalize frontmatter serialization, add MIT license where absent; preserve name and description.
|
|
15
|
+
- Correct the binlog {} placeholder SDK/MSBuild minimum in description and body.
|
|
16
|
+
- Add marked modification/source comment and applicability/Cratis precedence line with the skill-specific safety, ownership or compatibility caveat.
|
|
17
|
+
- Render unimported binlog-failure-analysis routing reference as plain topic text.
|
|
18
|
+
- Render unimported build-perf-diagnostics routing reference as plain topic text.
|
|
19
|
+
- Correct the binlog {} placeholder SDK/MSBuild minimum.
|
|
20
|
+
- 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.
|
|
21
|
+
|
|
22
|
+
NOTICE: Add this attribution, change list, and any download/execution disclosure.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: binlog-generation
|
|
3
|
+
description: 'Generates MSBuild binary logs (binlogs) by adding /bl:{} to dotnet build, test, pack, publish, or restore commands, capturing a full build trace for later investigation. Use when a build error or build-performance problem needs a binlog and none exists. The {} placeholder needs a recent MSBuild; PowerShell must quote the switch as ''-bl:{}''. Not for analyzing an existing binlog or measuring build performance (use the msbuild skill) or for non-MSBuild build systems such as npm, Maven, or CMake.'
|
|
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/binlog-generation/SKILL.md; see NOTICE) -->
|
|
8
|
+
|
|
9
|
+
# Generate Binary Logs
|
|
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. Binlogs embed project and imported file contents: keep them local and task-scoped.
|
|
12
|
+
|
|
13
|
+
**Pass the `/bl` switch when running any MSBuild-based command.** This is a non-negotiable requirement for all .NET builds.
|
|
14
|
+
|
|
15
|
+
## Commands That Require /bl
|
|
16
|
+
|
|
17
|
+
You MUST add the `/bl:{}` flag to:
|
|
18
|
+
- `dotnet build`
|
|
19
|
+
- `dotnet test`
|
|
20
|
+
- `dotnet pack`
|
|
21
|
+
- `dotnet publish`
|
|
22
|
+
- `dotnet restore`
|
|
23
|
+
- `msbuild` or `msbuild.exe`
|
|
24
|
+
- Any other command that invokes MSBuild
|
|
25
|
+
|
|
26
|
+
## Preferred: Use `{}` for Automatic Unique Names
|
|
27
|
+
|
|
28
|
+
> **Note:** The `{}` placeholder requires MSBuild 17.12+ / .NET 9.0.100 SDK or later.
|
|
29
|
+
|
|
30
|
+
The `{}` placeholder in the binlog filename is replaced by MSBuild with a unique identifier, guaranteeing no two builds ever overwrite each other — without needing to track or check existing files.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Every invocation produces a distinct file automatically
|
|
34
|
+
dotnet build /bl:{}
|
|
35
|
+
dotnet test /bl:{}
|
|
36
|
+
dotnet build --configuration Release /bl:{}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**PowerShell requires quoting the complete switch:**
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
# Keep the literal {} placeholder in one argument
|
|
43
|
+
dotnet build '-bl:{}'
|
|
44
|
+
dotnet test '-bl:{}'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Why This Matters
|
|
48
|
+
|
|
49
|
+
1. **Unique names prevent overwrites** - You can always go back and analyze previous builds
|
|
50
|
+
2. **Failure analysis** - When a build fails, the binlog is already there for immediate analysis
|
|
51
|
+
3. **Comparison** - You can compare builds before and after changes
|
|
52
|
+
4. **No re-running builds** - You never need to re-run a failed build just to generate a binlog
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# ✅ CORRECT - {} generates a unique name automatically (bash/cmd)
|
|
58
|
+
dotnet build /bl:{}
|
|
59
|
+
dotnet test /bl:{}
|
|
60
|
+
|
|
61
|
+
# ✅ CORRECT - quote the complete PowerShell argument
|
|
62
|
+
dotnet build '-bl:{}'
|
|
63
|
+
dotnet test '-bl:{}'
|
|
64
|
+
|
|
65
|
+
# ❌ WRONG - Missing /bl flag entirely
|
|
66
|
+
dotnet build
|
|
67
|
+
dotnet test
|
|
68
|
+
|
|
69
|
+
# ❌ WRONG - No filename (overwrites the same msbuild.binlog every time)
|
|
70
|
+
dotnet build /bl
|
|
71
|
+
dotnet build /bl
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## One build = one binlog
|
|
75
|
+
|
|
76
|
+
Add `/bl:{}` to **every** MSBuild invocation separately — never reuse a name and
|
|
77
|
+
never rely on bare `/bl`:
|
|
78
|
+
|
|
79
|
+
- Building several configurations, projects, or retrying a failed build? Each
|
|
80
|
+
command still gets its own `/bl:{}` so the logs never overwrite each other.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
dotnet build -c Debug /bl:{} # unique file
|
|
84
|
+
dotnet build -c Release /bl:{} # another unique file
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Verify the binlog exists
|
|
88
|
+
|
|
89
|
+
After the build, confirm a `.binlog` was actually produced before moving on to
|
|
90
|
+
analysis — a build that fails *before* MSBuild starts (e.g. a bad argument)
|
|
91
|
+
writes no binlog:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
ls -1 *.binlog # bash
|
|
95
|
+
dir /b *.binlog # Windows cmd
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```powershell
|
|
99
|
+
Get-ChildItem *.binlog # PowerShell
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Note the resulting path so binlog failure analysis or build perf diagnostics
|
|
103
|
+
can consume it.
|
|
104
|
+
|
|
105
|
+
## When a Specific Filename Is Required
|
|
106
|
+
|
|
107
|
+
If the binlog filename needs to be known upfront (e.g., for CI artifact upload), or if `{}` is not available in the installed MSBuild version, pick a name that won't collide with existing files:
|
|
108
|
+
|
|
109
|
+
1. Check for existing `*.binlog` files in the directory
|
|
110
|
+
2. Choose a name not already taken (e.g., by incrementing a counter from the highest existing number)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Example: directory contains 3.binlog — use 4.binlog
|
|
114
|
+
dotnet build /bl:4.binlog
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Cleaning the Repository
|
|
118
|
+
|
|
119
|
+
When cleaning the repository with `git clean`, **always exclude binlog files** to preserve your build history:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# ✅ CORRECT - Exclude binlog files from cleaning
|
|
123
|
+
git clean -fdx -e "*.binlog"
|
|
124
|
+
|
|
125
|
+
# ❌ WRONG - This deletes binlog files (they're usually in .gitignore)
|
|
126
|
+
git clean -fdx
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This is especially important when iterating on build fixes - you need the binlogs to analyze what changed between builds.
|
|
@@ -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.
|