Benedikt Meurer JavaScript Engine Hacker and Programming Language Enthusiast.

A brief introduction to the Xfce Developer Tools

The Xfce Developer Tools - xfce4-dev-tools for short - provide an easy way to handle the setup and maintaince of a projects build framework. They currently consist of a bunch of M4 macros for commonly used checks and the xdt-autogen script, which examines the projects configure.ac or configure.in file(s) and calls the appropriate autotools in the correct order.

As the name suggests, the Xfce Developer Tools are mainly useful for application developers to maintain their build environment. But users will also be required to install them if they plan to build software straight from CVS or SVN checkouts.

Preparing the project #

In this section I will describe how to use the developer tools with your project. We will therefore examine a sample project and discuss the various steps. Our sample project will contain only a single binary, which does nothing useful. The project itself will be named sample, and will depend on libxfcegui4 and can optionally use D-BUS (this is only to demonstrate the usage of some of the M4 macros). And our project will be translatable to other languages.

We start off with the Makefile.am's and the source code. First, we create the projects directory layout:

mkdir sample
mkdir sample/po
mkdir sample/sample

Next the sample/main.c source file:

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <libxfcegui4/libxfcegui4.h>

#ifdef HAVE_DBUS
#include <dbus/dbus.h>
#endif

int
main (int argc, char **argv)
{
xfce_textdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
g_print (_("Hello World!\n"));
return 0;
}

As you can see, we conditionally include dbus/dbus.h only if the HAVE_DBUS preprocessor macro is defined. The check for D-BUS is made in the configure.ac file, which is discussed below. Besides that, the only thing we do here is to print the string "Hello World!" in a translatable manner, after setting up the proper textdomain for gettext.

We continue with the sample/Makefile.am, which contains instructions about how to compile the sample/main.c file:

INCLUDES = \
-DDBUS_API_SUBJECT_TO_CHANGE \
-DG_LOG_DOMAIN=\"sample\" \
-DPACKAGE_LOCALE_DIR=\"$(localedir)\" \
-I$(top_srcdir)

bin_PROGRAMS = \
sample

sample_SOURCES = \
main.c

sample_CFLAGS = \
$(DBUS_CFLAGS) \
$(LIBXFCEGUI4_CFLAGS)

sample_LDFLAGS = \
$(DBUS_LIBS) \
$(LIBXFCEGUI4_LIBS)

The $(localedir) make variable is set by the XDT_I18N() macro and will point to the location where the translations are installed (e.g. /usr/local/share/locale). The $(DBUS_CFLAGS), $(DBUS_LIBS), $(LIBXFCEGUI4_CFLAGS) and $(LIBXFCEGUI4_LIBS) variables are set by the appropriate calls to XDT_CHECK_OPTIONAL_PACKAGE() and XDT_CHECK_PACKAGE(), which will be discussed below. Note that $(DBUS_CFLAGS) and $(DBUS_LIBS) will be empty if D-BUS was not found or disabled by the user.

We also need to define DBUS_API_SUBJECT_TO_CHANGE because the D-BUS API is not yet frozen, but that's an unimportant detail in the context of this article.

Furthermore we will also need a toplevel Makefile.am, which should look like this:

SUBDIRS = \
po \
sample

AUTOMAKE_OPTIONS = \
1.8 \
dist-bzip2

The AUTOMAKE_OPTIONS variable is used by automake to enable or disable special behaviour. In this case, the 1.8 tells automake that we need version 1.8 or better. And the dist-bzip2 option tells automake to generate a dist target, which will not only create the gzipped source tarball, but also a bzip2 compressed source tarball.

Now we are done with the Makefile.am's. The next step is to take care of the i18n setup. Therefore we need to create an empty po/ChangeLog file (the po/Makefile.in.in generated by glib-gettextize requires this file)

touch po/ChangeLog

and afterwards create the po/POTFILES.in file, which lists all source files (and sometimes other files like .desktop or .xml files as well) that contain translatable strings. In this case only the sample/main.c file contains a translatable string, so the file looks like this:

sample/main.c

Note that all paths in po/POTFILES.in must be relative to the projects toplevel directory. One last step before we go on to the configure.ac file is to create four required files for GNU projects:

touch AUTHORS ChangeLog NEWS README

You should of course add content to these files later on, but for now, we want to concentrate on the important facts, the contents of configure.ac:

dnl Version information
m4_define([sample_version_major], [0])
m4_define([sample_version_minor], [0])
m4_define([sample_version_micro], [1])
m4_define([sample_version], [sample_version_major().sample_version_minor().sample_version_micro()])

