From 7332bed14aa97ac548a8d3e2f3e680b15ff646b4 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sun, 13 Sep 2026 08:22:10 +0700 Subject: [PATCH] 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. --- BUILT.md | 31 +++++++++++++++++++++++++++++++ lib/cimport.ml | 3 ++- test/test_acceptance.ml | 32 +++++++++++++++----------------- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/BUILT.md b/BUILT.md index 2152c32..9c79c32 100644 --- a/BUILT.md +++ b/BUILT.md @@ -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 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 ` 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 60–90ms 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 reason `declare-c` remains the escape hatch: `LoadFontEx` and `LoadImageFromMemory` are bound `-raw` and wrapped by a Flan function of the diff --git a/lib/cimport.ml b/lib/cimport.ml index 387f5d4..2f99f6e 100644 --- a/lib/cimport.ml +++ b/lib/cimport.ml @@ -639,7 +639,8 @@ let of_dump ~env ~taken ~bound_syms ~config (d : dump) : imported = hidden := (flan_name config f.csym, 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) dropped; let groups = Hashtbl.create 512 in diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 54fd10b..b565855 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -802,23 +802,21 @@ let () = TextLength of "hello" is 5, which is only true if the generated wrapper NUL-terminated the copy. - Skipped without FLAN_RAYLIB_H, because the import is opt-in — a build - needs libraylib linkable and not raylib-devel installed, and that is a - property worth keeping. The importer's own table does not skip: it runs - against test/headers/sample.h, which is committed. *) - (match Sys.getenv_opt "FLAN_RAYLIB_H" with - | Some h when Sys.file_exists h - && Sys.command "ldconfig -p 2>/dev/null | grep -q libraylib" = 0 -> - let out = "10\n5\n287454020\n17\n34\n51\n68\n" in - outputs "raylib, bindings read from the header" "programs/raylib-imported.flan" out; - (* At -O0 too, for the reason the rest of the table is: every struct - here crosses as (addr v) on a local, which is the alloca mem2reg - would launder before anyone noticed it was wrong. *) - outputs ~opt:"-O0" "raylib, bindings read from the header, -O0" - "programs/raylib-imported.flan" out - | _ -> - print_endline - "acceptance: skipping the imported-bindings case (FLAN_RAYLIB_H unset)"); + This used to be skipped without FLAN_RAYLIB_H, because the generated + bindings only existed when a header was read. They are committed now — + vendor/raylib/generated.flan — so it runs on the same terms as every + other raylib case here: libraylib linkable, and no raylib-devel. That is + the change stated as a test rather than as a claim. If generated.flan + were ever regenerated empty or stale, this is what would say so, and it + would say so on an ordinary machine rather than only on one with a + header exported. *) + let out = "10\n5\n287454020\n17\n34\n51\n68\n" in + outputs "raylib, bindings generated from the header" "programs/raylib-imported.flan" out; + (* At -O0 too, for the reason the rest of the table is: every struct + here crosses as (addr v) on a local, which is the alloca mem2reg + would launder before anyone noticed it was wrong. *) + outputs ~opt:"-O0" "raylib, bindings generated from the header, -O0" + "programs/raylib-imported.flan" out; (* raylib's Image family, headless, and the strongest FFI case here: an Image is pixels in RAM, so raylib *computes* with it rather than