gir

GIR XML code generator for MoonBit GTK/GLib/GIO bindings

gir
codegen
gtk
gobject-introspection
moon add tonyfettes/gir@0.3.2
Download zip
Version
0.3.2
License
Apache-2.0
Last updated
16 days ago
Downloads
10
README

#GIR Code Generator

Generates MoonBit FFI bindings from GObject Introspection (GIR) XML files.

#Prerequisites

  • GIR files installed (typically at /usr/share/gir-1.0/)
  • clang-format for C formatting
  • moonfmt for MoonBit formatting

#Usage

Create a gir.json config file:

{ "gir": "Gtk-4.0", "docs_base_url": "https://docs.gtk.org/gtk4/", "deps": { "GObject-2.0": { "import": "@gobject", "classes": ["Object"] } }, "foreign-handles": { "cairo.Context": { "import": "@cairo", "moonbit-type": "Context", "c-type": "cairo_t", "header": "../cairo/moonbit_cairo.h", "wrapper-type": "MoonBitCairoContext", "unwrap-function": "moonbit_cairo_context_unwrap", "wrap-owned-function": "moonbit_cairo_context_wrap_owned", "wrap-borrowed-function": "moonbit_cairo_context_wrap_borrowed" } }, "includes": ["manual_widget.c"], "aliases": [], "classes": ["Widget", "Button", "Window", "Box", "Label", "Application"], "enums": ["Orientation", "Align"], "array-length-multipliers": { "gtk_example_get_ranges.ranges": 2 }, "boolean-out-results": { "default": "preserve", "overrides": { "gtk_example_parse_value": "option" } } }

includes lists hand-written C implementation files relative to the generated package directory. The generator adds them after all generated component .c files in the aggregate <pkg>.c native stub, and does not write or overwrite the listed files. Use this for ABI adapters or APIs that GIR cannot describe; keep the matching private extern "c" declarations in a hand-written .mbt file.

docs_base_url is the GI-DocGen root for the primary namespace. Generated MoonBit documentation links to the corresponding upstream type or member page; the generator does not copy documentation text from the GIR file. The field is optional for compatibility, and omitting it suppresses upstream documentation comments.

array-length-multipliers is an optional escape hatch for GIR APIs whose documented array length is a multiple of the referenced count parameter. Keys are <c-identifier>.<parameter-name>; omit the entry for the normal 1:1 case.

array-overrides can derive an array length from a restricted C function call or pointer path when GIR cannot express the relationship. Function and pointer-path lengths may specify an integer offset. Setting element-type on a pointer parameter also reinterprets it as an array when GIR presents only one element. If a caller-allocated out array already names a GIR length parameter, the generated public API hides that parameter and passes the derived length to the native function as its capacity. An override may also set direction to in, out, or inout when GIR's annotation is wrong. Inout by-value record arrays are copied into contiguous native storage, passed with their checked MoonBit length, and copied back into the original record wrappers after the call. Nullable by-value record input arrays preserve None as NULL; non-null arrays are capacity-checked and copied into contiguous native storage.

readonly-record-fields lists <C-record-type>.<field-name> keys whose GIR metadata says writable but whose native invariants require mutation through a method. The generated field getter remains public and the unsafe setter is omitted.

nullable-record-fields lists pointer-valued record fields whose GIR metadata cannot express that NULL is a valid value. Their private FFI getters use @glib.Nullable[T], while the public getters return T?. Other pointer fields and by-value embedded record fields retain their declared non-null API.

record-lifecycles supplies copy/free operations for selected records that GIR cannot expose as independently owned boxed types. Use lifecycle-c-type when the operations accept a common base record. For a by-value field embedded in an owner record, use owner-c-type and owner-field instead; the generator checks that the selected owner contains a by-value field with the declared record type before emitting the container-based copy/free wrapper.

receiver-anchored-returns lists C method identifiers whose returned record continues to reference its receiver even though GIR does not describe that lifetime. The generated wrapper retains the MoonBit receiver. It also releases the native return when GIR marks the return transfer-ownership="full", while a transfer-ownership="none" return remains a borrowed view.

boolean-out-results controls the public shape of callables that return a boolean status plus one or more out values. preserve retains the status in the result tuple; option emits T? (or a tuple Option for multiple outs), using None when the native status is false. The default is preserve when the section is omitted. Per-C-identifier overrides handle APIs where false still accompanies meaningful values, such as a clamped position. A GIR return-value marked skip="1" is always omitted from the public result while remaining part of the native ABI.

aliases selects the GIR aliases that should be emitted as public MoonBit type aliases in aliases.mbt. Selected aliases are preserved in generated callable, property, record-field, and constant signatures, while C bridge generation continues to use the alias target's ABI and ownership rules. Unselected aliases are expanded internally to their targets and do not appear in the public generated API.

foreign-handles maps a qualified GIR type owned by another non-GObject binding to its public MoonBit type and opaque C bridge. Input values use the configured unwrap function. Returned values use wrap-owned-function for transfer-ownership="full" and wrap-borrowed-function otherwise, so the generator never depends on the other binding's wrapper layout. Foreign handles are currently supported as ordinary input parameters and return values; out, callback, array, list, property, and async-return shapes are diagnosed as unsupported.

Selected GIR interfaces retain their declared prerequisites. The generated interface type implements each registered prerequisite trait, and a type-level factory whose GIR return is that prerequisite is exposed as the owning interface. The C bridge keeps the original return type, applies the interface's checked cast macro, and preserves the GIR transfer and nullability annotations.

GIR <virtual-method> entries are parsed separately from ordinary methods. For each supported entry, the generator emits one V<Class><Method> trait. For example, GtkButtonClass.clicked becomes VButtonClicked with the required method clicked(Self, Button). Self is the MoonBit state retained by a generated subclass and Button is the native object whose C class-function pointer was invoked. One trait per function pointer lets a subclass select clicked without also implementing activate. The optional GIR invoker name is kept in the GIR model; it is not treated as the virtual method's C symbol.

The ordinary binding generator deliberately stops at these one-method traits. gir/subclass reads the concrete #gobject.subclass declaration and selected impl V<Class><Method> declarations, then emits the class-slot assignments and C functions that forward those selected calls into MoonBit. Unsupported virtual method signatures produce explicit diagnostics, and no trait with a fabricated default implementation is emitted.

GObject Introspection treats Cairo as a built-in namespace, so installations commonly do not provide a standalone cairo-1.0.gir. The generator registers cairo.FontType as an external value type backed by @cairo.FontType; the Cairo package supplies its enum conversion helpers through cairo/enums.h.

Selected records without a GType are generated as copied values when their complete visible layout consists of scalars (including scalar aliases) and recursively embedded by-value records. Opaque, disguised, hidden, pointer, string, container, and cyclic layouts remain raw external tokens. Writable embedded value-record fields are assigned by value.

Readable record-array fields are generated as owned MoonBit Array snapshots when GIR supplies an explicit sibling integer length and the array elements are copyable value records. The generator does not expose a borrowed element view or an array-pointer setter.

Out arrays are generated when they are the callable's sole result and reference a 32-bit integer length. Callee-allocated arrays hide their out-length; caller-allocated arrays normally retain their input length in the public API while the C bridge allocates and releases the native buffer. A configured runtime length derives and hides that input instead. Primitive, boolean, enum/bitfield, object, and record elements are supported. Caller-allocated arrays must use transfer-ownership="none". Arrays without an explicit length and callables that combine an array with another result remain diagnostic skips.

Run the generator:

cd gir moon run src/main -- --config ../gtk/gir.json --output ../gtk --pkg gtk

#Legacy CLI mode

cd gir moon run src/main -- Gtk-4.0 --output ../gtk --pkg gtk --classes Widget,Button

#Options

FlagDescriptionDefault
--configPath to gir.json config file(none, uses legacy mode)
--output, -oOutput directorysrc/gtk
--pkg, -pPackage namegtk
--classes, -cComma-separated class whitelist (legacy mode)all classes
--gir-dirDirectory containing GIR files/usr/share/gir-1.0

#Output

For each class, three files are generated:

  • <type>.h -- C wrapper typedef with MOONBIT_GOBJECT_DEFINE macro
  • <type>.c -- C FFI stub implementations
  • <type>.mbt -- MoonBit extern declarations and public methods

Plus:
  • enums.mbt -- all selected enums
  • aliases.mbt -- all selected transparent type aliases
  • <pkg>.c -- aggregate include of generated components followed by the hand-written C sources configured in includes

#Diagnostics

The generator prints warnings to stderr for skipped functions and enums, with reasons:

warning: skipping GtkWidget.gtk_widget_set_cursor: nullable parameter 'cursor' (Gdk.Cursor) warning: skipping GtkWidget.gtk_widget_get_display: unsupported return type: Gdk.Display (GdkDisplay*) warning: skipping enum GtkFoo: member 'bar' has negative value -1

Skip reasons:
  • nullable parameter -- parameter marked nullable (partially supported for strings)
  • unsupported return type -- return type not registered in the generator
  • unsupported parameter -- parameter type not registered
  • unsupported out parameter -- out parameter without a supported result shape; allocated arrays require a referenced 32-bit length and must be the callable's sole result

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io