You are not logged in.

sprezzatech blog #0006

Dirty South Supercomputers and Waffles
UNIX, HPC, cyberwarfare, and chasing perf in Atlanta.

autoconfization? autotoolizing? autoconfiscation? adding autotools, pt. 2.
Mon May 7 03:18:32 EDT 2012

G. Branden Robinson
Robinson, dressed as a blog.
Good ol' G. Branden Robinson had the following to say in Debian Bug #233980:
“The AC_PATH_XTRA macro seems to be some sort of mindless miscellaneous thing that's only useful for developers who have no idea *what* their real dependencies are.

It should go away.

See bugs like #233969 for why.

The package has everything it needs to build, but the configure script fails because the brain-damaged AC_PATH_XTRA macro is used.”
Regarding AC_PATH_XTRA, the Autotools Manual says the following:
“Macro: AC_PATH_XTRA. An enhanced version of AC_PATH_X.”
With that duly noted, let's continue our stroll through the world of GNU Autotools.

Last time we discussed some general operating principles of Autotools and our objectives in adopting them. This post will concentrate on the gritty details of my first Makefile.am and configure.ac. You'll recall the latter had one line, the AC_INIT() macro necessary and sufficient for a minimal input to autoreconf -fi (and thus autoconf). Let's begin to flesh it out. configure.ac is going to be processed by autoconf (hence the file extension), which operates via invoking autom4te (a cacher) on the input's m4 macros. You are encouraged to examine the following diagram:
There most likely exists at least one error in this diagram's representation of Autotools flow, discounting the more general error of such a shitty diagram's existence.

Off the bat, we add the following to configure.ac:
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
Automake's machinations are, essentially:
  1. Read Makefile.am, writing through everything save automake comments (lines beginning with “##”), aggregating assignments (those using “+=”), automake conditionals and include directives through to Makefile.in.
I think of it as a Make preprocessor.

AM_INIT_AUTOMAKE performs necessary automake initialization, and accepts as arguments all Automake options. Here, we've indicated that we are “foreign” (non-GNU), and thus it is not necessary that we adhere to GNU documentation conventions. In particular, we needn't provide NEWS, AUTHORS, README, or ChangeLog. -Wall -Werror tells automate to emit all possible warnings, and to treat them as errors (i.e., abort).


This immediately identifies some problems in our Makefile.am:
[skynet](0) $ autoreconf 
Makefile.am:144: user target `install' defined here...
/usr/share/automake-1.11/am/install.am: ... overrides Automake target `install'
Makefile.am:151: user target `uninstall' defined here...
/usr/share/automake-1.11/am/install.am: ... overrides Automake target `uninstall'
Makefile.am:151: consider using uninstall-local instead of uninstall
Makefile.am:127: user target `clean' defined here...
automake: ... overrides Automake target `clean' defined here
Makefile.am:127: consider using clean-local instead of clean
autoreconf: automake failed with exit status: 1
[skynet](1) $

We move CFLAGS over to AM_CFLAGS, and they're picked up by the build. We comment out our clean, install, and uninstall targets, and begin converting them: clean is split into clean-local and maintainer-clean-local (details on the clean targets can be found here). We've inherited a 'distclean' target which cleans all configure outputs, including Makefile, though it requires running configure to do so—i.e. we generate output just to remove it. If we were using automake more completely, this would have been unnecessary—all outputs would have been determined by and known to autotools, which would clean them up itself. We're left with:
clean-local:
        rm -rf $(OUT) $(wildcard core*) $(wildcard vgcore*) tags
        rm -rf $(IANAOUI) $(IANAOUI).raw $(USBIDS)

maintainer-clean-local:
        rm -rf aclocal.m4 configure config config.in config.in~ \
		install-sh missing Makefile.in

install becomes install-exec-local and install-data-local. The chains here are:
install -> install-am -> all-am
				-> Makefile
				-> config.h
		-> install-exec-am -> install-exec-local
		-> install-data-am -> install-data-local

Along the way, we add $(DESTDIR) support (necessary for Debian packaging), and use Autotools-exported variables such as ${docdir}. Here's the result:
install-exec-local: all doc
	@mkdir -p $(DESTDIR)${bindir}
	$(ADDCAPS) $(BIN)
	$(INSTALL) -m 0755 $(BIN) $(DESTDIR)${bindir}

install-data-local: all doc
	@mkdir -p $(DESTDIR)${mandir}/man1 $(DESTDIR)${docdir}
	$(INSTALL_DATA) -m 0644 $(MAN1OBJ) $(DESTDIR)${mandir}/man1
	$(INSTALL_DATA) -m 0644 $(XHTML) $(DESTDIR)${docdir}/

Now, installing based on autotools invocation means we need look for the data in an autotools-specified place. One's first thought might be to directly export the value of, say, $(DESTDIR)${datadir}, to a header file using autoheader. This isn't considered a great idea by the autotools community; it is instead encouraged to add a construction like:
AM_CFLAGS+=-DDATADIR=\"$(datadir)\"

to Makefile.am. Either way, we remove the constant path from omphalos.c and make use of OMPHALOS_DATADIR. This raises a general question: how to minimally enforce dependencies on configuration changes? On one end is dependence on the user to run “make clean” at all appropriate times, on the other a dependency on $(MAKEFILE_LIST) for all leaf productions. The latter seems to me the most correct solution, but it's awfully annoying when one's actively working on the build system. While global changes (such as compiler or CFLAGS) by their nature require a global rebuild, definitions ought not. This can be effected by restricting their command-line definitions to compilation of a single production (use a specialization production in GNU Make), depping that production on $(MAKEFILE_LIST), and restricting other sources' use of the results to an extern interface. Of course, if the definitions' values are needed by other source files at compilation time, this won't fly.

And now there is happiness:
[skynet](0) $ sudo make install
make[1]: Entering directory `/home/dank/src/omphalos-0.99.4'
ctags --languages=C,Sh,Make -R .
make  all-am
make[2]: Entering directory `/home/dank/src/omphalos-0.99.4'
make[2]: Leaving directory `/home/dank/src/omphalos-0.99.4'
tools/addcaps out/omphalos/omphalos-coretest out/omphalos/omphalos-tty \
 out/omphalos/omphalos-x out/omphalos/omphalos-ncurses
/usr/bin/install -c -m 0755 out/omphalos/omphalos-coretest \
 out/omphalos/omphalos-tty out/omphalos/omphalos-x \
 out/omphalos/omphalos-ncurses /usr/local/bin
/usr/bin/install -c -m 644 -m 0644 out/doc/man/man1/omphalos-coretest.1 \
 out/doc/man/man1/omphalos-tty.1 out/doc/man/man1/omphalos-x.1 \
 out/doc/man/man1/omphalos-ncurses.1 /usr/local/share/man/man1 \
/usr/bin/install -c -m 644 -m 0644 out/doc/xhtml/omphalos-coretest.xhtml \
 out/doc/xhtml/omphalos-tty.xhtml out/doc/xhtml/omphalos-x.xhtml \
 out/doc/xhtml/omphalos-ncurses.xhtml /usr/local/share/doc/omphalos/
make[1]: Leaving directory `/home/dank/src/omphalos-0.99.4'
[skynet](0) $ 

Meanwhile, "./configure --prefix=/ && make install" does what we'd expect. Excellent!

Next, my original impetus for picking up Autotools: dealing with libnl. Per the libnl site, I added:
PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1, [have_libnl3=yes], [have_libnl3=no])
if (test "${have_libnl3}" = "yes"); then
        CFLAGS+="$LIBNL3_CFLAGS"
        LIBS+="$LIBNL3_LIBS"
