Installing Header Files

 One of the more difficult problems with GNU Autotools
driven projects is that each of them depends on
config.h (or its equivalent) and the project
specific symbols that it defines.  The purpose of this file is to
#include it from all of the project source files.
The preprocessor can tailor then the code in these files to the target
environment.     It is often difficult and sometimes impossible to not introduce a
dependency on config.h from one of the project's
installable header files. It would be nice if you could simply install
the generated config.h, but even if you name it
carefully or install it to a subdirectory to avoid filename problems,
the macros it defines will clash with those from any other GNU
Autotools based project which also installs its
config.h.     For example, if Sic installed its config.h as
/usr/include/sic/config.h, and had #include
<sic/config.h> in the installed
common.h, when another GNU Autotools based project
came to use the Sic library it might begin like this:
          
          #if HAVE_CONFIG_H
          #  include <config.h>
          #endif
          
          #if HAVE_SIC_H
          #  include <sic.h>
          #endif
          
          static const char version_number[] = VERSION;
           

But, sic.h says #include <sic/common.h>, which in turn says, #include <sic/config.h>. Even though the other project has the correct value for VERSION in its own config.h, by the time the preprocessor reaches the version_number definition, it has been redefined to the value in sic/config.h. Imagine the mess you could get into if you were using several libraries which each installed their own config.h definitions. GCC issues a warning when a macro is redefined to a different value which would help you to catch this error. Some compilers do not issue a warning, and perhaps worse, other compilers will warn even if the repeated definitions have the same value, flooding you with hundreds of warnings for each source file that reads multiple config.h headers.

The Autoconf macro, AC_OUTPUT_COMMANDS [1] , provides a way to solve this problem. The idea is to generate a system specific but installable header from the results of the various tests performed by configure. There is a 1-to-1 mapping between the preprocessor code that relied on the configure results written to config.h, and the new shell code that relies on the configure results saved in config.cache.

The following code is a snippet from configure.in, in the body of the AC_OUTPUT_COMMANDS macro:
         # Add the code to include these headers only if autoconf has
         # shown them to be present.
         if test x$ac_cv_header_stdlib_h = xyes; then
           echo '#include <stdlib.h>' >> $tmpfile
         fi
         if test x$ac_cv_header_unistd_h = xyes; then
           echo '#include <unistd.h>' >> $tmpfile
         fi
         if test x$ac_cv_header_sys_wait_h = xyes; then
           echo '#include <sys/wait.h>' >> $tmpfile
         fi
         if test x$ac_cv_header_errno_h = xyes; then
           echo '#include <errno.h>' >> $tmpfile
         fi
         cat >> $tmpfile << '_EOF_'
     #ifndef errno
     /* Some sytems #define this! */
     extern int errno;
     #endif
     _EOF_
         if test x$ac_cv_header_string_h = xyes; then
           echo '#include <string.h>' >> $tmpfile
         elif test x$ac_cv_header_strings_h = xyes; then
           echo '#include <strings.h>' >> $tmpfile
         fi
         if test x$ac_cv_header_assert_h = xyes; then
           cat >> $tmpfile << '_EOF_'
     
     #include <assert.h>
     #define SIC_ASSERT assert
     
     _EOF_
         else
             echo '#define SIC_ASSERT(expr)  ((void) 0)' >> $tmpfile
         fi
     
      

Compare this with the equivalent C pre-processor code from sic/common.h, which it replaces:
     #if STDC_HEADERS || HAVE_STDLIB_H
     #  include <stdlib.h>
     #endif
     
     #if HAVE_UNISTD_H
     #  include <unistd.h>
     #endif
     
     #if HAVE_SYS_WAIT_H
     #  include <sys/wait.h>
     #endif
     
     #if HAVE_ERRNO_H
     #  include <errno.h>
     #endif
     #ifndef errno
     /* Some systems #define this! */
     extern int errno;
     #endif
     
     #if HAVE_STRING_H
     #  include <string.h>
     #else
     #  if HAVE_STRING_H
     #    include <strings.h>
     #  endif
     #endif
     
     #if HAVE_ASSERT_H
     #  include <assert.h>
     #  define SIC_ASSERT assert
     #else
     #  define SIC_ASSERT(expr) ((void) 0)
     #endif
      

Apart from the mechanical process of translating the preprocessor code, there is some plumbing needed to ensure that the common.h file generated by the new code in configure.in is functionally equivalent to the old code, and is generated in a correct and timely fashion.

