2
votes

TL;DR

  • Why does AC_DEFUN try to expand AC_PROG_CC?
  • Why does a macro defined by m4_define seem not to expand?
  • Why does a macro defined in configure.ac behave differently than a macro defined in another file and included?

Background

I manage building the firmware and libraries for about half a dozen different boards that all use the same embedded processor. Because all of the boards use the same processor, there is significant common configuration (CC, CXX, CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS, etc.) and recipes. For the firmware, I use custom makefiles that include a common configuration file that defines all of the common configuration and recipes. To simplify installing libraries and headers on developers' computers, I use use the GNU Autotools for the libraries. However, there is significant duplicate configuration between the custom makefiles and the GNU Autotools files, so I am migrating the firmware to the GNU Autotools.

Objective

I want to have as little boilerplate as possible. A configure.ac for a board might look like the following:

AC_INIT([mppt], [0.1.0])
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])
: ${CC='arm-none-eabi-gcc -specs=nosys.specs'}
AC_PROG_CC
AM_PROG_CC_C_O
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

A configure.ac for a library might look like the following:

AC_INIT([gcc-arm-none-eabi-samples], [4.7+])
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])
: ${CXX='arm-none-eabi-g++ -specs=nosys.specs'}
AC_PROG_CXX
: ${CC='arm-none-eabi-gcc -specs=nosys.specs'}
AM_PROG_AS
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
AC_PROG_RANLIB
AC_PROG_CXX_C_O
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

There is a lot of common configuration between these files. I would like to have a single file in a local include directory that defines a macro with common Autoconf macros:

AC_DEFUN([TIVASDK],
[AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])
: ${CC='arm-none-eabi-gcc -specs=nosys.specs'}
AC_PROG_CC
: ${CXX='arm-none-eabi-g++ -specs=nosys.specs'}
AC_PROG_CXX
AM_PROG_AS
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
AC_PROG_RANLIB
AM_PROG_CC_C_O
AC_PROG_CXX_C_O
AC_CONFIG_FILES([Makefile])])

Then, I would like to be able to simplify the first example to the following:

AC_INIT([mppt], [0.1.0])
TIVASDK
AC_OUTPUT

This has a number of advantages besides simplicity. If, for example, the MPPT developers were to introduce C++ into firmware currently written entirely in C, then they or I would not have to update their configure.ac. If I were to make a change to the toolchain, I would only have to push an update to one repository, not to almost ten.

Problem

Autoconf does not understand the macro as I would expect. There are a couple different failure mechanisms.

expanded before it was required

See this answer.

I have a skeleton project that I am using for testing. Its very simple Makefile.am specifies ACLOCAL_AMFLAGS:

ACLOCAL_AMFLAGS = -I "$TIVASDK_HOME"/include

noinst_PROGRAMS = skel.axf

skel_axf_SOURCES = \
    src/main.cpp \
    src/abort.cpp

TIVASDK_HOME is defined in ~/.profile.

See this question. Its configure.ac uses the macro:

AC_INIT([skel], [0.1.0])
m4_define([TIVASDK_HOME], [esyscmd([printf "$TIVASDK_HOME"])])
AC_CONFIG_MACRO_DIR(TIVASDK_HOME[/include])
TIVASDK
AC_OUTPUT

Its autogen.sh builds out-of-tree in build:

#!/bin/sh

autoreconf -vfi .. && \
  ../configure --prefix="$TIVASDK_HOME" --host=arm-none-eabi

Running autogen.sh yields the following:

