Skip to content
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
4f5d351
Add find-repeated-build-work skill to dotnet-msbuild
YuliiaKovalova Oct 13, 2026
39e935e
Address repeated-build-work review feedback
YuliiaKovalova Oct 13, 2026
edceb93
Verify repeated-work fixes remain connected to builds
YuliiaKovalova Oct 13, 2026
1ba9b01
Clarify binlog capture and preferred MCP analysis
YuliiaKovalova Oct 13, 2026
c7f5d27
Address repeated-work review summary findings
YuliiaKovalova Oct 13, 2026
5c47b1b
Close repeated-build-work grader gaps
YuliiaKovalova Oct 13, 2026
1b62da9
Preserve repeated-work outputs and parallelism routing
YuliiaKovalova Oct 14, 2026
b7d1ec0
Exercise the submitted shared-project build end to end
YuliiaKovalova Oct 14, 2026
666524f
Clarify cross-command and second-build routing boundaries
YuliiaKovalova Oct 14, 2026
28339f6
Preserve unchanged shared generation timestamps
YuliiaKovalova Oct 14, 2026
bad8932
Check generated timestamps at the build boundary
YuliiaKovalova Oct 14, 2026
5449a22
Cover generic routing and schema output preservation
YuliiaKovalova Oct 14, 2026
bac5703
Accept CLI properties and partition instance diagnostics
YuliiaKovalova Oct 14, 2026
94e7dab
Exercise per-project generation hooks in the race grader
YuliiaKovalova Oct 14, 2026
fe492e1
Preserve build invocation and exercise submitted generators
YuliiaKovalova Oct 14, 2026
3243b0d
Check effective Compile items and accept offline summaries
YuliiaKovalova Oct 14, 2026
76d4ca8
Prioritize isolated evaluation and restore routing exclusions
YuliiaKovalova Oct 14, 2026
6d1d8d8
Merge branch 'main' into yuliiakovalova/find-repeated-build-work
YuliiaKovalova Oct 16, 2026
ed8ff5a
Merge main and reconcile build-performance routing
YuliiaKovalova Oct 16, 2026
7e39802
Fix conditional graders and broaden repeated-work eval coverage
YuliiaKovalova Oct 16, 2026
bc10c27
Enforce schema incrementality and correct batching savings
YuliiaKovalova Oct 16, 2026
8a152de
Merge branch 'main' into yuliiakovalova/find-repeated-build-work
YuliiaKovalova Oct 19, 2026
952d24c
Fix orchestrated and conditional MSBuild graders
YuliiaKovalova Oct 19, 2026
c0f6200
Accept implicit schema output directory creation
YuliiaKovalova Oct 19, 2026
d8f24a5
Merge remote-tracking branch 'origin/main' into HEAD
YuliiaKovalova Oct 21, 2026
087dbd0
Migrate repeated-build-work eval to Vally 0.18
YuliiaKovalova Oct 22, 2026
2bf9eca
Fix dormancy metadata lookup in grader contract test
YuliiaKovalova Oct 22, 2026
fd81af9
Evaluate conditional Git stamp assignments through MSBuild
YuliiaKovalova Oct 22, 2026
823b6b8
Replay CI build arguments in Git stamp probe
YuliiaKovalova Oct 22, 2026
fcb10c5
Keep MSBuild routing menu within runtime budget
YuliiaKovalova Oct 29, 2026
9a2004b
Merge branch 'main' into yuliiakovalova/find-repeated-build-work
YuliiaKovalova Oct 29, 2026
c0b8b62
Judge fallback guards and schema hooks by MSBuild behavior
YuliiaKovalova Oct 29, 2026
0ea5362
Trim repeated-build-work PR to skill-specific scope
YuliiaKovalova Oct 29, 2026
feaf8ae
Validate schema import reachability through MSBuild
YuliiaKovalova Oct 29, 2026
52bea49
Exercise the SDK build graph in Git-stamp grader
YuliiaKovalova Oct 29, 2026
92104cf
Verify compiled attributes and unchanged-build compilation
YuliiaKovalova Oct 30, 2026
157821c
Accept native revision orchestrators and renamed schema items
YuliiaKovalova Oct 30, 2026
8304af9
Validate schema rewrites through native MSBuild execution
YuliiaKovalova Oct 30, 2026
777c892
Reject added schemas without restricting helper imports
YuliiaKovalova Oct 30, 2026
1ace78e
Accept cached shared generation and reproduce the writer race
YuliiaKovalova Oct 30, 2026
da63404
Evaluate equivalent revision lookups using native Git
YuliiaKovalova Oct 30, 2026
f97403d
Strengthen the unchanged-build dormancy boundary
YuliiaKovalova Oct 30, 2026
931e9c6
Preserve grader script entry points and compilation history
YuliiaKovalova Oct 30, 2026
185e834
Validate native generation without a legacy helper requirement
YuliiaKovalova Oct 30, 2026
4fe6b22
Accept native clocks and SDK revision metadata
YuliiaKovalova Oct 30, 2026
9a4e320
Restart shared build probes across UTC rollover
YuliiaKovalova Oct 30, 2026
c60fd4d
Verify stamp changes against the complete warm cache
YuliiaKovalova Oct 30, 2026
414667e
Verify submitted build-info UTC defaults
YuliiaKovalova Oct 30, 2026
b996f90
Clarify shared-value fallback and property inheritance
YuliiaKovalova Oct 30, 2026
e436649
Clarify repeated-work routing and no-op decisions
YuliiaKovalova Nov 2, 2026
9b4f4b9
Preserve the complete generated schema source set
YuliiaKovalova Nov 2, 2026
7d7957d
Allow SDK sources beside generated schema outputs
YuliiaKovalova Nov 2, 2026
b208b7e
Keep submitted generated sources visible to native grading
YuliiaKovalova Nov 2, 2026
5e9eaeb
Clarify the within-build repeated-work skill scope
YuliiaKovalova Nov 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions msbuild-coexistence.experiment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ variants:
- plugins/dotnet-msbuild/skills/eliminate-redundant-restores
- plugins/dotnet-msbuild/skills/eval-performance
- plugins/dotnet-msbuild/skills/extension-points
- plugins/dotnet-msbuild/skills/find-repeated-build-work
- plugins/dotnet-msbuild/skills/including-generated-files
- plugins/dotnet-msbuild/skills/incremental-build
- plugins/dotnet-msbuild/skills/item-management
Expand Down
2 changes: 1 addition & 1 deletion plugins/dotnet-msbuild/skills/build-parallelism/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: build-parallelism
description: "Analyze an MSBuild solution, solution filter, or Build.proj that schedules multiple project files. USE FOR: measured idle worker nodes, `-m` that does not scale, a serial ProjectReference critical path, graph builds, or an MSBuild task that builds a Projects list. Requires at least two distinct project files whose scheduling or throughput must be analyzed. DO NOT USE for non-MSBuild build systems."
description: "Analyze MSBuild scheduling across at least two distinct projects in a solution/filter/Build.proj. USE FOR: measured idle workers, -m not scaling, serial ProjectReference critical paths, graph builds, MSBuild Projects-list scheduling. DO NOT USE FOR: duplicate executions or invariant-generation races (find-repeated-build-work), non-MSBuild systems."
license: MIT
---

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: build-perf-baseline
description: "Establish MSBuild/.NET build performance baselines before optimizing. USE FOR: unranked slow builds; cold, warm, incremental or no-op measurements; before/after comparisons; CI output caching; static graph, artifacts output and dependency graph trimming. Start here before build-perf-diagnostics, incremental-build or build-parallelism. DO NOT USE FOR: known repeated-restore fixes (use eliminate-redundant-restores), non-MSBuild builds. For measured target/task/analyzer bottlenecks, use build-perf-diagnostics."
description: "Establish MSBuild/.NET baselines before optimizing. USE FOR: unranked slow builds; cold/warm/incremental/no-op measurements; before/after comparisons; CI caching; static graph, artifacts output, dependency graph trimming. DO NOT USE FOR: measured bottlenecks (build-perf-diagnostics), known repeated restores (eliminate-redundant-restores), within-build duplicate calls (find-repeated-build-work), non-MSBuild builds."
license: MIT
---

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: build-perf-diagnostics
description: "Diagnose MSBuild bottlenecks from binlog timelines and Target/Task Performance Summaries after build-perf-baseline establishes measurements. USE FOR: unknown build slowness, RAR >5s, analyzers >30% of Csc, one target >50% of build time, node utilization <80%, excessive Copy tasks, finding restore overhead. DO NOT USE FOR: known repeated-restore fixes (use eliminate-redundant-restores), initial baselines (use build-perf-baseline), incremental rebuilds (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild builds."
description: "Diagnose MSBuild bottlenecks from binlog timelines and Target/Task Performance Summaries after baseline measurements. USE FOR: unknown build slowness, RAR >5s, analyzers >30% of Csc, a target >50% of build, node utilization below 80%, excessive Copy, restore overhead. DO NOT USE FOR: within-build duplicates (find-repeated-build-work), known repeated restores (eliminate-redundant-restores), initial baselines (build-perf-baseline), incremental rebuilds (incremental-build), parallelism tuning (build-parallelism), non-MSBuild builds."
license: MIT
---

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: check-bin-obj-clash
description: "Detects MSBuild projects with conflicting OutputPath or IntermediateOutputPath. USE FOR: builds failing with 'Cannot create a file when that file already exists', 'The process cannot access the file because it is being used by another process', intermittent build failures that succeed on retry, or missing/overwritten outputs in multi-project or multi-targeting builds where bin/obj (or project.assets.json) collide. Common causes: shared OutputPath, missing AppendTargetFrameworkToOutputPath, extra global properties (e.g. PublishReadyToRun), or SetTargetFramework on a ProjectReference to a single-targeting project. DO NOT USE FOR: file access errors unrelated to MSBuild (OS-level locking), single-project single-TFM builds, non-MSBuild build systems."
description: "Find OutputPath/IntermediateOutputPath collisions between distinct projects or required property/TFM variants. USE FOR: missing/overwritten bin/obj or project.assets.json, parallel 'Cannot create a file when that file already exists' or 'file is being used by another process', shared paths, missing AppendTargetFrameworkToOutputPath, PublishReadyToRun, SetTargetFramework on single-target references. DO NOT USE FOR: duplicates or invariant-generation races (find-repeated-build-work), OS locking, single-instance builds, non-MSBuild systems."
license: MIT
---

