192 lines
6.8 KiB
Markdown
192 lines
6.8 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```toml
|
|
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
|
|
|
|
```bash
|
|
swift-gtk-gen --gir-file Gtk-4.0.gir --output ./Gtk --generate-all
|
|
```
|
|
|
|
### Generate specific types
|
|
|
|
```bash
|
|
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:
|
|
|
|
```swift
|
|
/// 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))
|
|
}
|
|
}
|
|
```
|
|
|
|
```swift
|
|
/// 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
|
|
|
|
```bash
|
|
# 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
|
|
```
|