This is highly experimental and likely not suitable for use.
brew install cmake git ninja python3 castxml
cmake -B ./build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build ./build
./build/bin/hello-world
./build/bin/bunny-mark
cd ./build/bin
./bunny-mark
./bunny-mark-c
bunny-mark-c
is the original bunny-mark example from raylib.
bunny-mark
is the same example, ported to typescript and slightly modified to compiled with shermes.
brew install python3 castxml
hermes/tools/ffigen/ffigen.py
uses castxml to generate an AST of the c/cpp code and emit function definitions for the headers that you tell it to parse.
If a function returns a struct or union you will also need to run ffigen cwrap in order to generate wrapper functions that turn the return param into an out param as the first argument.
You can limit the scope of the headers that ffigen will include, so that you don't pick up system headers, or other undesired headers from the include chain.
ffigen js ./raylib-ffi.h builtin,raylib.h,raymath.h > js_externs.js
ffigen cwrap ./raylib-ffi.h builtin,raylib.h,raymath.h > js_externs_cwrap.c
In order to get the raylib headers to parse properly you must ensure that the defines were present that raylib expects in order to use c99 stdbool, instead of some custom unsupported bool type. This is done in raylib-ffi.h
by defining __STDC_VERSION__
to 199901L
and __STDC__
to 1
. There may be a better way to do this moving forward, but I did not see anyway to past args through ffigen to castxml.
The generated cwrap externs require you to include the headers that you are generating externs for, otherwise you will get errors about missing types. This is because the cwrap externs are just wrappers around the original functions, and they need to know about the types that they are wrapping. Some creative use of cmake -E cat
to concatenate the headers into a single file is implemented in src/shared/CMakeLists.txt
to make this work.
You must allocate memory for the return value of the function, and pass it as the first argument to the cwrap function. The cwrap function will then copy the return value into the memory that you allocated.
const ptr = malloc(_sizeof_Font);
_LoadFont(ptr, "path/to/font.png");
Every malloc should be paired with a free. If you are allocating memory for a return value, you should free it after you are done with it. Once finalizers are implemented we can design some better memory lifetimes and access patterns wrapped up in a higher level API.