Skip to content

Generate code from the unit definitions as part of the build - #1742

Open
tmilnthorp wants to merge 4 commits into
angularsen:masterfrom
tmilnthorp:tmilntho/build-codegen
Open

tmilnthorp wants to merge 4 commits into
angularsen:masterfrom
tmilnthorp:tmilntho/build-codegen

Conversation

@tmilnthorp

@tmilnthorp tmilnthorp commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Building the solution compiled CodeGen but never ran it. After editing a JSON file in Common/UnitDefinitions, you had to remember generate-code.bat (or .sh), otherwise the build silently used stale generated code. CI ran CodeGen in a separate step, so local builds and CI could produce different results.

Change

  • CodeGen runs itself after it builds (AfterTargets="Build" in CodeGen.csproj). UnitsNet has a build-order only reference to CodeGen (ReferenceOutputAssembly="false"), and every other project with generated code references UnitsNet, so they all build after the code is regenerated. No per-project opt-in is needed.
    • CodeGen runs once per build, and only when the unit definitions or CodeGen changed (MSBuild inputs/outputs), so a no-op build skips it.
    • Projects with a GeneratedCode folder pick up files that CodeGen added or removed in the same build, such as a new quantity (Directory.Build.targets).
    • It's skipped in IDE design-time builds, or with -p:SkipUnitsNetCodeGen=true.
    • Restore follows the reference, so building a single project (like the net48 job does) or UnitsNet.Modular.slnx works without CodeGen in the solution. --no-build doesn't build references, so pack and test with --no-build don't run it.
    • The reference doesn't show up as a package dependency.
  • CodeGen only writes files whose contents changed, so regenerating doesn't touch timestamps or trigger rebuilds needlessly.
  • CI no longer runs CodeGen as a separate step. Instead build.ps1 fails on CI if the build changed any generated files, meaning the committed generated code was out of date.
  • generate-code.bat and generate-code.sh are removed, and the docs say to build instead. dotnet run --project CodeGen still works for regenerating without building.

Contributor workflow becomes: edit the JSON, then dotnet build (or build in the IDE).

Testing

  • Editing Length.json and building regenerates the code; reverting and building restores it.
  • A new quantity JSON compiles into UnitsNet.dll in the first build.
  • A second build with no changes skips CodeGen.
  • A clean solution build produces no changes to the committed generated code.
  • The CI command shapes, with CodeGen not yet restored: a single test project build, pack --no-build, test --no-build, and the Modular restore + build --no-restore.

Not included

The header of generated files still says "generated by \generate-code.bat". It comes from the generator template, so changing it rewrites every generated file. I'd rather do that in a separate PR to keep this one reviewable.

🤖 Generated with Claude Code

https://claude.ai/code/session_012XKhDsyHDc5BHrmibxScqG

tmilnthorp and others added 3 commits October 6, 2026 10:16
Building the solution compiled CodeGen but never ran it, so the generated
code only updated when someone ran generate-code.bat/.sh. CI ran CodeGen
in a separate step, so local builds and CI could differ.

Projects with generated code now opt in with GenerateUnitsNetCode=true.
Before they build, Directory.Build.targets runs CodeGen through a target
in CodeGen.csproj:
- CodeGen runs at most once per build, shared by all projects and target
  frameworks, and only when the unit definitions or CodeGen changed.
- Files that CodeGen adds or removes are picked up in the same build.
- It is skipped in IDE design-time builds, or with SkipUnitsNetCodeGen=true.

CodeGen now only writes files whose contents changed, so regenerating
doesn't touch unchanged files.

CI no longer runs CodeGen separately; build.ps1 instead fails on CI if
the build changed any generated files, meaning the committed generated
code was out of date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XKhDsyHDc5BHrmibxScqG
Building the solution runs CodeGen when the unit definitions changed, so
generate-code.bat and generate-code.sh are no longer needed. The docs now
say to build instead, and mention `dotnet run --project CodeGen` for
regenerating without building.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XKhDsyHDc5BHrmibxScqG
Building a project or solution that doesn't include CodeGen, such as a
single test project or UnitsNet.Modular.slnx, failed because CodeGen was
never restored. Restore it first when its assets file is missing.

Packing or testing with --no-build ran CodeGen, which tried to build it
with NoBuild=true. Nothing is compiled then, so skip generating code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XKhDsyHDc5BHrmibxScqG
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98%. Comparing base (3bf1ef4) to head (92695b8).

Additional details and impacted files
@@          Coverage Diff           @@
##           master   #1742   +/-   ##
======================================
  Coverage      98%     98%           
======================================
  Files         515     515           
  Lines       24092   24092           
======================================
  Hits        23692   23692           
  Misses        400     400           
Flag Coverage Δ
net48 97% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Rather than each project with generated code opting in and running
CodeGen before it compiles, CodeGen now runs itself after it builds, and
UnitsNet has a build-order only reference to it. Every other project
with generated code references UnitsNet, so they all build after the
code is regenerated, and the GenerateUnitsNetCode opt-ins are removed.

Restore follows the reference, and building without --no-build doesn't
build references, so the explicit restore and NoBuild check are no
longer needed. Projects with a GeneratedCode folder still pick up files
that CodeGen added or removed in the same build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XKhDsyHDc5BHrmibxScqG
@tmilnthorp

Copy link
Copy Markdown
Collaborator Author

Related: #1745 proposes building on this so applications can generate their own quantities with the same CodeGen, as part of their own build, while still using the UnitsNet package.

🤖 Generated with Claude Code

https://claude.ai/code/session_012XKhDsyHDc5BHrmibxScqG

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant