1
0
Fork 0
GIR-to-Swift binding generator for GTK 4/libadwaita and its GObject dependency stack (GLib, GObject, GIO, GDK, GSK, Pango, Cairo, Soup, GStreamer, GdkPixbuf). Modelled on gtk-rs/gir.
Find a file
2026-07-17 23:26:34 -04:00
configs Rearchitect generator around binding-plan layer with function generation 2026-07-17 14:43:54 -04:00
docs/skip-baseline/tier1 Complete filename/naming remediation with PascalCase convention 2026-07-17 22:48:16 -04:00
scripts Generate class methods and constructors with runtime smoke tests 2026-07-17 15:43:30 -04:00
smoke Expand runtime smoke tests across binding categories 2026-07-17 21:04:43 -04:00
Sources Remove underscore prefix from generated filenames 2026-07-17 23:26:34 -04:00
Tests Remove underscore prefix from generated filenames 2026-07-17 23:26:34 -04:00
.gitignore Initial commit: SwiftGtkGen GIR-to-Swift binding generator 2026-07-01 03:27:41 -04:00
.swift-format Initial commit: SwiftGtkGen GIR-to-Swift binding generator 2026-07-01 03:27:41 -04:00
Package.swift Refactor CodeGen.swift into CodeGen+Signal.swift and CodeGen+Scaffolding.swift 2026-07-01 17:27:42 -04:00
README.md Rename Config.toml to config.toml across all files 2026-07-01 14:06:08 -04:00

SwiftGtkGen — GIR-to-Swift Binding Generator

Reads GObject Introspection (.gir) XML files and produces low-level Swift wrapper libraries for GTK and its dependency stack. Each wrapper package is a self-contained SwiftPM project with a Clang module map for C interop and per-type Swift source files.

Usage

swift-gtk-gen --gir-file /usr/share/gir-1.0/Gtk-4.0.gir --output ./Gtk

Library name and version are derived automatically from the GIR file's <namespace> element. Override via config.toml (see below).

Arguments

Argument Description Example
--gir-file PATH Path to the .gir XML file to process (required). /usr/share/gir-1.0/Gtk-4.0.gir
--output DIR Output directory for generated files (default: .). ./Sources/GTK
--config-toml PATH Path to config.toml for per-type config (default: config.toml). Sources/GTK/config.toml
--namespace NAME Namespace to generate (for multi-namespace GIR files). Gtk
--list-output-files Print expected output file paths and exit.
--girs-dirs DIRS Comma-separated list of directories to search for .gir files. /usr/share/gir-1.0,vendor/gir-files
--external-libs LIBS Comma-separated external library names for link flags. Gdk,Gsk,Pango
--generate TYPES Comma-separated list of types to generate (e.g. Gtk.Widget,Gtk.Window). When combined with --generate-all, this is ignored. Gtk.Widget,Gtk.Align
--generate-all Generate all types found in the namespace. Default if --generate is omitted.
--manual TYPES Comma-separated types to mark as manually implemented (skipped during generation). Gtk.Buildable
--ignore TYPES Comma-separated types to skip entirely. Gtk.Test
--emit-single-file Emit a single concatenated Swift file instead of one file per type.
--help, -h Show help text.

config.toml

Per-type configuration can be specified via a TOML file. Library name and version are auto-extracted from the GIR namespace. Example:

target_directory = "Sources/GTK"
girs_directories = ["/usr/share/gir-1.0"]
external_libraries = ["Gdk-4.0", "Gsk-4.0"]

generate = ["Gtk.Widget", "Gtk.Window", "Gtk.Button"]
manual = ["Gtk.Buildable"]
ignore = ["Gtk.Test"]

[[objects]]
name = "Gtk.Widget"
concurrency = "mainActor"
visibility = "public"

[[objects]]
name = "Gtk.Align"
rename = "Alignment"

Generate all types

swift-gtk-gen --gir-file Gtk-4.0.gir --output ./Gtk --generate-all

