Autotools

January 10, 2022

I recently bought the book: “Autotools: A practitioner’s guide to GNU Autoconf, Automake, and Libtool,” by John Calcote. I was reading some articles on build systems and I came across a post from the Sabotage Linux blog explaining build systems for C/C++. It’s a fairly good overview and there is another post talking about build tools in When progress is backwards: Showcase 5. During the holiday, I spent some time following the Linux From Scratch (LFS) guide, and if you learn anything from it, it’s how to build other people’s software. You will most likely run into autotools usage along the way, as most of the applications in a Linux distribution are written in C.

Back to the book. It’s a fairly nice introduction, but the larger examples became harder to follow along in the end without a project of my own to use for hands on experience. I think autotools code is probably best copied from a bunch of other recipes as if you were to make your own cookbook.

When you follow the LFS guide, you really begin to appreciate packages that ship with minimal dependencies. Autotools-based packages just need a POSIX shell and GNU Make, which is one of its largest strengths, not just because of the ease of bootstrapping, but also because of its transparency. Autoconf/Automake set up a bunch of common targets that distributions have come to expect (with regards to cross compiling, --prefix, DESTDIR=.., and so on), and people coding things themselves will likely leave a lot of those out to the chagrin of distribution maintainers.

Separately, but on a similar topic, I spent a little time before I read the book trying to learn the m4 macro processing language, which is a little tricky at first glance. m4 is used by autoconf to generate the configure script among other things. m4 takes a little time to wrap your head around if you’re new to it. Here is a sample program from the GNU m4 manual:

changequote([,])dnl
define([gl_STRING_MODULE_INDICATOR],
  [dnl comment
  GNULIB_[]translit([[$1]], [a-z], [A-Z])=1dnl
])dnl
  gl_STRING_MODULE_INDICATOR([strcase])

The output of the program is:

   GNULIB_STRCASE=1

It’s readable, but if you were asked why the following doesn’t work, you would need an understanding of whitespace tokenization and quoting in macro expansion.

changequote([,])dnl
define([gl_STRING_MODULE_INDICATOR],
  [
    dnl comment
    GNULIB_]translit([$1],[a-z],[A-Z])[=1
  ])dnl
  gl_STRING_MODULE_INDICATOR([strcase])
  
  
⇒        GNULIB_strcase=1