Autoconf, Automake, and Libtool: Convenience Libraries

| ## 11.5 Convenience Libraries Sometimes it is useful to group objects together in an intermediate stage of a project's compilation to provide a useful handle for that group without having to specify all of the individual objects every time. Convenience libraries are a portable way of creating such a partially linked object: Libtool will handle all of the low level details in a way appropriate to the target host. This section describes the use of convenience libraries in conjunction with Automake. The principles of convenience libraries are discussed in Creating Convenience Libraries. The key to creating Libtool convenience libraries with Automake is to use the ``noinst_LTLIBRARIES'` macro. For the Libtool libraries named in this macro, Automake will create Libtool convenience libraries which can subsequently be linked into other Libtool libraries. In this section I will create two convenience libraries, each in their own subdirectory, and link them into a third Libtool library, which is ultimately linked into an application. If you want to follow this example, you should create a directory structure to hold the sources by running the following shell commands:
    $ mkdir convenience
    $ cd convenience
    $ mkdir lib
    $ mkdir replace
1. The first convenience library is built from two source files in the ``lib'` subdirectory. ``source.c'`:
       #if HAVE_CONFIG_H
       #  include <config.h>
       #endif

       #if HAVE_MATH_H
       #  include <math.h>
       #endif

       void
       foo (double argument)
       {
         printf ("cos (%g) => %g\n", argument, cos (argument));
       }
2. This file defines a single function to display the cosine of its argument on standard output, and consequently relies on an implementation of the `cos` function from the system libraries. Note the conditional inclusion of ``config.h'`, which will contain a definition of ``HAVE_MATH_H'` if ``configure'` discovers a ``math.h'` system header (the usual location for the declaration of `cos`). The ``HAVE_CONFIG_H'` guard is by convention, so that the source can be linked by passing the preprocessor macro definitions to the compiler on the command line -- if ``configure.in'` does not use ``AM_CONFIG_HEADER'` for instance. ``source.h'`:
       extern void foo        (double argument);
For brevity, there is no `#ifndef SOURCE_H` guard. The header is not installed, so you have full control over where it is `#include`ed, and in any case, function declarations can be safely repeated if the header is accidentally processed more than once. In a real program, it would be better to list the function parameters in the declaration so that the compiler can do type checking. This would limit the code to working only with ANSI compilers, unless you also use a `PARAMS` macro to conditionally preprocess away the parameters when a K&R compiler is used. These details are beyond the scope of this convenience library example, but are described in full in 9.1.6 K&R Compilers. You also need a ``Makefile.am'` to hold the details of how this convenience library is linked:
    ## Makefile.am -- Process this file with automake to produce Makefile.in

    noinst_LTLIBRARIES        = library.la
    library_la_SOURCES        = source.c source.h
    library_la_LIBADD        = -lm
The ``noinst_LTLIBRARIES'` macro names the Libtool convenience libraries to be built in this directory, ``library.la'`. Although not required for compilation, ``source.h'` is listed in the ``SOURCES'` macro of ``library.la'` so that correct source dependencies are generated, and so that it is added to the distribution tarball by `automake`'s ``dist'` rule. Finally, since the `foo` function relies on the `cos` function from the system math library, ``-lm'` is named as a required library in the ``LIBADD'` macro. As with all Libtool libraries, interlibrary dependencies are maintained for convenience libraries so that you need only list the libraries you are using directly when you link your application later. The libraries used by those libraries are added by Libtool. The parent directory holds the sources for the main executable, ``main.c'`, and for a (non-convenience) Libtool library, ``error.c'` & ``error.h'`. Like ``source.h'`, the functions exported from the Libtool library ``liberror.la'` are listed in ``error.h'`:
    extern void gratuitous          (void);
    extern void set_program_name    (char *path);
    extern void error               (char *message);
The corresponding function definitions are in ``error.c'`:
    #include <stdio.h>

    #include "source.h"

    static char *program_name = NULL;

    void
    gratuitous (void)
    {
      /* Gratuitous display of convenience library functionality!  */
      double argument = 0.0;
      foo (argument);
    }

    void
    set_program_name (char *path)
    {
      if (!program_name)
        program_name = basename (path);
    }

    void
    error (char *message)
    {
      fprintf (stderr, "%s: ERROR: %s\n", program_name, message);
      exit (1);
    }
