Using Autotools

Adding a new library in a autotools project could be more or less easy depending on how the library is packaged. To take an example, imagine that we want to read an xml file using libxml2 (see its homepage at www.xmlsoft.org)

  1. Open the old tutorial project and replace main.c by the following.

    Example 4-1main.c using libxml2:
    #include <libxml/parser.h>			
    #include <stdio.h>
    			
    int main()
    {				
    	xmlDocPtr doc;                                                                                                                  
    	doc = xmlParseFile ("testfile.xml");
    
    	if (doc == NULL) {
    		printf ("Document not parsed successfully. \n");
    		return -1;
    	}
    	else {
    		printf ("Document parsed successfully.\n");
    		xmlFreeDoc(doc);
    		return 0;
    	}
    }

Our goal is now to compile it and make it work correctly. For that purpose, we must tell GCC two things: where to find libxml/parser.h (that is to say, give GCC the right include path) and what library (i.e. shared object) it should link our project against. There are several ways to do that, I will start with the easiest.

4.1.1. With pkg-config

pkg-config is tools for developers providing a unified interface for querying installed libraries with their version and all options needed to compile and link it. It comes with an Autoconf macro named PKG_CHECK_MODULES allowing to check the existence of the library and set all necessary flags.

  1. Add the following line in configure.ac.

    PKG_CHECK_MODULES(XML, libxml-2.0 >= 2.4)

    This macro will check the existence of libxml2 with a version higher or equal to 2.4 and create 2 variable XML_CFLAGS and XML_LIBS containing respectively, the flags for the C compiler and the linker. XML is an user defined name. libxml-2.0 is the name of the library. You can run pkg-config --list-all to get a list of all installed libraries.

  2. Add the following lines in Makefile.am.

    tut_prog_CPPFLAGS = $(XML_CFLAGS)
    tut_prog_LDFLAGS= $(XML_LIBS)

    This will use the options found by configure when the macro PKG_CHECK_MODULES is executed for compiling your program.

  3. That's all. You can run make again.

    cd . && /bin/sh /home/seb2008.1/Projects/tutprog/missing --run aclocal-1.10 
    cd . && /bin/sh /home/seb2008.1/Projects/tutprog/missing --run automake-1.10 --foreign 
    cd . && /bin/sh /home/seb2008.1/Projects/tutprog/missing --run autoconf
    /bin/sh ./config.status --recheck
    running CONFIG_SHELL=/bin/sh /bin/sh ./configure  --no-create --no-recursion
    checking for a BSD-compatible install... /usr/bin/install -c
    checking whether build environment is sane... yes
    checking for a thread-safe mkdir -p... /bin/mkdir -p
    checking for gawk... gawk
    checking whether make sets $(MAKE)... yes
    checking whether to enable maintainer-specific portions of Makefiles... yes
    checking for style of include used by make... GNU
    checking for gcc... gcc
    checking for C compiler default output file name... a.out
    checking whether the C compiler works... yes
    checking whether we are cross compiling... no
    checking for suffix of executables... 
    checking for suffix of object files... o
    checking whether we are using the GNU C compiler... yes
    checking whether gcc accepts -g... yes
    checking for gcc option to accept ISO C89... none needed
    checking dependency style of gcc... gcc3
    checking for library containing strerror... none required
    checking for gcc... (cached) gcc
    checking whether we are using the GNU C compiler... (cached) yes
    checking whether gcc accepts -g... (cached) yes
    checking for gcc option to accept ISO C89... (cached) none needed
    checking dependency style of gcc... (cached) gcc3
    checking for gcc... (cached) gcc
    checking whether we are using the GNU C compiler... (cached) yes
    checking whether gcc accepts -g... (cached) yes
    checking for gcc option to accept ISO C89... (cached) none needed
    checking dependency style of gcc... (cached) gcc3
    checking how to run the C preprocessor... gcc -E
    checking for grep that handles long lines and -e... /bin/grep
    checking for egrep... /bin/grep -E
    checking for ANSI C header files... yes
    checking for pkg-config... /usr/bin/pkg-config
    checking pkg-config is at least version 0.9.0... yes
    checking for XML... yes
    configure: creating ./config.status
     /bin/sh ./config.status
    config.status: creating Makefile
    config.status: creating config.h
    config.status: config.h is unchanged
    config.status: executing depfiles commands
    cd . && /bin/sh /home/seb2008.1/Projects/tutprog/missing --run autoheader
    rm -f stamp-h1
    touch config.h.in
    cd . && /bin/sh ./config.status config.h
    config.status: creating config.h
    config.status: config.h is unchanged
    make  all-am
    make[1]: Entering directory `/home/seb/Projects/tutprog'
    gcc -DHAVE_CONFIG_H -I.  -DPACKAGE_DATA_DIR=\""/usr/local/share"\" -I/usr/include/libxml2  \
            -Wall -g -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
    mv -f .deps/main.Tpo .deps/main.Po
    gcc -Wall -g -g -O2   -o tutprog main.o -lxml2   
    make[1]: Leaving directory `/home/seb/Projects/tutprog'

    There is no need to rerun Autoconf or Automake because the Makefile generated by it already contains some rules to do it.

