Firstly, apologies for the cross-posting, particularly giving the fact that 
I am including code, but this message contains issues relevant to both 
automake and autoconf.

For a while I have been thinking that it would be nice to be able to 
support "include" in autoconf/automake Makefiles.   This would be particularly 
nice with automake which generates very large repetitious Makefiles.  
Currently there are three ways to do inclusion that I can think of 
(AC_SUBST_FILE, the Makefile:include.mk hack and automake's include) but 
all these result in full expansion of the included file in the final 
Makefile.  I am talking about a real include that is done by Make and 
not by auto{conf|make}.

A big motivation for me, is to use this with automake.  I have at least one
project which has a large number of Makefiles and uses a single include
file to add extra rules.  The big problem with the current setup is a
single change to the include file means rerunning automake/config.status on
_every_ Makefile.  Using an include mechanism it should be possible to just
regenerate the include file and have everything worked out.

The problem is of course, that not all makes do include the same way.  
There are basically two flavours.

#SYSV and GNU make
include file

#BSD make (although most of the later BSD makes seem to support include)
.include "file"

And while I cannot actually find a version of make that doesn't support 
include in some form, for safety we probably want to support some sort of 
hack that does an inline expansion of the include file if there is no Make 
support.

To this end I have hacked up some autoconf macro's that do a check to see 
if include is supported and emulate it if it isn't.  You use it this way:

In your Makefile do:

#Lots of lovely make rules ...
@INCLUDE@ @INCLUDE_QUOT@@top_srcdir@/include.mk@INCLUDE_QUOT@

(Yes I know the syntax is ugly and hard to read, but it is the easiest way 
to do it without having to dig through autoconf internals).

Then in your configure.ac do:

AC_CONFIG_MAKEFILES(include.mk Makefile)

Which automatically calls AC_PROG_MAKE_INCLUDE to check for include support
(if it hasn't already been called).

I have a reasonable amount of comments in the code, so it should be easy
enough to follow, and it seems to work on all the systems I have access to.
I have tried to avoid using autoconf internals wherever possible.  It is a 
hack and a bit fragile in places, but as a proof of concept it is probably 
a good start.  

Better support would probably require some access to automake internals.
For example, you would tend to want to have a single include.mk with all
the VAR=@VAR@ stuff and include this from all the other Makefiles so it
makes sense to have the @INCLUDE@ subsitution very close to the top of the
sed commands list to avoid having to run through all the other
substitutions.  This would speed up config.status for a lot of files.

This might also make it easier to address the problem of recursive 
Makefiles in automake by doing the following:

Generate top level include with all the common stuff.
Generate directory specific stuff in a per-directory include file
Generate directory specific Makefile including top-level and per-directory 
include
Generate top-level Makefile with directory specific and all the 
per-directory files included.

This allows you to still do "cd foo; make" when that make sense.  Of course
resolving the namespace issues here would be non-trivial.

Any comments, suggestions?

# AC_PROG_MAKE_INCLUDE
# --------------------
# See if the version of make we are using supports include.  This macro
# runs make trying include and then .include to see if make supports including
# in this way.  It substitutes the variables @INCLUDE@ and @INCLUDE_QUOT@
# in the output file.  If make does not support either include or .include
# Then @INCLUDE@ is set to %INCLUDE% and the AC_CONFIG_MAKEFILES macro emulates
# the include.  This macro is automatically called by AC_CONFIG_MAKEFILES if
# has not already been run.
AC_DEFUN([AC_PROG_MAKE_INCLUDE],
[#Check how make supports include
ac_make_var=$MAKE; if test "x$ac_make_var" = x ; then ac_make_var=make; fi
set dummy $ac_make_var; ac_make=`echo "$[2]" | sed 'y,./+-,__p_,'`
AC_CACHE_CHECK([how $ac_make_var supports include],dnl
ac_cv_prog_make_${ac_make}_inc,
[#Create file to include
cat >conftestmakeinc <<\EOF
all:
        @echo 'ac_maketemp=$(ac_make_inc)'
EOF

#Set defaults which are used if this macro cannot find a method to do
#Makefile includes
ac_try_quot='"'
eval "ac_cv_prog_make_${ac_make}_inc_quot='$ac_try_quot'"
eval ac_cv_prog_make_${ac_make}_inc=none

#Try to discover how to do a Makefile include.  We try "include" first as this
#is supported by the widest range of Makes (SYSV, GNU Make and some versions
#of BSD Make with SYSV support)
for ac_try_make_inc in include .include ; do

case $ac_try_make_inc in
  .include) #BSD style make, we need to quote the argument
            ac_try_quot='"';;
  include)  #SYS V style make don't quote the argument
            ac_try_quot=;;
esac
      
  #Create test Makefile with the candidate include method
  cat > conftestmake <<EOF
ac_make_inc=$ac_try_make_inc
$ac_try_make_inc ${ac_try_quot}conftestmakeinc${ac_try_quot}
EOF

  #See if it worked
  eval `$ac_make_var -f conftestmake 2>/dev/null | grep temp=`
  if test -n "$ac_maketemp"; then
    eval ac_cv_prog_make_${ac_make}_inc_quot='$ac_try_quot'
    eval ac_cv_prog_make_${ac_make}_inc=$ac_maketemp
    break
  fi
done

rm -f conftestmake
rm -f conftestmakeinc])dnl

eval ac_maketemp="`echo '$ac_cv_prog_make_'${ac_make}_inc`"
eval ac_makequot="`echo '$ac_cv_prog_make_'${ac_make}_inc_quot`"

#If our make has no support for include then we will need
#to emulate it.  This is done by a sed script in AC_CONFIG_MAKEFILES 
INCLUDE_PATTERN="# -- Included automatically by configure from"
if test x$ac_maketemp = xnone ; then
  INCLUDE="$INCLUDE_PATTERN"
  ac_emulate_make_inc=1
else
  INCLUDE=$ac_maketemp
fi
INCLUDE_QUOT=$ac_makequot

dnl Do substituitions
AC_SUBST(INCLUDE)
AC_SUBST(INCLUDE_QUOT)
]) #AC_PROG_MAKE_INCLUDE

