| Autoconf, Automake, and Libtool | ||
|---|---|---|
| <<< Previous | Using GNU Libtool with configure.in and Makefile.am | Next >>> |
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 the section called Creating Convenience Libraries in the chapter called Introducing GNU Libtool.
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
|
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));
}
|
source.h:
extern void foo (double argument);
|
You also need a Makefile.am to hold the details of how this convenience library is linked:
## 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 functon 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 =
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
AUTOMAKE_OPTIONS = foreign
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 propogated 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_INIT(error.c)
AM_CONFIG_HEADER(config.h)
AM_INIT_AUTOMAKE(convenience, 1.0)
AC_PROG_CC
AM_PROG_LIBTOOL
AC_CHECK_HEADERS(math.h)
AC_CHECK_HEADERS(string.h strings.h, break)
AC_CHECK_FUNCS(strrchr)
AC_REPLACE_FUNCS(basename)
Xsed="sed -e s/^X//"
LTLIBOBJS=echo X"$LIBOBJS" | \
$Xsed -e "s,\.[^.]* ,.lo ,g;s,\.[^.]*\$,.lo,"`
AC_SUBST(LTLIBOBJS)
AC_OUTPUT(replace/Makefile lib/Makefile Makefile)
|
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 propogated 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 the section called Executing Uninstalled Binaries in the chapter called Introducing GNU Libtool. 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.
| <<< Previous | Home | Next >>> |
| Library Versioning | Up | A Large GNU Autotools Project |