Vala is designed to allow access to existing C libraries, especially GObject-based libraries, without the need for runtime bindings. Each to be used library requires a Vala API file at compile-time, containing the class and method declarations in Vala syntax. Vala currently comes with experimental bindings for GLib and GTK+. It's planned to provide generated bindings for the full GNOME Platform at a later stage.

To generate bindings for Vala get the source from Vala/Release. Configure vala with "--enable-vapigen" and build the system. You'll find the bindings generator in the vapigen subdirectory.

Generating the GI file (GObject Introspection)

The .gi files defines the objects, structs, consts, enums and functions in an abstract markup language. Since GLib and GObject follows a well defined naming conventions for object members, functions, etc., it's possible to generate this independent definitions from the source code. The resulting .gi file is then used to generate the .vapi files that defines the vala bindings.

A real-life binding tutorial

This is how we generate the vala bindings for poppler-glib, not that you cannot bind any C library so easily, only GLib based APIs can use the .gi format.

The following example demonstrates how to write a poppler-glib bindings for Vala, poppler has a glib wrapper, and we're going to use it to generate our own vala api.

$ vala-gen-introspect
Usage: vala-gen-introspect PKGNAME BASEPATH

vala-gen-introspect, the command responsible to generate our .gi interfaces, needs the package development files to be installed.

$ mkdir poppler-glib
$ ls
poppler-glib

Now, we proceed to generate the gi files:

$ vala-gen-introspect poppler-glib poppler-glib
cat: poppler-glib/poppler-glib.files: No such file or directory
cat: poppler-glib/poppler-glib.namespace: No such file or directory
ERROR: Need at least one header file.

Oops! What's going on here? Let's figure it out... two things are wrong here:

First, it seems that it lacks some filename (poppler-glib.files and poppler-glib.namespace) /!\ NOTE: The package name (PKGNAME argument) has to be the same as the pkg-config package file (.pc) of the original library.

vala-gen-introspect is not a so magical tool (yet), you need to provide a list of directories where the glib/gobject source files are stored inside the package and the namespace to populate those files (package.files and package.namespace), the namespace is the prefix used in the functions and struct names.

How do we figure this out? We need to take a look at the headers:

First we figure out the pkg-config package name:

$ ls /usr/lib/pkgconfig/ | grep poppler
poppler-cairo.pc
poppler-glib.pc
poppler.pc
poppler-splash.pc

So, poppler-glib is the one we were looking for since it's the library that we're going to bind. Now we need to figure out where the poppler-glib headers and libraries are to populate the poppler-glib.files, this file is going to have all the directories with gobject headers and libraries:

$ grep Cflags /usr/lib/pkgconfig/poppler-glib.pc
Cflags: -I${includedir}/poppler/glib
$ ls /usr/lib/libpoppler*.so
/usr/lib/libpoppler-glib.so  /usr/lib/libpoppler.so

$(prefix)/include/poppler/glib/ seems a good candidate, isn't it? Okay, now the namespace, that's pretty easy, we only need to look inside some header file, for example, glib/poppler.h:

$ grep 'typedef struct' /usr/include/poppler/glib/poppler.h
typedef struct _PopplerDocument    PopplerDocument;
typedef struct _PopplerIndexIter   PopplerIndexIter;
typedef struct _PopplerFontsIter   PopplerFontsIter;
...

So it seems that the prefix used as source namespace is Poppler, we're ready to generate the .gi file. Remember, we use now the package name for the .files and the .namespace file, and we have to include it after the path:

poppler-0.5.4 $ cd ..
$ echo "include/poppler/glib/" > poppler-glib/poppler-glib.files
$ echo "lib/libpoppler-glib.so" >> poppler-glib/poppler-glib.files
$ echo "Poppler" > poppler-glib/poppler-glib.namespace
$ vala-gen-introspect poppler-glib poppler-glib
Unknown symbol: GObject
Unknown symbol: GObject

$ ls poppler-glib/
poppler-glib.files  poppler-glib.gi  poppler-glib.namespace

So that's it, we've generated our first .gi file!

Generating the bindings

To convert this .gi file into a Vala API file use:

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

Tweaking the bindings generation

Exclude files from parsing

If ever you some header files are useless for gobject-introspection and can even trigger errors, add them to poppler-glib/poppler-glib.excludes and gen-introspect will ignore them.

Fix vapi generation

Sometimes it is necessary to fix 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.

The metadata file can be used:

add custom vala code

Sometimes you need to write extra vala code like helper and such. Use poppler-glib-custom.vala and append it to vapigen params.

Vala/Bindings (last edited 2009-02-24 21:56:53 by WilliamWhittle)