GirToBac  0.2
Create FB Headers from GObject Introspection *.gir files
 All Classes Files Functions Variables Enumerations Enumerator Macros Pages
Tutorial

This project is about a new header generator for the FreeBasic (FB) programming language. It creates FB headers by scanning *.gir files.

*.gir files are generated by the GObject-Introspection (GI) technology. GNOME libraries based on GLib/GObject use these technology to support polyglot application development. This means a library developed in C will be used in higher level programming languages like JavaScript, Python, Vala or others. The GI system helps to auto-generate bindings for these high-level programming languages.

GirToBac is the first attempt to connect FreeBasic (FB) to these supports. It reads the GI *.gir files (generated during compilation of the C library) and transforms their context to FB headers. Some manual control is needed to adapt the output. This is done by a configuration file (one for each *.gir file). These configuration files can be re-used for up-dated *.gir files further on.

How does this work?

GirToBac is a command line tool, being shipped as FB source code. The code needs to get compiled first. The executable expects one parameter, the name of the *.gir file (maybe prepended by its path). It loads this file and additionally a configuration file with the same base-name and the suffix .GirToBac (if present in the current folder). Then the context of the *.gir file gets translated to FB source by respecting the rules in the configuration file. The result gets written to the current folder as an FB header file named after the basename and with suffix .bi (overwriting existent files). In best case the header contains all information to use the library in FB source code.

The headers are designed to be stored in one directory, one header for each library (= dll binary). Each header loads the dependencies its library is based on, so the user need not care about chaining-up. Just load the highest level header and your source contains all dependencies (the binaries must have been installed before).

To generate an FB header the *.gir files gets scanned several times:

  1. First, some constants get #DEFINEd and TYPE aliases and ENUM blocks gets translated.
  2. In the second pass UNIONs, TYPEs and callback prototypes are done. Some of them are based on others and fbc need to read them in the logical order (use the control file to re-order).
  3. The last step is to translate static SUBs/FUNCTIONs declarations.

What's new, what's different?

There're small differences between the headers generated by GirToBac and to the headers shipped with FreeBasic up to version fbc-0.90. GI (in its *.gir files) only supports constant macros (a string or numerical constant behind the #define keyword). But the files don't contain any code-generating macros nor inline functions.

So the GirToBac generated FB headers just contain macros for constants. In addition, some standard macros for the GObject-classes (for C-styled headers) are generated by GirToBac.

The lack of code creating macros seems to be no big deal, since the GI-libraries are used without these macros in the above mentioned high-level programming languages for years. (In the translation process the missing macros are an advantage, since un-needed macros and pre-processors need no manual removing.) But sometimes it may be better to have some further macro support. In this cases it's possible to extend the GirToBac header with a conventional header-subset containing the missing macros.

GirToBac currently generates headers in a classic (C-like) style. Additionally it's prepared to generate OOP wrappers for GI libraries as well. Most of the high-level languages are OOP languages and GI is used for translating the GObject C code into real classes. The *.gir files contain all informations to wrap a C library in real FB objects with CONSTRUCTORs , SUBs, FUNCTIONs, PROPERTYs, ... This can help to make FB code more readable and to better support memory management.

Unfortunatelly the inheritance support in FB is currently (fbc-0.90.1) limited (ie. FB cannot extend a type by several interfaces). And some other features are not well prepared for that style of library bindings (ie EXPLICIT statement in ENUM blocks). So the development of the OOP features in GirToBac is stopped ATM and we have to wait for advanced features in the FB compiler.

How to get started?

As an example the file example/Gio-2.0.gir is shipped with these archive. You can make your first translation using these file and compare the output against the file Gir/Gio-2.0.bi.org to check the GirToBac executable and get familar with how to call GirToBac.

Follow these steps:

  • Preparation
    1. Download and extract GirToBac
    2. Jump to src directory and compile the source code
      fbc -w all GirToBac.bas
  • Translation
    1. Jump to folder Gir and call GirToBac to create a new header
      ../src/GirToBac ../example/Gio-2.0
    2. Compare the new file Gio-2.0.bi against the original shipped in the archive
      diff Gio-2.0.bi Gio-2.0.bi.org
      The only output from diff should be (no further differences)
      4c4
      < ' Auto-translated from file ../example/Gio-2.0.gir
      ---
      > ' Auto-translated from file Gio-2.0.gir
    3. To use headers from GirToBac in your FB source, move the folder Gir and its files in your freebasic/include path. Then use
      #INCLUDE ONCE "Gir/NAME.bi"
      in your code and replace NAME by the libraries name (ie Gio-2.0).

How to get and translate *.gir files?

A set of GLib headers was created during the development of GirToBac for testing purposes. First check if your header is included. You can download at http://www.freebasic-portal.de/downloads/bibliotheken/gtk-3-header-dateien-fuer-freebasic-191.html If your library isn't included or isn't up-to-date, you need do make your customized translation.

*.gir files get generated when a GI-library gets compiled. So you can either get them by downloading the source package of a library and compile it (and all its dependencies, using option –enable-introspection=yes). Thats's the way to go on windows systems.

On LINUX systems the *.gir files are provided in the package management system. They're included in the *-dev packages (hopefully this will change in near future). Ie Gtk-3.0.gir is in package libgtk-3-dev on Debian based systems. (You'll need this package to compile your source.)