The `gratuitous()` function calls the `foo()` function defined in the ``library.la'` convenience library in the ``lib'` directory, hence ``source.h'` is included. The definition of `error()` displays an error message to standard error, along with the name of the program, `program_name`, which is set by calling `set_program_name()`. This function, in turn, extracts the basename of the program from the full path using the system function, `basename()`, and stores it in the library private variable, `program_name`. Usually, `basename()` is part of the system C library, though older systems did not include it. Because of this, there is no portable header file that can be included to get a declaration, and you might see a harmless compiler warning due to the use of the function without a declaration. The alternative would be to add your own declaration in ``error.c'`. The problem with this approach is that different vendors will provide slightly different declarations (with or without `const` for instance), so compilation will fail on those architectures which do provide a declaration in the system headers that is different from the declaration you have guessed. For the benefit of architectures which do not have an implementation of the `basename()` function, a fallback implementation is provided in the ``replace'` subdirectory. The file ``basename.c'` follows:
    #if HAVE_CONFIG_H
    #  include <config.h>
    #endif

    #if HAVE_STRING_H
    #  include <string.h>
    #elif HAVE_STRINGS_H
    #  include <strings.h>
    #endif

    #if !HAVE_STRRCHR
    #  ifndef strrchr
    #    define strrchr rindex
    #  endif
    #endif

    char*
    basename (char *path)
    {
      /* Search for the last directory separator in PATH.  */
      char *basename = strrchr (path, '/');

      /* If found, return the address of the following character,
         or the start of the parameter passed in.  */
      return basename ? ++basename : path;
    }
For brevity, the implementation does not use any `const` declarations which would be good style for a real project, but would need to be checked at configure time in case the end user needs to compile the package with a K&R compiler. The use of `strrchr()` is noteworthy. Sometimes it is declared in ``string.h'`, otherwise it might be declared in ``strings.h'`. BSD based Unices, on the other hand, do not have this function at all, but provide an equivalent function, `rindex()`. The preprocessor code at the start of the file is designed to cope with all of these eventualities. The last block of preprocessor code assumes that if `strrchr` is already defined that it holds a working macro, and does not redefine it. ``Makefile.am'` contains:
    ## Process this file with automake to produce Makefile.in

    noinst_LTLIBRARIES      = libreplace.la
    libreplace_la_SOURCES   = dummy.c
    libreplace_la_LIBADD    = @LTLIBOBJS@
Once again, the ``noinst_LTLIBRARIES'` macro names the convenience library, ``libreplace.la'`. By default there are no sources, since we expect to have a system definition of `basename()`. Additional Libtool objects which should be added to the library based on tests at configure time are handled by the ``LIBADD'` macro. ``LTLIBOBJS'` will contain ``basename.lo'` if the system does not provide `basename`, and will be empty otherwise. Illustrating another feature of convenience libraries: on many architectures, ``libreplace.la'` will contain no objects. Back in the toplevel project directory, all of the preceding objects are combined by another ``Makefile.am'`:
    ## Process this file with automake to produce Makefile.in

    SUBDIRS                 = replace lib .

    CPPFLAGS                = -I$(top_srcdir)/lib

    include_HEADERS         = error.h

    lib_LTLIBRARIES         = liberror.la
    liberror_la_SOURCES     = error.c
    liberror_la_LDFLAGS     = -no-undefined -version-info 0:0:0
    liberror_la_LIBADD      = replace/libreplace.la lib/library.la

    bin_PROGRAMS            = convenience
    convenience_SOURCES     = main.c
    convenience_LDADD       = liberror.la