Expand Down
2 changes: 1 addition & 1 deletion plugins/dotnet-msbuild/skills/eval-performance/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: eval-performance
description: "Guide for diagnosing and improving MSBuild project evaluation performance. USE FOR: builds slow before any compilation starts, high evaluation time in binlog analysis, expensive glob patterns walking large directories (node_modules, .git, bin/obj), deep import chains (>20 levels), preprocessed output >10K lines indicating heavy evaluation, property functions with file I/O ($([System.IO.File]::ReadAllText(...))), multiple evaluations per project. Covers the 5 MSBuild evaluation phases, glob optimization via DefaultItemExcludes, import chain analysis with /pp preprocessing. DO NOT USE FOR: compilation-time slowness (use build-perf-diagnostics), incremental build issues (use incremental-build), non-MSBuild build systems."
description: "Diagnose measured MSBuild evaluation cost before targets run. USE FOR: evaluation-only repeats under global-property variants, expensive node_modules/.git/bin/obj globs, import depth >20, /pp >10K lines, file-I/O property functions, five-phase evaluation. DO NOT USE FOR: duplicate executions (find-repeated-build-work), execution bottlenecks (build-perf-diagnostics), incremental rebuilds (incremental-build), non-MSBuild systems."
license: MIT
---

Expand Down
113 changes: 113 additions & 0 deletions plugins/dotnet-msbuild/skills/find-repeated-build-work/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
name: find-repeated-build-work
description: "Audit target/task executions in ONE MSBuild invocation. NEVER INVOKE for evaluation-only repeats (eval-performance) or restores across commands (eliminate-redundant-restores), even if those skills are unavailable. USE FOR: invariant per-project Exec, redundant project builds, per-item Exec, run-once races, execution-count audits. DO NOT USE FOR: unranked slowness (build-perf-baseline), required-variant bin/obj collisions (check-bin-obj-clash), unchanged reruns (incremental-build), idle workers/serial chains (build-parallelism)."
license: MIT
---

