Skip to content

Add net10 Solution.g codegen console + toggle (groundwork to free the SourceGenerators ns2.0 cone) - #442

Draft
ChrisonSimtian wants to merge 11 commits into
Fallout-build:mainfrom
ChrisonSimtian:build/free-sourcegenerators-cone
Draft

Add net10 Solution.g codegen console + toggle (groundwork to free the SourceGenerators ns2.0 cone)#442
ChrisonSimtian wants to merge 11 commits into
Fallout-build:mainfrom
ChrisonSimtian:build/free-sourcegenerators-cone

Conversation

@ChrisonSimtian

@ChrisonSimtian ChrisonSimtian commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Groundwork for #441 (move the Roslyn generator off the netstandard2.0 floor). Adds a net10 pre-build codegen path for the strongly-typed [Solution] accessor so the in-compiler generator can eventually shed its Solution/Persistence/Utilities/Globbing dependency cone. Non-breaking and dogfooded.

Warning

Behavior note — opt-in path only, not an API break. Under FalloutSolutionCodegenMode=Build, Solution.g.cs is produced by a pre-build step instead of the in-compiler generator, so live in-IDE regeneration on Visual Studio / Windows (design-time build + IntelliSense) can differ — the accessor refreshes on build rather than on edit. The default path (in-compiler generator) is unchanged, so this affects only builds that opt in (today: this repo's _build). Verifying VS design-time behavior on Windows before any consumer default is flipped.

What changed

  • Fallout.Solution.Codegen (net10 console) — discovers [Solution(GenerateProjects = true)] syntactically (no compilation pre-build), reads the solution with the real Fallout.Persistence.Solution parser, emits <Member>.g.cs.
  • Fallout.Solution.Codegen.Emit (new netstandard2.0;net10.0 project) — the shared emitter, referenced by both the console and the generator. Replaces the old src/shared/ linked-source hack; output stays byte-identical. Bundled into the analyzer via the existing GetDependencyTargetPaths.
  • StronglyTypedSolutionGenerator — self-suppresses when FalloutSolutionCodegenMode == Build; otherwise unchanged, so it remains the in-IDE fallback.
  • Fallout.Solution.Codegen.targets — reusable pre-build target + toggle plumbing (FalloutSolutionCodegenProject in-repo, or FalloutSolutionCodegenAssembly for packaged consumers).
  • build/_build.csproj — dogfoods the console path (Mode=Build).
  • TestsFallout.Solution.Codegen.Specs covers discovery (qualified/aliased names, relativePath:, properties/fields, multiple members, negatives), the emit pipeline, stale-output removal, the parameters-file fallback, and identifier escaping; plus generator self-suppression specs and generator/console output-parity specs. Discovery also prunes bin/obj/hidden in the bare---root fallback scan.

Why

The generator parses solutions at compile time, which forces the parser cone to netstandard2.0. Moving codegen to a net10 console lets the generator later drop those refs. Shipped as non-breaking groundwork: both paths coexist (toggle-gated), so consumers are untouched until the console ships in the NuGet.

Verification

  • Full fallout.slnx build: 0 errors.
  • Fallout.Solution.Codegen.Specs incl. new GeneratorConsoleParitySpecs (drives the in-compiler generator and the console over one fixture; asserts identical + correct Solution.g.cs across attribute spellings, fancy names, and the GenerateProjects=false no-op); Fallout.SourceGenerators.Specs incl. the generator Verify snapshot.
  • Dogfood _build: console emits Solution.g.cs → generator suppresses → compiles.

Part of #441.

Follow-ups (not here)

  • Ship the console + targets (and the .Emit DLL) in the consumer NuGet (build/), flip the consumer default to Build, then drop the generator's cone refs (frees Solution/Utilities to net10) and the .Emit ns2.0 target.
  • The .targets happy path is covered by the dogfood _build (+ Fallout.Canary), not an automated spawn-build test.
  • The pre-build target passes every compile item as --source on one Exec line; a very large Mode=Build consumer could hit the Windows ~32K command-line limit (the --root fallback mitigates). Batch/response-file if that ever bites.
  • FancyNames is currently dead code — tracked in [Solution] FancyNames option is dead code #445.

@ChrisonSimtian
ChrisonSimtian requested a review from a team as a code owner June 29, 2026 11:17
@ChrisonSimtian ChrisonSimtian added enhancement New feature or request target/vCurrent Targets the current version labels Jun 29, 2026
@ChrisonSimtian
ChrisonSimtian requested review from Copilot and dennisdoomen and removed request for dennisdoomen June 29, 2026 11:18
@ChrisonSimtian
ChrisonSimtian marked this pull request as draft June 29, 2026 11:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Groundwork for issue #441 by introducing a net10 pre-build codegen path for the strongly-typed [Solution] accessor (Solution.g.cs), sharing a single emitter between the console path and the Roslyn generator fallback, and dogfooding the build-path in build/_build.csproj.

Changes:

  • Add Fallout.Solution.Codegen (net10 console) + MSBuild .targets to generate *.g.cs before compile when FalloutSolutionCodegenMode=Build.
  • Extract shared Scriban emit logic to src/shared/SolutionEmitter.cs and link it into both the console and StronglyTypedSolutionGenerator.
  • Update solution + Verify snapshot and wire the dogfood build to use the new build-time codegen path.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Fallout.SourceGenerators.Specs/StronglyTypedSolutionGeneratorSpecs.Test#Solution.g.verified.cs Snapshot update reflecting the new Fallout.Solution.Codegen project presence.
src/shared/SolutionEmitter.cs New shared emitter used by both generator and console to keep output byte-identical.
src/Fallout.SourceGenerators/StronglyTypedSolutionGenerator.cs Refactor onto shared emitter + self-suppress when FalloutSolutionCodegenMode=Build.
src/Fallout.SourceGenerators/Fallout.SourceGenerators.csproj Link in shared emitter source file.
src/Fallout.Solution.Codegen/Program.cs New net10 console that syntactically discovers [Solution(GenerateProjects=true)] and emits *.g.cs.
src/Fallout.Solution.Codegen/Fallout.Solution.Codegen.csproj New net10 console project + linked emitter + references/packages.
src/Fallout.Solution.Codegen/build/Fallout.Solution.Codegen.targets New MSBuild target to run the console pre-build and include generated sources.
fallout.slnx Adds the new Fallout.Solution.Codegen project to the repo solution.
build/_build.csproj Dogfoods the console path (Mode=Build) and imports the new targets.
AssemblyInfo.cs Adds InternalsVisibleTo("Fallout.Solution.Codegen").

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Fallout.Solution.Codegen/build/Fallout.Solution.Codegen.targets
Comment thread src/Fallout.Solution.Codegen/Program.cs Outdated
Comment thread src/Fallout.Solution.Codegen/Program.cs Outdated
Comment thread src/Fallout.Solution.Codegen/Program.cs Outdated
@ChrisonSimtian
ChrisonSimtian requested a review from a team June 30, 2026 08:55
@ChrisonSimtian
ChrisonSimtian marked this pull request as ready for review June 30, 2026 08:55
@ChrisonSimtian

Copy link
Copy Markdown
Collaborator Author

@dennisdoomen this one would be good, groundwork PR for other stuff

@ChrisonSimtian
ChrisonSimtian force-pushed the build/free-sourcegenerators-cone branch from 16192ed to dff46af Compare July 12, 2026 09:35
ChrisonSimtian and others added 10 commits July 12, 2026 21:38
First half of freeing the Fallout.SourceGenerators dependency cone (issue
Fallout-build#441, Lever 1): introduce a net10 pre-build codegen path for the
strongly-typed [Solution] accessor as the future default, with the in-compiler
generator retained as the toggle fallback.

- src/shared/SolutionEmitter.cs: the Scriban emit logic extracted out of
  StronglyTypedSolutionGenerator into a single shared source file, linked into
  BOTH the generator and the new console so they emit byte-identical Solution.g.cs.
- StronglyTypedSolutionGenerator: refactored to call SolutionEmitter (behaviour
  unchanged — still the in-IDE/live fallback).
- src/Fallout.Solution.Codegen: net10 console that discovers
  [Solution(GenerateProjects = true)] members by parsing the build project's .cs
  syntactically (no compilation available pre-build), reads the solution with the
  real Fallout.Persistence.Solution parser (no parsing-parity risk), and emits via
  the shared emitter. Verified end-to-end against this repo's own build/Build.cs +
  fallout.slnx — output matches the generator.
- AssemblyInfo: IVT for the console (it uses Build.Shared's internal Constants).

Remaining (next commits on this branch): the packaged pre-build MSBuild target +
FalloutSolutionCodegenMode toggle (default = console; suppress the generator in
console mode), dogfood-build wiring, and — as the deliberate follow-up — dropping
the now-removable cone refs from Fallout.SourceGenerators once the fallback is
retired/cone-freed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…1, steps 1-2)

- StronglyTypedSolutionGenerator self-suppresses when FalloutSolutionCodegenMode == Build
  (reads it via CompilerVisibleProperty), so exactly one path emits.
- src/Fallout.Solution.Codegen/build/Fallout.Solution.Codegen.targets: reusable pre-build
  target that runs the console (via FalloutSolutionCodegenProject or a published
  FalloutSolutionCodegenAssembly), writes <Member>.g.cs into obj/, and includes it. Exposes
  the toggle to the fallback generator.
- build/_build.csproj: dogfoods the console path (FalloutSolutionCodegenMode=Build). Verified:
  the target runs the console, emits Solution.g.cs from fallout.slnx, the generator suppresses,
  and _build compiles (0 errors). Full fallout.slnx build green.

Default is unchanged for everyone else (generator runs unless a project opts into Build), so
this is non-breaking. Remaining follow-ups: ship the console + targets in the consumer package
and flip the default to Build, then drop the now-removable cone refs from Fallout.SourceGenerators.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…allout.Solution.Codegen project

The generator's output for fallout.slnx now includes the new Fallout.Solution.Codegen
project; the only snapshot change is that one accessor line (confirming the shared-emitter
refactor preserved output). BOM stripped to match the repo's no-BOM verified convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the syntactic [Solution] discovery out of Program into a testable
SolutionMemberDiscovery, and tighten it to match the symbol-based generator:

- Match the attribute by its rightmost name segment so qualified/aliased usages
  ([Fallout.Solutions.Solution], [Solutions.Solution], [global::...Attribute])
  are recognised, not just the bare [Solution]/[SolutionAttribute].
- Accept the relativePath constructor argument written positionally or as
  'relativePath:' (NameColon).
- Prune bin/obj/hidden directories in the bare --root fallback scan instead of
  walking AllDirectories.
- Delete any existing *.g.cs in the output directory before emitting, so a
  removed or renamed [Solution] member can't leave an orphan that still compiles.

GenerateProjects/FancyNames stay NameEquals-only on purpose: they are settable
properties, so '=' is the only valid syntax.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unit tests for SolutionMemberDiscovery: rightmost-segment attribute matching,
positional vs. relativePath: arguments, FancyNames, property vs. field members,
and the negatives (GenerateProjects=false, unrelated attributes). Wires the new
project into fallout.slnx and InternalsVisibleTo.

The generator Verify snapshot gains one accessor line for the newly-added test
project (the generator enumerates the real solution).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the discover -> resolve -> emit loop out of Program's top-level statements
into a testable SolutionCodegenRunner; Program is now a thin arg-parsing shell.
This puts the previously untested output side-effects under test:

- emits the accessor for a discovered member against a real (minimal) .slnx
- clears stale *.g.cs before emitting (orphan removal)
- removes all outputs when no member remains

Also adds a test for EnumerateProjectSources confirming bin/obj/hidden dirs are
pruned from the bare --root fallback scan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The emitter was linked-source into both the Roslyn generator and the net10
console via a src/shared/ directory. Promote it to a proper multi-targeted
project, Fallout.Solution.Codegen.Emit (netstandard2.0;net10.0), that both
reference:

- netstandard2.0 so the in-compiler StronglyTypedSolutionGenerator (also ns2.0)
  can reference it; its DLL is bundled into the analyzer via the existing
  GetDependencyTargetPaths mechanism.
- net10.0 so the console gets a modern build. When the generator fallback is
  retired (Fallout-build#441), the ns2.0 target can simply be dropped.

SolutionEmitter becomes public (it now crosses an assembly boundary). Scriban
moves to the emit project and reaches the console transitively. No /shared.

It stays a dedicated codegen library rather than folding into Fallout.Solution:
the emitter pulls in Scriban, which shouldn't leak into everything that consumes
the Solution model. Output is still byte-identical (the generator Verify
snapshot passes; the one delta is the new project's own accessor line).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fills the gaps in the previously thin spots:

- Generator self-suppression: asserts the in-compiler generator no-ops when
  FalloutSolutionCodegenMode=Build (case-insensitive) and still emits otherwise.
  This is the toggle that guarantees exactly one path emits Solution.g.cs.
- Parameters-file fallback: the runner resolves a [Solution] member with no path
  argument via .fallout/parameters.json.
- Multiple members: discovery and the runner both handle more than one
  [Solution(GenerateProjects = true)] member, emitting one file each.
- Emitter escaping: dotted project names become valid identifiers, and names
  starting with a digit get an underscore prefix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a spec exercising the recursive folder declaration (Unsafe.As / nested
SolutionFolder class) — previously only covered transitively by the generator
snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rojects

The rebase onto main brought in main's proper FancyNames implementation
(delimiter U+A78F) and its fancy-naming snapshot, which predated the three
Fallout.Solution.Codegen* projects this branch adds to the solution. Regenerate
the snapshot so it lists them with the correct fancy delimiter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ChrisonSimtian
ChrisonSimtian force-pushed the build/free-sourcegenerators-cone branch from dff46af to 59b899c Compare July 12, 2026 09:38
Drives the in-compiler StronglyTypedSolutionGenerator (symbol-based discovery)
and the net10 pre-build console (syntactic discovery) over one shared fixture and
asserts they (a) emit identical Solution.g.cs and (b) emit the expected accessor.
Covers attribute spellings, fancy-name delimiter, and the GenerateProjects=false
no-op, pinning that the two discovery paths can't silently diverge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ChrisonSimtian
ChrisonSimtian marked this pull request as draft July 19, 2026 12:39
ChrisonSimtian added a commit to ChrisonSimtian/Fallout that referenced this pull request Jul 28, 2026
The comment named Fallout.SourceGenerators and Fallout.MSBuildTasks as the
consumers forcing netstandard2.0. Neither reads Constants — the generator used
only the path helpers, which stayed behind in Fallout.Build.Shared as
FalloutPaths, and MSBuildTasks does not reference Build.Shared at all.

The real forcing function is Fallout.Build.Shared itself: it multi-targets
netstandard2.0, its own code (FalloutPaths, Notifications) reads these values,
and it keeps that target only so the Roslyn generator can reference it. Say so,
and point the exit condition at Fallout-build#442/Fallout-build#441 where that actually gets resolved.

netstandard2.1 goes: Core has no conditional compilation and no 2.1-only API, so
that asset was IL-identical to the netstandard2.0 one and a 2.1 consumer resolves
netstandard2.0 anyway. Verified by dropping it — solution builds clean and the
full suite passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request target/vCurrent Targets the current version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants