Generate usable D-Bus C bindings with gdbus-codegen
You will turn a D-Bus introspection XML file into a C header and source file containing a GObject interface, a client proxy and a server-side skeleton. The examples use the gdbus-codegen shipped by Ubuntu's libglib2.0-dev-bin package, version 2.80.0-6ubuntu3.9 at the time of writing. Allow about 15 minutes if you already have the XML, or longer if you still need to define the interface.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
Install the development package on a machine where you are allowed to change packages. This is the only elevated action in this guide:
sudo apt install libglib2.0-dev-bin
gdbus-codegen --help | sed -n '1,24p'
The command reads D-Bus Introspection XML. It does not design an interface for you, start a bus, or connect to a service. Keep the XML in your source tree and treat the generated files as build outputs. The generated C includes GIO headers, so a project compiling the result also needs the GLib and GIO development files.
Checkpoint
Continue when gdbus-codegen --help prints options including --generate-c-code, --c-namespace and --output-directory.
1. Create a small introspection file
Save this as demo.xml. The interface has one method, one signal and one read-write property. Method arguments need directions; signal arguments do not.
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.example.Greeter">
<method name="Hello">
<arg name="name" type="s" direction="in"/>
<arg name="reply" type="s" direction="out"/>
</method>
<signal name="Changed">
<arg name="value" type="s"/>
</signal>
<property name="Enabled" type="b" access="readwrite"/>
</interface>
</node>
Use a validator or your normal XML checks before generating code. A well-formed XML document can still describe an interface that does not match the service you intend to run. Names and signatures are the API: changing them changes the generated C functions and the wire contract.
2. Generate both C files in a dedicated directory
Create the output directory first, then run the generator from the project directory:
mkdir -p build/generated
gdbus-codegen --interface-prefix org.example. --c-namespace Example --generate-c-code greeter --output-directory build/generated demo.xml
Expected files are:
build/generated/greeter.c
build/generated/greeter.h
The prefix is removed before C names are calculated. Thus org.example.Greeter becomes the ExampleGreeter type, with lower-case function names such as example_greeter_get_type(). The namespace controls the generated C naming separately from the D-Bus name.
Checkpoint
Verify that the header contains the expected type and that the source includes the generated header:
grep -E 'ExampleGreeter|example_greeter' build/generated/greeter.h | head
grep -F '#include "greeter.h"' build/generated/greeter.c
--generate-c-code is marked deprecated by this installed manpage, but it remains the convenient one-command form for producing a matching pair. If your build policy avoids deprecated options, generate a single file at a time with --header --output build/generated/greeter.h demo.xml and --body --output build/generated/greeter.c demo.xml. Do not combine those single-file modes with --generate-c-code or --output-directory.
3. Compile-check the generated result
Generation succeeding only proves that the XML was accepted. Ask the compiler to parse the result as well:
cc -fsyntax-only $(pkg-config --cflags gio-2.0) -Ibuild/generated build/generated/greeter.c
A successful check returns status zero. Warnings from the generated code still deserve attention, especially if your build treats warnings as errors. A missing gio-2.0 package or header is a development-environment problem, not a D-Bus interface error. If the compiler reports a generated function or type you did not expect, inspect the XML names, argument directions and annotations before editing generated code.
When linking an application, use the corresponding library flags:
cc app.c build/generated/greeter.c $(pkg-config --cflags --libs gio-2.0) -Ibuild/generated -o greeter-app
The generated interface is not an implementation. Client code normally uses the generated proxy to call a remote object. Server code implements the generated interface or uses its skeleton, then exports it on a GDBusConnection. You still need a running service, an object path and a bus name before a real call can succeed.
4. Choose compatibility deliberately
Without a version option, the installed documentation guarantees compatibility with GLib versions from 2.30 upwards. Set the minimum only when the consumers of the generated code can accept the resulting API changes:
gdbus-codegen --glib-min-required 2.64 --glib-max-allowed 2.80 --c-namespace Example --generate-c-code greeter --output-directory build/generated demo.xml
At a minimum of 2.64, file-descriptor parameters get a GUnixFDList parameter and generated method calls gain flags and timeout arguments. Those are source-level changes for callers. Increasing the minimum can also break the API or ABI of a library that exposes generated types, so make it a project decision rather than copying the host GLib version.
The maximum defaults to the GLib version that provides the generator. It is a ceiling for generated dependencies, not a promise that every older system can run your whole application. The maximum must not be lower than the minimum.
Common traps and recovery
- Output in the wrong directory:
--output-directorydefaults to the current directory. Use an explicit build directory and remove only that generated directory when regenerating. Do not delete your XML or source tree to fix a naming problem. - Unexpected C names: check
--interface-prefix,--c-namespaceand anyorg.gtk.GDBus.C.Nameannotations. Regenerate after correcting the inputs; never patch the generated files. - Conflicting output options: one-file modes use
--output. Pair generation uses--generate-c-codeand may use--output-directory. The manpage rejects incompatible combinations. - Accidentally replacing files: generation writes named outputs. Check
git diff -- build/generatedbefore adding them to a build. To undo a local generated change, regenerate from the intended XML or restore the generated files using your version control workflow. - Assuming documentation is code:
--generate-docbookand--generate-rstdescribe interfaces. They do not replace the C header and source modes, and their output names include each interface name.
Done means
demo.xmlor your project XML is reviewed and matches the service contract.greeter.candgreeter.hare in a disposable build directory.- The compiler syntax check exits zero with the intended GLib flags.
- The generated names are stable and the selected minimum and maximum GLib versions are documented in the build.
- Your project, rather than the generator, owns the service implementation, bus setup and runtime error handling.