# Find Repeated Build Work

MSBuild runs targets once per project instance. Work that does not depend on the project, but sits in `Directory.Build.targets` or a shared target, therefore runs once per project: a `git rev-parse` in 40 projects is 40 process launches. A project built with different global properties is a different instance, so a shared library can compile several times in one build. The binlog's `N calls` column shows both. This skill finds the repeats, separates them from work that is legitimately per project, and removes only what is safe to remove.

## When to Use

- The same `Exec` or task (version stamping, tool probes, file generation) runs in every project
- Target or Task Performance Summary shows a call count far above the number of projects that need the work
- One project appears many times in the Project Performance Summary with different global properties
- An `Exec` spawns one process per item in a list
- A "run once per build" guard (`Exists` check, flag property) still runs repeatedly or fails with file-in-use errors under `-m`

## When Not to Use

- Targets that run again on a second, unchanged build: use `incremental-build`
- Slow evaluation before any target runs: use `eval-performance`
- Required project variants colliding in shared bin/obj directories: use `check-bin-obj-clash`
- Idle nodes or a serial dependency chain: use `build-parallelism`
- NuGet restore repeated across separate `dotnet restore`, `dotnet build` and `dotnet test` commands: use `eliminate-redundant-restores`
- Unranked "why is my build slow": start with `build-perf-baseline`, then `build-perf-diagnostics`