$ ./autogen.sh 
autoreconf: Entering directory `..'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force -I /home/matthew/github.gatech.edu/GTSR/tiva-sdk/include
configure.ac:4: warning: AC_REQUIRE: `AC_PROG_CC' was expanded before it was required
configure.ac:4: http://www.gnu.org/software/autoconf/manual/autoconf.html#Expanded-Before-Required
/home/matthew/github.gatech.edu/GTSR/tiva-sdk/include/tivasdk.m4:1: TIVASDK is expanded from...
configure.ac:4: the top level
configure.ac:4: warning: AC_REQUIRE: `AC_PROG_CC' was expanded before it was required
configure.ac:4: http://www.gnu.org/software/autoconf/manual/autoconf.html#Expanded-Before-Required
/home/matthew/github.gatech.edu/GTSR/tiva-sdk/include/tivasdk.m4:1: TIVASDK is expanded from...
configure.ac:4: the top level
autoreconf: configure.ac: tracing
configure.ac:4: warning: AC_REQUIRE: `AC_PROG_CC' was expanded before it was required
configure.ac:4: http://www.gnu.org/software/autoconf/manual/autoconf.html#Expanded-Before-Required
aclocal.m4:1173: TIVASDK is expanded from...
configure.ac:4: the top level
autoreconf: configure.ac: not using Libtool
autoreconf: running: /usr/bin/autoconf --force
configure.ac:4: warning: AC_REQUIRE: `AC_PROG_CC' was expanded before it was required
configure.ac:4: http://www.gnu.org/software/autoconf/manual/autoconf.html#Expanded-Before-Required
aclocal.m4:1173: TIVASDK is expanded from...
configure.ac:4: the top level
autoreconf: configure.ac: not using Autoheader
autoreconf: running: automake --add-missing --copy --force-missing
configure.ac:4: warning: AC_REQUIRE: `AC_PROG_CC' was expanded before it was required
configure.ac:4: http://www.gnu.org/software/autoconf/manual/autoconf.html#Expanded-Before-Required
aclocal.m4:1173: TIVASDK is expanded from...
configure.ac:4: the top level
configure.ac:4: installing './compile'
configure.ac:4: installing './install-sh'
configure.ac:4: installing './missing'
Makefile.am: installing './depcomp'
autoreconf: Leaving directory `..'
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for arm-none-eabi-strip... arm-none-eabi-strip
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for style of include used by make... GNU
checking for arm-none-eabi-gcc... arm-none-eabi-gcc
checking whether we are using the GNU C compiler... no
checking whether arm-none-eabi-gcc accepts -g... no
checking for arm-none-eabi-gcc option to accept ISO C89... unsupported
checking whether arm-none-eabi-gcc understands -c and -o together... yes
checking dependency style of arm-none-eabi-gcc... gcc3
checking for arm-none-eabi-gcc... (cached) arm-none-eabi-gcc
checking whether the C compiler works... no
configure: error: in `/home/matthew/gatech.edu/sp2019/vip4602/tmp/skel/build':
configure: error: C compiler cannot create executables
See `config.log' for more details

command not found

I had a hunch that AC_DEFUN expands some Autoconf macros for some reason. I replaced the AC_DEFUN with m4_define:

m4_define([TIVASDK],
[AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])
: ${CC='arm-none-eabi-gcc -specs=nosys.specs'}
AC_PROG_CC
: ${CXX='arm-none-eabi-g++ -specs=nosys.specs'}
AC_PROG_CXX
AM_PROG_AS
AM_PROG_CC_C_O
AC_PROG_CXX_C_O
AC_CONFIG_FILES([Makefile])])

Running autogen.sh yields the following:

$ ./autogen.sh 
autoreconf: Entering directory `..'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force -I /home/matthew/github.gatech.edu/GTSR/tiva-sdk/include
autoreconf: configure.ac: tracing
autoreconf: configure.ac: not using Libtool
autoreconf: running: /usr/bin/autoconf --force
autoreconf: configure.ac: not using Autoheader
autoreconf: configure.ac: not using Automake
autoreconf: Leaving directory `..'
../configure: line 1678: TIVASDK: command not found
configure: creating ./config.status

It seems that TIVASDK is not expanding at all. I tried replacing m4_define with define to no avail.

Minimal working example

Simply placing the macro definition in configure.ac fixes the issue:

m4_define([TIVASDK],
[AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])
: ${CC='arm-none-eabi-gcc -specs=nosys.specs'}
AC_PROG_CC
: ${CXX='arm-none-eabi-g++ -specs=nosys.specs'}
AC_PROG_CXX
AM_PROG_AS
AM_PROG_CC_C_O
AC_PROG_CXX_C_O
AC_CONFIG_FILES([Makefile])])
AC_INIT([skel], [0.1.0])
TIVASDK
AC_OUTPUT

Running autogen.sh yields the following:

$ ./autogen.sh 
autoreconf: Entering directory `..'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force -I /home/matthew/github.gatech.edu/GTSR/tiva-sdk/include
autoreconf: configure.ac: tracing
autoreconf: configure.ac: not using Libtool
autoreconf: running: /usr/bin/autoconf --force
autoreconf: configure.ac: not using Autoheader
autoreconf: running: automake --add-missing --copy --force-missing
configure.ac:12: installing './compile'
configure.ac:12: installing './install-sh'
configure.ac:12: installing './missing'
Makefile.am: installing './depcomp'
autoreconf: Leaving directory `..'
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for arm-none-eabi-strip... arm-none-eabi-strip
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for arm-none-eabi-gcc... arm-none-eabi-gcc -specs=nosys.specs
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... yes
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether arm-none-eabi-gcc -specs=nosys.specs accepts -g... yes
checking for arm-none-eabi-gcc -specs=nosys.specs option to accept ISO C89... none needed
checking whether arm-none-eabi-gcc -specs=nosys.specs understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of arm-none-eabi-gcc -specs=nosys.specs... gcc3
checking whether we are using the GNU C++ compiler... yes
checking whether arm-none-eabi-g++ -specs=nosys.specs accepts -g... yes
checking dependency style of arm-none-eabi-g++ -specs=nosys.specs... gcc3
checking dependency style of arm-none-eabi-gcc -specs=nosys.specs... gcc3
checking whether arm-none-eabi-g++ -specs=nosys.specs understands -c and -o together... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
1

1 Answers

1
votes
  • Why does AC_DEFUN try to expand AC_PROG_CC?

I cannot explain the reason for your error. I did replicate it with your code, but not with similar code that I initially tried. There seems to be a combination of factors involved, among them the presence of both AC_PROG_CC and AC_PROG_CXX together.

However, starting with your initial attempt, presented in the question above the "Problem" heading, and based on information from the online Autoconf manual page to which the error message refers, I was able to solve the problem simply by calling AC_PROG_CC indirectly through AC_REQUIRE instead of directly:

AC_DEFUN([TIVASDK], [
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])
: ${CC='arm-none-eabi-gcc -specs=nosys.specs'}

# HERE:
AC_REQUIRE([AC_PROG_CC])

: ${CXX='arm-none-eabi-g++ -specs=nosys.specs'}
AC_PROG_CXX
# ...

I find, by the way, that there is no reason, at least in the skeleton project, to define or use the TIVASDK_HOME macro. The ACLOCAL_AMFLAGS defined in Makefile.in apparently suffice.

I'm not much enamored by the idea of using an environment variable to define the location of the wanted macro include directory, and the example does not make it clear to me why that's useful, but if you continue with that then I encourage you to

  • use AC_ARG_VAR to document that variable and make it precious, AND
  • indeed do remove the macro of the same name.
  • Why does a macro defined by m4_define seem not to expand?

I presume that this would be because external macro files are not used directly, but rather processed by aclocal to generate file aclocal.m4, which is what gets included. I think you'll find that the direct m4_define, appearing outside the scope of any Autoconf macro definition, did not get copied over.

  • Why does a macro defined in configure.ac behave differently than a macro defined in another file and included?

I don't think it does. But an M4 macro defined in configure.ac certainly does behave differently than one defined elsewhere and not included.