Generate specific types

swift-gtk-gen --gir-file Gtk-4.0.gir --output ./Gtk \
  --generate Gtk.Widget,Gtk.Window,Gtk.Button,Gtk.Align

Output Structure

./Gtk/
├── Package.swift
├── Sources/
│   ├── CGtk/
│   │   ├── CGtk.h
│   │   └── module.modulemap
│   └── Gtk/
│       ├── Widget.swift
│       ├── Window.swift
│       ├── Button.swift
│       ├── Align.swift
│       └── ...
  • Package.swift — SwiftPM manifest with a systemLibrary target (for C interop via pkgConfig) and a Swift target for the generated wrappers.
  • Sources/C<Library>/ — Clang module map and umbrella header, derived from the GIR file's <c:include> and shared-library attributes.
  • Sources/<Library>/ — One .swift file per generated type (class, protocol, enum, OptionSet, callback typealias, free function).

Generated Code Example

The following is a sample of generated output for a subset of GTK types:

/// The base class for all widgets.
///
/// It manages the widget lifecycle, layout, states and style.
public final class Widget: GObject.InitiallyUnowned {
    let pointer: UnsafeMutableRawPointer

    public init(pointer: UnsafeMutableRawPointer) {
        g_object_ref_sink(pointer)
        self.pointer = pointer
    }

    deinit {
        g_object_unref(pointer)
    }

    public var halign: Align {
        get {
            var value = GValue()
            g_value_init(&value, G_TYPE_OBJECT)
            g_object_get_property(pointer, "halign", &value)
            let result = g_value_get_object(&value)
            g_value_unset(&value)
            return result
        }
        set {
            var value = GValue()
            g_value_init(&value, G_TYPE_OBJECT)
            g_value_set_object(&value, newValue)
            g_object_set_property(pointer, "halign", &value)
            g_value_unset(&value)
        }
    }

    public func show() {
        gtk_widget_show(pointer)
    }

    public func getVisible() -> Bool {
        return (gtk_widget_get_visible(pointer) != 0)
    }

    public func getParent() -> Widget? {
        return gtk_widget_get_parent(pointer).map { Widget(pointer: $0) }
    }

    public func connectDestroy(_ handler: @escaping () -> Void) -> Int {
        let boxed = Unmanaged.passRetained(handler as AnyObject).toOpaque()
        let callback: @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void =
            { (_, data) in
                let stored = Unmanaged<AnyObject>.fromOpaque(data!).takeUnretainedValue() as! () -> Void
                stored()
            }
        let destroy: @convention(c) (UnsafeMutableRawPointer?) -> Void = {
            Unmanaged<AnyObject>.fromOpaque($0!).release()
        }
        return Int(g_signal_connect_data(pointer, "destroy", callback, boxed, destroy, 0))
    }
}
/// stretch to fill all space, but align the baseline.
public enum Align: Int {
    case fill = 0
    case start = 1
    case end = 2
    case center = 3
    case baselineFill = 4
    case baseline = 4
    case baselineCenter = 5
}

What Gets Generated

GIR Type Swift Output
<class> final class with UnsafeMutableRawPointer storage, ref-counted init/deinit, GValue-based properties, C function-call methods, g_signal_connect_data signal handlers, convenience constructors
<interface> protocol with method requirements and property requirements
<enumeration> enum: Int with camelCase cases
<bitfield> struct: OptionSet with rawValue: Int and bit-shifted constants
<callback> typealias with @convention(c)
<function> public func wrapping the C function call with type conversion

Generated Package Buildability

The generated Package.swift includes a systemLibrary target with pkgConfig for the native library. The host system must have the corresponding development packages installed (e.g. libgtk-4-dev for Gtk, libglib2.0-dev for GLib) for the generated package to compile.

Development

# Build the generator
swift build

# Run the test suite
swift test

# Format generated code (uses .swift-format config)
swift format lint --configuration .swift-format path/to/generated/file.swift