## Workflow

### Step 1: Count executions

Use a supplied performance summary with execution counts as valid input; a raw binlog is not required. If the user cannot run the build or forbids execution or new logs, work from the summary and repository files. Mark unavailable timing or overlap evidence `unknown` and proposed fixes `not validated`; do not execute a build or collect another log.

If neither usable execution counts nor a binlog is supplied, capture one only when build execution is available and permitted. Use the `binlog-generation` skill: add `/bl:{}` to the original MSBuild invocation, preserving its configuration, global properties, parallelism and restore behavior. In PowerShell, quote the complete switch as `'-bl:{}'`. If a script drives the build, instrument its underlying MSBuild invocation rather than replace it with a different build command. Confirm the binlog exists and record its path. If capture is not permitted, report the missing evidence instead of running the build.

For an available binlog, prefer the bundled **binlog MCP server** (`Microsoft.AITools.BinlogMcp`, exposed under the `binlog` MCP namespace). Discover its available tools and query project, target and task execution counts and timings from that binlog. Do not read the binary file as text.

If MCP is unavailable or its tools do not expose execution counts, replay the captured binlog to a text performance summary and read the `N calls` column of the Target and Task Performance Summaries and the Project Performance Summary:

```bash
dotnet msbuild build.binlog -noconlog "-flp:v=diag;logfile=full.log;performancesummary"
```

Compare each count with the project instances that genuinely need the operation. TFM-dependent work such as `Csc` normally runs once per project and target framework; copy-local work may also run in several targets. A multi-targeted project can legitimately show one outer build plus one inner build per TFM, so four project calls for three TFMs are not automatically duplicates. Build-invariant work is different: an identical git lookup needs one computation for the build, so 38 calls in 38 projects can still be waste. Confirm that inputs, working directories and relevant properties do not change the result; identical command text alone does not prove invariance. Investigate extra instances only after accounting for required TFMs and global properties.

