0009: Split NetPrints.Generation out of NetPrints.Generator
Status
Accepted (2026-09-28). Supersedes ADR-0005's "Editor → Generator project reference" subsection.
Context
ADR-0005 gave NetPrints.Editor a ProjectReference to NetPrints.Generator.csproj (an
OutputType=Exe project) with ReferenceOutputAssembly="false", paired with a plain
<Reference Include="NetPrints.Generator"> whose HintPath pointed at Generator's own,
non-RID build output. That combination was meant to keep Generator out of the .NET SDK's
"referenced executable" publish graph (NETSDK1067/1150/1151) while still giving the Editor
GraphCodeGenerator at compile time.
It does not survive a clean clone. dotnet build src/NetPrints.Editor -c Release -r linux-x64 (and
dotnet build NetPrints.slnx -r <rid>) fails with CS0234: 'Generator' does not exist in namespace 'NetPrints', because the RID flows to Generator and its build output moves to
bin/Release/net10.0/linux-x64/, a path the HintPath never accounted for. Worse, in a tree that
already has a stale non-RID build sitting in bin/Release/net10.0/, the same command compiles
silently against that stale NetPrints.Generator.dll instead of failing. The documented CI and
publish commands (dotnet publish src/NetPrints.Desktop -r …) happened to avoid this because the
Editor is RID-agnostic behind Desktop, which hid the defect from every exercised path.
The actual NETSDK1150 fix Microsoft documents for "a library-like Exe project referenced by an
app that itself gets RID-published" is to stop referencing the Exe project at all: move the shared
code into a library both sides reference normally.
Decision
Extract GraphCodeGenerator and GenerateRequestFile (with their GraphJob/GenerateRequest/
GeneratedFileResult records) out of NetPrints.Generator into a new class library,
NetPrints.Generation (namespace NetPrints.Generation), referencing NetPrints.Core,
NetPrints.Extensibility and NetPrints.Serialization exactly as Generator did.
NetPrints.Generatorkeeps onlyProgram.cs(thegenerate <request.rsp>CLI entry point,UseAppHost=false, always run viadotnet execfromNetPrints.Sdk.targets) and becomes a thin host: a plainProjectReferencetoNetPrints.Generation.NetPrints.Editor(MainEditorVM/ClassEditorVM) takes a plainProjectReferencetoNetPrints.Generation— a normal library reference, not to anExeproject — soReferenceOutputAssembly="false"and theHintPathworkaround are gone entirely. There is noExeproject on the Editor's reference graph anymore, so a self-contained, RID-published build has nothing NETSDK1150/1151/1067 to trip over, on any RID.NetPrints.Sdk.csproj's own build-order-only reference toNetPrints.Generator.csproj(ReferenceOutputAssembly="false", so the generator builds before the SDK package packs) is unchanged: publishingNetPrints.Generator(framework-dependent, intotools/net10.0/) already pulls in its own project references transitively, so the packedNetPrints.Sdkpackage gains one extra file,NetPrints.Generation.dll, alongsideNetPrints.Generator.dll— the package shape (build/,tools/net10.0/, nolib/) is unchanged.
Consequences
- A clean-clone
dotnet build/dotnet publish -r <rid>of the Editor or Desktop no longer depends on a same-configuration Generator build having happened first, and cannot silently compile against a stale Generator DLL — the failure mode ADR-0005 accepted as a documented limitation is gone. docs/adr/0005-release-and-docs-stack.md's "Editor → Generator project reference" subsection and its "do not simplify this back to a plainProjectReference" warning in Consequences describe the now-superseded design; this ADR is the one to follow instead. ADR-0005's other decisions (versioning, packaging, publishing, docs site, wiki) are unaffected.- One more project to keep in step with
AssemblyReferenceGateTests(never referencesAvalonia*or, alongside Generator,Microsoft.Build*).