dnl Initialize autoconf
AC_COPYRIGHT([Copyright (c) 2005
Benedikt Meurer <benny@xfce.org>])
AC_INIT([sample], [sample_version()], [benny@xfce.org])

dnl Initialize automake
AM_INIT_AUTOMAKE([AC_PACKAGE_TARNAME()], [AC_PACKAGE_VERSION()])
AM_CONFIG_HEADER([config.h])
AM_MAINTAINER_MODE()

dnl check for basic programs
AC_PROG_CC()
AC_PROG_INSTALL()
AC_PROG_LIBTOOL()

dnl Check for i18n support
XDT_I18N([de])

dnl Check for required packages
XDT_CHECK_PACKAGE([LIBXFCEGUI4], [libxfcegui4-1.0], [4.2.0])

dnl Check for optional packages
XDT_CHECK_OPTIONAL_PACKAGE([DBUS], [dbus-1], [0.22], [dbus], [D-BUS support])

AC_OUTPUT([
Makefile
po/Makefile.in
sample/Makefile
])

You should be familar with most of the above, therefore we will only discuss the XDT_I18N(), XDT_CHECK_PACKAGE() and XDT_CHECK_OPTIONAL_PACKAGE() macros here. If you are not familar with the basic autoconf and automake macros, you should have a look at the GNU manuals.

XDT_I18N(LINGUAS [, PACKAGE]) #

This macro takes care of setting up everything for i18n support.

If PACKAGE isn't specified, it defaults to the package tarname; see the description of AC_INIT() for an explanation of what makes up the package tarname. Normally, you don't need to specify PACKAGE, but you can stick with the default.

XDT_CHECK_PACKAGE(VARNAME, PACKAGE, VERSION [, ACTION-IF [, ACTION-IF-NOT]]) #

Checks if PACKAGE >= VERSION is installed on the target system, using the pkg-config utility. If the dependency is met, VARNAME_CFLAGS, VARNAME_LIBS, VARNAME_VERSION and VARNAME_REQUIRED_VERSION will be set and marked for substition. For example, if you specify GTK for the VARNAME parameter, the variables GTK_CFLAGS, GTK_LIBS, GTK_VERSION and GTK_REQUIRED_VERSION will be set appropriately and substituted.

VARNAME_REQUIRED_VERSION will be set to the value of VERSION. This is mostly useful to automatically place the correct version information into the RPM .spec file.

In addition, if the dependency is met, ACTION-IF will be executed if given.

If the package check fails, ACTION-IF-NOT will be executed. If this parameter isn't specified, a diagnostic message will be printed and the configure script will be terminated with exit code 1.

XDT_CHECK_OPTIONAL_PACKAGE(VARNAME, PACKAGE, VERSION, OPTIONNAME, HELPSTRING [, DEFAULT]) #

Checks for an optional dependency on PACKAGE >= VERSION. DEFAULT can be "yes" or "no" (defaults to "yes" if not specified) and controls whether configure should check this dependency by default, or only if the user explicitly enables it using a command line switch.

This macro automatically adds a commandline switch based on the OPTIONNAME parameter (--enable-optionname and --disable-optionname), which allows the user to explicitly control whether this optional dependency should be enabled or not. The HELPSTRING parameter gives a brief(!) description about this dependency.

If the user chose to enable this dependency and the required package was found, this macro defines the variable VARNAME_FOUND and sets it to the string "yes", in addition to the 4 variables set by XDT_CHECK_PACKAGE(). But VARNAME_FOUND will not be marked for substition. Furthermore, a C preprocessor define HAVE_VARNAME will be placed in config.h (or added to the cc command line, depending on your configure.ac content) and set to 1.

First run #

Now that everything is in place we are ready for the first run. Therefore, go to the toplevel directory of the project and execute the

xdt-autogen

command that comes with the developer tools. This will run all the required autotools (in our case these are glib-gettextize, libtoolize, aclocal, autoheader, automake and autoconf), and afterwards run the generated configure script.

The next thing to do is to generate all language files specified with the XDT_I18N() macro (we've only specified de in our configure.ac). Therefore execute the following commands:

cd po
make sample.pot
cp sample.pot de.po

Now if you check po/de.po, it will contain exactly one translatable string, the one from sample/main.c.

That's it, now you can simply run

make

in the projects toplevel directory in order to build your project for the first time. Congratulations, you've successfully completed the build framework setup.

The source code for the sample project is available here.

For more details on the internal workings and the other available macros, check the M4 files in the m4macros/ subdirectory of the Xfce Developer Tools distribution.