### Step 2: Classify each candidate

| Pattern | Signal | Fix | Never |
|---|---|---|---|
| Invariant per-project `Exec` or task (commit hash, tool version, generated file) | `Exec` calls equal the project count and the command line is identical | Compute it once outside the per-project targets: the build script passes `-p:Name=value`, or a pre-build step or orchestrator target runs first | A flag property or `Exists(stamp)` guard: properties are per project instance and `-m` worker nodes are separate processes, so the guard does not dedupe and races |
| Same project built several times | One project path with N calls and different global properties (for example `Properties="ComponentName=%(Component.Filename)"` on an `MSBuild` task). Any extra global property, even an unused one, creates a new instance | Stop passing the property, or add `GlobalPropertiesToRemove` metadata on the shared `ProjectReference` (`RemoveProperties` on the `MSBuild` task) | Removing a property the project actually reads |
| One process per item (`Exec` batched on `%(Item.Identity)`) | `Exec` calls equal the item count | One invocation that takes a list or response file, after checking the tool supports it; keep `Inputs`/`Outputs` (with a 1:1 transform MSBuild still passes only the stale items into the target) | Dropping `Inputs`/`Outputs` while merging |
| Legitimate per-project or per-TFM work | Count equals projects times target frameworks | Leave it alone | Changing it to look tidy |

### Step 3: Size the win

For eliminated invariant computations, estimate avoidable aggregate task time as `(calls - expected) x average ms`. Per-item batching does not eliminate the required input processing: estimate `(old process starts - new process starts) x startup ms`, retaining every input's processing cost and any required metadata groups. Account for additional list/batch overhead when known. If startup cost is unavailable, mark that estimate unknown rather than treating the entire average task duration as removable.

Wall-clock saving depends on actual overlap and the critical path. Batched per-item `Exec` calls in one target run sequentially on one node, so avoided startup time is not divided by unrelated busy nodes. Only divide by effective parallelism when the removed work actually overlaps across nodes. Use the binlog timeline to establish overlap, and mark wall-clock savings unknown when that evidence is missing. Work off the critical path can yield no wall-clock gain. For performance-only changes, if even the aggregate upper bound is below about 1% of build wall-clock, report it as not worth changing. Correctness races still need a fix regardless of the timing gain.

### Step 4: Fix without changing behavior

1. Keep the value available before its first consumer.
2. A fallback `Condition="'$(X)' == ''"` is valid only when the value arrives as a global property (command line or an orchestrator), because those are inherited by every project. It keeps single-project and IDE builds working.
3. Define the computation in one place.
4. Preserve generated-file timestamps when content is unchanged. Use a write-if-different generator, or generate to a temporary file and replace only on content change; deduplication must not force dependent recompilation on an unchanged rerun.

```xml
<!-- Before: every project launches git -->
<Target Name="StampCommit" BeforeTargets="CoreCompile">
<Exec Command="git rev-parse HEAD" ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommit" />
</Exec>
</Target>

<!-- After: the build script computes the hash once and passes -p:GitCommit=<hash> -->
<Target Name="StampCommit" BeforeTargets="CoreCompile">
<Exec Command="git rev-parse HEAD" ConsoleToMSBuild="true" Condition="'$(GitCommit)' == ''">
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommit" />
</Exec>
</Target>
```

If Source Link already supplies `SourceRevisionId`, it can replace an `Exec` only when it is available before the consumer and preserves the existing stamp representation. `SourceRevisionId` normally contains the full commit ID; do not substitute it for `git rev-parse --short HEAD` without retaining the original abbreviation contract.

### Step 5: Validate and report truthfully

1. When execution is available and permitted, capture a new binlog of the same invocation. Calls must have dropped to the expected count. Otherwise, do not run the build or collect another log; report the fix as proposed and not validated.
2. Confirm outputs are unchanged (the stamped value, generated file contents). For generated sources, also check that an unchanged second build stays incremental.
3. Report timing as the median of at least three runs; if you could not run the build, say "not validated".

