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:
to mark out parameters, using function_name.parameter is_out="1"
to mark ref parameters, using function_name.parameter is_ref="1"
to not implicitly pass length for type[] array parameters to a function using function_name no_array_length="1"
to mark parameter where the ownership is transfered (#), using function_name.parameter transfer_ownership="1"
to mark function where the result ownership is transfered, using function_name transfer_ownership="1"
to mark structures as value types, using TypeName is_value_type="1"
to mark (usually string) fields of structures to be non-weak, use TypeName.field_name weak="0"
- to hide data types, methods, signals and properties, using:
TypeName hidden="1"
TypeName:property_name hidden="1"
TypeName::signal_name hidden="1"
type_name_method hidden="1"
- to change the name of types and methods, using:
TypeName name="int"
TypeName name="Datalist" namespace="GLib"
type_name_property name="type_name_get_property"
to mark parameters nullable, using function_name.parameter nullable="1"
to mark that a method has variable argument list, using original_C_function_name ellipsis="1"
to restrict a methods return value to contain only a certain type, using function_name type_arguments="string"
to mark that an enum is a GError domain: enum_name errordomain="1"
to mark that function should not throw an exception, but use an out parameter instead: function_name throws="0"
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.