The initial ``SUBDIRS'` macro is necessary to ensure that the libraries in the subdirectories are built before the final library and executable in this directory. Notice that I have not listed ``error.h'` in ``liberror_la_SOURCES'` this time, since ``liberror.la'` is an installed library, and ``error.h'` defines the public interface to that library. Since the ``liberror.la'` Libtool library is installed, I have used the ``-version-info'` option, and I have also used ``-no-undefined'` so that the project will compile on architectures which require all library symbols to be defined at link time -- the reason `program_name` is maintained in ``liberror'` rather than ``main.c'` is so that the library does not have a runtime dependency on the executable which links it. The key to this example is that by linking the ``libreplace.la'` and ``library.la'` convenience libraries into ``liberror.la'`, all of the objects in both convenience libraries are compiled into the single installed library, ``liberror.la'`. Additionally, all of the inter-library dependencies of the convenience libraries (``-lm'`, from ``library.la'`) are propagated to ``liberror.la'`. A common difficulty people experience with Automake is knowing when to use a ``LIBADD'` primary versus a ``LDADD'` primary. A useful mnemonic is: ``LIBADD'` is for ADDitional LIBrary objects. ``LDADD'` is for ADDitional linker (LD) objects. The executable, ``convenience'`, is built from ``main.c'`, and requires only ``liberror.la'`. All of the other implicit dependencies are encoded within ``liberror.la'`. Here is ``main.c'`:
    #include <stdio.h>
    #include "error.h"

    int
    main (int argc, char *argv[])
    {
      set_program_name (argv[0]);
      gratuitous ();
      error ("This program does nothing!");
    }
The only file that remains before you can compile the example is ``configure.in'`:
    # Process this file with autoconf to create configure.

    AC_PREREQ(2.59)

    AC_INIT([convenience], [2.0], [gary@gnu.org])
    AC_CONFIG_HEADERS([config.h:config-h.in])

    AM_INIT_AUTOMAKE([1.9 foreign])

    AC_PROG_CC
    AC_PROG_LIBTOOL

    AC_CHECK_HEADERS([math.h])
    AC_CHECK_HEADERS([string.h strings.h], [break])

    AC_CHECK_FUNCS([strrchr])
    AC_REPLACE_FUNCS([basename])

    AC_CONFIG_FILES([replace/Makefile lib/Makefile Makefile])
    AC_OUTPUT
There are checks for all of the features used by the sources in the project: ``math.h'` and either ``string.h'` or ``strings.h'`; the existence of `strrchr` (after the tests for string headers); adding ``basename.o'` to ``LIBOBJS'` if there is no system implementation; and the shell code to set ``LTLIBOBJS'`. With all the files in place, you can now bootstrap the project:
    $ ls -R
    .:
    Makefile.am  configure.in  error.c  error.h  lib  main.c  replace

    lib:
    Makefile.am  source.c  source.h

    replace:
    Makefile.am  basename.c
    $ aclocal
    $ autoheader
    $ automake --add-missing --copy
    automake: configure.in: installing ./install-sh
    automake: configure.in: installing ./mkinstalldirs
    automake: configure.in: installing ./missing
    configure.in: 7: required file ./ltconfig not found
    $ autoconf
    $ ls -R
    .:
    Makefile.am   config.h.in   error.c     ltconfig   mkinstalldirs
    Makefile.in   config.sub    error.h     ltmain.sh  replace
    aclocal.m4    configure     install-sh  main.c
    config.guess  configure.in  lib         missing

    lib:
    Makefile.am  Makefile.in  source.c  source.h

    replace:
    Makefile.am  Makefile.in  basename.c
With these files in place, the package can now be configured:
    $ ./configure
    ...
    checking how to run the C preprocessor... gcc -E
    checking for math.h... yes
    checking for string.h... yes
    checking for strrchr... yes
    checking for basename... yes
    updating cache ./config.cache
    creating ./config.status
    creating replace/Makefile
    creating lib/Makefile
    creating Makefile
    creating config.h
