Generating a VAPI with GObject Introspection

Vala is designed to allow access to existing C libraries, especially GObject-based libraries, without the need for runtime bindings. Each library to be used requires a Vala API file (.vapi) at compile-time, containing the class and method declarations in Vala syntax.

To generate bindings for Vala using GObject introspection the vapigen tool is used. This is usually included with valac by the distributions, but can also be compiled from source. Get the Vala source from Release.

The binding generation requires several steps:

  • getting a GObject Introspection GIR file
  • using vapigen to generate the VAPI binding from the GIR file

  • tweaking the binding generation with metadata and custom code

See also UpstreamGuide, which will guide you to support Vala bindings from upstream projects (including automake integration).

For non-GLib libraries, see LegacyBindings.

Getting the GIR File (GObject Introspection)

The introspection file defines the objects, structures, constants, enumerations and functions in an abstract markup language. Since GLib and GObject follow a well-defined naming convention for object members, functions, etc., it's possible to generate these independent definitions from the source code. A .gir file is then used to generate the .vapi file that defines the Vala bindings.

A GIR file can be obtained from:

  • a distribution as part of the development files for the library
  • the source code by using g-ir-scanner to generate the GIR

You may come across documentation about generating a GIR from a typelib file, but GIR files generated in this way will have lost relevant information and lead to an awkward binding with Vala.

A GIR file can be generated using GObjectIntrospection method.

Use GObjectIntrospection tools to generate a .gir description file.

Generating the VAPI File

To convert the .gir file into a Vala API file use:

$ vapigen --library poppler-glib poppler-glib/poppler-glib.gir

If you are updating an officially maintained vala binding in the source code tree, you can go in the vapi directory and run:

$ ../vapigen/vapigen --library clutter-gtk-1.0 --vapidir=. --metadatadir=packages/clutter-gtk-1.0/ packages/clutter-gtk-1.0/clutter-gtk-1.0.gir
or just:
$ make clutter-gtk-1.0

Do not forget to include the packages needed by the library. If the library uses GTK+ and GConf, use:

$ vapigen --pkg gtk+-2.0 --pkg gconf-2.0 --library [...]

Otherwise you'll get errors like that, or an incomplete binding:

