Generating the FFI library

We have installed gir, set up our repo and have all the .gir files we need. Now let's work on the unsafe bindings of the -sys crate. Let's change into the directory of the sys crate.

cd pango/pango-sys

The Gir.toml file

The first step is to let gir know what to generate. This is what the Gir.toml file that we created inside the pango-sys folder is for. This file will not be replaced when we run gir again. The file is currently empty so let's add the following to it:

[options]
library = "Pango"
version = "1.0"
min_cfg_version = "1.0"
target_path = "."
girs_directories = ["../../gir-files/"]
work_mode = "sys"
single_version_file = true
  • library stands for the name of the library we want to generate.
  • version stands for the version of the library to be used.
  • min_cfg_version will be the minimum version supported by the generated bindings.
  • target_path stands for the location where the files will be generated.
  • girs_directories stands for the location of the .gir files.
  • work_mode stands for the mode gir is using. The options here are sys and normal.
  • single_version_file is a very useful option when you have a lot of generated files (like we'll have). Instead of generating the gir hash commit used for the generation in the header of all generated files, it'll just write it inside one file, removing git diff noise a lot.

You can find out the values for library and version by looking at the name of the .gir file of your library. In our case it is called Pango-1.0.gir. This tells us that the library is Pango and the version is 1.0. If you don't know what value to use for min_cfg_version, use the same as you use for version. If not all needed .gir files reside in ../../gir-files/, you can add the path to the other files by changing girs_directories. If for example you also have .gir files in the root of your project folder, change it to girs_directories = ["../../gir-files/", "../.."]. Because we are generating the unsafe bindings, we use the sys work mode.

Let's generate the sys crate now:

gir -o .

You should now see new files and a new folder.

  • build.rs
  • Cargo.toml
  • Gir.toml
  • src/lib.rs
  • tests/

Now let's try to build it:

cargo build

Surprise. It doesn't build at all and you should see a lot of errors. Well, that was expected. We need to add some dependencies in order to make it work. Have a look at the errors of the compiler to find out which are missing. In our example, the compiler throws the following errors:

#![allow(unused)]
fn main() {
use of undeclared crate or module `glib`
use of undeclared crate or module `gobject`
}

The dependencies need to be added to the external_libraries part. The names of the dependencies are the same as in the .gir files and not what the packages to install the libraries might be called. The compiler told us that the GLib and the GObject dependencies are missing. Let's update our Gir.toml file to fix it:

[options]
library = "Pango"
version = "1.0"
min_cfg_version = "1.0"
target_path = "."
girs_directories = ["../../gir-files/"]
work_mode = "sys"

external_libraries = [
    "GLib",
    "GObject",
]

If one of your .gir files changed or you want to use an updated version of gir to generate the code, there is no need to delete the Cargo.toml or the Cargo.lock files before you regenerate the code. Because we made some changes to the Gir.toml file, we have to run gir again. Changing the content of the external_libraries array means that additional dependencies have to be added. gir does this automatically for you, but only if there is no Cargo.toml and no Cargo.lock file present. Just remove the Cargo.* files and run gir again and the additional dependencies will be added. If you made any manual changes to the file, you would have to do these changes again. After regenerating the code, we build the crate to see if the errors are gone.

rm Cargo.*
gir -o .
cargo build

When executing the above commands, there should not be any errors and everything should work fine. Just to be sure everything was correctly generated, we can run some tests (graciously generated by gir as well):

cargo test

Normally, all tests passed. If you get an error when running those tests, it's very likely that the sys generation is invalid and/or incomplete.

The Gir.toml file

This file was automatically generated but it will not be replaced when we run gir again. Make sure to look at your Cargo.toml to optionally add more information to it. If there are any [features], you should try building and testing with these features activated as well. If you'd like, you can also set the default features. This can be useful for example if you want to always activate the newest version unless the user of the crate specifies an older version.

For our example, we now have a working sys crate containing all functions and objects definition. We are done here and can go back to the folder of the wrapper crate:

cd ..

Time to generate the high-level Rust API!