`flan dev` now builds one binary that is the compiled Flan program and holds the whole OCaml compiler, and execs it. The program keeps main() — macOS needs the window there — and caml_startup happens on a pthread beside it, next to the listener flan_agent.c already starts. The editor's socket and the wire protocol are untouched: Emacs cannot tell the difference. Two rules are written into lib/dev.ml rather than discovered later. The game thread must never call into OCaml, because a native thread has no safe points and so can never be stopped by the collector — which is exactly why a frame is never paused, and exactly what one convenient direct call would undo. And no OCaml value may be stored in Flan memory without caml_register_global_root, which is the way the spike's "the GC does not touch the arenas" measurement stops being true. The link is spelled in dev.ml out of Build's existing public pieces rather than as a mode of Build.executable: lib/build.ml belongs to another lane this week. It should collapse into Build once that lands. A Flan main does not return — Emit ends it with flan_exit and an unreachable — so in one process that call would take the compiler down with a program that merely finished. flan_rt.c grows a hook, null in every other build, that the merged entry point uses to flush, close stdout and park. The compiler then learns the program is done the same way the daemon did: the pipe reads EOF. --two-process keeps the old shape for a machine that cannot build the compiler object, and nothing has been deleted.
Description
Languages
OCaml
67.2%
Emacs Lisp
15.2%
C
10.4%
HTML
2.9%
Standard ML
2.8%
Other
1.5%