error: The type name `GLib.tkWidget' could not be found

Fixing VAPI Generation with Metadata

Sometimes it is necessary to fix up the generated VAPI file; for instance, vapigen might not identify out or ref parameters, or identify structures that should generally be put on the stack instead of allocated, and passed by reference to methods.

Instead of updating the VAPI file, and keeping it updated with every upstream API change, vapigen output can be tweaked with a .metadata file. For instance, in poppler-glib the poppler_page_get_size function has two out parameters, width and height; in order to create a valid Vala signature in our VAPI file, we need to add these lines inside the poppler-glib.metadata file:

poppler_page_get_size.width is_out="1"
poppler_page_get_size.height is_out="1"

Which translates to: "the width parameter of poppler_page_get_size is an out parameter" and "the height parameter of poppler_page_get_size is an out parameter".

Metadata files must have the same base name as the GIR, but instead of a gir extension they use metadata.

To get vapigen to pick up your metadata file, you must provide the name of the directory to look for it in:

vapigen \
    --library foo \
    --pkg bar-1.0 \
    --metadatadir ./metadata/ \

GObject Introspection and Vala support different things. Sometimes one not supporting something the other does is a bug, sometimes it is that one has made certain assumptions about APIs that the other does not. If there is a problem generating the VAPI then it is best to work through the problem in this order:

  1. Check the C source for missing GObject Introspection annotations, e.g. null
  2. Check the arguments to g-ir-scanner, e.g. C header files

  3. Add metadata for vapigen

For detailed information on the features and syntax of metadata files, see the Vala Manual section on GIR metadata format.

C Headers

Most libraries tend to install one header file, which will then include any additional headers. If the VAPI does not have the correct header filename then the best fix is to amened the generation process of the GIR. To include a C header filename in a GIR g-ir-scanner has the --c-include option. For example:

g-ir-scanner --c-include=example/example.h project_source.c

vapigen will then use this filename from the GIR.

Some libraries need multiple header files in the VAPI. Using --c-include multiple times will allow these to be included in the GIR and so included in the VAPI.

If it is not possible to amend the GIR generation then the header can be included using vapigen and metadata. This can be done for a namespace or for a type. For example:

PnpIds cheader_filename="libgnome-desktop/gnome-pnp-ids.h"

Duplicate Symbols

The single most common error seen the first time one tries to generate a VAPI is one about duplicate symbols. Vala has a single scope for methods, virtual methods, signals, and properties. Assuming that the signatures match, vapigen will automatically combine several of these into a single entity--the most extreme example of this is probably a virtual signal, which can combine a signal, virtual method, and method in one item. For example, GIO has the following in GLib.Application:

public virtual signal void activate ();

That said, some conflicts cannot be resolved automatically by vapigen and will require some metadata. The most common conflict is when a method, virtual method, or signal disagrees with another method, virtual method, or signal with the same name regarding arguments or return values. For example, ClutterActor has an event signal, which takes a single argument: a ClutterEvent intance. It also has an event method which takes two arguments: a ClutterEvent instance and a boolean. In this case, we resolve the conflict by renaming the method to "emit_event":

Actor.event#method name="emit_event"

Another common problem is when a symbol of a subclass has the same name as that of a base class but the signatures do not match. Depending on the situation, you can rename or skip one of the symbols (usually in the subclass).

Nested Namespaces

GIR does not support nested namespaces (bug #660879), but Vala does. If you prefer, you can just ignore this Vala feature, but some bindings can be quite a bit cleaner if we make use of it.

A good example of nested namespaces in Vala is moving the hundreds of keysmys in Clutter into a Clutter.Key namespace, allowing us to use Clutter.Key.Right instead of Clutter.KEY_Right. This is accomplished with a single line of metadata:

KEY_* skip=false name="KEY_(.+)" parent="Clutter.Key"

We can also use the same technique to group similar functions together, like for the GContentType family in GIO:

content_type_* parent="GLib.ContentType" name="content_type_(.*)"

Nullability of Return Values

GIR assumes all pointer return values are nullable ("allow-none" in G-I terminology) and does not provide a way to override this assumption (bug #660879). Vala, on the other hand, assumes return values are not nullable unless otherwise otherwise specified, and comparing a non-nullable value to null (e.g., to check for validity) will cause a warning. Luckily, making a value nullable is easy to do from a metadata file, as you can see from this example (for clutter_actor_get_parent):

Actor.get_parent nullable

Variadic Functions

GObject introspection does not currently support variadic methods. It actually generates all the information Vala needs to do so, but it will mark the function as introspectable="0", which is the same that happens when you add a "skip" annotation to the method. Therefore, in order to expose these functions in Vala, we need a simple annotation to un-skip the symbol. For example, this is how clutter_actor_animate is exposed from metadata:

Actor.animate skip=false

Ownership of Struct Fields

GObject introspection does not currently offer a way to specify whether or not fields contain an owned reference. It is therefore impossible for Vala to know whether or not it should ref or copy a value being assigned to this field. Again, this is easy to fix with metadata... using GDBusAnnotationInfo as an example:

DBusAnnotationInfo.*#field unowned=false

Virtual Methods Without Invokers

Some libraries contain virtual methods without emitters, which GObject introspection does not currently offer a way to annotate (bug #730480). Fixing these basically means adding any information that would normally go in annotations to the metadata.

Abstract/Virtual Distinction

Vala distinguishes between abstract and virtual methods (virtual methods do not need to be implemented by an class which implements the interface whereas abstract methods do require an implementation) while GIR does not. In order to mark a method as virtual instead of abstract, you could do something like this (from gtk_source_completion_proposal_equal):

CompletionProposal.equal#virtual_method virtual

Generic Types

GObject Introspection only supports a few different generic types, and that support is hard-coded and cannot currently be extended to other types which should be generic (bug #639908). For example, GDataList is a generic in Vala but is not supported as such by GObject Introspection, so the the following is necessary for soup_form_encode_datalist:

form_encode_datalist.form_data_set type_arguments="string"

GClosure Types

GIR does not provide a way to annotate the type of a callback (bug #636812) contained in a GClosure. Although this is not an error which will cause bindings to not be generated, the result is an API that is extremely difficult to use correctly. For example, you can provide the delegate type of clutter_binding_pool_install_closure from the metadata:

BindingPool.install_closure.closure type="owned BindingActionFunc"


GObject Introspection currently only handles inheritance for GObject-derived types (bug #560692). To get around it in metadata you can use "base_type":

Buffer base_type="Gst.MiniObject"

Asynchronous Finish Functions

GObject Introspection does not currently offer a way to annotate the relationship between an async function and its corresponding finish function (bug #623635). By default Vala will look for function with the same base name, but a "_finish" suffix, but you can point it to other functions in metadata using "finish_name":

Service.lookupv finish_name="secret_service_lookup_finish"


Since GObject Introspection is focused primarily on runtime bindings for languages such as Python and JavaScript, it ignores preprocessor macros. Although this decision makes sense for them (you can't dlsym(3) a macro), Vala is capable of utilizing macros. However, since no information on macros is included in the GIR the only way to expose macros is by adding them to a *-custom.vala file.

Fixing VAPI Generation with Custom Vala Code

Remember that thing about the world not being perfect? Well, a metadata file isn't always enough either. Sometimes you'll need the ability to inject custom Vala code into your VAPI. Technically, this file can have any name and there can be more than one per package, but the convention is to use the same file name and directory as the GIR followed by "-custom.vala". For instance, our Foo-1.0.gir might have a corresponding metadata file named Foo-1.0-custom.vala. Once you have your custom Vala file, simply include it in the argument list you pass to vapigen:

vapigen \
    --library foo-1.0 \
    --pkg bar-1.0 \
    --metadatadir ./metadata/ \
    Foo-1.0.gir \

No Generic Methods

Vala supports generic methods, such as g_object_get, while GObject Introspection does not. Unfortunately, metadata alone cannot currently resolve this issue--you will need to skip the method in metadata and recreate it in custom.vala.

A Note on the Deprecated GIDL Method

The traditional approach was to use vala-gen-introspect to generate .gi files. Then use vapigen to generate the VAPI. This method is now deprecated.

For the maintenance of existing bindings see the Vala Manual GIDL metadata format. This metadata format was used to control the generation of the VAPI from a .gi file.

Projects/Vala/Bindings (last edited 2017-08-07 20:21:50 by AlThomas)