Taking my lead from some of the Automake generated make rules to regenerate Makefile from Makefile.in by calling config.status, I have added some similar rules to sic/Makefile.am to regenerate common.h from common-h.in.
     # Regenerate common.h with config.status whenever common-h.in changes.
     common.h: stamp-common
             @:
     stamp-common: $(srcdir)/common-h.in $(top_builddir)/config.status
             cd $(top_builddir) \
               && CONFIG_FILES= CONFIG_HEADERS= CONFIG_OTHER=sic/common.h \
               $(SHELL) ./config.status
             echo timestamp > $@
     
      

The way that AC_OUTPUT_COMMANDS works, is to copy the contained code into config.status (see the chapter called Generated File Dependencies). It is actually config.status that creates the generated files - for example, automake generated

Makefiles are able to regenerate themselves from corresponding Makefile.ins by calling config.status if they become out of date. Unfortunately, this means that config.status doesn't have direct access to the cache values generated while configure was running (because it has finished its work by the time config.status is called). It is tempting to read in the cache file at the top of the code inside AC_OUTPUT_COMMANDS, but that only works if you know where the cache file is saved. Also the package installer can use the --cache-file option of configure to change the location of the file, or turn of caching entirely with --cache-file=/dev/null.

AC_OUTPUT_COMMANDS accepts a second argument which can be used to pass the variable settings discovered by configure into config.status. It's not pretty, and is a little error prone. In the first argument to AC_OUTPUT_COMMANDS, you must be careful to check that every single configure variable referenced is correctly set somewhere in the second argument.

A slightly stripped down example from the sic project configure.in looks like this:
     # ----------------------------------------------------------------------
     # Add code to config.status to create an installable host dependent
     # configuration file.
     # ----------------------------------------------------------------------
     AC_OUTPUT_COMMANDS([
       if test -n "$CONFIG_FILES" && test -n "$CONFIG_HEADERS"; then
         # If both these vars are non-empty, then config.status wasn't run by
         # automake rules (which always set one or the other to empty).
         CONFIG_OTHER=${CONFIG_OTHER-sic/common.h}
       fi
       case "$CONFIG_OTHER" in
       *sic/common.h*)
         outfile=sic/common.h
         stampfile=sic/stamp-common
         tmpfile=${outfile}T
         dirname="sed s,^.*/,,g"
     
         echo creating $outfile
         cat > $tmpfile << _EOF_
     /*  -*- Mode: C -*-
      * --------------------------------------------------------------------
      * DO NOT EDIT THIS FILE!  It has been automatically generated
      * from:    configure.in and `echo $outfile|$dirname`.in
      * on host: `(hostname || uname -n) 2>/dev/null | sed 1q`
      * --------------------------------------------------------------------
      */
     
     #ifndef SIC_COMMON_H
     #define SIC_COMMON_H 1
     
     #include <stdio.h>
     #include <sys/types.h>
     _EOF_
     
         if test x$ac_cv_func_bzero = xno && \
            test x$ac_cv_func_memset = xyes; then
           cat >> $tmpfile << '_EOF_'
     #define bzero(buf, bytes) ((void) memset (buf, 0, bytes))
     _EOF_
         fi
         if test x$ac_cv_func_strchr = xno; then
           echo '#define strchr index' >> $tmpfile
         fi
         if test x$ac_cv_func_strrchr = xno; then
           echo '#define strrchr rindex' >> $tmpfile
         fi
     
         # The ugly but portable cpp stuff comes from here
         infile=$srcdir/sic/`echo $outfile | sed 's,.*/,,g;s,\..*$,,g'`-h.in
         sed '/^##.*$/d' $infile >> $tmpfile
     
     ],[
       srcdir=$srcdir
       ac_cv_func_bzero=$ac_cv_func_bzero
       ac_cv_func_memset=$ac_cv_func_memset
       ac_cv_func_strchr=$ac_cv_func_strchr
       ac_cv_func_strrchr=$ac_cv_func_strrchr
     ])
     
      

You will notice that the contents of common-h.in are copied into common.h verbatim as it is generated. It's just an easy way of collecting together the code that belongs in common.h, but which doesn't rely on configuration tests, without cluttering configure.in any more than necessary.

I should point out that, although this method has served me well for a number of years now, it is inherently fragile because it relies on undocumented internals of both Autoconf and Automake. There is a very real possibility that if you also track the latest releases of GNU Autotools, it may stop working. Future releases of GNU Autotools will address the interface problems that force us to use code like this, for the lack of a better way to do things.

Notes

[1]

This is for Autoconf version 2.13. Autoconf version 2.50 recommends @code{AC_CONFIG_COMMANDS}.