When installed, each library supporting pkg-config copy a small text file, with .pc extension, in a system directory; normally /usr/lib/pkgconfig. If you install a library from source it will be by default in /usr/local/lib/pkgconfig. You can ask pkg-config to search in this directory too, by defining the environment variable PKG_CONFIG_PATH=/usr/local/lib/pkgconfig.

4.1.2. With an Autoconf macro

If the library does not support pkg-config: it does not appear in the list returned by pkg-config --list-all. You need a Autoconf macro:

  • Check if the library author shipped a M4 macro, and use it if present. It is a text file named like the library with a .m4 extension often installed in /usr/share/aclocal.
  • If your library is a basic one, it might be checked by the standard Autoconf macros (see the list here).
  • Perhaps the M4 macro you need has already be programmed by someone else. Look at the contributions here.
  • If all that fail, go deeper in M4, make your own macro, and donate it to the library's author!

When, you have this macro, you can copy it in /usr/share/aclocal, so aclocal will find it. Then, you just need to look in the macro file to see how to use it. libxml2 installs a macro named AM_PATH_XML2 which is in /usr/share/aclocal/libxml.m4.

  1. Add the following line in configure.ac.

    AM_PATH_XML2(2.4.0)

    This macro will check the existence of the library with a version higher or equal to 2.4 and create 2 variable XML_CPPFLAGS and XML_LIBS containing respectively, the flags for the C compiler and the linker. You get these information from the comments in the macro file.

  2. Add the following lines in Makefile.am.

    tut_prog_CPPFLAGS = $(XML_CPPFLAGS)
    tut_prog_LDFLAGS= $(XML_LIBS)

    This will use the options found by configure for compiling your program. Note that the macro defined XML_CPPFLAGS instead of XML_CFLAGS with pkg-config. Using CPPFLAGS makes more sense, because these flags are used by the C preprocessor, most of the time only to setup the path of the include files.

  3. That's all. You can run make again. The generated Makefile is almost the same.

4.1.3. With hardcoded library path

It is the approach one could naturally have: let's give GCC the stuff it needs directly ! On my system, libxml/parser.h is in /usr/include/libxml2, and the shared object is 'libxml.so', located in /usr/lib. (I will assume it's all the same for you).

  1. Add the following lines in Makefile.am.

    tut_prog_CPPFLAGS = -I /usr/include/libxml2
    tut_prog_LDFLAGS= -lxml2

    There is no need to change configure.ac because you don't check anything and just assume that all host system will have the right library in the same place than you.

  2. You can run make and it should work if you have the same system than me.

     cd . && /bin/sh /home/seb2008.1/Projects/tutprog/missing --run automake-1.10 --foreign  Makefile
     cd . && /bin/sh ./config.status Makefile depfiles
    config.status: creating Makefile
    config.status: executing depfiles commands
    make  all-am
    make[1]: Entering directory `/home/seb/Projects/tutprog'
    gcc -DHAVE_CONFIG_H -I.  -DPACKAGE_DATA_DIR=\""/usr/local/share"\" -I /usr/include/libxml2  \
        -Wall -g -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
    mv -f .deps/main.Tpo .deps/main.Po
    gcc -Wall -g -g -O2   -o tutprog main.o -lxml2 
    make[1]: Leaving directory `/home/seb/Projects/tutprog'

I have described this here to show that it is possible to do simple thing using Autotools. But this approach has several drawbacks:

  • It is not portable to various linuxes: perhaps on other distribution the include path is different.
  • If the next versions of libxml have different paths, or different needed libraries, we will need to update the project.
  • We don't test whether the system of the packager/user has the library.
  • We cannot check the version of the libxml2 we use.