The acceptance case stops asking for a header it no longer needs

The imported-bindings program was skipped without FLAN_RAYLIB_H, which was
right when the bindings only existed if a header was read and is now the one
gate hiding the change. It runs on the same terms as every other raylib case:
libraylib linkable, no raylib-devel. A generated.flan regenerated empty or
stale is now caught on an ordinary machine rather than only on one with a
header exported.

Reach.link already answers the shim worry BUILT.md's cold-build attribution
would otherwise raise: sand.flan links 110 wrappers, not 425, because the
bindings nothing reachable calls are dropped. What is left is 65ms of frontend
on a cold build, against a header read that was 60-90ms of a fresh session and
15.5ms of every redefinition. Both numbers are in BUILT.md now.

Also a swallowed line continuation in the exclusion message.
This commit is contained in:
Joseph Ferano 2026-09-13 08:22:10 +07:00
parent 65fd49f0c6
commit 7332bed14a
3 changed files with 48 additions and 18 deletions

View File

@ -437,6 +437,37 @@ split this change exists to remove. The C symbol is kept verbatim in
`Ast.DeclareC` either way, so a rename costs nothing: it is still what is `Ast.DeclareC` either way, so a rename costs nothing: it is still what is
called and still what the check compares against. called and still what the check compares against.
#### What committing 253 more declarations costs, measured
The section above says the cost is the check. That is the cost that mattered,
but it is not the only one, and this file does not omit measured numbers.
Every build now carries 425 `declare-c` where a default build carried 172, and
the obvious worry is the shim: BUILT.md's own cold-build attribution above
blames "the object cache compiling a shim with 428 wrappers in it", and that
was the *opt-in* path. It is not what happens, because `Reach.link` drops the
bindings nothing reachable calls and the shim comes back in parts for exactly
that purpose. `sand.flan` links **110** wrappers, not 425 — `nm sand | grep -c
flan_shim_`. A program that never draws still compiles no drawing wrapper.
(`flan shim <file>` prints the unpruned view, so it says 427 and is not the
number a build pays.)
What is left is frontend work on 253 more declarations, and it is small:
| cold build of `sand.flan`, object cache cleared | best of 3 |
|---|---|
| 425 declarations (committed bindings) | 2.16s |
| 172 declarations (`generated.flan` moved aside) | 2.10s |
**+65ms, about 3%, and cold only** — the object cache serves the shim after one
build, and the redefinition path never recompiles C at all. Against it: the
header read this removes was 6090ms of a fresh session by the measurement
above, and DISCUSS.md 6a measured it at 15.5ms on *every* redefinition, which is
a 50% increase on the number the dev-loop lane exists to keep small. So for
anyone who had the opt-in switched on this is a straight win, and for everyone
else it is 65ms once per cold build in exchange for 253 bindings that were
previously unreachable.
Two bindings the config cannot express stay hand-written, and they are the Two bindings the config cannot express stay hand-written, and they are the
reason `declare-c` remains the escape hatch: `LoadFontEx` and reason `declare-c` remains the escape hatch: `LoadFontEx` and
`LoadImageFromMemory` are bound `-raw` and wrapped by a Flan function of the `LoadImageFromMemory` are bound `-raw` and wrapped by a Flan function of the

View File

@ -639,7 +639,8 @@ let of_dump ~env ~taken ~bound_syms ~config (d : dump) : imported =
hidden := hidden :=
(flan_name config f.csym, (flan_name config f.csym,
Printf.sprintf Printf.sprintf
"%s is excluded by the package's binding config, so no declaration is generated for it" f.csym) "%s is excluded by the package's binding config, so no \
declaration is generated for it" f.csym)
:: !hidden) :: !hidden)
dropped; dropped;
let groups = Hashtbl.create 512 in let groups = Hashtbl.create 512 in

View File

@ -802,23 +802,21 @@ let () =
TextLength of "hello" is 5, which is only true if the generated wrapper TextLength of "hello" is 5, which is only true if the generated wrapper
NUL-terminated the copy. NUL-terminated the copy.
Skipped without FLAN_RAYLIB_H, because the import is opt-in a build This used to be skipped without FLAN_RAYLIB_H, because the generated
needs libraylib linkable and not raylib-devel installed, and that is a bindings only existed when a header was read. They are committed now
property worth keeping. The importer's own table does not skip: it runs vendor/raylib/generated.flan so it runs on the same terms as every
against test/headers/sample.h, which is committed. *) other raylib case here: libraylib linkable, and no raylib-devel. That is
(match Sys.getenv_opt "FLAN_RAYLIB_H" with the change stated as a test rather than as a claim. If generated.flan
| Some h when Sys.file_exists h were ever regenerated empty or stale, this is what would say so, and it
&& Sys.command "ldconfig -p 2>/dev/null | grep -q libraylib" = 0 -> would say so on an ordinary machine rather than only on one with a
let out = "10\n5\n287454020\n17\n34\n51\n68\n" in header exported. *)
outputs "raylib, bindings read from the header" "programs/raylib-imported.flan" out; let out = "10\n5\n287454020\n17\n34\n51\n68\n" in
(* At -O0 too, for the reason the rest of the table is: every struct outputs "raylib, bindings generated from the header" "programs/raylib-imported.flan" out;
here crosses as (addr v) on a local, which is the alloca mem2reg (* At -O0 too, for the reason the rest of the table is: every struct
would launder before anyone noticed it was wrong. *) here crosses as (addr v) on a local, which is the alloca mem2reg
outputs ~opt:"-O0" "raylib, bindings read from the header, -O0" would launder before anyone noticed it was wrong. *)
"programs/raylib-imported.flan" out outputs ~opt:"-O0" "raylib, bindings generated from the header, -O0"
| _ -> "programs/raylib-imported.flan" out;
print_endline
"acceptance: skipping the imported-bindings case (FLAN_RAYLIB_H unset)");
(* raylib's Image family, headless, and the strongest FFI case here: an (* raylib's Image family, headless, and the strongest FFI case here: an
Image is pixels in RAM, so raylib *computes* with it rather than Image is pixels in RAM, so raylib *computes* with it rather than