## Output

For on-target requests, emit a findings table with at least one row and a `Verdict:` line. Use `unknown` for unavailable timing or critical-path evidence; distinguish estimated aggregate task time from measured wall-clock savings.

| Work | Calls | Expected | Avoidable ms | Cause | Fix | On critical path? |
|---|---|---|---|---|---|---|

After verification: `Verdict: removed N repeated executions, measured wall-clock saving X s`.

Without verification: `Verdict: proposed removal of N repeated executions; estimated task-time saving X s; wall-clock not validated`.

When no change is worthwhile: `Verdict: no repeated work worth changing`.

## Common Pitfalls

| Pitfall | Solution |
|---|---|
| Treating `Csc` or copy-local counts as waste | Compare with projects times target frameworks first |
| Guarding with `Exists(file)` or a flag property | Move the work out of the per-project path |
| Fallback condition on a property that is not a global property | Pass it as a global property, or the fallback never applies |
| Claiming a saving from total task time | Check actual overlap and the critical path; do not divide sequential work by unrelated busy nodes |
| Merging per-item `Exec` and losing incrementality | Keep `Inputs` and `Outputs` on the target |
2 changes: 1 addition & 1 deletion plugins/dotnet-msbuild/skills/incremental-build/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: incremental-build
description: "Guide for optimizing MSBuild incremental builds. USE FOR: builds slower than expected on subsequent runs, 'nothing changed but it rebuilds anyway', diagnosing why targets re-execute unnecessarily, fixing broken no-op builds. Covers 8 common causes: missing Inputs/Outputs on custom targets, volatile properties in output paths (timestamps/GUIDs), file writes outside tracked Outputs, missing FileWrites registration, glob changes, Visual Studio Fast Up-to-Date Check (FUTDC) issues. Key diagnostic: look for 'Building target completely' vs 'Skipping target' in binlog. DO NOT USE FOR: first-time build slowness (use build-perf-baseline), parallelism issues (use build-parallelism), evaluation-phase slowness (use eval-performance), non-MSBuild build systems."
description: "Fix MSBuild incremental/no-op builds: 'nothing changed but it rebuilds anyway'. USE FOR: targets re-executing on subsequent builds, missing Inputs/Outputs or FileWrites, volatile output paths, untracked writes, glob changes, Visual Studio Fast Up-to-Date Check (FUTDC); 'Building target completely' vs 'Skipping target' in binlog. DO NOT USE FOR: within-build duplicates (find-repeated-build-work), first-build slowness (build-perf-baseline), parallelism (build-parallelism), slow evaluation (eval-performance), non-MSBuild systems."
license: MIT
---

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project DefaultTargets="BuildAll">
<PropertyGroup>
<BuildInfoPath>$(MSBuildThisFileDirectory)obj/buildinfo.g.cs</BuildInfoPath>
</PropertyGroup>
<ItemGroup>
<Component Include="Api/Api.csproj;Worker/Worker.csproj" />
</ItemGroup>

<Target Name="Restore">
<MSBuild Projects="@(Component)" Targets="Restore"
Properties="BuildInfoPath=$(BuildInfoPath)" BuildInParallel="true" />
</Target>

<Target Name="GenerateSharedBuildInfo"
Inputs="$(MSBuildThisFileDirectory)buildinfo.cs.in"
Outputs="$(BuildInfoPath)">
<MakeDir Directories="$(MSBuildThisFileDirectory)obj" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)buildinfo.cs.in" DestinationFiles="$(BuildInfoPath)" />
</Target>

<Target Name="BuildAll" DependsOnTargets="GenerateSharedBuildInfo">
<MSBuild Projects="@(Component)" Targets="Build"
Properties="BuildInfoPath=$(BuildInfoPath)" BuildInParallel="true" />
</Target>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<ItemGroup>
<Compile Include="$(BuildInfoPath)" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
internal static class BuildInfo
{
public const string Version = "3.2.0";
}
Loading
Loading