Lift the netstandard2.0 floor off the tooling stack via an out-of-process MSBuild bridge#447
Lift the netstandard2.0 floor off the tooling stack via an out-of-process MSBuild bridge#447ChrisonSimtian wants to merge 10 commits into
Conversation
…MSBuild bridge Decision record for freeing Fallout.Tooling + Fallout.Tooling.Generator from the ns2.0 floor that Fallout.MSBuildTasks's net472 target imposes. Extract task logic into a net10 engine; serve dotnet/SDK builds with an in-proc net10 task and VS2019/2022 full-framework MSBuild with a thin net472 ToolTask bridge that shells out to a standalone net10 worker. Unblocks lifting AbsolutePath / Host / IFalloutBuild into Fallout.Core. Implements Lever 2 of Fallout-build#441 (Lever 1 / SourceGenerators cone is Fallout-build#442, independent). Bridge is time-boxed and deletable once VS2026's .NET MSBuild lands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First step of ADR-0009. Lifts the bodies of CodeGenerationTask / EmbedPackagesForSelfContained / PackPackageTools into an MSBuild-free, net10-only engine (CodeGenerationEngine + PackageToolingEngine, plain data in/out via PackagedToolFile). This is the shared home both the in-process net10 task and the out-of-process worker will call, so the net472 host can later become a thin bridge and Fallout.Tooling can drop the ns2.0 floor. Additive — nothing consumes the engine yet; the existing tasks are untouched. Solution + full build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Second step of ADR-0009. Fallout.MSBuildTasks.Protocol is a dependency-free contract (netstandard2.0;net10) of request/response POCOs + System.Text.Json helpers, referenced by both the net472 bridge and the net10 worker. Fallout.MSBuildTasks.Worker is the standalone net10 console the bridge shells out to: <verb> <input.json> <output.json>, dispatching codegen / embed-packages / pack-tools to the engine and writing a WorkerResponse (exit 0/1/2). Additive — nothing invokes the worker yet. Smoke-tested out-of-process: bad-invocation → exit 2; embed-packages against a real project.assets.json → exit 0 with a valid package list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Third step of ADR-0009. Fallout.MSBuildTasks.Bridge (net472, RootNamespace Fallout.MSBuildTasks so the task type names match the .targets UsingTask) is three ToolTask shims — CodeGenerationTask / EmbedPackagesForSelfContainedTask / PackPackageToolsTask — over a base WorkerBridgeTask that writes inputs to a temp JSON, shells out to `dotnet Fallout.MSBuildTasks.Worker.dll <verb> in out` (dotnet host via DOTNET_HOST_PATH, else PATH), and reads item outputs back. It references only the dependency-free Protocol — no Fallout.Tooling. To suit ToolTask, the worker now emits messages on stdout and errors on stderr (LogStandardErrorAsError), with the exit code as success; the output JSON carries only item outputs. WorkerResponse trimmed accordingly. Builds green (worker net10, bridge net472). The flip (TFMs net10-only + .targets runtime switch) is next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… .targets The flip (final step of ADR-0009). The in-proc CodeGeneration / EmbedPackages / PackPackageTools tasks now delegate to Fallout.MSBuildTasks.Engine and Fallout.MSBuildTasks goes net10-only. Fallout.Tooling drops netstandard2.0 (now net8.0;net10.0 — net8 kept for Fallout.ProjectModel's down-level targets; net8 consumes ns2.1, so Core absorption is unblocked) and Fallout.Tooling.Generator goes net10-only. The .targets picks the assembly by $(MSBuildRuntimeType): Full (VS2019/2022) -> the net472 bridge, Core (dotnet/SDK) -> the in-proc net10 task. ns2.0 — the only TFM that can't consume Fallout.Core (ns2.1) — is gone from the tooling stack, so AbsolutePath / Host / Configure<T> / the tool contracts can now move into Core (Phase 2b/2c/2e). Full solution build + test suite green (StronglyTypedSolution snapshot refreshed for the four new projects). VS full-framework path can't be exercised on this OS; the net10/CLI path is verified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…x folder Fallout.Common (the consumer-facing build package) packed MSBuildTasks's net472 publish output into build\netfx; with MSBuildTasks now net10-only that glob is dead. Replace it with the net472 bridge plus the net10 worker (same folder, so the bridge finds Fallout.MSBuildTasks.Worker.dll beside itself). build\netcore still ships the in-proc net10 task. Also point the dogfood's inert FalloutTasksDirectory at the real net10 publish path (was a stale net8.0). NOTE: the consumer task path (especially full-framework VS) and `Pack` output are not exercised by this repo's build/test (the dogfood runs with FalloutTasksEnabled=False), and the net472/VS path can't be smoke-tested on non-Windows — both need a Windows + Pack verification before merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Bridge smoke test (Windows + VS2019/2022 full-framework MSBuild): the Ran the consumer codegen path on this branch (commit 7220ab5) across all three runtimes:
The bridge throws before it ever spawns the worker: Root cause: This contradicts ADR-0009 §3 ("the bridge references only Fix (pushed): Repro runbook: |
…net472 bridge loads The net472 bridge loads Fallout.MSBuildTasks.Protocol in-process inside full-framework MSBuild.exe (VS2019/2022). The Protocol serialized with System.Text.Json, whose packaged assembly version (10.0.0.8) never matches the compile-time reference (10.0.0.0); .NET Framework fusion needs an exact match or a host binding redirect, and MSBuild.exe has neither for STJ. The bridge threw FileNotFoundException in WorkerJson.Serialize before it could spawn the worker. .NET Core roll-forward masked this on the in-proc task and the worker, so only the Full path broke and CI (which runs FalloutTasksEnabled=False) missed it. Switch WorkerJson to the in-box DataContractJsonSerializer (System.Runtime.Serialization) and drop the System.Text.Json PackageReference. The DTOs are trivial POCOs that round-trip cleanly, both ends share WorkerJson so the wire format stays symmetric, and STJ no longer ships in build/netfx. This makes the bridge genuinely "Microsoft.Build.* + BCL only" as ADR-0009 intends. Verified: dotnet (Core), VS2019 and VS2022 (Full) now produce byte-identical codegen output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Documents how to verify the full-framework (VS2019/2022) MSBuild bridge from ADR-0009: which MSBuild runtime exercises which path, the homelab VM spec, VS version currency, the exact repro steps, and the System.Text.Json binding defect plus its fix. This path is Windows-only and not covered by CI (the dogfood runs FalloutTasksEnabled=False), so it needs a manual smoke test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Covers the wire contract the net472 bridge and net10 worker share via WorkerJson: - round-trip each DTO (CodegenRequest, EmbedPackagesRequest, PackToolsRequest, WorkerResponse, including empty/default collections) through DataContractJsonSerializer; - assert the Protocol assembly references no System.Text.Json — the regression guard for the bridge binding failure (STJ can't load in-process under full-framework MSBuild.exe). A round-trip test alone can't catch that (net10 roll-forward and the test host's binding redirects both mask it); the dependency invariant does, cross-platform. End-to-end bridge behaviour stays covered by the Windows-only smoke test in docs/agents/msbuild-bridge-smoke-test.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adding Fallout.MSBuildTasks.Protocol.Specs to fallout.slnx adds one project to the StronglyTypedSolutionGenerator output; refresh the Verify snapshot to match (one new line). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
TLDR; in order to break up the whole god-class cluster, I need to break a few things open, this being one of them. Also, interesting read on VS support: Classic Microsoft move... I'd say with that in mind: we do a bridge. We keep it side-by-side as long as we can but drop it latest 2032 (oh god thats in 6 years from now!) |
|
This also needs some testing. So please be my guest, run it against all sorts of pre 2026 VS instances :-) |
|
@dennisdoomen not quite tidy but have a look and let me know what you think. |
Fallout.Tooling/Fallout.Tooling.Generatordropnetstandard2.0(nownet8.0;net10.0andnet10.0) andFallout.MSBuildTasksdropsnet472. A consumer referencing those assemblies directly from anetstandard2.0/net472project must move to net8+/net10. The MSBuild integration's behaviour is preserved for VS2019/2022 via a new out-of-process bridge (below).Implements Lever 2 of #441 — frees the tooling stack from the ns2.0 floor so
Fallout.Core(ns2.1) can absorbConfigure<T>/AbsolutePath/ the tool contracts (de-static Phase 2b/2c/2e). Design recorded in ADR-0009 (in this PR).How
netstandard2.0is the only TFM that can't consumeFallout.Core(ns2.1), and the floor came solely fromFallout.MSBuildTasks'snet472target (loaded by full-framework VS MSBuild) referencingFallout.Tooling.Fallout.MSBuildTasks.Engine.dotnet/SDK): in-process net10 task — the hot path, keeps ALC isolation.ToolTaskbridge that shells out to a standalone net10 worker (Fallout.MSBuildTasks.Worker) — zero Fallout deps, soToolingno longer needs ns2.0..targetsselects by$(MSBuildRuntimeType); packaged intobuild\netcore(task) /build\netfx(bridge + worker).Fallout.Toolingkeeps anet8flavour forFallout.ProjectModel's down-level targets — net8 consumes ns2.1, so the floor is still lifted.The bridge is time-boxed: delete it once VS2026's .NET MSBuild can load net10 tasks in-process.
Verification⚠️ (why this is a draft)
Packoutput, and the net472 VS full-framework bridge — these aren't exercised on non-Windows or by this repo's dogfood (which runsFalloutTasksEnabled=False). Needs a Windows + VS +Packsmoke test before un-drafting.🤖 Generated with Claude Code