Autoconf 2.70 was released at the end of 2021. This release had several compatibility issues and defects, and was rapidly replaced by Autoconf 2.71.
The upstream community is going to release another version, Autoconf 2.72. It may be time to have a closer look at its potential impact.
In a previous article, I explained that we developed a tool that can be useful in such a use case. The idea behind this tool is to massively rebuild packages that depend on a component for which we want to release a new version. With this tool, we could evaluate how many packages would break if the community decided to use Autoconf 2.72 at this point in development. The evaluation would allow us to propose fixes ahead of this release.
There were still some packages for which no fix could be made in Autoconf, because these packages were at fault for incorrect API usage.
In this article we cover common build failures that are seen with the new version, and how they can be fixed.
But everything was working fine before this new version!
Let's be honest, m4 is horrible as a language. It's easy to make mistakes, and these mistakes don't even necessarily lead to obvious errors.
The easiest and most common mistake is underquoting.
Some functions accept parameters, which may or may not need to be quoted (using [] by default). Yet, it may happen that not using these quotes will not break the processing, nor generate incorrect shell code; until it does.
During Autoconf 2.72 development, some code snippet was modified in order to port AC_LANG_CALL to C++, this small and innocent change was accompanied by a change in the associated comment, which introduced a comma, as shown in the diff below:
Port AC_LANG_CALL(C) to C++
* lib/autoconf/c.m4 (AC_LANG_CALL(C)): Add an extern "C" if C++.
Problem reported by Vincent Lefèvre (sr #110532).
diff --git a/lib/autoconf/c.m4 b/lib/autoconf/c.m4
index 44443a39..48bd49a3 100644
--- a/lib/autoconf/c.m4
+++ b/lib/autoconf/c.m4
@@ -126,7 +126,13 @@ m4_define([AC_LANG_CALL(C)],
m4_if([$2], [main], ,
[/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code, supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
char $2 ();])], [return $2 ();])])
This little character, between 'code' and 'supporting' broke 70 package builds in Fedora, out of around 1200 packages that need autoconf.
Why did they break? Because of underquoting. This function gets executed within other functions which need their parameters to be quoted by the caller, but are not. The result being that m4 interpreted the "," in the C code's comment as a split of the parameter list of the calling function, leading to the generation of incorrect shell code.
Another common mistake is a wrongly placed 'fi'.
This one was even found in Autoconf itself, during the development, and got fixed as follows:
diff --git a/lib/autoconf/functions.m4 b/lib/autoconf/functions.m4
index e7c54846..51aeb0b9 100644
--- a/lib/autoconf/functions.m4
+++ b/lib/autoconf/functions.m4
@@ -448,8 +448,10 @@ void *alloca (size_t);
]], [[char *p = (char *) alloca (1);
if (p) return 0;]])],
[ac_cv_func_alloca_works=yes],
- [ac_cv_func_alloca_works=no])])
+ [ac_cv_func_alloca_works=no]
+ )
fi
+])
if test $ac_cv_func_alloca_works = yes; then
AC_DEFINE(HAVE_ALLOCA, 1,
Why is this one popping up now? Because Autoconf modified the way it handles "if" statements, and more specifically the "else" part. It now makes use of a trick involving "case" statements in order to properly deal with cases where there is nothing to do in this "else".
The consequence is that these errors get unveiled, and now need to be fixed.
Another less common case is the appearance of a circular dependency. This kind of error isn't really the user's fault, as we can't ask you to know the internals of Autoconf before writing your code, but can still be avoided by using different constructs.
How are these errors detected?
Both underquoting and bad "fi" placement may lead to the same kind of error.
Basically, the generated configure code will likely raise a "syntax error" when being executed.
If you have underquoted code, the error will likely look like the following:
checking for IPv6 compile-time support without -DINET6...
./configure: line 10559: syntax error near unexpected token ';;'
./configure: line 10559: 'rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;'
If you have a misplaced "fi", the error will likely look like the following:
checking for CAIRO... yes
./configure: line 30733: syntax error near unexpected token 'fi'
./configure: line 30733: 'fi'
Yet, sometimes the error may be completely different from the above:
checking for zlib.h... yes
./configure: line 14589: syntax error near unexpected token 'newline'
./configure: line 14589: ' '''
In this case, the syntax error happened way after the actual quoting error, which may make the analysis more difficult.
How to find the source of the problem, and fix it?
In order to find the source of the error, you need to contextualize it first.
Look a few lines above, in order to figure out what kind of test was being executed, so that you can identify the piece of configure.ac that was wrongly translated to generate the error.
Then, you need to review the corresponding piece of code. Look at common AS_* functions, and verify that the parameters are properly quoted:
AS_IF([test "$enable_tests" = yes],
- PKG_CHECK_MODULES([GLIB], [glib-2.0])
+ [PKG_CHECK_MODULES([GLIB], [glib-2.0])
AC_CHECK_LIB([event], [event_base_new], [EVENT_LIBS=-levent])
- AC_SUBST(EVENT_LIBS))
+ AC_SUBST(EVENT_LIBS)])
In the example above, you can see that quotes were missing around the second argument of the AS_IF function. These quotes should be there even if there is only one function call as the second argument:
-AS_IF([test "x$MD5PROG" = "x"], AC_CHECK_PROG([MD5PROG], [md5sum], [md5sum -t]), [])
+AS_IF([test "x$MD5PROG" = "x"], [AC_CHECK_PROG([MD5PROG], [md5sum], [md5sum -t])], [])
If you have some "if" statement, check that the "fi" is properly placed:
AC_CHECK_LIB(edit, readline, [READLINE=edit EDITLINE_FLG=Y],
AC_CHECK_LIB(editline, readline, [READLINE=editline EDITLINE_FLG=Y],
AC_CHECK_LIB(readline, readline, [READLINE=readline EDITLINE_FLG=Y],
[STD_EDITLINE=false
if test "$EDITLINE_FLG" = "Y"; then
- AC_MSG_WARN([[[--with-system-editline specified, not found. Using bundled editline]]])])))
+ AC_MSG_WARN([[[--with-system-editline specified, not found. Using bundled editline]]])
fi
+ ])))
Finally, avoid to use AC_EGREP_CPP when AC_PREPROC_IFELSE can do the job (and more efficiently):
dnl -------------------------
dnl CHECK_HEADER_DEFINE(LABEL, HEADER [,ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ] ])
AC_DEFUN([CHECK_HEADER_DEFINE],
[
AC_MSG_CHECKING("if $1 is defined in $2")
- AC_EGREP_CPP(yes,
-[#include <$2>
-#ifdef $1
- yes
+ AC_PREPROC_IFELSE(
+[[#include <$2>
+#ifndef $1
+#error not defined
#endif
-], [
+]], [
AC_MSG_RESULT(yes)
[$3]
], [
AC_MSG_RESULT(no)
[$4]
This change fixed a circular dependency introduced by a change in the implementation of AC_EGREP_CPP. A side effect is simpler shell code for the test: about 25 lines of shell code that got removed from the configure script, per CHECK_HEADER_DEFINE call.
Conclusion
Autoconf 2.72 may be made available in a future version of Red Hat Enterprise Linux, by then, it may be useful to take the time to build your components that make use of it, in order to prepare for this upgrade. At the time of writing, there are EPEL packages available with Autoconf 2.71, which is already a big step forward from Autoconf 2. 69.