fi

Well, not quite. The libnl site contained an error (spaces around the += operators), which doesn't surprise me at all, since the libnl project appears to be run by meth-addled simians roaming the jungle on some horrible open source safari. Furthermore, omphalos needs actually check for 3.0, because 3.1 broke essentially everything about the library, as one might expect from a minor version update from a supposedly-mature project, and I've not yet figured out how to deal with it. Without the removal of the whitespace, we get:
./configure: line 5504: CFLAGS: command not found
./configure: line 5505: LIBS: command not found

and, guess what, we don't link! So, the example code posted by the libnl team doesn't work, at all, and has perhaps never been run, and the output certainly hasn't been looked at, or if it was they were like HEY WHO CARES ABOUT COMMANDS NOT FOUND WE DON'T EVEN HAVE A COMMAND THERE DERRRRRP! I sure love using libnl! Deep breaths. Calm blue oceans. I informed libnl maintainers of the error, and verified that it had been corrected.
— Macro: PKG_CHECK_MODULES(prefix, list-of-modules, action-if-found, action-if-not-found)
This uses pkg-config to search for a library (we'll see AC_CHECK_LIB and AC_TRY_LINK can be used for linkables not registered with pkgconfig). As provided by libnl, ./configure will continue running in the absence of the library. By removing [have_libnl3=no] aka the action-if-not-found (and the now-useless conditional structure following the test), we force ./configure to abort:
checking for LIBNL3... no
configure: error: Package requirements (libnl-3.0 >= 3.1) were not met:

Requested 'libnl-3.0 >= 3.1' but version of libnl is 3.0

Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.

Alternatively, you may set the environment variables LIBNL3_CFLAGS
and LIBNL3_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
[skynet](1) $ 

This is, admittedly, much more helpful than a page of linker errors. libsysfs and zlib were quickly converted. Those libraries lacking pkgconfig support were converted to AC_CHECK_LIB macros:
— Macro: AC_CHECK_LIB (library, function, [action-if-found], [action-if-not-found], [other-libraries])
The default action is to append -llibrary to LIBS and define HAVE_LIBlibrary, which is pretty much exactly what we want.

We need a manpage reindexer (makewhatis on FreeBSD, mandb on Linux):
AC_CHECK_PROGS(MANBIN,makewhatis mandb,no)
if test "$MANBIN" = no ; then
 AC_MSG_ERROR(Cannot find manpage indexer.)
fi
Now our install and uninstall targets appear to completely work, as does distcheck.

Go check out omphalos's configure.ac and Makefile.am if you want the whole story. I'm tired of writing on this topic, and feel insecure regarding my understanding of it, and think the toolchain overall is pretty ad hoc and crappy. Finishing up this post (if you can call this “finishing”) has kept me from writing much more interesting material, regarding which I'd be infinitely more authoritative, and I refuse to allow it to do so any longer. :D Hack on!

LINKS TO BETTER AUTOTOOLS COVERAGE THAN IS AVAILABLE HERE:
  1. X.Org New Module Guidelines
  2. GNU Autoconf [macro] Archive
  3. Autotools Mythbuster
  4. Using Automake and Autoconf with C++
  5. Existing Tests