Create your header by following these steps:

  • Preparation
    1. Create your output folder (usually .../freebasic/include/Gir)
    2. Copy basis header Gir/_GirToBac-0.0.bi in your output folder (or install the header set mentioned above).
    3. Download and extract the *.gir file (ie /usr/lib/girepository-1.0/Xyz-1.0.gir)
  • Translation
    1. Jump to your output folder and call GirToBac for translation
      /PATH/TO/GirToBac /usr/lib/girepository-1.0/Xyz-1.0
    2. Create a test file (ie test.bas in any folder) containing the line
      #INCLUDE ONCE "Gir/Xyz-1.0.bi"
      and compile it
      fbc -wall test.bas
    3. In case of no compiler errors you're done (the linker may report that -lXyz-1.0 is missing because you didn't install the binaries yet)
    4. Otherwise see section How to fix Problems?. Create a control file (ie Xyz-1.0.GirToBac) in the output folder and add entries. Recompile the test.bas code until fbc doesn't complain any error.

How to fix Problems?

Here's a brief summary of the most common problems when compiling the test code (test.bas) and how to fix them. A control file in your output folder (.../freebasic/include/Gir) is used to add some translation rules. It's named after the inputs base name with suffix GirToBac (ie Xyz-1.0.GirToBac for a Xyz-1.0.gir file). The rules are specified in XML syntax (see section The control file (*.GirToBac)). Depending on the error messages thrown by the fbc when compiling the test file, different entries should be done:

  • Duplicated definition ... There's a naming conflict. This usually happens because C syntax is case-sensitve and FBs isn't. Or it happens when an FB keyword is used as a symbol name in C code. Here you should change the FB name to make it unique (usually by adding an underscre character, but also consider to use an ALIAS statement). Example:
    <name search='window' add='_' />
  • Expected identifier ... There's a conflict between the C type name and an FB keyword. Or a C type is used that wasn't priviously specified. Rename the type to make it unique or known. Example:
    <type search='int' replace='gint' />
  • Illegal specification ... The symbol named in the error message isn't specified before this position (code line). Its declaration needs to get move forward. Example:
    <first search='synbol_name' />
  • File not found, "Xyz-0.0.bi" The library depends on an other library which isn't translated jet. Download the Xyz-0.0.gir file and translate it first. Then continue with the original translation.

You may face further problems or error messages when compiling the test file. Sorry, it's not possible to cover all of them here. If you cannot handle an issue don't hasitate to ask for help at http://www.freebasic.net/forum in the Libraries subforum.

The control file (*.GirToBac)

Similar to the *.gir files a control file is XML formated and is named by the same base-name with suffix *.GirToBac. Each control file contains adaption rules for one *.gir file, so each *.gir file may have one *.GirToBac file (located in the output folder). Ie when the GirToBac tool gets called by

../src/GirToBac ../example/Gio-2.0

it loads the file Gio-2.0.gir form directory ../example and file Gio-2.0.GirToBac (if present in the current folder). If the later isn't present, translation gets done without any rules. The generated output gets written to the file Gio-2.0.bi in the current folder (where GirToBac is called from).

Adaption rules may get specified for

  • a mismatch between the library name and its internal namespace
  • naming conflicts in variable names (because C naming is case-sensitive and FB's isn't)
  • typing conflicts in type names (GI is work in process, some C types are untranslated in the *.gir files, currently)
  • reordering of declarations (the *.gir files contain the symbol declarations in alphabetical order. Since fbc is a single pass compiler it needs to re-order some of them in logical order.)
  • a symbol check to switch off a dummy header when the complete header is in use.
  • a missmatch between the library name and the name declared in the *.gir file.

Therefor the parser for the configuration file understands six XML-tags and – depending on the tag type – the attributes name, add and replace:

  • binary to override the name of the binary. This is useful if the .gir file doesn't contain the right library name. Example (cairo):
    <binary name='cairo' />
  • check to enclose the complete header source by
    #IFNDEF symbol
    ...
    #ENDIF
    This is useful for a dummy binding that contains just a few type declarations. To avoid interferences with the complete binding, the dummy source can get switched of. Example (cairo):
    <check name='CAIRO_H' />
  • pack to adapt the library namespace (attribute name contains the new name in camel case letters). Example (GLib):
    <pack name='G' />
    or (GooCanvas):
    <pack name='Goo' />
  • name to adapt a symbol name (search contains a single word, further attributes add or replace). Example:
    <name search='gtk_stock_add' add='_ ALIAS "gtk_stock_add"' />
    or
    <name search='GTK_RC_STYLE' replace='_GTK_RC_STYLE' />
  • type to adapt types (search contains any text, further attributes add or replace). Example:
    <type search='GIconv' add=' PTR' />
    or
    <type search='void' replace='ANY' />
    <type search='const char* const' replace='CONST gchar PTR CONST' />
  • first to prepend the symbol (search contains a single word, no attribute) declaration before the rest. Example:
    <first search='GtkWidget' />
    <first search='GtkWidgetClass' />

The text in the attribute search needs to be specified case-sensitive. It may contain more then one word in case of a type rule, but just one word for name and first rules. replace and add attributes are used as-is in the output. See *.GirToBac files in folder Gir for further examples.

GirToBac reports duplicated tags while reading the control file. The first tag gets used in that case and all further get skipped. After translating the *.gir file, GirToBac reports about nonexistent symbols (defined in a search attribute but not existing in the *.gir file). Removing the corresponding tags from the control file speads up the translation process.

The control files should be shipped together with the translated header files. On one hand side this helps the users to learn about the differences between C and FB source. On the other hand side the control file helps anyone who want to generate an up-date for the header, further on.

Have fun, share your results.