Skip to content

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

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

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>
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