The generated bindings are committed, and the hand-written three stay excluded from them
This commit is contained in:
commit
324d1c6c60
120
BUILT.md
120
BUILT.md
@ -164,6 +164,8 @@ described is refused with that reason, so `vendor/raylib` describing thirteen
|
||||
structs is what makes the import thirteen structs wide, and describing a
|
||||
fourteenth widens it. Of raylib 5.5's 581 functions, 256 import, 153 are
|
||||
refused, and 172 are left alone because the package already binds them by hand.
|
||||
(253 and 156 once `bindings` excludes raylib's three allocator entry points —
|
||||
see the next section.)
|
||||
|
||||
Not generating `defstruct`s is what makes the check possible at all. Generate
|
||||
them and the header becomes the authority on layout, and comparing the
|
||||
@ -357,6 +359,124 @@ header cache and the same build is 0.92s against 0.83s. So the header read is
|
||||
compiling a shim with 428 wrappers in it, which is the object cache's business
|
||||
and already warm after one build.
|
||||
|
||||
### The bindings are committed now — `generated.flan`, `bindings`, `flan generate-c`
|
||||
|
||||
The section above reads the header at build time, behind an opt-in
|
||||
`?${FLAN_RAYLIB_H}` in `headers`, because a build should need libraylib
|
||||
linkable and not raylib-devel installed. That opt-in was doing two jobs and
|
||||
only one of them was defensible: it decided whether a *check* ran, which is
|
||||
fine to make optional, and it decided whether a package had 172 bindings or
|
||||
428, which is not. `DrawTexturePro` being reachable only by exporting an
|
||||
environment variable is the shape of that second job, and it blocked a real
|
||||
game — see PORTING.md.
|
||||
|
||||
**Generate once, commit the result, regenerate when raylib moves.**
|
||||
`flan generate-c vendor/raylib` reads the header named by `headers`, writes
|
||||
`vendor/raylib/generated.flan`, and that file is checked in. No header is
|
||||
needed by anybody: every build gets all 425 declarations, they are greppable,
|
||||
and they show up in a diff when the library moves. **Caching is not the
|
||||
argument** — the dump is already cached on disk and in memory, so a build that
|
||||
reads a header pays for it once either way. The argument is the dependency and
|
||||
the diff.
|
||||
|
||||
**What it costs is the check, so regeneration runs it and the check gates the
|
||||
write.** A file on disk has no second opinion, so nothing compares the bindings
|
||||
against the library on an ordinary build any more. The one function that writes
|
||||
`generated.flan` therefore compares first — every `defstruct` against the
|
||||
header's record, every hand-written `declare-c` against the header's signature
|
||||
— and writes nothing when they disagree. It is not possible to regenerate
|
||||
without comparing, because there is no other way to write the file. Pointed at
|
||||
the 5.1-dev header on this machine while the package is written for 5.5, it
|
||||
reports the same ten real differences the diff above found and writes nothing.
|
||||
|
||||
**The 172 hand-written lines stay, and the reason is not caution.** It was
|
||||
tempting to delete them: 136 of the 172 are exactly what the kebab rule would
|
||||
have produced, and the other 36 could be spelled as name overrides, so the
|
||||
generated set really is a superset. The argument against is the one this
|
||||
section is about. Everything the generator emits agrees with the header *by
|
||||
construction* — the declaration and the prototype come from one dump — so
|
||||
diffing generated output against the header it came from is a tautology, and
|
||||
replacing the hand-written set would quietly reduce the signature half of the
|
||||
check to nothing. The hand-written lines were transcribed from raylib's
|
||||
documentation by a person; they are the only declarations in the package a
|
||||
header can actually contradict. All ten of the 5.1-dev differences came from
|
||||
them. They are not a parallel set to maintain — they are the second opinion,
|
||||
and the check is what maintains them.
|
||||
|
||||
The opt-in did not go away, it stopped deciding anything important. With
|
||||
`FLAN_RAYLIB_H` set, an ordinary build still reads the header, and since every
|
||||
C symbol is now bound — by hand or by generation — the importer generates
|
||||
nothing and the read is purely the check. It is a *better* check than before:
|
||||
425 declarations rather than 172, because `generated.flan` is a package file
|
||||
like any other and is checked like one.
|
||||
|
||||
**`bindings`, beside `headers`, is what survives regeneration.** A committed
|
||||
generated file cannot be hand-corrected — the next run overwrites it and the
|
||||
edit is destroyed without anybody being told, which is the worst shape an edit
|
||||
can have — so the corrections have to live somewhere regeneration *reads*. Two
|
||||
directives, which are the two things the header cannot decide: `exclude <symbol
|
||||
or pattern>` and `name <symbol> <flan-name>`. A postprocessing transform pass
|
||||
was considered and rejected: a second program to understand, run over text the
|
||||
generator had already committed to.
|
||||
|
||||
Both are applied *while* the declarations are made, which is not a detail. The
|
||||
kebab rule is consulted in exactly one place, so collision groups are computed
|
||||
on the name a function will really take — which means renaming one of two
|
||||
colliding symbols dissolves the collision instead of leaving both refused, and
|
||||
`Spin2D`/`spin2d` gains a way out that is not a hand-written line. An excluded
|
||||
symbol still reports that it was excluded rather than going quiet: "there is no
|
||||
such binding" and "the package decided against this binding" are different
|
||||
answers.
|
||||
|
||||
What is actually in raylib's: `exclude Mem*`, because raylib exports
|
||||
malloc/realloc/free under its own names and binding them would put a second
|
||||
untracked heap behind three innocuous-looking Flan names, against plan.org's
|
||||
rule that an operation never falls back to a hidden allocator. And 19 `name`
|
||||
lines giving the generated predicates the `?` spelling the hand-written ones
|
||||
already use — `window-ready?` rather than `is-window-ready`, because
|
||||
`key-pressed?` and `is-window-ready` living in one package is precisely the
|
||||
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 <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 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
|
||||
same name without the suffix, one taking a slice and one answering with an
|
||||
`Option`. The importer refuses them by name collision with those wrappers,
|
||||
which is the correct answer.
|
||||
|
||||
### What a headless FFI test can and cannot pin
|
||||
|
||||
Worth knowing before writing another one, because two plausible tests in a row turned out to check nothing.
|
||||
|
||||
15
DISCUSS.md
15
DISCUSS.md
@ -182,6 +182,10 @@ build, and a permuted `Texture2D` or an `f64` for a `float` is caught by name.
|
||||
|
||||
What is left is two decisions, and both are the author's.
|
||||
|
||||
**6a and 6b are answered. See BUILT.md, "The bindings are committed now".** It became a code generator whose
|
||||
output is committed, and the 172 hand-written lines were *not* migrated — for a reason 6b did not reach. The two
|
||||
sections below are kept as the reasoning that got there.
|
||||
|
||||
### 6a. Does reading the header stay a build-time step, or become a code generator?
|
||||
|
||||
The cost, measured, with the wrappers pruned by `Reach` as they already were:
|
||||
@ -222,6 +226,17 @@ better than the rule's. `IsKeyPressed` is `key-pressed?` by hand and `is-key-pre
|
||||
would become an integer at the call site. A migration is therefore not a deletion; it is a deletion plus a
|
||||
kept list of the lines whose face is deliberately nicer than the header's.
|
||||
|
||||
**Answered: no, and the blocker is not the one above.** Vendoring stopped being the question once the *output* was
|
||||
committed rather than the header — no header is needed at any build. The count was also smaller than feared: 136 of
|
||||
the 172 are exactly what the rule produces, and the other 36 are expressible as `name` overrides in `bindings`.
|
||||
|
||||
What actually decides it is that migration would gut the check. Everything the generator emits agrees with the header
|
||||
by construction, so diffing generated output against its own source proves nothing; the hand-written lines have a
|
||||
different author, so they are the only declarations a header can contradict — and all ten of the 5.1-dev differences
|
||||
came from them. Delete them and the signature half of the check silently becomes a tautology. The enum point above
|
||||
survives intact as a second reason: `(rl/key-down? :space)` keeps its `Key` parameter only because that line is
|
||||
hand-written.
|
||||
|
||||
## 7. Watching variables
|
||||
|
||||
Raised while designing item 1, and deliberately separated from it.
|
||||
|
||||
37
NEXT.md
37
NEXT.md
@ -1,27 +1,9 @@
|
||||
## Queued: commit the generated bindings, with a config beside them
|
||||
## Queued: an idiomatic layer over the generated bindings
|
||||
|
||||
**Decided.** `flan import-c` already exists and prints the lines; kebab-casing is already implemented and reversible
|
||||
(the C symbol is kept verbatim in `Ast.DeclareC`, so the rule never has to be undone).
|
||||
|
||||
**Generate once, commit the result, regenerate when raylib moves.** What changes against today's opt-in header read:
|
||||
no header is needed by anyone, so the `?${FLAN_RAYLIB_H}` split disappears and every build gets all 428 bindings; the
|
||||
bindings become greppable and diffable in the repo; and the hand-written 172 stop being a separate set to maintain.
|
||||
Caching is not the argument — the dump is already cached on disk and in memory.
|
||||
|
||||
**What is given up, and it is the real cost:** the build-time check against the real header stops being automatic and
|
||||
becomes something run at regeneration. That check earned its place — it verified all 172 hand-written declarations and
|
||||
all 16 struct layouts against raylib 5.5 and found them exactly right, and against a 5.1-dev header on the same
|
||||
machine it found ten genuine differences. Keep it as a step, and make regeneration run it.
|
||||
|
||||
**A config file beside `headers`**, because the generated file is committed and therefore any hand-edit is destroyed by
|
||||
the next regeneration — the config is what survives. Wanted: **exclude patterns**, and **name overrides** where
|
||||
kebab-casing gives something ugly. A postprocessing transform pass was considered and rejected: it is a second program
|
||||
to understand and the config covers the real cases.
|
||||
|
||||
**Document it on the web page.** `web/index.html` has no section on the FFI's generated half.
|
||||
|
||||
**Later, not now: an idiomatic layer.** Thin Flan-shaped wrappers *over* the generated bindings, not instead of them —
|
||||
the generated set stays honest to C, and the layer is where a Flan-shaped API lives.
|
||||
Thin Flan-shaped wrappers **over** the generated bindings, not instead of them. The generated set stays honest to C —
|
||||
that is what makes it checkable against the header — and the layer is where a Flan-shaped API lives. Two of these
|
||||
already exist by hand in `vendor/raylib/raylib.flan` and are the shape to copy: `collision-point-poly?` takes a slice
|
||||
and `collision-lines` answers with an `Option`, each wrapping a `-raw` binding of the same name.
|
||||
|
||||
## Queued: a restart is not a transaction, and the docs must say so
|
||||
|
||||
@ -50,6 +32,15 @@ surface and must not be load-bearing, because the default build has no `FLAN_RAY
|
||||
|
||||
**2. `Key` has no `left-shift` — DONE.** `left-shift 340`, and nothing else: `PORTING.md` §5 checked every other enum
|
||||
value the game touches and they were all already right.
|
||||
**1. `DrawTexturePro` — done, and not by a hand-written line.** It was the one true blocker for `siam-farmer`
|
||||
(see `PORTING.md`: every tile in both implementations goes through it, and neither `DrawTextureRec` nor
|
||||
`DrawTextureEx` substitutes). It was reachable only through the opt-in `FLAN_RAYLIB_H` import. The bindings are
|
||||
committed now, so `rl/draw-texture-pro` is in `vendor/raylib/generated.flan` and a default build has it. **Nothing
|
||||
should add it by hand** — a second `declare-c` for the same C symbol is refused for the whole program.
|
||||
|
||||
**2. `Key` has no `left-shift`.** Both implementations use shift+1..5 to pick the tilemap. One enum member, and
|
||||
still a hand edit: the importer generates functions and only functions, so no `defenum` comes out of the header.
|
||||
`vendor/raylib/raylib.flan` is where `Key` lives.
|
||||
|
||||
**3. An out-of-bounds index should signal a condition, not `exit(134)` — DONE.** A failed bounds check signals
|
||||
`BoundsError` with `error`; `runtime/flan_rt.c`'s `flan_bounds_error`/`flan_slice_error` walk the handlers, then offer
|
||||
|
||||
76
bin/main.ml
76
bin/main.ml
@ -221,6 +221,10 @@ let () =
|
||||
let imported, dump, env =
|
||||
Flan.Cimport.header ~loc:(Flan.Loc.make header 0 0) ~header ~flags
|
||||
~known_structs:(List.map fst structs) ~known_enums ~taken ~bound_syms
|
||||
~config:
|
||||
(match pkg with
|
||||
| f :: _ -> Flan.Load.binding_config (Filename.dirname f)
|
||||
| [] -> Flan.Cimport.no_config)
|
||||
in
|
||||
List.iter
|
||||
(fun d -> print_endline (Flan.Cimport.decl_source d))
|
||||
@ -267,6 +271,77 @@ let () =
|
||||
x.Flan.Cimport.dflan x.Flan.Cimport.dsym x.Flan.Cimport.dwhy)
|
||||
ds)
|
||||
|
||||
(* Regeneration. [import-c] prints what it would produce; this writes it, and
|
||||
the difference between the two is that this one cannot skip the check.
|
||||
|
||||
It reads the header out of the package's own `headers`, not off the
|
||||
command line, because the version that may be read is a property of the
|
||||
package — `headers` is where it says which one, and `link` is where it
|
||||
says which library that has to match. And it reads every .flan in the
|
||||
directory *except* the file it writes, so the hand-written declarations
|
||||
still win and the generated ones are not mistaken for them on the next
|
||||
run.
|
||||
|
||||
Non-zero and nothing written when the package and the header disagree.
|
||||
That is the whole point: the committed file is the one thing here with no
|
||||
second opinion, so the moment of writing it is the only moment left at
|
||||
which the library can contradict it. *)
|
||||
| _ :: "generate-c" :: dir :: _ ->
|
||||
with_errors dir (fun () ->
|
||||
let loc = Flan.Loc.make dir 0 0 in
|
||||
let out = Filename.concat dir "generated.flan" in
|
||||
let ds =
|
||||
List.concat_map
|
||||
(fun f -> Flan.Parse.program (Flan.Reader.read_file f))
|
||||
(List.filter
|
||||
(fun f -> not (String.equal f out))
|
||||
(Flan.Load.entries dir ".flan"))
|
||||
in
|
||||
let config = Flan.Load.binding_config dir in
|
||||
match Flan.Load.header_specs ~loc dir with
|
||||
| [] ->
|
||||
Printf.eprintf
|
||||
"flan generate-c: %s/headers names no header that is there. \
|
||||
Regeneration reads the library's own header, at the version \
|
||||
%s/link names — export it and run this again.\n" dir dir;
|
||||
exit 2
|
||||
| _ :: _ :: _ ->
|
||||
Printf.eprintf
|
||||
"flan generate-c: %s/headers names more than one header, and one \
|
||||
generated file cannot come from several — the declarations would \
|
||||
depend on which was read last.\n" dir;
|
||||
exit 2
|
||||
| [ (h, flags) ] ->
|
||||
let r = Flan.Cimport.regenerate ~loc ~header:h ~flags ~ds ~config ~out in
|
||||
List.iter
|
||||
(fun (n, why) -> Printf.printf ";; refused %s: %s\n" n why)
|
||||
r.Flan.Cimport.ghidden;
|
||||
List.iter
|
||||
(fun (n, why) -> Printf.eprintf "DISAGREES %s: %s\n" n why)
|
||||
r.Flan.Cimport.gstructs;
|
||||
List.iter
|
||||
(fun (x : Flan.Cimport.sig_diff) ->
|
||||
Printf.eprintf "DIFFERS %s (%s): %s\n"
|
||||
x.Flan.Cimport.dflan x.Flan.Cimport.dsym x.Flan.Cimport.dwhy)
|
||||
r.Flan.Cimport.gsigs;
|
||||
if r.Flan.Cimport.gwrote then
|
||||
Printf.printf
|
||||
"wrote %s: %d declarations, %d refused, of %d functions in %s.\n\
|
||||
Every defstruct and every hand-written declare-c agrees with it.\n"
|
||||
out r.Flan.Cimport.gdecls
|
||||
(List.length r.Flan.Cimport.ghidden) r.Flan.Cimport.gfns h
|
||||
else begin
|
||||
Printf.eprintf
|
||||
"flan generate-c: %s disagrees with %s — %d struct layouts and %d \
|
||||
hand-written signatures. Nothing was written: a generated file \
|
||||
made against a header the library does not match is the silent \
|
||||
failure this check exists to prevent.\n"
|
||||
dir h
|
||||
(List.length r.Flan.Cimport.gstructs)
|
||||
(List.length r.Flan.Cimport.gsigs);
|
||||
exit 1
|
||||
end)
|
||||
|
||||
(* The IR is target-independent — [Emit] writes no triple and no datalayout,
|
||||
which is what lets one .ll serve both targets — so there is nothing for a
|
||||
target to change here. Refused rather than accepted and ignored: silently
|
||||
@ -411,6 +486,7 @@ let () =
|
||||
prerr_endline
|
||||
"usage: flan (read|parse|check|emit|shim) <file.flan>...\n\
|
||||
\ flan import-c <header.h> [package.flan...] [clang flags...]\n\
|
||||
\ flan generate-c <package-dir>\n\
|
||||
\ flan build <file.flan> [-o out] [--no-bounds-checks] [--dev] \
|
||||
[--debug] [--sanitize] [--target=wasm32-wasi|web]\n\
|
||||
\ flan run <file.flan> [args...]\n\
|
||||
|
||||
247
lib/cimport.ml
247
lib/cimport.ml
@ -502,6 +502,102 @@ type imported = {
|
||||
hidden : (string * string) list;
|
||||
}
|
||||
|
||||
(* ── The config beside the header ──────────────────────────────────── *)
|
||||
|
||||
(* Why a config file exists at all, when the importer decides everything else
|
||||
from the header.
|
||||
|
||||
Because the generated declarations are *committed*. A generated file that is
|
||||
read at build time can be hand-corrected and the correction survives, since
|
||||
nothing rewrites it. A generated file that is checked in is rewritten by the
|
||||
next regeneration, so a hand-edit to it is destroyed without anybody being
|
||||
told — which is the worst shape an edit can have. The edits therefore have
|
||||
to live somewhere regeneration *reads* rather than somewhere it writes, and
|
||||
this is that place.
|
||||
|
||||
Two directives, which are the two things the header cannot decide:
|
||||
|
||||
- [exclude], a C symbol or a pattern of them, for what should not be
|
||||
generated at all.
|
||||
- [name], a C symbol and the Flan name it is to take, for where the kebab
|
||||
rule gives something ugly.
|
||||
|
||||
A postprocessing transform pass over the generated file was the alternative
|
||||
and was rejected: a second program to understand, run over text the
|
||||
generator had already committed to. Both directives here are applied
|
||||
*while* the declarations are made, so the file on disk is already what the
|
||||
config says and nothing reads it twice. *)
|
||||
|
||||
type config = {
|
||||
excludes : string list;
|
||||
(* C symbol to Flan name. Keyed on the symbol and not on the kebab result,
|
||||
because the symbol is the only spelling that is stable — the whole point
|
||||
of an override is that the kebab result is not what is wanted. *)
|
||||
renames : (string * string) list;
|
||||
}
|
||||
|
||||
let no_config = { excludes = []; renames = [] }
|
||||
|
||||
(* [*] stands for any run of characters and nothing else does anything. Enough
|
||||
for [rl*] or [*Callback], and small enough to read at a glance; a package
|
||||
that needs more than this wants a hand-written declare-c, which it has. *)
|
||||
let matches (pat : string) (s : string) =
|
||||
let np = String.length pat and ns = String.length s in
|
||||
let rec go i j =
|
||||
if i = np then j = ns
|
||||
else if pat.[i] = '*' then
|
||||
let rec from k = (k <= ns && go (i + 1) k) || (k < ns && from (k + 1)) in
|
||||
from j
|
||||
else j < ns && Char.equal pat.[i] s.[j] && go (i + 1) (j + 1)
|
||||
in
|
||||
go 0 0
|
||||
|
||||
let excluded cfg sym = List.exists (fun p -> matches p sym) cfg.excludes
|
||||
|
||||
(* The one place the kebab rule is consulted, so an override is not a special
|
||||
case anywhere below: collisions are computed on the name a function will
|
||||
actually take, which means renaming one of two colliding symbols resolves
|
||||
the collision rather than leaving both refused. *)
|
||||
let flan_name cfg sym =
|
||||
match List.assoc_opt sym cfg.renames with Some n -> n | None -> kebab sym
|
||||
|
||||
(* The file, in the shape of [headers] and [link] beside it: one directive per
|
||||
line, [#] comments, blank lines ignored. A line that is neither directive is
|
||||
an error rather than a line quietly skipped — the house rule against
|
||||
swallowing things applies to a config as much as to a flag, and a typo in a
|
||||
name override would otherwise show up as a binding under the wrong name. *)
|
||||
let read_config path : config =
|
||||
if not (Sys.file_exists path) then no_config
|
||||
else begin
|
||||
let ch = open_in path in
|
||||
let excludes = ref [] and renames = ref [] in
|
||||
let rec go n =
|
||||
match input_line ch with
|
||||
| line ->
|
||||
let t = String.trim line in
|
||||
if t <> "" && t.[0] <> '#' then begin
|
||||
let ws =
|
||||
String.split_on_char ' ' t
|
||||
|> List.concat_map (String.split_on_char '\t')
|
||||
|> List.filter (fun w -> w <> "")
|
||||
in
|
||||
match ws with
|
||||
| [ "exclude"; p ] -> excludes := p :: !excludes
|
||||
| [ "name"; sym; flan ] -> renames := (sym, flan) :: !renames
|
||||
| _ ->
|
||||
close_in ch;
|
||||
fail (Loc.make path n 0)
|
||||
"a line here is `exclude <C symbol or pattern>` or `name <C \
|
||||
symbol> <flan-name>`, and this is neither: %s" t
|
||||
end;
|
||||
go (n + 1)
|
||||
| exception End_of_file -> ()
|
||||
in
|
||||
go 1;
|
||||
close_in ch;
|
||||
{ excludes = List.rev !excludes; renames = List.rev !renames }
|
||||
end
|
||||
|
||||
(* [taken] is every name the package already declares, which is what makes the
|
||||
sidecar additive: a hand-written [(declare-c get-gamepad-name ...)] wins
|
||||
over the header, and a C symbol already bound by hand is not bound twice —
|
||||
@ -510,7 +606,7 @@ type imported = {
|
||||
[-c] as well as the name itself, because [Shim] generates [foo-c] beside a
|
||||
[foo] whose signature has a struct in it, and a collision there is refused
|
||||
for the whole program rather than for the one binding. *)
|
||||
let of_dump ~env ~taken ~bound_syms (d : dump) : imported =
|
||||
let of_dump ~env ~taken ~bound_syms ~config (d : dump) : imported =
|
||||
let decls = ref [] and hidden = ref [] in
|
||||
(* Collisions are found before anything is emitted, and they take *every*
|
||||
name in the colliding group down with them.
|
||||
@ -530,16 +626,33 @@ let of_dump ~env ~taken ~bound_syms (d : dump) : imported =
|
||||
let candidates =
|
||||
List.filter (fun f -> not (List.mem f.csym bound_syms)) d.fns
|
||||
in
|
||||
(* Excluded before anything else looks at them, so an excluded symbol is not
|
||||
in a collision group either — which is one of the things exclusion is for.
|
||||
It still says why, under the name it would have taken: "there is no such
|
||||
binding" and "the package decided against this binding" are different
|
||||
answers and a reader deserves the second one. *)
|
||||
let dropped, candidates =
|
||||
List.partition (fun f -> excluded config f.csym) candidates
|
||||
in
|
||||
List.iter
|
||||
(fun f ->
|
||||
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)
|
||||
:: !hidden)
|
||||
dropped;
|
||||
let groups = Hashtbl.create 512 in
|
||||
List.iter
|
||||
(fun f ->
|
||||
let k = kebab f.csym in
|
||||
let k = flan_name config f.csym in
|
||||
Hashtbl.replace groups k (f.csym :: Option.value ~default:[]
|
||||
(Hashtbl.find_opt groups k)))
|
||||
candidates;
|
||||
List.iter
|
||||
(fun f ->
|
||||
let flan = kebab f.csym in
|
||||
let flan = flan_name config f.csym in
|
||||
let skip why = hidden := (flan, why) :: !hidden in
|
||||
match List.rev (Hashtbl.find groups flan) with
|
||||
| _ :: _ :: _ as all ->
|
||||
@ -547,7 +660,8 @@ let of_dump ~env ~taken ~bound_syms (d : dump) : imported =
|
||||
(Printf.sprintf
|
||||
"%s all kebab to %s, and which one got the name would depend on \
|
||||
the order the header declares them in — so none of them takes \
|
||||
it. Bind the one you want with a hand-written declare-c"
|
||||
it. Give the one you want a `name` in the binding config, or \
|
||||
bind it with a hand-written declare-c"
|
||||
(String.concat ", " all) flan)
|
||||
| _ ->
|
||||
if Hashtbl.mem taken flan || Hashtbl.mem taken (flan ^ "-c") then
|
||||
@ -811,7 +925,8 @@ let dump_of ~loc ~header ~flags =
|
||||
|
||||
let env_of ~known_structs ~known_enums d = { known_structs; known_enums; d }
|
||||
|
||||
let header ~loc ~header:h ~flags ~known_structs ~known_enums ~taken ~bound_syms =
|
||||
let header ~loc ~header:h ~flags ~known_structs ~known_enums ~taken ~bound_syms
|
||||
~config =
|
||||
let k =
|
||||
(* Sorted, because neither the taken table nor the declaration order is a
|
||||
fact about the package — two loads of the same file that enumerate them
|
||||
@ -822,14 +937,19 @@ let header ~loc ~header:h ~flags ~known_structs ~known_enums ~taken ~bound_syms
|
||||
:: "\001" :: sorted known_structs
|
||||
@ ("\001" :: sorted known_enums)
|
||||
@ ("\001" :: sorted (Hashtbl.fold (fun n () acc -> n :: acc) taken []))
|
||||
@ ("\001" :: sorted bound_syms))
|
||||
@ ("\001" :: sorted bound_syms)
|
||||
(* The config is part of the question: two loads that disagree about
|
||||
what is excluded or renamed are different questions, and serving one
|
||||
the other's answer is the bug this key exists to prevent. *)
|
||||
@ ("\001" :: sorted config.excludes)
|
||||
@ ("\001" :: sorted (List.map (fun (a, b) -> a ^ "=" ^ b) config.renames)))
|
||||
in
|
||||
match Hashtbl.find_opt imports k with
|
||||
| Some r -> r
|
||||
| None ->
|
||||
let d = dump_of ~loc ~header:h ~flags in
|
||||
let env = env_of ~known_structs ~known_enums d in
|
||||
let r = (of_dump ~env ~taken ~bound_syms d, d, env) in
|
||||
let r = (of_dump ~env ~taken ~bound_syms ~config d, d, env) in
|
||||
Hashtbl.replace imports k r;
|
||||
r
|
||||
|
||||
@ -955,3 +1075,116 @@ let diff_bound ~env ~(bound : (Ast.fn * string) list) (d : dump) =
|
||||
(match want with None -> "nothing" | Some t -> ty_source t)
|
||||
c.cret)))
|
||||
bound
|
||||
|
||||
|
||||
(* ── Regenerating the committed declarations ───────────────────────── *)
|
||||
|
||||
(* Generate once, commit the result, regenerate when the library moves.
|
||||
|
||||
What that buys is in DISCUSS.md item 6 and it is not caching — the dump is
|
||||
already cached on disk and in memory, so a build that reads the header pays
|
||||
for it once either way. It is that no header is needed by *anybody*: the
|
||||
declarations are in the repository, so they are greppable, they diff when
|
||||
raylib moves, and a build needs libraylib linkable and nothing else. The
|
||||
opt-in that used to decide whether a package had 172 bindings or 428 stops
|
||||
deciding anything.
|
||||
|
||||
What it costs is the check. A header read at build time compared every
|
||||
hand-written declaration against the library on every build; a committed
|
||||
file compares nothing, because a file on disk has no second opinion. That
|
||||
check is not decoration — it verified all 172 hand-written declarations and
|
||||
all 16 struct layouts against raylib 5.5 and found them exactly right, and
|
||||
against a 5.1-dev header on the same machine it found ten real differences.
|
||||
|
||||
So regeneration runs it, and the check *gates the write*. There is no way to
|
||||
ask for new declarations without comparing the package against the header
|
||||
they come from, because the one function that writes the file is this one
|
||||
and it refuses when the two disagree. A regeneration that quietly rewrote
|
||||
the bindings against a header the library does not match would produce
|
||||
exactly the failure BUILT.md warns about — a permuted struct read as five
|
||||
plausible numbers rather than as a link error — and it would produce it in a
|
||||
committed file that looks reviewed.
|
||||
|
||||
The hand-written declarations are what make the signature half of that check
|
||||
mean anything, which is why they stay. Everything the generator emits agrees
|
||||
with the header by construction, so diffing generated output against the
|
||||
header it came from is a tautology; the hand-written lines were transcribed
|
||||
from raylib's documentation by a person, so they are an independent second
|
||||
opinion and the only thing here that the header can actually contradict. *)
|
||||
|
||||
type regen = {
|
||||
gwrote : bool;
|
||||
gdecls : int;
|
||||
gfns : int;
|
||||
ghidden : (string * string) list;
|
||||
gstructs : (string * string) list;
|
||||
gsigs : sig_diff list;
|
||||
}
|
||||
|
||||
let banner h =
|
||||
Printf.sprintf
|
||||
";;;; Generated from %s by `flan generate-c`. Do not edit this file.\n\
|
||||
;;;;\n\
|
||||
;;;; Every line here was read out of the C header named by `headers`, and\n\
|
||||
;;;; the next regeneration overwrites the file — so a correction made here\n\
|
||||
;;;; is destroyed without anybody being told. Corrections go in `bindings`\n\
|
||||
;;;; beside it, which is read *while* these lines are made: `exclude` drops\n\
|
||||
;;;; a function, `name` gives one a Flan name the kebab rule would not.\n\
|
||||
;;;; Anything neither directive can express is a hand-written declare-c in\n\
|
||||
;;;; the package's own .flan, which wins over this file and is left alone.\n\
|
||||
;;;;\n\
|
||||
;;;; Regenerating compares the package against the header first and\n\
|
||||
;;;; refuses to write when they disagree, so this file and the\n\
|
||||
;;;; hand-written declarations beside it agreed with %s when it was made.\n\n"
|
||||
(Filename.basename h) (Filename.basename h)
|
||||
|
||||
(* [ds] is the package's *hand-written* declarations: every .flan in the
|
||||
directory except the one being written. Reading the output back in would
|
||||
make regeneration idempotent in the worst way — every symbol would already
|
||||
be bound, so the second run would generate nothing and cheerfully write an
|
||||
empty file. *)
|
||||
let regenerate ~loc ~header:h ~flags ~(ds : Ast.decl list) ~config ~out =
|
||||
let taken = Hashtbl.create 64 in
|
||||
List.iter
|
||||
(fun d ->
|
||||
match Ast.declared_name d with
|
||||
| Some n -> Hashtbl.replace taken n ()
|
||||
| None -> ())
|
||||
ds;
|
||||
let pick f = List.filter_map f ds in
|
||||
let structs =
|
||||
pick (fun (d : Ast.decl) ->
|
||||
match d.Ast.d with Ast.Defstruct (n, fs) -> Some (n, fs) | _ -> None)
|
||||
and known_enums =
|
||||
pick (fun (d : Ast.decl) ->
|
||||
match d.Ast.d with Ast.Defenum (n, _) -> Some n | _ -> None)
|
||||
and bound_syms =
|
||||
pick (fun (d : Ast.decl) ->
|
||||
match d.Ast.d with
|
||||
| Ast.Declare (_, s) | Ast.DeclareC (_, s) -> Some s
|
||||
| _ -> None)
|
||||
and bound =
|
||||
pick (fun (d : Ast.decl) ->
|
||||
match d.Ast.d with Ast.DeclareC (fn, s) -> Some (fn, s) | _ -> None)
|
||||
in
|
||||
let imported, dump, env =
|
||||
header ~loc ~header:h ~flags ~known_structs:(List.map fst structs)
|
||||
~known_enums ~taken ~bound_syms ~config
|
||||
in
|
||||
let gstructs = check_structs ~env ~structs dump in
|
||||
let gsigs = diff_bound ~env ~bound dump in
|
||||
let gwrote = gstructs = [] && gsigs = [] in
|
||||
if gwrote then begin
|
||||
let b = Buffer.create 65536 in
|
||||
Buffer.add_string b (banner h);
|
||||
List.iter
|
||||
(fun d ->
|
||||
Buffer.add_string b (decl_source d);
|
||||
Buffer.add_char b '\n')
|
||||
imported.decls;
|
||||
let ch = open_out out in
|
||||
output_string ch (Buffer.contents b);
|
||||
close_out ch
|
||||
end;
|
||||
{ gwrote; gdecls = List.length imported.decls;
|
||||
gfns = List.length dump.fns; ghidden = imported.hidden; gstructs; gsigs }
|
||||
|
||||
12
lib/load.ml
12
lib/load.ml
@ -587,6 +587,16 @@ let header_specs ~loc dir =
|
||||
else Some (h, flags)))
|
||||
(read_lines path)
|
||||
|
||||
(* The binding config, beside [headers] and read for the same package: which
|
||||
functions not to generate, and what to call the ones whose kebab name is
|
||||
not wanted. Absent is the ordinary case and means neither.
|
||||
|
||||
It is read here rather than by the importer because it is a property of the
|
||||
*package*, like [headers] and [link] — the importer is given a header and a
|
||||
config and has no directory to look in. See [Cimport.read_config] for why a
|
||||
config exists at all once the generated declarations are committed. *)
|
||||
let binding_config dir = Cimport.read_config (Filename.concat dir "bindings")
|
||||
|
||||
let real dir = try Unix.realpath dir with Unix.Unix_error _ -> dir
|
||||
|
||||
(* One package, and whatever it imports.
|
||||
@ -758,7 +768,7 @@ let rec import ~seen ~open_ ~loc alias dir =
|
||||
in
|
||||
let r, dump, env =
|
||||
Cimport.header ~loc ~header:h ~flags ~known_structs ~known_enums
|
||||
~taken ~bound_syms
|
||||
~taken ~bound_syms ~config:(binding_config dir)
|
||||
in
|
||||
(* The point of reading the header, and the reason it is not
|
||||
enough to generate declarations out of it.
|
||||
|
||||
@ -838,23 +838,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
|
||||
|
||||
@ -1377,7 +1377,7 @@ let () =
|
||||
in
|
||||
let i, d, e =
|
||||
Cimport.header ~loc:Loc.unknown ~header:"headers/sample.h" ~flags:[]
|
||||
~known_structs ~known_enums ~taken ~bound_syms:[]
|
||||
~known_structs ~known_enums ~taken ~bound_syms:[] ~config:Cimport.no_config
|
||||
in
|
||||
(i, d, e, ds)
|
||||
in
|
||||
@ -1437,6 +1437,105 @@ let () =
|
||||
check "nor is the other half of the collision"
|
||||
(not (List.exists (fun l -> contains l "\"spin2d\"") produced));
|
||||
|
||||
(* ── The config beside the header (Cimport.read_config) ────────── *)
|
||||
|
||||
(* Why there is a config at all: the generated declarations are committed, so
|
||||
a hand-edit to them is destroyed by the next regeneration and the edit has
|
||||
to live somewhere regeneration reads instead. These are the two things it
|
||||
can say. *)
|
||||
let with_config config =
|
||||
let taken = Hashtbl.create 16 in
|
||||
List.iter
|
||||
(fun d ->
|
||||
match Ast.declared_name d with
|
||||
| Some n -> Hashtbl.replace taken n ()
|
||||
| None -> ())
|
||||
fixture_ds;
|
||||
let known_structs =
|
||||
List.filter_map
|
||||
(fun (d : Ast.decl) ->
|
||||
match d.Ast.d with Ast.Defstruct (n, _) -> Some n | _ -> None)
|
||||
fixture_ds
|
||||
and known_enums =
|
||||
List.filter_map
|
||||
(fun (d : Ast.decl) ->
|
||||
match d.Ast.d with Ast.Defenum (n, _) -> Some n | _ -> None)
|
||||
fixture_ds
|
||||
in
|
||||
let i, _, _ =
|
||||
Cimport.header ~loc:Loc.unknown ~header:"headers/sample.h" ~flags:[]
|
||||
~known_structs ~known_enums ~taken ~bound_syms:[] ~config
|
||||
in
|
||||
(List.map Cimport.decl_source i.Cimport.decls, i.Cimport.hidden)
|
||||
in
|
||||
|
||||
let lines, hidden =
|
||||
with_config { Cimport.excludes = [ "set_seed" ]; renames = [] }
|
||||
in
|
||||
check "an excluded symbol is not generated"
|
||||
(not (List.exists (fun l -> contains l "\"set_seed\"") lines));
|
||||
(* Not silently absent. "there is no such binding" and "the package decided
|
||||
against this binding" are different answers and a reader gets the second,
|
||||
which is what [hidden] is for. *)
|
||||
check "and it says it was excluded rather than going quiet"
|
||||
(List.exists
|
||||
(fun (n, why) -> n = "set-seed" && contains why "excluded by the package")
|
||||
hidden);
|
||||
|
||||
let lines, _ =
|
||||
with_config { Cimport.excludes = [ "add_*" ]; renames = [] }
|
||||
in
|
||||
check "an exclude pattern matches by prefix"
|
||||
(not (List.exists (fun l -> contains l "\"add_ints\"") lines));
|
||||
check "and leaves everything it does not match"
|
||||
(List.exists (fun l -> contains l "\"set_seed\"") lines);
|
||||
|
||||
let lines, _ =
|
||||
with_config
|
||||
{ Cimport.excludes = []; renames = [ ("set_seed", "seed!") ] }
|
||||
in
|
||||
(* The C symbol is kept verbatim, so an override changes the Flan face and
|
||||
nothing else — which is what makes it safe to spell a predicate the way
|
||||
Lisp spells one. *)
|
||||
check "a name override is the Flan name, and the C symbol is untouched"
|
||||
(List.mem "(declare-c seed! [seed u32] \"set_seed\")" lines);
|
||||
|
||||
(* The collision above is refused because neither Spin2D nor spin2d may take
|
||||
[spin-2d] by an accident of header order. Naming one of them is the way
|
||||
out, and it is the reason the kebab rule is consulted in exactly one
|
||||
place: the groups are computed on the name a function will really take,
|
||||
so the rename dissolves the group rather than leaving both refused. *)
|
||||
let lines, hidden =
|
||||
with_config
|
||||
{ Cimport.excludes = []; renames = [ ("Spin2D", "spin-2d-upper") ] }
|
||||
in
|
||||
check "a rename resolves a collision for both halves"
|
||||
(List.exists (fun l -> contains l "\"Spin2D\"") lines
|
||||
&& List.exists (fun l -> contains l "\"spin2d\"") lines);
|
||||
check "and the collision is no longer refused"
|
||||
(not (List.mem_assoc "spin-2d" hidden));
|
||||
|
||||
(* The file, since a typo in it would otherwise show up as a binding under
|
||||
the wrong name. A line that is neither directive is an error rather than a
|
||||
line quietly skipped. *)
|
||||
let config_file text =
|
||||
let f = Filename.temp_file "flan-bindings" "" in
|
||||
let ch = open_out f in
|
||||
output_string ch text;
|
||||
close_out ch;
|
||||
f
|
||||
in
|
||||
let c = Cimport.read_config (config_file "# a comment\n\nexclude Mem*\nname IsWindowReady window-ready?\n") in
|
||||
check "read_config reads an exclude" (c.Cimport.excludes = [ "Mem*" ]);
|
||||
check "read_config reads a name override"
|
||||
(c.Cimport.renames = [ ("IsWindowReady", "window-ready?") ]);
|
||||
check "a missing config is no exclusions and no overrides"
|
||||
(Cimport.read_config "no-such-bindings-file" = Cimport.no_config);
|
||||
check "a line that is neither directive is refused"
|
||||
(match Cimport.read_config (config_file "rename Foo bar\n") with
|
||||
| _ -> false
|
||||
| exception Loc.Error { Loc.dmsg = m; _ } -> contains m "and this is neither");
|
||||
|
||||
(* A refused name is a name that exists and cannot be had — Zig's failDecl,
|
||||
which Load.refuse_hidden already implements for main. Nothing may be in
|
||||
both lists, or asking for a name that works would report that it does
|
||||
|
||||
78
vendor/raylib/bindings
vendored
Normal file
78
vendor/raylib/bindings
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
# How the generated bindings in generated.flan are shaped. One directive per
|
||||
# line, `#` comments, blank lines ignored. A line that is neither directive is
|
||||
# an error rather than a line quietly skipped.
|
||||
#
|
||||
# exclude <C symbol or pattern> do not generate a binding for this
|
||||
# name <C symbol> <flan-name> call it this instead of the kebab rule's
|
||||
#
|
||||
# `*` in an exclude pattern stands for any run of characters, and nothing else
|
||||
# does anything.
|
||||
#
|
||||
# Why this file exists. generated.flan is committed, so it is not a build
|
||||
# artefact anybody can hand-correct: the next `flan generate-c vendor/raylib`
|
||||
# overwrites it, and an edit made there is destroyed without anybody being
|
||||
# told. This file is read *while* those lines are made, so it is the edit that
|
||||
# survives regeneration. A postprocessing pass over the generated text was the
|
||||
# alternative and was rejected — a second program to understand, applied to
|
||||
# output the generator had already committed to.
|
||||
#
|
||||
# What does NOT belong here. Anything neither directive can express is a
|
||||
# hand-written `declare-c` in raylib.flan, which wins over the generated file
|
||||
# and is left alone by the importer. That is the escape hatch for a signature
|
||||
# the importer gets wrong and for a Flan face the header cannot describe —
|
||||
# load-font-ex-raw and load-image-from-memory-raw are both that, each wrapped
|
||||
# by a Flan defn of the same name without the suffix.
|
||||
|
||||
# ── raylib's allocator ──────────────────────────────────────────────
|
||||
#
|
||||
# raylib exports malloc, realloc and free under its own names. Flan's memory
|
||||
# model is explicit — plan.org's rule is that an operation never falls back to
|
||||
# a hidden allocator — so binding these would put a second, untracked heap
|
||||
# behind three innocuous-looking Flan names, and a pointer from one freed by
|
||||
# the other is the kind of bug that does not point at itself. Nothing in the
|
||||
# package needs them: raylib's own Load*/Unload* pairs own everything raylib
|
||||
# allocates.
|
||||
exclude MemAlloc
|
||||
exclude MemRealloc
|
||||
exclude MemFree
|
||||
|
||||
# ── Predicates read as questions ────────────────────────────────────
|
||||
#
|
||||
# The kebab rule gives `is-window-ready`, which is C's phrasing wearing Flan's
|
||||
# punctuation. The hand-written bindings in raylib.flan settled the convention
|
||||
# years of Lisp settled first — a predicate ends in `?` and does not begin with
|
||||
# `is` — and these are the generated half of the same set, so they follow it.
|
||||
# key-pressed? and window-ready? being spelled differently would be the split
|
||||
# this whole change exists to remove.
|
||||
#
|
||||
# The C symbol is kept verbatim in the declaration either way, so nothing here
|
||||
# is lost: the name is the Flan face and `IsWindowReady` is still what is
|
||||
# called and still what the check compares against.
|
||||
name IsWindowReady window-ready?
|
||||
name IsWindowFullscreen window-fullscreen?
|
||||
name IsWindowHidden window-hidden?
|
||||
name IsWindowMinimized window-minimized?
|
||||
name IsWindowMaximized window-maximized?
|
||||
name IsWindowFocused window-focused?
|
||||
name IsWindowResized window-resized?
|
||||
name IsWindowState window-state?
|
||||
name IsCursorOnScreen cursor-on-screen?
|
||||
name IsKeyUp key-up?
|
||||
name IsKeyPressedRepeat key-pressed-repeat?
|
||||
name IsMouseButtonUp mouse-button-up?
|
||||
name IsFileDropped file-dropped?
|
||||
name IsFileExtension file-extension?
|
||||
name IsFileNameValid file-name-valid?
|
||||
name IsPathFile path-file?
|
||||
name IsAudioStreamValid audio-stream-valid?
|
||||
name IsAudioStreamPlaying audio-stream-playing?
|
||||
name IsAudioStreamProcessed audio-stream-processed?
|
||||
|
||||
# Hand-written in raylib.flan, so excluded here: a second declare-c for one C
|
||||
# symbol is refused for the whole program. These three are on a game's
|
||||
# per-frame path (PORTING.md), and a hand-written line is what the signature
|
||||
# check has to compare against -- generated output agrees with the header by
|
||||
# construction, so it can only check the hand-written half.
|
||||
exclude DrawTexturePro
|
||||
exclude ImageFromImage
|
||||
exclude IsWindowReady
|
||||
264
vendor/raylib/generated.flan
vendored
Normal file
264
vendor/raylib/generated.flan
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
;;;; Generated from raylib.h by `flan generate-c`. Do not edit this file.
|
||||
;;;;
|
||||
;;;; Every line here was read out of the C header named by `headers`, and
|
||||
;;;; the next regeneration overwrites the file — so a correction made here
|
||||
;;;; is destroyed without anybody being told. Corrections go in `bindings`
|
||||
;;;; beside it, which is read *while* these lines are made: `exclude` drops
|
||||
;;;; a function, `name` gives one a Flan name the kebab rule would not.
|
||||
;;;; Anything neither directive can express is a hand-written declare-c in
|
||||
;;;; the package's own .flan, which wins over this file and is left alone.
|
||||
;;;;
|
||||
;;;; Regenerating compares the package against the header first and
|
||||
;;;; refuses to write when they disagree, so this file and the
|
||||
;;;; hand-written declarations beside it agreed with raylib.h when it was made.
|
||||
|
||||
(declare-c window-fullscreen? [] bool "IsWindowFullscreen")
|
||||
(declare-c window-hidden? [] bool "IsWindowHidden")
|
||||
(declare-c window-minimized? [] bool "IsWindowMinimized")
|
||||
(declare-c window-maximized? [] bool "IsWindowMaximized")
|
||||
(declare-c window-focused? [] bool "IsWindowFocused")
|
||||
(declare-c window-resized? [] bool "IsWindowResized")
|
||||
(declare-c window-state? [flag u32] bool "IsWindowState")
|
||||
(declare-c set-window-state [flags u32] "SetWindowState")
|
||||
(declare-c clear-window-state [flags u32] "ClearWindowState")
|
||||
(declare-c toggle-fullscreen [] "ToggleFullscreen")
|
||||
(declare-c toggle-borderless-windowed [] "ToggleBorderlessWindowed")
|
||||
(declare-c maximize-window [] "MaximizeWindow")
|
||||
(declare-c minimize-window [] "MinimizeWindow")
|
||||
(declare-c restore-window [] "RestoreWindow")
|
||||
(declare-c set-window-icon [image Image] "SetWindowIcon")
|
||||
(declare-c set-window-icons [images (Ptr Image) count i32] "SetWindowIcons")
|
||||
(declare-c set-window-title [title string] "SetWindowTitle")
|
||||
(declare-c set-window-position [x i32 y i32] "SetWindowPosition")
|
||||
(declare-c set-window-monitor [monitor i32] "SetWindowMonitor")
|
||||
(declare-c set-window-min-size [width i32 height i32] "SetWindowMinSize")
|
||||
(declare-c set-window-max-size [width i32 height i32] "SetWindowMaxSize")
|
||||
(declare-c set-window-size [width i32 height i32] "SetWindowSize")
|
||||
(declare-c set-window-opacity [opacity f32] "SetWindowOpacity")
|
||||
(declare-c set-window-focused [] "SetWindowFocused")
|
||||
(declare-c get-window-handle [] (Ptr u8) "GetWindowHandle")
|
||||
(declare-c get-render-width [] i32 "GetRenderWidth")
|
||||
(declare-c get-render-height [] i32 "GetRenderHeight")
|
||||
(declare-c get-monitor-count [] i32 "GetMonitorCount")
|
||||
(declare-c get-current-monitor [] i32 "GetCurrentMonitor")
|
||||
(declare-c get-monitor-position [monitor i32] Vector2 "GetMonitorPosition")
|
||||
(declare-c get-monitor-width [monitor i32] i32 "GetMonitorWidth")
|
||||
(declare-c get-monitor-height [monitor i32] i32 "GetMonitorHeight")
|
||||
(declare-c get-monitor-physical-width [monitor i32] i32 "GetMonitorPhysicalWidth")
|
||||
(declare-c get-monitor-physical-height [monitor i32] i32 "GetMonitorPhysicalHeight")
|
||||
(declare-c get-monitor-refresh-rate [monitor i32] i32 "GetMonitorRefreshRate")
|
||||
(declare-c get-window-position [] Vector2 "GetWindowPosition")
|
||||
(declare-c get-window-scale-dpi [] Vector2 "GetWindowScaleDPI")
|
||||
(declare-c set-clipboard-text [text string] "SetClipboardText")
|
||||
(declare-c get-clipboard-image [] Image "GetClipboardImage")
|
||||
(declare-c enable-event-waiting [] "EnableEventWaiting")
|
||||
(declare-c disable-event-waiting [] "DisableEventWaiting")
|
||||
(declare-c enable-cursor [] "EnableCursor")
|
||||
(declare-c disable-cursor [] "DisableCursor")
|
||||
(declare-c cursor-on-screen? [] bool "IsCursorOnScreen")
|
||||
(declare-c end-mode-3d [] "EndMode3D")
|
||||
(declare-c end-shader-mode [] "EndShaderMode")
|
||||
(declare-c begin-blend-mode [mode i32] "BeginBlendMode")
|
||||
(declare-c end-blend-mode [] "EndBlendMode")
|
||||
(declare-c begin-scissor-mode [x i32 y i32 width i32 height i32] "BeginScissorMode")
|
||||
(declare-c end-scissor-mode [] "EndScissorMode")
|
||||
(declare-c end-vr-stereo-mode [] "EndVrStereoMode")
|
||||
(declare-c swap-screen-buffer [] "SwapScreenBuffer")
|
||||
(declare-c poll-input-events [] "PollInputEvents")
|
||||
(declare-c wait-time [seconds f64] "WaitTime")
|
||||
(declare-c set-random-seed [seed u32] "SetRandomSeed")
|
||||
(declare-c get-random-value [min i32 max i32] i32 "GetRandomValue")
|
||||
(declare-c load-random-sequence [count u32 min i32 max i32] (Ptr i32) "LoadRandomSequence")
|
||||
(declare-c unload-random-sequence [sequence (Ptr i32)] "UnloadRandomSequence")
|
||||
(declare-c take-screenshot [file-name string] "TakeScreenshot")
|
||||
(declare-c open-url [url string] "OpenURL")
|
||||
(declare-c load-file-data [file-name string data-size (Ptr i32)] (Ptr u8) "LoadFileData")
|
||||
(declare-c unload-file-data [data (Ptr u8)] "UnloadFileData")
|
||||
(declare-c save-file-data [file-name string data (Ptr u8) data-size i32] bool "SaveFileData")
|
||||
(declare-c export-data-as-code [data (Ptr u8) data-size i32 file-name string] bool "ExportDataAsCode")
|
||||
(declare-c file-exists [file-name string] bool "FileExists")
|
||||
(declare-c directory-exists [dir-path string] bool "DirectoryExists")
|
||||
(declare-c file-extension? [file-name string ext string] bool "IsFileExtension")
|
||||
(declare-c get-file-length [file-name string] i32 "GetFileLength")
|
||||
(declare-c make-directory [dir-path string] i32 "MakeDirectory")
|
||||
(declare-c change-directory [dir string] bool "ChangeDirectory")
|
||||
(declare-c path-file? [path string] bool "IsPathFile")
|
||||
(declare-c file-name-valid? [file-name string] bool "IsFileNameValid")
|
||||
(declare-c file-dropped? [] bool "IsFileDropped")
|
||||
(declare-c compress-data [data (Ptr u8) data-size i32 comp-data-size (Ptr i32)] (Ptr u8) "CompressData")
|
||||
(declare-c decompress-data [comp-data (Ptr u8) comp-data-size i32 data-size (Ptr i32)] (Ptr u8) "DecompressData")
|
||||
(declare-c decode-data-base-64 [data (Ptr u8) output-size (Ptr i32)] (Ptr u8) "DecodeDataBase64")
|
||||
(declare-c compute-crc32 [data (Ptr u8) data-size i32] u32 "ComputeCRC32")
|
||||
(declare-c compute-md5 [data (Ptr u8) data-size i32] (Ptr u32) "ComputeMD5")
|
||||
(declare-c compute-sha1 [data (Ptr u8) data-size i32] (Ptr u32) "ComputeSHA1")
|
||||
(declare-c set-automation-event-base-frame [frame i32] "SetAutomationEventBaseFrame")
|
||||
(declare-c start-automation-event-recording [] "StartAutomationEventRecording")
|
||||
(declare-c stop-automation-event-recording [] "StopAutomationEventRecording")
|
||||
(declare-c key-pressed-repeat? [key i32] bool "IsKeyPressedRepeat")
|
||||
(declare-c key-up? [key i32] bool "IsKeyUp")
|
||||
(declare-c get-key-pressed [] i32 "GetKeyPressed")
|
||||
(declare-c get-char-pressed [] i32 "GetCharPressed")
|
||||
(declare-c set-exit-key [key i32] "SetExitKey")
|
||||
(declare-c set-gamepad-mappings [mappings string] i32 "SetGamepadMappings")
|
||||
(declare-c set-gamepad-vibration [gamepad i32 left-motor f32 right-motor f32 duration f32] "SetGamepadVibration")
|
||||
(declare-c mouse-button-up? [button i32] bool "IsMouseButtonUp")
|
||||
(declare-c get-mouse-x [] i32 "GetMouseX")
|
||||
(declare-c get-mouse-y [] i32 "GetMouseY")
|
||||
(declare-c get-mouse-delta [] Vector2 "GetMouseDelta")
|
||||
(declare-c set-mouse-position [x i32 y i32] "SetMousePosition")
|
||||
(declare-c set-mouse-offset [offset-x i32 offset-y i32] "SetMouseOffset")
|
||||
(declare-c set-mouse-scale [scale-x f32 scale-y f32] "SetMouseScale")
|
||||
(declare-c get-mouse-wheel-move-v [] Vector2 "GetMouseWheelMoveV")
|
||||
(declare-c set-mouse-cursor [cursor i32] "SetMouseCursor")
|
||||
(declare-c draw-line-strip [points (Ptr Vector2) point-count i32 color Color] "DrawLineStrip")
|
||||
(declare-c draw-line-bezier [start-pos Vector2 end-pos Vector2 thick f32 color Color] "DrawLineBezier")
|
||||
(declare-c draw-circle-sector [center Vector2 radius f32 start-angle f32 end-angle f32 segments i32 color Color] "DrawCircleSector")
|
||||
(declare-c draw-circle-sector-lines [center Vector2 radius f32 start-angle f32 end-angle f32 segments i32 color Color] "DrawCircleSectorLines")
|
||||
(declare-c draw-circle-gradient [center-x i32 center-y i32 radius f32 inner Color outer Color] "DrawCircleGradient")
|
||||
(declare-c draw-rectangle-pro [rec Rectangle origin Vector2 rotation f32 color Color] "DrawRectanglePro")
|
||||
(declare-c draw-rectangle-gradient-v [pos-x i32 pos-y i32 width i32 height i32 top Color bottom Color] "DrawRectangleGradientV")
|
||||
(declare-c draw-rectangle-gradient-h [pos-x i32 pos-y i32 width i32 height i32 left Color right Color] "DrawRectangleGradientH")
|
||||
(declare-c draw-rectangle-gradient-ex [rec Rectangle top-left Color bottom-left Color top-right Color bottom-right Color] "DrawRectangleGradientEx")
|
||||
(declare-c draw-triangle-fan [points (Ptr Vector2) point-count i32 color Color] "DrawTriangleFan")
|
||||
(declare-c draw-triangle-strip [points (Ptr Vector2) point-count i32 color Color] "DrawTriangleStrip")
|
||||
(declare-c draw-poly [center Vector2 sides i32 radius f32 rotation f32 color Color] "DrawPoly")
|
||||
(declare-c draw-poly-lines [center Vector2 sides i32 radius f32 rotation f32 color Color] "DrawPolyLines")
|
||||
(declare-c draw-poly-lines-ex [center Vector2 sides i32 radius f32 rotation f32 line-thick f32 color Color] "DrawPolyLinesEx")
|
||||
(declare-c draw-spline-linear [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineLinear")
|
||||
(declare-c draw-spline-basis [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineBasis")
|
||||
(declare-c draw-spline-catmull-rom [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineCatmullRom")
|
||||
(declare-c draw-spline-bezier-quadratic [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineBezierQuadratic")
|
||||
(declare-c draw-spline-bezier-cubic [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineBezierCubic")
|
||||
(declare-c draw-spline-segment-linear [p-1 Vector2 p-2 Vector2 thick f32 color Color] "DrawSplineSegmentLinear")
|
||||
(declare-c draw-spline-segment-basis [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 thick f32 color Color] "DrawSplineSegmentBasis")
|
||||
(declare-c draw-spline-segment-catmull-rom [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 thick f32 color Color] "DrawSplineSegmentCatmullRom")
|
||||
(declare-c draw-spline-segment-bezier-quadratic [p-1 Vector2 c-2 Vector2 p-3 Vector2 thick f32 color Color] "DrawSplineSegmentBezierQuadratic")
|
||||
(declare-c draw-spline-segment-bezier-cubic [p-1 Vector2 c-2 Vector2 c-3 Vector2 p-4 Vector2 thick f32 color Color] "DrawSplineSegmentBezierCubic")
|
||||
(declare-c get-spline-point-linear [start-pos Vector2 end-pos Vector2 t f32] Vector2 "GetSplinePointLinear")
|
||||
(declare-c get-spline-point-basis [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 t f32] Vector2 "GetSplinePointBasis")
|
||||
(declare-c get-spline-point-catmull-rom [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 t f32] Vector2 "GetSplinePointCatmullRom")
|
||||
(declare-c get-spline-point-bezier-quad [p-1 Vector2 c-2 Vector2 p-3 Vector2 t f32] Vector2 "GetSplinePointBezierQuad")
|
||||
(declare-c get-spline-point-bezier-cubic [p-1 Vector2 c-2 Vector2 c-3 Vector2 p-4 Vector2 t f32] Vector2 "GetSplinePointBezierCubic")
|
||||
(declare-c load-image-raw [file-name string width i32 height i32 format i32 header-size i32] Image "LoadImageRaw")
|
||||
(declare-c load-image-anim [file-name string frames (Ptr i32)] Image "LoadImageAnim")
|
||||
(declare-c load-image-anim-from-memory [file-type string file-data (Ptr u8) data-size i32 frames (Ptr i32)] Image "LoadImageAnimFromMemory")
|
||||
(declare-c load-image-from-texture [texture Texture2D] Image "LoadImageFromTexture")
|
||||
(declare-c load-image-from-screen [] Image "LoadImageFromScreen")
|
||||
(declare-c export-image-to-memory [image Image file-type string file-size (Ptr i32)] (Ptr u8) "ExportImageToMemory")
|
||||
(declare-c export-image-as-code [image Image file-name string] bool "ExportImageAsCode")
|
||||
(declare-c gen-image-gradient-linear [width i32 height i32 direction i32 start Color end Color] Image "GenImageGradientLinear")
|
||||
(declare-c gen-image-gradient-radial [width i32 height i32 density f32 inner Color outer Color] Image "GenImageGradientRadial")
|
||||
(declare-c gen-image-gradient-square [width i32 height i32 density f32 inner Color outer Color] Image "GenImageGradientSquare")
|
||||
(declare-c gen-image-checked [width i32 height i32 checks-x i32 checks-y i32 col-1 Color col-2 Color] Image "GenImageChecked")
|
||||
(declare-c gen-image-white-noise [width i32 height i32 factor f32] Image "GenImageWhiteNoise")
|
||||
(declare-c gen-image-perlin-noise [width i32 height i32 offset-x i32 offset-y i32 scale f32] Image "GenImagePerlinNoise")
|
||||
(declare-c gen-image-cellular [width i32 height i32 tile-size i32] Image "GenImageCellular")
|
||||
(declare-c gen-image-text [width i32 height i32 text string] Image "GenImageText")
|
||||
(declare-c image-copy [image Image] Image "ImageCopy")
|
||||
(declare-c image-from-channel [image Image selected-channel i32] Image "ImageFromChannel")
|
||||
(declare-c image-text [text string font-size i32 color Color] Image "ImageText")
|
||||
(declare-c image-text-ex [font Font text string font-size f32 spacing f32 tint Color] Image "ImageTextEx")
|
||||
(declare-c image-format [image (Ptr Image) new-format i32] "ImageFormat")
|
||||
(declare-c image-to-pot [image (Ptr Image) fill Color] "ImageToPOT")
|
||||
(declare-c image-alpha-crop [image (Ptr Image) threshold f32] "ImageAlphaCrop")
|
||||
(declare-c image-alpha-clear [image (Ptr Image) color Color threshold f32] "ImageAlphaClear")
|
||||
(declare-c image-alpha-mask [image (Ptr Image) alpha-mask Image] "ImageAlphaMask")
|
||||
(declare-c image-alpha-premultiply [image (Ptr Image)] "ImageAlphaPremultiply")
|
||||
(declare-c image-blur-gaussian [image (Ptr Image) blur-size i32] "ImageBlurGaussian")
|
||||
(declare-c image-kernel-convolution [image (Ptr Image) kernel (Ptr f32) kernel-size i32] "ImageKernelConvolution")
|
||||
(declare-c image-resize-canvas [image (Ptr Image) new-width i32 new-height i32 offset-x i32 offset-y i32 fill Color] "ImageResizeCanvas")
|
||||
(declare-c image-mipmaps [image (Ptr Image)] "ImageMipmaps")
|
||||
(declare-c image-dither [image (Ptr Image) r-bpp i32 g-bpp i32 b-bpp i32 a-bpp i32] "ImageDither")
|
||||
(declare-c image-rotate [image (Ptr Image) degrees i32] "ImageRotate")
|
||||
(declare-c image-rotate-cw [image (Ptr Image)] "ImageRotateCW")
|
||||
(declare-c image-rotate-ccw [image (Ptr Image)] "ImageRotateCCW")
|
||||
(declare-c image-color-tint [image (Ptr Image) color Color] "ImageColorTint")
|
||||
(declare-c image-color-invert [image (Ptr Image)] "ImageColorInvert")
|
||||
(declare-c image-color-grayscale [image (Ptr Image)] "ImageColorGrayscale")
|
||||
(declare-c image-color-contrast [image (Ptr Image) contrast f32] "ImageColorContrast")
|
||||
(declare-c image-color-brightness [image (Ptr Image) brightness i32] "ImageColorBrightness")
|
||||
(declare-c image-color-replace [image (Ptr Image) color Color replace Color] "ImageColorReplace")
|
||||
(declare-c load-image-colors [image Image] (Ptr Color) "LoadImageColors")
|
||||
(declare-c load-image-palette [image Image max-palette-size i32 color-count (Ptr i32)] (Ptr Color) "LoadImagePalette")
|
||||
(declare-c unload-image-colors [colors (Ptr Color)] "UnloadImageColors")
|
||||
(declare-c unload-image-palette [colors (Ptr Color)] "UnloadImagePalette")
|
||||
(declare-c get-image-alpha-border [image Image threshold f32] Rectangle "GetImageAlphaBorder")
|
||||
(declare-c image-clear-background [dst (Ptr Image) color Color] "ImageClearBackground")
|
||||
(declare-c image-draw-pixel-v [dst (Ptr Image) position Vector2 color Color] "ImageDrawPixelV")
|
||||
(declare-c image-draw-line [dst (Ptr Image) start-pos-x i32 start-pos-y i32 end-pos-x i32 end-pos-y i32 color Color] "ImageDrawLine")
|
||||
(declare-c image-draw-line-v [dst (Ptr Image) start Vector2 end Vector2 color Color] "ImageDrawLineV")
|
||||
(declare-c image-draw-line-ex [dst (Ptr Image) start Vector2 end Vector2 thick i32 color Color] "ImageDrawLineEx")
|
||||
(declare-c image-draw-circle [dst (Ptr Image) center-x i32 center-y i32 radius i32 color Color] "ImageDrawCircle")
|
||||
(declare-c image-draw-circle-v [dst (Ptr Image) center Vector2 radius i32 color Color] "ImageDrawCircleV")
|
||||
(declare-c image-draw-circle-lines [dst (Ptr Image) center-x i32 center-y i32 radius i32 color Color] "ImageDrawCircleLines")
|
||||
(declare-c image-draw-circle-lines-v [dst (Ptr Image) center Vector2 radius i32 color Color] "ImageDrawCircleLinesV")
|
||||
(declare-c image-draw-rectangle [dst (Ptr Image) pos-x i32 pos-y i32 width i32 height i32 color Color] "ImageDrawRectangle")
|
||||
(declare-c image-draw-rectangle-v [dst (Ptr Image) position Vector2 size Vector2 color Color] "ImageDrawRectangleV")
|
||||
(declare-c image-draw-rectangle-rec [dst (Ptr Image) rec Rectangle color Color] "ImageDrawRectangleRec")
|
||||
(declare-c image-draw-rectangle-lines [dst (Ptr Image) rec Rectangle thick i32 color Color] "ImageDrawRectangleLines")
|
||||
(declare-c image-draw-triangle [dst (Ptr Image) v-1 Vector2 v-2 Vector2 v-3 Vector2 color Color] "ImageDrawTriangle")
|
||||
(declare-c image-draw-triangle-ex [dst (Ptr Image) v-1 Vector2 v-2 Vector2 v-3 Vector2 c-1 Color c-2 Color c-3 Color] "ImageDrawTriangleEx")
|
||||
(declare-c image-draw-triangle-lines [dst (Ptr Image) v-1 Vector2 v-2 Vector2 v-3 Vector2 color Color] "ImageDrawTriangleLines")
|
||||
(declare-c image-draw-triangle-fan [dst (Ptr Image) points (Ptr Vector2) point-count i32 color Color] "ImageDrawTriangleFan")
|
||||
(declare-c image-draw-triangle-strip [dst (Ptr Image) points (Ptr Vector2) point-count i32 color Color] "ImageDrawTriangleStrip")
|
||||
(declare-c image-draw [dst (Ptr Image) src Image src-rec Rectangle dst-rec Rectangle tint Color] "ImageDraw")
|
||||
(declare-c image-draw-text [dst (Ptr Image) text string pos-x i32 pos-y i32 font-size i32 color Color] "ImageDrawText")
|
||||
(declare-c image-draw-text-ex [dst (Ptr Image) font Font text string position Vector2 font-size f32 spacing f32 tint Color] "ImageDrawTextEx")
|
||||
(declare-c load-texture-cubemap [image Image layout i32] Texture2D "LoadTextureCubemap")
|
||||
(declare-c update-texture [texture Texture2D pixels (Ptr u8)] "UpdateTexture")
|
||||
(declare-c update-texture-rec [texture Texture2D rec Rectangle pixels (Ptr u8)] "UpdateTextureRec")
|
||||
(declare-c gen-texture-mipmaps [texture (Ptr Texture2D)] "GenTextureMipmaps")
|
||||
(declare-c set-texture-filter [texture Texture2D filter i32] "SetTextureFilter")
|
||||
(declare-c set-texture-wrap [texture Texture2D wrap i32] "SetTextureWrap")
|
||||
(declare-c color-is-equal [col-1 Color col-2 Color] bool "ColorIsEqual")
|
||||
(declare-c color-to-int [color Color] i32 "ColorToInt")
|
||||
(declare-c color-from-hsv [hue f32 saturation f32 value f32] Color "ColorFromHSV")
|
||||
(declare-c color-tint [color Color tint Color] Color "ColorTint")
|
||||
(declare-c color-brightness [color Color factor f32] Color "ColorBrightness")
|
||||
(declare-c color-contrast [color Color contrast f32] Color "ColorContrast")
|
||||
(declare-c color-alpha [color Color alpha f32] Color "ColorAlpha")
|
||||
(declare-c color-alpha-blend [dst Color src Color tint Color] Color "ColorAlphaBlend")
|
||||
(declare-c color-lerp [color-1 Color color-2 Color factor f32] Color "ColorLerp")
|
||||
(declare-c get-pixel-color [src-ptr (Ptr u8) format i32] Color "GetPixelColor")
|
||||
(declare-c set-pixel-color [dst-ptr (Ptr u8) color Color format i32] "SetPixelColor")
|
||||
(declare-c get-pixel-data-size [width i32 height i32 format i32] i32 "GetPixelDataSize")
|
||||
(declare-c load-font-from-image [image Image key Color first-char i32] Font "LoadFontFromImage")
|
||||
(declare-c load-font-from-memory [file-type string file-data (Ptr u8) data-size i32 font-size i32 codepoints (Ptr i32) codepoint-count i32] Font "LoadFontFromMemory")
|
||||
(declare-c load-font-data [file-data (Ptr u8) data-size i32 font-size i32 codepoints (Ptr i32) codepoint-count i32 type i32] (Ptr GlyphInfo) "LoadFontData")
|
||||
(declare-c gen-image-font-atlas [glyphs (Ptr GlyphInfo) glyph-recs (Ptr (Ptr Rectangle)) glyph-count i32 font-size i32 padding i32 pack-method i32] Image "GenImageFontAtlas")
|
||||
(declare-c unload-font-data [glyphs (Ptr GlyphInfo) glyph-count i32] "UnloadFontData")
|
||||
(declare-c export-font-as-code [font Font file-name string] bool "ExportFontAsCode")
|
||||
(declare-c draw-text-pro [font Font text string position Vector2 origin Vector2 rotation f32 font-size f32 spacing f32 tint Color] "DrawTextPro")
|
||||
(declare-c draw-text-codepoints [font Font codepoints (Ptr i32) codepoint-count i32 position Vector2 font-size f32 spacing f32 tint Color] "DrawTextCodepoints")
|
||||
(declare-c set-text-line-spacing [spacing i32] "SetTextLineSpacing")
|
||||
(declare-c load-codepoints [text string count (Ptr i32)] (Ptr i32) "LoadCodepoints")
|
||||
(declare-c unload-codepoints [codepoints (Ptr i32)] "UnloadCodepoints")
|
||||
(declare-c get-codepoint-count [text string] i32 "GetCodepointCount")
|
||||
(declare-c get-codepoint [text string codepoint-size (Ptr i32)] i32 "GetCodepoint")
|
||||
(declare-c get-codepoint-next [text string codepoint-size (Ptr i32)] i32 "GetCodepointNext")
|
||||
(declare-c get-codepoint-previous [text string codepoint-size (Ptr i32)] i32 "GetCodepointPrevious")
|
||||
(declare-c text-is-equal [text-1 string text-2 string] bool "TextIsEqual")
|
||||
(declare-c text-length [text string] u32 "TextLength")
|
||||
(declare-c text-split [text string delimiter i8 count (Ptr i32)] (Ptr (Ptr i8)) "TextSplit")
|
||||
(declare-c text-find-index [text string find string] i32 "TextFindIndex")
|
||||
(declare-c text-to-integer [text string] i32 "TextToInteger")
|
||||
(declare-c text-to-float [text string] f32 "TextToFloat")
|
||||
(declare-c draw-grid [slices i32 spacing f32] "DrawGrid")
|
||||
(declare-c load-wave-from-memory [file-type string file-data (Ptr u8) data-size i32] Wave "LoadWaveFromMemory")
|
||||
(declare-c update-sound [sound Sound data (Ptr u8) sample-count i32] "UpdateSound")
|
||||
(declare-c export-wave-as-code [wave Wave file-name string] bool "ExportWaveAsCode")
|
||||
(declare-c load-music-stream-from-memory [file-type string data (Ptr u8) data-size i32] Music "LoadMusicStreamFromMemory")
|
||||
(declare-c load-audio-stream [sample-rate u32 sample-size u32 channels u32] AudioStream "LoadAudioStream")
|
||||
(declare-c audio-stream-valid? [stream AudioStream] bool "IsAudioStreamValid")
|
||||
(declare-c unload-audio-stream [stream AudioStream] "UnloadAudioStream")
|
||||
(declare-c update-audio-stream [stream AudioStream data (Ptr u8) frame-count i32] "UpdateAudioStream")
|
||||
(declare-c audio-stream-processed? [stream AudioStream] bool "IsAudioStreamProcessed")
|
||||
(declare-c play-audio-stream [stream AudioStream] "PlayAudioStream")
|
||||
(declare-c pause-audio-stream [stream AudioStream] "PauseAudioStream")
|
||||
(declare-c resume-audio-stream [stream AudioStream] "ResumeAudioStream")
|
||||
(declare-c audio-stream-playing? [stream AudioStream] bool "IsAudioStreamPlaying")
|
||||
(declare-c stop-audio-stream [stream AudioStream] "StopAudioStream")
|
||||
(declare-c set-audio-stream-volume [stream AudioStream volume f32] "SetAudioStreamVolume")
|
||||
(declare-c set-audio-stream-pitch [stream AudioStream pitch f32] "SetAudioStreamPitch")
|
||||
(declare-c set-audio-stream-pan [stream AudioStream pan f32] "SetAudioStreamPan")
|
||||
(declare-c set-audio-stream-buffer-size-default [size i32] "SetAudioStreamBufferSizeDefault")
|
||||
53
vendor/raylib/headers
vendored
53
vendor/raylib/headers
vendored
@ -4,29 +4,52 @@
|
||||
# means "if it is there" — an optional line with nothing behind it is simply
|
||||
# not read.
|
||||
#
|
||||
# Why this exists. The declare-c lines in raylib.flan were transcribed by
|
||||
# hand from raylib's documentation, and until now nothing could check that
|
||||
# any of them matched the real function — BUILT.md records that as trusted
|
||||
# rather than guaranteed. Point this at raylib's own header and the compiler
|
||||
# reads the signatures instead: every hand-written line is compared against
|
||||
# the library's, every defstruct against the header's record, and any raylib
|
||||
# function the package has not bound becomes available under its own name.
|
||||
# What this is for, now that the bindings are committed. generated.flan holds
|
||||
# every declaration the importer produced, in the repository, so a build needs
|
||||
# libraylib linkable and no header at all. This line is read by two things:
|
||||
#
|
||||
# Why it is optional. A build needs libraylib linkable and *not* raylib-devel
|
||||
# installed, which is a property worth keeping; requiring a header would take
|
||||
# it from everyone to give the check to whoever has one. So the default build
|
||||
# is unchanged and this is opt-in, the same shape as ${FLAN_RAYLIB_WEB} in
|
||||
# `link`.
|
||||
# 1. `flan generate-c vendor/raylib`, which is the only way generated.flan
|
||||
# is written. It reads the header named here, compares the package
|
||||
# against it, and refuses to write when they disagree — so it is not
|
||||
# possible to regenerate the bindings without comparing them to the
|
||||
# library they claim to bind.
|
||||
#
|
||||
# 2. an ordinary build, when the variable happens to be set. Every C symbol
|
||||
# is bound already — by hand in raylib.flan or by generation in
|
||||
# generated.flan — so the importer generates nothing and the header read
|
||||
# is purely the check. It runs over all 425 declarations rather than the
|
||||
# 172 hand-written ones, because the generated file is a package file like
|
||||
# any other and is checked like one — though only the hand-written ones can
|
||||
# actually disagree, since the generated half came out of this header and
|
||||
# agrees with it by construction. That is also why the hand-written lines
|
||||
# were kept rather than replaced by generated ones.
|
||||
#
|
||||
# Why it is still optional. A build needs libraylib linkable and *not*
|
||||
# raylib-devel installed, which is a property worth keeping; requiring a header
|
||||
# would take it from everyone to give the check to whoever has one. Before the
|
||||
# bindings were committed this marker also decided how many bindings a build
|
||||
# got, which was the real cost of it being opt-in; it no longer decides that,
|
||||
# and all it now withholds is a check that regeneration has already run once.
|
||||
# Same shape as ${FLAN_RAYLIB_WEB} in `link`, and for the same reason.
|
||||
#
|
||||
# The version must match the shared library `link` names — 5.5, libraylib.so.550.
|
||||
# Reading one version's header while linking another's library is exactly the
|
||||
# silent disagreement this exists to prevent, and `flan import-c` will say so:
|
||||
# against a 5.1-dev header it reports ten differences that are all real.
|
||||
# silent disagreement this exists to prevent, and it is caught rather than
|
||||
# described: against a 5.1-dev header, regeneration reports ten differences
|
||||
# that are all real and writes nothing.
|
||||
#
|
||||
# export FLAN_RAYLIB_H=/path/to/raylib-5.5/src/raylib.h
|
||||
#
|
||||
# To see what it would do without building anything:
|
||||
# vendor/raylib/build-web.sh already clones that exact tag to build the browser
|
||||
# archive, so a tree that has built for web has the matching header at
|
||||
# vendor/raylib/web/raylib-5.5/src/raylib.h.
|
||||
#
|
||||
# To see what regeneration would produce without writing anything:
|
||||
#
|
||||
# flan import-c $FLAN_RAYLIB_H vendor/raylib/raylib.flan
|
||||
#
|
||||
# What shapes the generated half — which functions are skipped, and what they
|
||||
# are called — is `bindings` beside this file. See its comments for why a
|
||||
# committed generated file needs a config at all.
|
||||
#
|
||||
?${FLAN_RAYLIB_H}
|
||||
|
||||
@ -1140,15 +1140,77 @@ void flan_shim_get_mouse_position_5ad0e205(flan_ty_Vector2_1bebc5ae *out) {
|
||||
*out = GetMousePosition();
|
||||
}</code></pre>
|
||||
|
||||
<p>No library header is read, deliberately, so a build needs the shared library to be
|
||||
linkable and not the <code>-devel</code> package to be installed.
|
||||
<p>No library header is read during a build, deliberately, so a build needs the shared
|
||||
library to be linkable and not the <code>-devel</code> package to be installed.
|
||||
<strong>Guaranteed:</strong> the C typedef
|
||||
and the Flan struct come from the same <code>defstruct</code>, so they cannot disagree,
|
||||
and clang type-checks the wrapper against the generated prototype.
|
||||
<strong>Trusted:</strong> that the <code>defstruct</code> matches the library's real
|
||||
struct, and that the <code>declare-c</code> signature is the function's real signature.
|
||||
A scalar's width now carries ABI weight — <code>f64</code> where the library says
|
||||
<code>float</code> emits <code>double</code>, and the library reads garbage.</p>
|
||||
<code>float</code> emits <code>double</code>, and the library reads garbage. That
|
||||
trusted half is what the generator below checks.</p>
|
||||
|
||||
<h3>Generated bindings, committed</h3>
|
||||
|
||||
<p>Writing a binding per function by hand does not scale past the ones a program
|
||||
happens to call, so most of raylib's package is not written by hand. A package
|
||||
directory may carry a <code>headers</code> file naming the library's own C header;
|
||||
<code>flan generate-c <package-dir></code> reads it with
|
||||
<code>clang -Xclang -ast-dump=json</code>, turns every function it can represent into
|
||||
the same <code>declare-c</code> line a person would have written, and writes them to
|
||||
<code>generated.flan</code> in the package — which is <em>committed</em>.</p>
|
||||
|
||||
<pre><code class="sh">$ export FLAN_RAYLIB_H=/path/to/raylib-5.5/src/raylib.h
|
||||
$ flan generate-c vendor/raylib
|
||||
wrote vendor/raylib/generated.flan: 253 declarations, 156 refused, of 581 functions.
|
||||
Every defstruct and every hand-written declare-c agrees with it.</code></pre>
|
||||
|
||||
<p>Committing the output rather than generating at build time is what keeps the
|
||||
no-header property honest: the declarations are in the repository, so every build gets
|
||||
all of them, they are greppable, and they show up in a diff when the library moves. The
|
||||
argument is not caching — the clang dump is already cached on disk and in memory.</p>
|
||||
|
||||
<p><strong>Regeneration is the check.</strong> The cost of committing the output is that
|
||||
nothing compares the bindings against reality on every build any more, so the one
|
||||
function that writes the file compares first and <em>refuses to write</em> when the
|
||||
package and the header disagree: every <code>defstruct</code> against the header's
|
||||
record, and every hand-written <code>declare-c</code> against the header's signature.
|
||||
Pointed at a raylib 5.1-dev header while the package is written for 5.5, it reports ten
|
||||
real differences and writes nothing — which is exactly the silent version skew a
|
||||
generated file would otherwise bake in and make look reviewed.</p>
|
||||
|
||||
<p>This is also why the hand-written bindings are kept rather than replaced by generated
|
||||
ones. Everything the generator emits agrees with the header by construction, so diffing
|
||||
generated output against the header it came from proves nothing; the hand-written lines
|
||||
were transcribed by a person, so they are the only declarations a header can actually
|
||||
contradict. All ten of those differences came from them.</p>
|
||||
|
||||
<p>A committed generated file cannot be hand-corrected — the next regeneration destroys
|
||||
the edit without telling anybody — so the corrections live in a <code>bindings</code>
|
||||
file beside <code>headers</code>, which is read <em>while</em> the declarations are made.
|
||||
Two directives:</p>
|
||||
|
||||
<pre><code class="sh"># raylib's own malloc/realloc/free, which would be a second untracked heap
|
||||
# behind three innocuous Flan names.
|
||||
exclude Mem*
|
||||
|
||||
# The kebab rule gives is-window-ready. Lisp spells a predicate with a ?.
|
||||
name IsWindowReady window-ready?</code></pre>
|
||||
|
||||
<p>An excluded function still says it was excluded rather than going quiet, and a name
|
||||
override changes only the Flan face — the C symbol is kept verbatim in the declaration,
|
||||
so it is still what is called and still what the check compares. Renaming is also the
|
||||
way out of a collision: <code>Spin2D</code> and <code>spin2d</code> both kebab to
|
||||
<code>spin-2d</code>, so neither takes the name, because which one won would otherwise
|
||||
depend on the order the header happens to declare them in.</p>
|
||||
|
||||
<p>Anything neither directive can express is a hand-written <code>declare-c</code> in the
|
||||
package's own source, which wins over the generated file and is left alone by the
|
||||
generator. That is the escape hatch for a signature the importer gets wrong and for a
|
||||
Flan face the header cannot describe — raylib keeps two, each a raw binding wrapped by a
|
||||
Flan function of the same name, one taking a slice and one answering with an
|
||||
<code>Option</code>.</p>
|
||||
|
||||
<p>Everything the boundary cannot represent is refused by name with the reason, rather
|
||||
than half-supported: an <code>Option</code>, a union, a fixed array, a map, a returned
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user