2. Non-recursive Automake

One of the common criticisms of automake is that most projects that make use of it use multiple Makefile.am files, one per sub-directory of the project, causing pollution, confusion and further problems. For quite a while, though, automake supports non-recursive builds, with a single Makefile.am (or at least a reduced number of those).

This is, actually, the best method to use with automake, since this not only covers the problems discussed in [MillerRecursiveMake] but also avoids the need for multiple convenience libraries to store the partial results.

To summarise, the advantages of non-recursive make over the recursive “classic” version are:

2.1. Achieving Non-Recursive Make

The use of non-recursive automake is actually simpler than the use of the recursive variant; instead of creating multiple files, you just need a top-level Makefile.am file that references source files with a relative path:

bin_PROGRAMS = foo

foo_SOURCES = \
        src/component1/component1.c \
        src/component2/component2.c \
        src/main.c

Warning

Even though repeating the same path prefix for each source file might seem a useless repetition, it is a common mistake to try using a variable to hold the prefix. Indeed automake does not expand variables in _SOURCES definitions.

While this is likely going to be addressed in future versions of automake, at the time of writing (January 2016) no released version contains a fix for this. Even if they did, it is recommended against relying on it for maximum compatibility.

This will compile all the source files in object files directly inside the top build directory; this works fine for most cases, but it might not be desirable if either the source tree is very big (and thus a high number of files would be added to the same directory) or there are source files with the same name minus the path.

To solve this problem, you just have to ask automake to create objects in sub-directories, following the same structure the sources are in. To do so, you just have to change the AM_INIT_AUTOMAKE call in configure.ac and add the option subdir-objects:

AM_INIT_AUTOMAKE([subdir-objects])

For most needs, this will solve the problem of non-recursive automake just fine, and no more tweaks will be needed. For more specific cases, check the following sections as well.

2.2. Sub-dividing results

While the previous section has shown the use of subdir-objects to keep the object files in the same structure as the source files, it has only declared the main program to be built in the top-level build directory. Sometimes this is not the needed behaviour.

It is certainly common to desire some organisation of the build products. For grouping together libraries, tools, examples and tests for instance and automake allows that without having to resort to the recursive variant.

Example 2.2. Grouping tests together in a non-recursive Makefile.am

FOO_BUILT_TESTS = tests/foo_test1 tests/foo_test2 tests/foo_test3
TESTS = $(FOO_BUILT_TESTS) tests/foo_test4.sh
check_PROGRAMS = $(FOO_BUILT_TESTS)

tests_foo_test1_SOURCES = tests/foo_test1.c
tests_foo_test1_LDADD = libfoo.la

tests_foo_test2_SOURCES = tests/foo_test2.c
tests_foo_test2_LDADD = libfoo.la

tests_foo_test3_SOURCES = tests/foo_test3.c
tests_foo_test3_LDADD = libfoo.la

In the fragment of Makefile.am above, you can see that the tests for the package are listed with their relative path even in the check_PROGRAMS variable, where the output names are used.

Further down the file, the variables used for passing sources and libraries to the tests also use the full relative path, replacing the / character with the safe value for variable names _.


2.3. Custom Rules and Non-Recursive Makefile.am

When using custom rules to generate files, there are a few problems to be considered. Rules that don't use full relative paths for targets and dependencies could be fouled up by stray files left around. Take for instance the following snippet:

pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = pkgconfig/foo.pc pkgconfig/foo-bar.pc

%-bar.pc: %.pc
        $(LN_S) $^ $@

If a Makefile.am is using the code above, it would fail, creating a symbolic link that also contains the relative path: pkgconfig/foo-bar.pcpkgconfig/pkgconfig/foo.pc.

To avoid this kind of problem, you can make use of GNU make extended functions in the rules, to transform the path from full-relative form to base form (without the path). For instance the fragment above should be replaced by the following:

pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = pkgconfig/foo.pc pkgconfig/foo-bar.pc

%-bar.pc: %.pc
        $(LN_S) $(notdir $^) $@