# AC_CONFIG_MAKEFILES(FILES,[CMDS],[INIT-CMDS])
# --------------------------------------------
# Output Makefiles.  If make does not support a mechanism for including other
# makefiles, this macro will emulate it.  This should be used in the 
# following way:
#
# In the Makefile.in:
#
# @INCLUDE@ @INCLUDE_QUOT@file/to/include.mk@INCLUDE_QUOT@
#
# In ./configure.ac:
# 
# AC_CONFIG_MAKEFILES(file/to/include.mk Makefile)
#
# The following limitations apply to the use of @INCLUDE@ in Makefiles.
#
# 1. The file must not include any Make variables (i.e. $(var)) as these will
# not be expanded if include is being emulated.  You may however, use any
# variables substituted by configure (i.e. @var@).
#
# 2. @INCLUDE@ should start in the first column of the file, with no leading
#    space or tabs.
#
# 3. The string "# -- Included automatically by configure from" should not
# appear anywhere in your Makefile.
#
# 4. Included files may include other files.  However, if include emulation
# is being used order is important in AC_CONFIG_MAKEFILES.  You need to
# specify the files in the order: includedbyx x yincludesx.  In addition,
# when running ./config.status to generate a single Makefile
# that has changed, you should also update all files that depend on this
# Makefile.  A way to do this is to add a dependency list to the Makefile
# itself.
#
# CMDS, and INIT-CMDS work the same way as they do for AC_CONFIG_FILES, but 
# are run before any include substitutions are done.
#
# Note: This code makes aomw assumptions about some internals of autoconf.
#
# 1. That the current Makefile being substituted is in the variable $ac_file.
# 2. That the variable $as_me contains the basename of the config.status 
# script.
# 3. $tmp is a valid temporary directory that has been created
# 4. The variable CONFIG_COMMANDS lists any command tags to be run 
#
AC_DEFUN([AC_CONFIG_MAKEFILES],
[
AC_REQUIRE([AC_PROG_MAKE_INCLUDE])
AC_CONFIG_FILES([$1], [CONFIG_MAKEFILES="$CONFIG_MAKEFILES $ac_file"]
[$2], 
CONFIG_COMMANDS="$CONFIG_COMMANDS emulate-make-include"
[$3])

AC_CONFIG_COMMANDS(emulate-make-include, 
[#Emulate include in Makefiles if needed
if test "x$ac_emulate_make_inc" != "x" && test "x$ac_emulate_make_run" = "x"
then
for ac_file in $CONFIG_MAKEFILES ; do
  echo $as_me: emulating make include for $ac_file >&2
  changequote(, )dnl
  dir=`expr "x$ac_file" : 'x\(.*\)/[^/]*' \| . : '\(.\)'`
  basename=`expr //$ac_file : '.*/\(.*\)'`

  (cd $dir;
  # The following extracts lines with the include pattern from a Makefile
  # and uses the information in them to generate a sed script to do the
  # actual substitution
  #
  # Note: the regular expression in the following sed statement replaces
  # occurances of $INCLUDE "path" with a new sed command to include 
  # "path" in the target Makefile.  Because '/' often appears in 
  # pathnames \|RE| is used to do address matching instead of /RE/ 
  #
  # Note: that the [ ] below contains a space and a literal tab
  grep "$INCLUDE" $basename | sort -u | \
    sed 's:\('"$INCLUDE"'[      ]\{1,\}\)"\(.*\)".*$:\\|\1"\2".*$|r \2:' \
    > $tmp/makeincludes.sed
  changequote([, ])dnl
  #Should really break up this script, but we are unlikely to 
  #exceed the max number of lines we are allowed in a sed
  sed -f $tmp/makeincludes.sed < $basename > $tmp/$basename.tmp
  mv $tmp/$basename.tmp $basename
  rm -f $tmp/makeincludes.sed
 )
done
#Protect against multiple executions, otherwise files will get included into 
#the Makefile more than once
ac_emulate_make_run=1
fi],
[INCLUDE="$INCLUDE"; ac_emulate_make_inc="$ac_emulate_make_inc"])
]) # AC_CONFIG_MAKEFILES

Reply via email to