Notice that my host has an implementation of `basename()`. Here are the highlights of the compilation itself:
    $ make
    Making all in replace
    make[1]: Entering directory /tmp/replace
    /bin/sh ../libtool --mode=link gcc  -g -O2  -o libreplace.la
    rm -fr .libs/libreplace.la .libs/libreplace.* .libs/libreplace.*
    ar cru .libs/libreplace.al
    ranlib .libs/libreplace.al
    creating libreplace.la
    (cd .libs && rm -f libreplace.la && ln -s ../libreplace.la \
    libreplace.la)
    make[1]: Leaving directory /tmp/replace
Here the build descends into the ``replace'` subdirectory and creates ``libreplace.la'`, which is empty on my host since I don't need an implementation of `basename()`:
    Making all in lib
    make[1]: Entering directory /tmp/lib
    /bin/sh ../libtool --mode=compile gcc -DHAVE_CONFIG_H  -I. -I. \
    -g -O2 -c source.c
    rm -f .libs/source.lo
    gcc -DHAVE_CONFIG_H -I. -I. -g -O2 -c -fPIC -DPIC source.c \
    -o .libs/source.lo
    gcc -DHAVE_CONFIG_H -I. -I. -g -O2 -c source.c \
    -o source.o >/dev/null 2>&1
    mv -f .libs/source.lo source.lo
    /bin/sh ../libtool --mode=link gcc  -g -O2  -o library.la source.lo -lm
    rm -fr .libs/library.la .libs/library.* .libs/library.*
    ar cru .libs/library.al source.lo
    ranlib .libs/library.al
    creating library.la
    (cd .libs && rm -f library.la && ln -s ../library.la library.la)
    make[1]: Leaving directory /tmp/lib
Next, the build enters the ``lib'` subdirectory to build ``library.la'`. The ``configure'` preprocessor macros are passed on the command line, since no ``config.h'` was created by `AC_CONFIG_HEADER`: Here, ``main.c'` is compiled (not to a Libtool object, since it is not compiled using `libtool`), and linked with the ``liberror.la'` Libtool library:
    gcc -DHAVE_CONFIG_H -I. -I.  -I./lib  -g -O2 -c main.c
    /bin/sh ./libtool --mode=link gcc  -g -O2  -o convenience  main.o \
    liberror.la
    gcc -g -O2 -o .libs/convenience main.o ./.libs/liberror.so -lm \
    -Wl,--rpath -Wl,/usr/local/lib
    creating convenience
    make[1]: Leaving directory /tmp/convenience
`libtool` calls `gcc` to link the `convenience` executable from ``main.o'` and the shared library component of ``liberror.la'`. `libtool` also links with ``-lm'`, the propagated inter-library dependency of the ``library.la'` convenience library. Since ``libreplace.la'` and ``library.la'` were convenience libraries, their objects are already present in ``liberror.la'`, so they are not listed again in the final link line -- the whole point of convenience archives. This just shows that it all works:
    $ ls
    Makefile      config.h       configure.in  install-sh   main.c
    Makefile.am   config.h.in    convenience   lib          main.o
    Makefile.in   config.log     error.c       liberror.la  missing
    aclocal.m4    config.status  error.h       libtool      mkinstalldirs
    config.cache  config.sub     error.lo      ltconfig     replace
    config.guess  configure      error.o       ltmain.sh
    $ libtool --mode=execute ldd convenience
            liberror.so.0 => /tmp/.libs/liberror.so.0 (0x40014000)
            libm.so.6 => /lib/libm.so.6 (0x4001c000)
            libc.so.6 => /lib/libc.so.6 (0x40039000)
            /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
    $ ./convenience
    cos (0) => 1
    lt-convenience: ERROR: This program does nothing!
Notice that you are running the uninstalled executable, which is in actual fact a wrapper script, See section 10.5 Executing Uninstalled Binaries. That is why you need to use `libtool` to run `ldd` on the real executable. The uninstalled executable called by the wrapper script is called `lt-convenience`, hence the output from `basename()`. Finally, you can see from the output of `ldd`, that `convenience` really isn't linked against either ``library.la'` and ``libreplace.la'`.

This document was generated by Gary V. Vaughan on February, 8 2006 using texi2html