Skip to content

Latest commit

 

History

History
78 lines (63 loc) · 4.51 KB

File metadata and controls

78 lines (63 loc) · 4.51 KB

Model types and Adaptify (*.g.fs)

Types marked [<ModelType>] get an adaptive counterpart generated by Adaptify: Surface-Model.fsSurface-Model.g.fs, right next to it. The *.g.fs files are not checked in (.gitignore: /src/**/*.g.fs); every checkout generates them locally.

TL;DR

  • build.cmd / build.sh and the runTests / runAllTests / run-tests / runUiTests scripts generate whatever is missing or stale before they compile. Nothing to do there.
  • Building from the IDE or with plain dotnet build: run adapt.cmd / adapt.sh first — after a fresh clone or worktree, and after a pull, merge or branch switch that touched a model. When everything is up to date it only checks, in a few seconds.
  • Changed a [<ModelType>]: run adapt.cmd / adapt.sh, then build.
  • New model file X.fs: add <Compile Include="X.g.fs" /> directly after X.fs in the .fsproj, then run adapt.cmd / adapt.sh.
  • Never edit a *.g.fs — change the model and regenerate.
adapt.cmd            regenerate the projects with a missing or stale *.g.fs
adapt.cmd --all      regenerate every model project
adapt.cmd --check    only report; exit code 1 if anything is missing or stale

The first run on a fresh clone takes several minutes (about 5 on a desktop machine): it builds PRo3D.Base, PRo3D.Core, PRo3D.GIS and PRo3D.SimulatedViews in Release, which the following Release build then reuses.

How it works

Everything lives in utilities/Adapt.fsx; adapt.cmd / adapt.sh, the Adapt target in Build.fs (a dependency of Compile, CompileDebug, Tests, CopyToElectron and Publish) and the test scripts all run it.

  1. Model projects are the .fsproj files under src/ that list a *.g.fs. They are processed in project-reference order (Base, Core, SimulatedViews, Viewer, Lite, GeometryLab), so no list needs maintaining when a project gains models.
  2. Stale check. Adaptify writes the hash of the model source into the first line of each *.g.fs (MD5 of the file text, as a Guid). The script recomputes it; a missing file or a different hash marks the project for regeneration.
  3. Generation, per marked project:
    • dotnet msbuild <proj> -restore -t:ResolveProjectReferences -p:Configuration=Release restores the project and builds only its references. Adaptify type-checks the models against the referenced assemblies, so they must exist and be current. The restore matters too: without the project's own obj/project.assets.json Adaptify sees no NuGet references and silently finds no model types (this is what broke the old adapt.cmd on a fresh clone).
    • dotnet adaptify --lenses --local --force --release <proj>.
  4. Verification. Adaptify exits with 0 even when it generates nothing, so the stale check runs again afterwards and the script fails if anything is still missing or stale. To see why, run dotnet adaptify --lenses --local --force --release --verbose <proj>.

Limits

  • Staleness is judged by the model file alone. If the generated code has to change for a reason outside it — say a type in a referenced project becomes or stops being a [<ModelType>] — run adapt.cmd --all.
  • --force regenerates every model file of a project, so that project recompiles once.
  • An IDE only sees the generated types once they exist; before the first adapt it reports missing files and unknown Adaptive* types.

Why the generated files are not checked in

Until 20.11.2024 generation ran implicitly through Adaptify.MSBuild; that was too slow for interactive work, so PRo3D switched to Adaptify's local mode and checked the *.g.fs in. That made every model change a merge conflict waiting to happen, and the checked-in files drifted: when they were removed, 3 of the 45 were stale against their sources, and a fourth (SequencedBookmarks-Model.g.fs) differed from a fresh generation of its unchanged source in member order - so even untouched models produced diffs depending on who generated them.

Adaptify.MSBuild with LocalAdaptify=true would generate during every build instead, but version 1.3.7 drops up-to-date files from its cache (Runner.fs), so every second build regenerates all *.g.fs and recompiles every model project.

Branches that still track *.g.fs

Merging develop into such a branch produces modify/delete conflicts on the *.g.fs files. Resolve them by deleting: git rm <file>.g.fs (the local copy is regenerated by adapt).