|
w w w . a q u a m e n t u s . c o m |
What is autotools?
Coding Standards
Makefile.am
Primaries/patterns
configure.ac
AC_INIT
AM_INIT_AUTOMAKE
AC_PROG_*
AC_CONFIG_FILES
AC_OUTPUT
Specific examples
Case 1a: one .c file
Case 1b: one .c, with internal .h
Case 2a: one .c file, in src/
Case 2c: src/.c file with include/.h file
Case 3: one .c file, and a README to install
Case 4: one .c file, and a .h you want to export
Case 5: one .c file, one man file
Case 6: one .c file, one .texi file
Case 7: multiple .c files
Case 8: one .c file, and generate config.h
Case 9a: one .c file, becoming an exported static lib
Case 9b: one .c file, becoming an unexported static lib
Case 9c: one .c file, becoming an exported static lib used internally
Case 9a-2: one .c file, with exported static lib, but using libtool
Case 10a: one .c file that turns into an exported dynamic lib
Case 10b: one .c file becoming an unexported dynamic lib
Case 10c: one .c file, becoming an exported dynamic lib used internally
Case 11a: one .c, using an external .a
Case 11b: one .c, using an external .so or .dylib
What is autotools?
Autotools is a collection of programs that help package up source code
for distribution and deployment. Historically, it's most relevant
for compiled languages (C and C++), since compilation is highly
susceptible to platform issues. (For completeness, it directly
supports a few other languages/tools: objective-C, objective-C++,
fortran77, fortran90, yacc, lex..probably others.)
For purposes of this tutorial, I'm trying to get the most number of consumers
running with the least amount of drink-from-the-firehose. Thus, this is
targeted for C or C++ projects that don't use lex/yacc.
Autotools is really a trio of tools:
- automake converts a Makefile.am to Makefile, smoothing out platform
differences in declaring "what" to build.
- autoconf converts Makefile.in to Makefile, smoothing out the
platform differences in the question of "how" to build.
- libtool smoothes out the platform differences in dynamic libs
(and handles static libs as a bonus).
One question I haven't been able to answer is whether these are needed at all
for POSIX systems, since the point of defining standards is to eliminate
hacks for differences.
Since we're using something to generate the Makefile we usually manage by
hand, that means there are new ways to do all the things you're used to do
doing, such as:
- adding more files to compile
- adding more targets to build/link
- adding -I flags for the compiler
- adding -L/-l flags for the linker
- adding -D flags for build-specific overrides
Your original workflow would go something like this:
- edit Makefile
- edit source
- "make"
- if program problem, go to [2]
- if build problem, go to [1], then [3]
The autotools workflow is a little different:
- edit Makefile.am and/or configure.ac
- run "autoreconf --install"
- run "./configure"
- edit source
- "make"
- if program problem, go to [4]
- if build problem, go to [1], [2], [3], [5]
When all your code is ready to go, you can package it up like so:
- make dist # generates a $pkg.$ver.tar.gz file
- make distcheck # unpacks/builds the $pkg.$ver.tar.gz to make sure it's okay
Then your users can download it and install as usual like so:
- tar xf $pkg.$ver.tar.gz
- cd $pkg.$ver
- ./configure
- make
- make check
- make install
SourceForge has a pretty good tutorial
here.
Coding Standards
It's worth mentioning the
GNU coding standards
because autotools tries valiantly to enforce them. At
85 pages, the Coding Standards are not a quick read, but there's
some interesting stuff in there.
Here are the highlights you might want to know about for your autotools
project.
First, put your code in a src/
directory, and your headers in an
include/
directory. As your project gets more productized, you may
also want to create man/
, data/
, info/
, and who knows what else.
Starting off with these functional directories is the first step to keeping
your code manageable as it scales.
Second, since GNU packages are handed off to complete strangers who don't
give one whit about internals, there is a distinct interface defined for
packages. This interface makes GNU packages consistent to users, which
-- as a user yourself -- I'm sure you can appreciate. To that end, the
following additional files are expected (though not required):
- README
- INSTALL
- NEWS
- AUTHORS
- ChangeLog
- COPYING
Makefile.am
The first of two very important files, the Makefile.am is where you'll define
what targets to make and what goes into them. This not a generic makefile,
but one that expects targets with specific naming conventions in order to
accomplish specific things.
(The ".am" suffix is for "automake", by the way.)
You'll have one Makefile.am in each directory to build, plus one at the top
level. To follow standards, that means at least ./Makefile.am
and
src/Makefile.am
. To say what the build directories are, the one at the
top-level will contain:
SUBDIRS = src
Primaries/patterns
For whatever reason, automake uses the word "primaries" for what the rest of
the world calls "makefile patterns", so when you read the
documentation, that's what that means. Here are some of the suffix-type
patterns/primaries you can use:
- .*_PROGRAMS: binaries to be compiled and linked. You'll see "bin_PROGRAMS" a lot, and that
means that the listed program targets should be installed to
bin
. (Well,
after some indirection.)
- .*_SOURCES: list of source files for a particular program, which will be used
both to compile and to be included in the "make dist" tarball. This is
just the .c files, not the .h files.
- .*_SCRIPTS:
- .*_DATA: architecture-independent data files that are usually just copied
to their install location. Note that
data
files have historically
been autogenerated, so by default they're not included in the package.
Add the "dist_" prefix to have them included.
- .*_LIBRARIES:
- .*_LTLIBRARIES:
- .*_LISP:
- .*_PYTHON:
- .*_JAVA:
- .*_HEADERS:
- .*_MANS:
- .*_TEXINFOS:
- .*dir: declares the install area for specific targets
I've also seen .*_CPPFLAGS
and .*_LDADD.
In addition, there are some prefix-type patterns:
- noinst_.*: build but do not install the listed things
- check_.*: build only if 'make check' is run, and also do not install
- dist_.*: add the listed things to the distribution
- nodist_.*: inhibit addition of the listed things to the distribution
- nobase_.*: do not flatten include-file directories in installation
Anything not matched by the Makefile.am patterns is considered a sort of
identifier for the actual target. Here's an example of us coming up with
a custom 'xml' target for some package:
xmldir = $(datadir)/xml
dist_xml_DATA = foo.xml # foo.xml now installed to $(datadir)/xml
A very common identifier you'll see is bin
, which semantically means the
top-level program(s).
Because of the reliance on predictable patterns, the list of characters allowed
in your file names is really strict. Anything in your actual target names that
isn't a letter, number, or "@" will have to be converted to an "_".
Note: if you want to set compiler flags, set AM_CFLAGS or AM_CXXFLAGS. They will
(eventually) get added to the real CFLAGS.
configure.ac
The configure.ac
file is just a collection of function calls (which are
executed via expansion by m4
). These function calls usually begin with
one of two things: "AC_" means it's an autoconf macro, and "AM_" means
it's an automake macro.
As you've probably guessed, the ".ac" extension is for "autoconf".
Here are some of the more common functions you'll encounter/use.
AC_INIT
The AC_INIT function declares information about the package. It has three
parameters:
- the name of the package
- the version of the package
- an email address where users can get ahold of you.
The version should adhere to
common standards.
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE
This function fills in some control points for the Makefile that will
eventually be generated. It takes one parameter, which itself is a
space-separated list of random crap. Some of the random crap is flags to
give to the compiler, and some of it is keywords that automake recognizes.
In particular, you might want to specify foreign
, which lifts the requirement
of defining all the files needed to make the package adhere to GNU Coding
Standards.
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_*
This function tells autoconf to check and make sure that a particular
program exists on the target machine. This is usually used to make sure the
compiler's found.
AC_PROG_CC
AC_CONFIG_FILES
This function takes a single parameter, which is a space-separated list
of files to generate. This is where to declare that Makefile
is one
of the things you'd like to make.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
This very important function tells autoconf to actually generate the files.
It's also very important is to call this last, after everything else has
been set up or declared.
AC_OUTPUT
Specific examples
The objective of these examples is to show you a minimal working example, and
then a bunch of the common extensions to that baseline. The minimal example
is a project consisting entirely of a single .c file; some of the common
extensions then are things like:
- having more than one .c file
- having (and maybe exporting) .h files
- building (and maybe exporting) static libs
- building (and maybe exporting) dynamic libs
- exporting data files
- exporting man files
- using config.h
Case 1a: one .c file
This is the minimal project that we can send through autotools, so it's
basically the "hello, world" example. The .c file is in the current dir. It
requires no headers (either internal or external), does not generate a
lib, does not use any external packages, and does not need the config.h.
- create your foo.c
- create your Makefile.am:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
- create your configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo". omg it works!
- run "make dist". It creates foo-0.1.0.tar.gz which you can then publish.
Case 1b: one .c, with internal .h
This is the same as Case 1 except we also have a header file (which we don't
want to export). Since we're not exporting it, and compilers don't need to
be told to look in the current directory for headers, there's actually
nothing special we need to tell autotools. Thus, this is the same as
the above.
Case 2a: one .c file, in src/
This is pretty much the same as above, except it puts the code into
the src
dir like GNU asks.
- create your src/foo.c
- create the src/Makefile.am:
bin_PROGRAMS = foo
foo_SOURCES = foo.c
- create the ./Makefile.am:
SUBDIRS = src
- create the configure.ac. It's almost the same as the above, except add
"src/Makefile" to the AC_CONFIG_FILES call:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile src/Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 2c: src/.c file with include/.h file
This shows you how to inject a local -I into your generated makefile.
- create src/foo.c and include/foo.h.
- create the src/Makefile.am as above in [2a], but also add AM_CPPFLAGS:
bin_PROGRAMS = foo
foo_SOURCES = foo.c
- create the ./Makefile.am:
SUBDIRS = src
AM_CPPFLAGS = -I$(srcdir)/../include
- create the configure.ac. Same as [2a]:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile src/Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 3: one .c file, and a README to install
- create your foo.c and your README
- create the Makefile.am. It's almost the same as [case 1], except you
add
dist_doc_DATA
:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
dist_doc_DATA = README # list of files to install to /usr/local/share/doc/foo/
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 4: one .c file, and a .h you want to export
Note that exporting a .h means installing to /usr/local/include
- create your foo.c and foo.h
- create the Makefile.am. It's almost the same as [case 1], except you
add
include_HEADERS
:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
include_HEADERS = foo.h
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Just to warn about a pitfall: make install
copies the header files to
$(includedir)
, which means any local subdirs you're using are essentially removed.
If that's bad, use nobase_include_HEADERS
instead to preserve the relative
directories on install.
Case 5: one .c file, one man file
- create your foo.c and foo.2 (or whatever section you want it in)
- create the Makefile.am. It's almost the same as [case 1], except you
add
man_MANS
:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
man_MANS = foo.2 # list of files to install to /usr/local/share/man/man(#section)
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Another pitfall warning: man files are not considered 'source' (because
they're commonly autogenerated), so they're not included in 'make dist'!
If your man page actually is source, use dist_man_MANS
instead to force
inclusion in 'make dist')
Case 6: one .c file, one .texi file
.texi files are used by the info
program, which some rather vocal people
think are better than man
pages.
- create your foo.c and foo.texi
- create the Makefile.am. It's almost the same as [case 1], except you
add
info_TEXINFOS
:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
info_TEXINFOS = foo.texi # produces foo.info, includes it in 'make dist' and installs it to /usr/local/share/info
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 7: multiple .c files
- create your foo.c and bar.c files
- create the Makefile.am. It's almost the same as [case1], except you
set the
_SOURCES
field to the complete list of your .c files:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c bar.c
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 8: one .c file, and generate config.h
The config.h
file contains a bunch of #defines
that record lots of
configuration info that was detected when the user ran ./configure
.
For example, it has the version number that you declared
in configure.ac
, so if you wanted your help message to print the version,
you could get it from there instead of hardcoding it in multiple places.
- create a foo.c that #include's "config.h"
- create the Makefile.am as usual:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
- create the configure.ac as usual, but add AC_CONFIG_HEADERS somewhere
before the AC_OUTPUT:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_CONFIG_HEADERS([config.h]) # make it generate the config.h
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 9a: one .c file, becoming an exported static lib
- create foo.c as usual
- create the Makefile.am for the lib build and export:
lib_LIBRARIES = libfoo.a
libfoo_a_SOURCES = foo.c
- create the configure.ac as usual, but add
AC_PROG_RANLIB
and
AM_PROG_AR
somewhere before the AC_OUTPUT
:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_PROG_RANLIB # check for ranlib
AM_PROG_AR # check for ar
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 9b: one .c file, becoming an unexported static lib
This obviously only makes sense if the unexported static lib is used by some
second .c file in this package.
- create bar.c to become libbar.a, and foo.c that uses libbar.a
- create the Makefile.am:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
noinst_LIBRARIES = libbar.a # declare need to build libbar.a, without installing
libbar_a_SOURCES = bar.c # how to build bar.a
foo_LDADD = libbar.a # declare that foo needs libbar.a
- create the configure.ac just like in [case 9a]:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_PROG_RANLIB # check for ranlib
AM_PROG_AR # check for ar
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 9c: one .c file, becoming an exported static lib used internally
- create bar.c to become libbar.a, and foo.c that uses libbar.a
- create the Makefile.am:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
lib_LIBRARIES = libbar.a # declare need to build libbar.a, installing to $libdir
libbar_a_SOURCES = bar.c # how to build bar.a
foo_LDADD = libbar.a # declare that foo needs libbar.a
3. create the configure.ac just like in [case 9a]:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_PROG_RANLIB # check for ranlib
AM_PROG_AR # check for ar
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Note: it's possible all the static libs can be done by the libtool
mechanism, making all the case 9's a subset of the case 10's. In fact,
let's do [9a] over again, but using libtool:
Case 9a-2: one .c file, with exported static lib, but using libtool
- create foo.c as usual
- create the Makefile.am like so:
lib_LIBRARIES = libfoo.a
libfoo_a_SOURCES = foo.c
ACLOCAL_AMFLAGS = -I m4
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_PROG_AR
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AM_PROG_LIBTOOL
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_CONFIG_MACRO_DIR([m4])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Yes, even though libtool
is meant to replace ar
and ranlib
, you still need
AM_PROG_AR
. And, bizarrely, they want it before AM_INIT_AUTOMAKE
.
Case 10a: one .c file that turns into an exported dynamic lib
- create foo.c as usual
- create the Makefile.am:
lib_LTLIBRARIES = libfoo.la
libfoo_la_SOURCES = foo.c
ACLOCAL_AMFLAGS = -I m4
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_PROG_AR
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AM_PROG_LIBTOOL
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_CONFIG_MACRO_DIR([m4])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 10b: one .c file becoming an unexported dynamic lib
Again, this only makes sense if there's a second .c file that's using the
dynamic lib. However, this particular situation is highly inadvisable because if you don't
export the dynamic lib, then the exported binary won't be able to run.
So, I'm not even going to show you how to do it.
Case 10c: one .c file, becoming an exported dynamic lib used internally
- create foo.c to become libfoo.so, and bar.c that uses libfoo.so
- create the Makefile.am like so:
bin_PROGRAMS = bar
bar_SOURCES = bar.c
bar_LDADD = libfoo.la
lib_LTLIBRARIES = libfoo.la
libfoo_la_SOURCES = foo.c
ACLOCAL_AMFLAGS = -I m4
- create the configure.ac like so:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_PROG_AR
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AM_PROG_LIBTOOL
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_CONFIG_MACRO_DIR([m4])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Case 11a: one .c, using an external .a
- create foo.c as usual
- create the Makefile.am as usual, but add an
_LDADD
:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
foo_LDADD = /usr/local/lib/libsomething.a
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Note that you specify the full path to the .a file.
Case 11b: one .c, using an external .so or .dylib
- create foo.c as usual
- create the Makefile.am as usual, but add an
_LDADD
:
bin_PROGRAMS = foo # list of programs to install to /usr/local/bin/
foo_SOURCES = foo.c
foo_LDADD = -lsomething
Note that you could specify -L yourself:
..
foo_LDADD = -L/usr/local/lib/ -lsomething
- create the configure.ac:
AC_INIT([foo], [0.1.0], [foo@bar.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
- run "autoreconf --install"
- run "./configure"
- run "make"
- run "./foo"
- run "make dist"
Chris verBurg
2015-03-08