Appendix H. Wrapping C Libraries with gmmproc

Table of Contents

gtkmm uses the gmmproc tool to generate most of its source code, using .defs files that define the APIs of GObject-based libraries. So it's quite easy to create additional gtkmm-style wrappers of other glib/GObject-based libraries.

This involves a variety of tools and some copying of existing build files, but it does at least work, and has been used successfully by several projects.

The build structure

Generation of the source code for a gtkmm-style wrapper API requires use of tools such as gmmproc and generate_wrap_init.pl. In theory you could write your own build files to use these appropriately, but in practice it's usually easier to simply copy an existing project and modify it for your needs. Note however, that there is plenty of scope for improvement in the build structure that we use, so try to copy the latest version, and feel free to suggest improvements to make it more generic.

For instance, let's pretend that we are wrapping a C library called libexample. It provides a GObject-based API with types named, for instance, ExampleThing and ExampleStuff.

Copying an existing project

Typically our wrapper library would be called libexamplemm. We can start by copying an existing *mm library, such as libgdamm, after checking it out from svn.

$ svn co svn.gnome.org/svn/gnomemm/libgdamm/trunk libsomethingmm

This provides a directory structure for the source .hg and .ccg files and the generated .h and .cc files, with Makefile.am fragments that can specify the various files in use, in terms of generic Makefile.am variables. The directory structure usually looks like this, after we have renamed the directories appropriately:

  • libsomethingmm: The top-level directory.

    • libsomething: Contains the main include file and the pkg-config .pc file.

      • src: Contains .hg and .ccg source files.

      • libsomethingmm: Contains generated and hand-written .h and .cc files.

        • private: Contains generated *_p.h files.

As well as renaming the directories, we should rename some of the source files. For instance:

$ mv libsomething/libgdamm-2.0.pc.in libsomething/libsomethingmm-1.0.pc.in
$ mv libsomething/libgdammconfig.h.in libsomething/libsomethingmmconfig.h.in
$ mv libsomething/libgdamm.h libsomething/libsomethingmm.h
$
$ mv libsomething/src/libgda.defs libsomething/src/libsomething.defs
$ mv libsomething/src/libgda_enums.defs libsomething/src/libsomething_enums.defs
$ mv libsomething/src/libgda_methods.defs libsomething/src/libsomething_methods.defs
$ mv libsomething/src/libgda_others.defs libsomething/src/libsomething_others.defs
$ mv libsomething/src/libgda_signals.defs libsomething/src/libsomething_signals.defs
$ mv libsomething/src/libgda_vfuncs.defs libsomething/src/libsomething_vfuncs.defs
$ mv libsomething/src/libgda_docs.xml libsomething/src/libsomething_docs.xml
$ mv libsomething/src/libgda_docs_override.xml libsomething/src/libsomething_docs_override.xml

A multiple-file renaming tool, such as prefixsuffix might help with this. We will provide the contents of these files later. In addition, if you started from an svn checkout, you'll probably want to get rid of all of the extra .svn directories in the source tree.

Note that files ending in .in will be used to generate files with the same name but without the .in suffix, by replacing some variables with actual values during the configure stage.

Modifying build files

Now we edit the files to adapt them to to our needs. You might prefer to use a multiple-file search-replace too for this, such as regexxer.

autogen.sh

For instance, in autogen.sh:

  • PKG_NAME must contain the new package name, such as libexamplemm.

  • The test script lines must check for appropriate directory names, such as $srcdir/libsomething/src.

configure.in

In configure.in (or configure.ac in newer projects),

  • The AC_INIT() line must mention a file in our library. We can edit this later if we don't yet know the names of any of the files that we will create.

  • The PACKAGE variable must be changed to the correct name of the project, such as libsomethingmm.

  • The version numbers should be reset to something small, such as 0.0.1. You may want to rename the version variables if they are not something generic. For instance, we would rename LIBGDAMM_MAJOR_VERSION to LIBSOMETHINGMM_MAJOR_VERSION or LIBGENERICMM_MAJOR_VERSION.

  • The AM_CONFIG_HEADER() line must mention the correctly named config header file.

  • The PKG_CHECK_MODULES() line must be modified to check for the correct dependencies. For instance, it might be changed to PKG_CHECK_MODULES(LIBGENERICMM, gtkmm-2.4 >= 2.6.0 libsomething-1.0 >= 1.0.0).

  • The AC_OUTPUT() block must mention the correct directory names, as described above.

  • The m4 script to generate doxygen input directory paths must mention the correct directory.

Makefile.am files

Next we must adapt the various Makefile.am files:

  • The top-level Makefile.am must mention the correct child directory in the SUBDIRS variable.

  • The libexample/Makefile.am must mention:

    • The correct child directories in the SUBDIRS variable.

    • The correct filenames in EXTRA_DIST.

    • The correct filenames in *_includedir, *_include_HEADERS, *_configdir, and *_config_DATA.

  • In libexample/libexamplemm/Makefile.am we must mention the correct names in the generic variables that are used elsewhere in the build system:

    sublib_name

    The name of the library, such as libsomethingmm.

    sublib_libname

    The versioned name of the library, such as libsomethingmm-1.0

    sublib_namespace

    The name of the C++ namespace to use for this library, such as Something.

    files_defs

    The list of .defs and *docs*.xml files.

    lib_LTLIBRARIES

    This variable must mention the correct library name, and this library name must be used to form the _SOURCES, _LDFLAGS, and _LIBADD variable names.

  • In libexample/libexamplemm/private/Makefile.am, private_includedir must contain the correct path.

  • In examples/Makefile.am_fragment, local_libgenericmm_lib and all_includes must contain the correct paths.

  • In libexample/src/Makefile.am we must set some more generic variables:

    sublib_name

    The name of the library, as in libexample/libexamplemm/Makefile.am.

    sublib_namespace

    The name of the C++ namespace, as in libexample/libexamplemm/Makefile.am

    sublib_parentdir

    The name of the directory containing the generated files, such as libexamplemm.

    files_defs

    The list of .defs and *docs*.xml files.

  • In build_shared/Makefile_gensrc.am_fragment, you should remove the --namespace=Gnome option from the gen_wrap_init_args variable if your namespace will not be under the Gnome namespace. This should be the only place that you need to edit the generic build_shared/ files.

Creating .hg and .ccg files

We should now create our first .hg and .ccg files, to wrap one of the objects in the C library. We will delete any existing .hg and .ccg files:

$ rm -rf libexample/src/*.hg
$ rm -rf libexample/src/*.ccg

and create new files:

$ touch libexample/src/thing.hg
$ touch libexample/src/thing.ccg

We must mention all of our .hg and .ccg files in the libexample/src/Makefile_list_of_hg.am_fragment file, in the files_hg variable.

Any extra non-generated .h and .cc source files may be placed in libexample/libexamplemm/ and mentioned in libexample/libexamplemm/Makefile.am, in the files_extra_h and files_extra_cc variables.

In the .hg and .ccg files section you can learn about the syntax used in these files.