Skip to content

CLI: enable Native AOT publish#109

Merged
sjvrensburg merged 1 commit intomainfrom
experiment/cli-aot-publish
Apr 14, 2026
Merged

CLI: enable Native AOT publish#109
sjvrensburg merged 1 commit intomainfrom
experiment/cli-aot-publish

Conversation

@sjvrensburg
Copy link
Copy Markdown
Owner

Summary

Phase 2 of the AOT exploration. The CLI now produces a Native AOT binary on dotnet publish -c Release -r <rid>. End-to-end verified on linux-x64.

What changed

  • <PublishAot>true</PublishAot> on RailReader2.Cli.csproj (replaces PublishSingleFile, which is incompatible).
  • New CliJsonContext source-gen context covers StructureOutput, AnnotationExportOutput, VlmOutput, and shared output records.
  • StructureCommand, AnnotationsCommand, VlmCommand switched to source-gen typed Serialize/Deserialize overloads.
  • Shared.JsonOptions sets TypeInfoResolver = CliJsonContext.Default so future consumers of these options get the source-gen path automatically.

Surprises (good)

  • OnnxRuntime, OpenAI, PDFium, SkiaSharp all AOT-clean as-is — no shims, no workarounds.
  • Only fix needed was the CLI's own three JsonSerializer.Serialize<T> call sites.
  • Output byte-identical between AOT and JIT.

Measurements (linux-x64)

AOT JIT
Main binary 12 MB requires .NET runtime
Cold-start --help 6–37 ms ~500 ms (self-contained)
structure --analyze 2 pages incl. ONNX init 5.4s 15.2s

Not in scope

  • CI workflow still uses JIT publish; an AOT release artifact target is a follow-up.
  • Windows / macOS AOT publish not tested locally — the per-platform native libs should pick up automatically but each RID needs its own publish-and-test pass.

Test plan

  • dotnet build RailReader2.slnx -c Release clean
  • 281/281 tests pass
  • dotnet publish -c Release -r linux-x64 succeeds with 0 warnings
  • All 5 commands (render, structure, annotations, vlm, export) run end-to-end
  • Structure JSON output byte-identical to JIT path

🤖 Generated with Claude Code

The CLI now produces a Native AOT binary on `dotnet publish -c Release
-r <rid>`. End-to-end test against a real PDF passed all five commands
(render, structure, annotations, vlm, export) with byte-identical
output to the JIT path.

Changes:
- <PublishAot>true</PublishAot> on the CLI csproj (replaces
  PublishSingleFile, which is incompatible).
- New CliJsonContext (System.Text.Json source-generated) covers the
  CLI's three top-level output types (StructureOutput,
  AnnotationExportOutput, VlmOutput) plus shared output records.
- StructureCommand, AnnotationsCommand, and VlmCommand use the
  source-gen typed Serialize/Deserialize overloads.
- Shared.JsonOptions now sets TypeInfoResolver = CliJsonContext.Default
  so any future consumers picking up these options get the source-gen
  path automatically.

Measurements (Linux x64):
- AOT main binary: 12 MB. Total publish dir: 52 MB excluding ONNX model.
- Cold start `--help`: 6-37ms (vs ~500ms+ for self-contained JIT).
- Real workload (structure --analyze, 2 pages incl. ONNX init): 5.4s
  AOT vs 15.2s JIT.
- All native libs (PDFium, SkiaSharp, ONNX Runtime) work without
  modification — the libraries themselves are AOT-clean.

CI / packaging hooks not yet updated; existing release workflow still
targets the JIT publish path. That's a follow-up.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sjvrensburg sjvrensburg merged commit 5b3e582 into main Apr 14, 2026
1 check passed
@sjvrensburg sjvrensburg deleted the experiment/cli-aot-publish branch April 14, 2026 14:56
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