Linux From Scratch builds coreutils with an extra patch, the i18n patch, which adds multibyte character support to tools like cut and expand. In my scripted LFS build, applying that one patch took four rounds of fixes spread over eleven days. Each fix worked against the error in front of it and left a different one behind, and the last of them depended on understanding why automake wanted to run at all.
Round one: a 404 page saved as a patch
My build's version file pinned coreutils 9.11, and the download rule built the patch's URL from that version number. LFS only ever published the i18n patch for 9.10, so the URL for a 9.11 patch didn't exist.
The download didn't fail, though. The rule ran curl -L -o with the URL, and without --fail, curl treats an HTTP error as a successful transfer of whatever the server sent. The curl manual describes the --fail option as the way to get an error code and no output for responses of 400 or above; without it, the server's error page is the output. So the "patch file" on disk was an HTML error page, and nothing failed at download time.
The fix hardcoded 9.10 in the URL while keeping the local file name tied to the version variable, because the build script and the source-population step both expected that name.
Round two: the patch didn't fit 9.11
With the real patch downloaded, it still didn't apply to 9.11. Several hunks in cut.c, expand.c and unexpand.c failed outright instead of applying with fuzz, and in expand.c and unexpand.c every single hunk failed. The patch was written against 9.10, and 9.10 is the version the LFS book pairs it with. So the build pinned coreutils to 9.10, and the patch applied cleanly.
Round three: make wanted automake-1.18
Then make stopped, trying to run aclocal-1.18 and automake-1.18, neither of which was installed on the host. Nothing in the build had asked for automake. The tarball ships generated files, including configure, aclocal.m4 and every Makefile.in, precisely so that users don't need the autotools.
The reason is in the generated files themselves. Coreutils 9.10's Makefile.in has rebuild rules: Makefile.in depends on Makefile.am and several included fragments, among them tests/local.mk, plus everything configure depends on, and configure and aclocal.m4 depend on configure.ac and the m4 macro files. AM_MAINTAINER_MODE is the macro a project uses to switch those rules off for ordinary builds, and coreutils doesn't use it, so they stay active. If any source is newer than the file generated from it, make regenerates it. And the generated configure names the tool with its version: it sets automake to automake-1.18, through automake's missing wrapper, unless something else is given.
The i18n patch edits tests/local.mk, configure.ac and m4/mbfile.m4. Patching makes those files newer than the generated ones, so make tried to regenerate them with tools of the exact version that made the tarball.
The first workaround was to touch Makefile.in after patching, so it looked newer than its sources. That wasn't enough, because configure.ac had changed too, so the next attempt touched the whole chain in dependency order: aclocal.m4, then configure, then every Makefile.in. A comment in the script said the patch's code changes didn't depend on the regeneration.
Round four: the regeneration was the point
That comment was wrong. Among the patch's changes to configure.ac is an AC_DEFINE for GNULIB_MBFILE, and the struct mbchar in lib/mbchar.h depends on it. Skip the regeneration and the definition never reaches the build, so struct mbchar lacks a member the patch's own mbfile.h uses. The build failed with:
'struct mbchar' has no member named 'buf'
An attempt that replaced the autotools with true had the same problem: it avoided the missing binary and silently skipped the change the patch needed.
The real fix came from reading configure.ac. It asks for automake 1.11.2 or newer, in AM_INIT_AUTOMAKE([1.11.2 ...]). The host had automake 1.16.5 and autoconf 2.71, comfortably above that. Version 1.18 was only the version that happened to generate the tarball. And because configure only uses automake-1.18 when nothing else is given, passing the plain tool names overrides it:
./configure ... ACLOCAL=aclocal AUTOCONF=autoconf AUTOHEADER=autoheader AUTOMAKE=automake
With that, make regenerates the files for real, with the host's tools, the definition ends up in the build, and the package compiles.
What generalises
Two things. First, if a patch touches configure.ac, Makefile.am, or anything they include, regeneration isn't an obstacle to route around; it's how the patch takes effect. Touching timestamps makes the error go away and the change with it. Second, the version in an error like "automake-1.18 is missing" is the version that generated the tarball, not necessarily the version the project needs. configure.ac says what it needs, and that's usually much older.
Several of this build's problems had the same shape: an error that pointed one way and a cause somewhere else, as with the font bug that traced back to a configure flag.
Leave a comment
Comments are reviewed before they appear.