https://github.com/ppebb/cosmo-stub-generator
Generate a stub for any C library for use with Cosmopolitan
https://github.com/ppebb/cosmo-stub-generator
Last synced: about 1 year ago
JSON representation
Generate a stub for any C library for use with Cosmopolitan
- Host: GitHub
- URL: https://github.com/ppebb/cosmo-stub-generator
- Owner: ppebb
- License: mit
- Created: 2025-01-11T04:34:08.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-01-16T04:15:34.000Z (over 1 year ago)
- Last Synced: 2025-01-24T08:15:28.154Z (over 1 year ago)
- Language: Lua
- Homepage:
- Size: 588 KB
- Stars: 14
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# cosmo-stub-generator
This project uses libclang to parse the header files of any project and output
a stub for use with [cosmopolitan](https://github.com/jart/cosmopolitan).
## Generating Stubs
0. Have lua 5.4
1. Build libclang-lua (make sure to checkout submodules), following the
instructions at https://github.com/ppebb/libclang-lua.
2. Install [luafilesystem](https://lunarmodules.github.io/luafilesystem/)
3. Add a header file to `./specs` that `#include`s whichever headers you wish
to process
4. Add a lua file to `./specs` which returns a table in the following format,
with gobject as an example:
```lua
local utils = require("src.utils")
return {
-- The name of the stub, will output in ./stubs/name-stub
name = "gobject",
-- A list of filters (written as lua patterns) for the paths containing
-- your header files. If you don't have these, libclang will traverse
-- every header in the entire translation unit, and you'll end up with
-- things like time.h in your stub
filter = { "%/usr%/include%/glib%-2%.0%/gobject%/", "%/usr%/include%/glib%-2%.0%/glib%-object%.h%/" },
-- The header file you want to use from step 1, any path. The below will
-- resolve to ./specs/gobject_spec.h
hfile = utils.path_combine(utils.spec_path(), "gobject_spec.h"),
-- The shared object, the name is just used as the variable name internally
-- for the loaded so, fnames is a list of acceptable library names which will
-- be searched on program launch
so = { name = "gobject", fnames = { "libgobject-2.0.so", "libgobject-2.0-0.dll" } },
-- The directories for libclang to search for headers in, included using
-- the -isystem argument
search_dirs = {
"/usr/include/glib-2.0/",
"/usr/lib/glib-2.0/include/",
},
-- Headers to include in your generated stub
header_includes = {
"glib-object.h",
},
-- Optional, function bodies you can define to be included in the stub to
-- overwrite ones automatically generated. Use cases include variadic
-- functions which do not map correctly to an automatically located function
-- taking a va_list
explicit_function_bodies = {
-- From https://github.com/GNOME/glib/blob/83200855579964a20d3929f37a37431e4952d156/gobject/gobject.c#L2406
g_object_new = [[gpointer g_object_new(GType object_type, const gchar *first_property_name, ...) {
GObject *object;
va_list var_args;
/* short circuit for calls supplying no properties */
if (!first_property_name)
return stub_funcs.ptr_g_object_new_with_properties(object_type, 0, NULL, NULL);
va_start(var_args, first_property_name);
object = stub_funcs.ptr_g_object_new_valist(object_type, first_property_name, var_args);
va_end(var_args);
return object;
}]],
-- From https://github.com/GNOME/glib/blob/7129521966fa7c4cd876b2aa429f1c8d50290902/gobject/gsignal.c#L1399
g_signal_new = [[guint g_signal_new (const gchar *signal_name, GType itype, GSignalFlags signal_flags, guint class_offset, GSignalAccumulator accumulator, gpointer accu_data, GSignalCMarshaller c_marshaller, GType return_type, guint n_params, ...) {
va_list args;
guint signal_id;
g_return_val_if_fail(signal_name != NULL, 0);
va_start(args, n_params);
signal_id = stub_funcs.ptr_g_signal_new_valist(signal_name, itype, signal_flags,
class_offset ? stub_funcs.ptr_g_signal_type_cclosure_new(itype, class_offset): NULL,
accumulator, accu_data, c_marshaller, return_type, n_params, args);
va_end(args);
return signal_id;
}]],
},
}
```
Additional examples can be located in the `./specs` directory
5. `require` your lua spec in the `stubs` table in `./src/generate.lua`
6. Run `lua src/generate.lua path/to/clang/include`, path/to/clang/include should be somewhere like `/usr/lib/clang/19/include/`
## Included Stubs
This repository includes full[^1] stubs for
* GTK (and GSK, GDK)
* GLib
* GIO
* GObject,
* GModule
* GIRepository
* GLFW
* OpenGL
* SDL2
* SDL2_ttf
[^1]: Some variadic functions may be
missing, but their equivalents should exist.
## Using Stubs
The stubs should work out of the box, just copy the relevant stub directory
(alongside `./stubs/stub.c` and `./stubs/stub.h`) and then include them in your
project. If you don't want 12000 lines of C to appear, add them to
linguist-vendored (see `.gitattributes` at the root of this repository).
In your main function, make sure you initialize each stub on program launch and
close them on program load. The functions follow the naming convention
`initialize_soname()` and `close_soname()`, using the sonames provided in the
spec.
## Notes
Certain function attributes and keywords may not be handled
currently.`noreturn` functions alongside `volatile` and `restrict` parameters
are examples which are handled. For anything similar that is missing, open up
an issue or a PR.
Additionally, libclang resolves types such as `unsigned int` or `unsigned char`
to `UInt` and `UChar`, which must be manually handled. Refer to the function
`cxtype_name` in `./src/generate.lua`. I have not handled every case, so if you
encounter one not present then you can add another case there (PRs welcome!).