I've written a macro which makes it easier to write more flexible
autoconf-macros, and I thought some of you might find it interesting.
It's in the public domain so do whatever you want with it (except
pornographic stuff - I won't have any of that... ;)

I've called it SIM_PARSE_MODIFIER_LIST() and it handles parsing of
modifier-list arguments.  A modifier-list is an optional argument
that can contain keywords (modifiers) that will change the behaviour
of your macro.  All parsing is done on the m4-level, so most problems
will be caught at m4-run-time (while running e.g. aclocal and autoconf)
instead of while running configure.  Error messages are printed for
a lot of error-cases.

The macro sets up a set of defines with default values, and then a
set of modifiers and how they change the previously given defines.

Let's say you're writing a macro: AC_my_TEST( arg1, arg2, opt MODIFIERS )

AC_DEFUN([AC_my_TEST],[dnl

SIM_PARSE_MODIFIER_LIST([$3],[
  m4_my_debug_setting      false
  m4_my_default_setting    true
],[
  debug      m4_my_debug_setting    true
  nodebug    m4_my_debug_setting    false
  default    m4_my_default_setting  true
  nodefault  m4_my_default_setting  false
])

After having invoked this macro, you now have the two defines
"m4_my_debug_setting" (default value false) and "m4_my_default_setting"
(default value true) which you can test the value of, both on the m4-level
and at the shell level, like for instance this:

ifelse(m4_my_debug_setting,
       true,
       [errprint([problem right here!
]),
       [])

or

if test x"m4_my_debug_setting" = "xtrue"; then
  AC_MSG_ERROR([macro was set up incorrectly])
fi

Having the define set on the m4-level gives the advantage that you
can get the value of it into e.g. the AC_ARG_WITH() help-text.  A
shell-variable won't be set up at the time the help-text is displayed,
so the macro setting can't affect the help-text.  You can also catch
problems already at m4-run-time.

Now, over to your configure.in, you would now be able to invoke your
macro like this:

AC_my_TEST([arg1], [arg2])             => default values will be used

AC_my_TEST([arg1], [arg2], nodefault)  => m4_my_default_setting will be
                                          false during macro invocation.

AC_my_TEST([arg1], [arg2], nodeflllt debug)
error=> SIM_PARSE_MODIFIER_LIST: modifier(s) parse error: "nodeflllt"

 (settings will be configured for the macro invocation for the modifiers
  that are parsed correctly - m4_my_debug_setting will be true)

You get the drift...

I find this macro useful, and I hope you will too.  It would of course
be even more useful as a standard part of autoconf so you won't have
to install the modifierlist.m4 fiel together with the macros that use
it :(

Any comments?

[attachment 1: modifierlist.m4  (the macro itself)]
[attachment 2: simage.m4        (sample usage og macro)]

  Lars J
dnl ************************************************************************
dnl Usage:
dnl   SIM_PARSE_MODIFIER_LIST( MODIFIER-LIST-STRING, MODIFIER-VARIABLES, 
dnl       MODIFIER-LIST, opt ACTION-ON-SUCCESS, opt ACTION-ON-FAILURE )
dnl
dnl Description:
dnl   This macro makes it easy to let macros have a MODIFIER-LIST argument
dnl   which makes the macro more flexible and lets the macro caller configure
dnl   some of the macro beaviour from the calling place.
dnl
dnl   Everything is done on the m4-level, which means things are handled at
dnl   autoconf-run-time, not configure-run-time.  This lets you discover
dnl   problems at an earlier stage, which is nice.  It also lets you insert
dnl   the modifier values into e.g. the help strings, something you can't
dnl   do with a shell variable.
dnl
dnl   MODIFIER-LIST-STRING is the string of modifiers used in the
dnl   macro invocation.
dnl
dnl   MODIFIER-VARIABLES is a list of variables and their default values.
dnl   The variables and values are recognized as words matching [[^\s-]*]
dnl   separated by whitespace, and they must of course come in pairs.
dnl
dnl   MODIFIER-LIST is a description-list of all the valid modifiers that
dnl   can be used in the MODIFIER-LIST-STRING argument.  They must come in
dnl   tuples of three and three words (same word-definition as above) where
dnl   the first word is the modifier, the second word is the variable
dnl   that is to be set by the modifier, and last the value the modifier
dnl   variable should be set to.
dnl
dnl   ACTION-ON-SUCCESS is the expansion of the macro if all the modifiers
dnl   in MODIFIER-LIST-STRING pass through without problem.  The default
dnl   expansion is nothing.
dnl
dnl   ACTION-ON-FAILURE is the expansion of the macro if some of the
dnl   modifiers in MODIFIER-LIST-STRING doesn't pass through.  The default
dnl   expansion is nothing, but warnings are printed to stderr on the
dnl   modifiers causing the problem.
dnl
dnl Sample Usage:
dnl   [to come later]
dnl
dnl Authors:
dnl   Lars J. Aas <[EMAIL PROTECTED]>
dnl
dnl TODO:
dnl * [larsa:20000222] warn on creating modifiers for unknown variables
dnl

define([$IM_STRING_COMPACT],[dnl
patsubst(patsubst([$1],[[
         ]+],[ ]),[^ \| $],[])])

define([$IM_STRING_WORDCOUNT_COMPACT],[dnl
builtin([eval],(1+len(patsubst([$1],[[^ ]+],[_])))/2)])

define([$IM_STRING_WORDCOUNT],[dnl
indir([$IM_STRING_WORDCOUNT_COMPACT],indir([$IM_STRING_COMPACT],[$1]))])

define([$IM_DEFINE_VARIABLE],[dnl
dnl errprint([define( $1, $2 )
dnl ])dnl
define([$1],[$2])
])

define([$IM_DEFINE_VARIABLES],[dnl
ifelse(indir([$IM_STRING_WORDCOUNT_COMPACT],[$1]),
       2,
       [patsubst([$1],[^\([^ ]+\) \([^ ]+\)],
                 [indir([$IM_DEFINE_VARIABLE],[\1],[\2])])],
       [patsubst([$1],[^\([^ ]+\) \([^ ]+\) \(.*\)],
                 
[indir([$IM_DEFINE_VARIABLE],[\1],[\2])indir([$IM_DEFINE_VARIABLES],[\3])])])dnl
])

define([$IM_PUSHDEF_MODIFIER],[dnl
dnl errprint([modifier( $1, $2, $3 )
dnl ])dnl
ifelse(defn([$2]),
       [],
       [errprint([SIM_PARSE_MODIFIER_LIST: invalid variable (arg 3): "$2"
])],
       [pushdef([$1],[define([$2],[$3])])])dnl
])

dnl [pushdef([$1],[define([$2],[$3])])]

define([$IM_PUSHDEF_MODIFIERS],[dnl
ifelse(indir([$IM_STRING_WORDCOUNT_COMPACT],[$1]),
       3,
       [patsubst([$1],[^\([^ ]+\) \([^ ]+\) \([^ ]+\)],
                 [indir([$IM_PUSHDEF_MODIFIER],[\1],[\2],[\3])])],
       [patsubst([$1],[^\([^ ]+\) \([^ ]+\) \([^ ]+\) \(.*\)],
                 
[indir([$IM_PUSHDEF_MODIFIER],[\1],[\2],[\3])indir([$IM_PUSHDEF_MODIFIERS],[\4])])dnl
])dnl
])

define([$IM_POPDEF_MODIFIER],[dnl
dnl errprint([popdef( $1 )
dnl ])rnl
popdef([$1])dnl
])

define([$IM_POPDEF_MODIFIERS],[dnl
ifelse(indir([$IM_STRING_WORDCOUNT_COMPACT],[$1]),
       3,
       [patsubst([$1],[^\([^ ]+\) \([^ ]+\) \([^ ]+\)],
                 [indir([$IM_POPDEF_MODIFIER],[\1])])],
       [patsubst([$1],[^\([^ ]+\) \([^ ]+\) \([^ ]+\) \(.*\)],
                 
[indir([$IM_POPDEF_MODIFIER],[\1])indir([$IM_POPDEF_MODIFIERS],[\4])])])dnl
])

define([$IM_PARSE_MODIFIER_LIST],[dnl
pushdef([wordcount],builtin([eval],(indir([$IM_STRING_WORDCOUNT],[$2]))))dnl
ifelse(builtin([eval], (wordcount % 2) == 0 && wordcount > 0),
       1,
       [],
       [errprint([SIM_PARSE_MODIFIER_LIST: invalid word count (arg 2): 
"]indir([$IM_STRING_COMPACT],[$2])["
])])dnl
popdef([wordcount])dnl
indir([$IM_DEFINE_VARIABLES],[$2])dnl
pushdef([wordcount],builtin([eval],(indir([$IM_STRING_WORDCOUNT],[$3]))))dnl
ifelse(builtin([eval], (wordcount % 3) == 0 && wordcount > 0),
       1,
       [],
       [errprint([SIM_PARSE_MODIFIER_LIST: invalid word count (arg 3): "$3"
])])dnl
popdef([wordcount])dnl
indir([$IM_PUSHDEF_MODIFIERS],[$3])dnl
ifelse(indir([$IM_STRING_COMPACT],[$1]),
       [],
       [ifelse($4, , , $4)],
       [ifelse($5, , [errprint([SIM_PARSE_MODIFIER_LIST: modifier(s) parse error: 
]"indir([$IM_STRING_COMPACT],[$1])"[
])], $5)])dnl
indir([$IM_POPDEF_MODIFIERS],[$3])dnl
])

AC_DEFUN([SIM_PARSE_MODIFIER_LIST],[dnl
indir([$IM_PARSE_MODIFIER_LIST],
      indir([$IM_STRING_COMPACT],[$1]),
      indir([$IM_STRING_COMPACT],[$2]),
      indir([$IM_STRING_COMPACT],[$3]),
      [$4],
      [$5])dnl
])

dnl ************************************************************************
dnl Usage:
dnl   SIM_CHECK_SIMAGE( ACTION-IF-FOUND, ACTION-IF-NOT-FOUND, ATTRIBUTE-LIST )
dnl
dnl Description:
dnl   This macro locates the simage development system.  If it is found, the
dnl   set of variables listed below are set up as described and made available
dnl   to the configure script.
dnl
dnl ATTRIBUTE-LIST Options:
dnl   [no]default              whether --with-simage is default or not
dnl                            (default on)
dnl   [no]searchprefix         whether $exec_prefix is to be searched
dnl                            (default off)
dnl
dnl Autoconf Variables:
dnl   $sim_ac_simage_avail     yes | no
dnl   $sim_ac_simage_cppflags  (extra flags the compiler needs for simage)
dnl   $sim_ac_simage_ldflags   (extra flags the linker needs for simage)
dnl   $sim_ac_simage_libs      (link libraries the linker needs for simage)
dnl   $CPPFLAGS                $CPPFLAGS $sim_ac_simage_cppflags
dnl   $LDFLAGS                 $LDFLAGS $sim_ac_simage_ldflags
dnl   $LIBS                    $sim_ac_simage_libs $LIBS
dnl
dnl Automake Conditionals:
dnl   HAVE_LIBSIMAGE           (code disabled)
dnl
dnl Config.h Defines:
dnl   HAVE_LIBSIMAGE           (code disabled)
dnl   HAVE_SIMAGE_H            (code disabled)
dnl
dnl Authors:
dnl   Morten Eriksen, <[EMAIL PROTECTED]>
dnl   Lars J. Aas, <[EMAIL PROTECTED]>
dnl
dnl TODO:
dnl * [mortene:20000122] make sure this work on MSWin (with Cygwin)
dnl * [larsa:20000220] find a less strict AC_PREREQ setting
dnl

AC_DEFUN(SIM_CHECK_SIMAGE,[
dnl Autoconf is a developer tool, so don't bother to support older versions.
AC_PREREQ([2.14.1])

SIM_PARSE_MODIFIER_LIST([$3],[
  sim4_simage_with          yes
  sim4_simage_searchprefix  no
],[
  default         sim4_simage_with          yes
  nodefault       sim4_simage_with          no
  searchprefix    sim4_simage_searchprefix  yes
  nosearchprefix  sim4_simage_searchprefix  no
])

AC_ARG_WITH(simage, AC_HELP_STRING([--with-simage=DIR], changequote({,}){use simage 
for loading texture files [default=}sim4_simage_with{]}changequote([,])), , 
[with_simage=sim4_simage_with])

sim_ac_simage_avail=no

if test "x$with_simage" != "xno"; then
  sim_ac_path=$PATH
  if test "x$with_simage" != "xyes"; then
    sim_ac_path=${with_simage}/bin:$PATH
    ifelse(sim4_simage_searchprefix, yes,
    [if test "x$exec_prefix" != "xNONE"; then
      sim_ac_path=$sim_ac_path:$sim_ac_path/bin
    fi], :)
  fi

  AC_PATH_PROG(sim_ac_conf_cmd, simage-config, false, $sim_ac_path)
  if test "x$sim_ac_conf_cmd" = "xfalse"; then
    AC_MSG_WARN(could not find 'simage-config' in $sim_ac_path)
  fi

  sim_ac_simage_cppflags=`$sim_ac_conf_cmd --cppflags`
  sim_ac_simage_ldflags=`$sim_ac_conf_cmd --ldflags`
  sim_ac_simage_libs=`$sim_ac_conf_cmd --libs`

  AC_CACHE_CHECK([whether the simage library is available],
    sim_cv_lib_simage_avail, [
    sim_ac_save_cppflags=$CPPFLAGS
    sim_ac_save_ldflags=$LDFLAGS
    sim_ac_save_libs=$LIBS
    CPPFLAGS="$CPPFLAGS $sim_ac_simage_cppflags"
    LDFLAGS="$LDFLAGS $sim_ac_simage_ldflags"
    LIBS="$sim_ac_simage_libs $LIBS"
    AC_TRY_LINK([#include <simage.h>],
                [(void)simage_read_image(0L, 0L, 0L, 0L);],
                sim_cv_lib_simage_avail=yes,
                sim_cv_lib_simage_avail=no)
    CPPFLAGS=$sim_ac_save_cppflags
    LDFLAGS=$sim_ac_save_ldflags
    LIBS=$sim_ac_save_libs
  ])

  if test x"$sim_cv_lib_simage_avail" = xyes; then
    sim_ac_simage_avail=yes
    CPPFLAGS="$CPPFLAGS $sim_ac_simage_cppflags"
    LDFLAGS="$LDFLAGS $sim_ac_simage_ldflags"
    LIBS="$sim_ac_simage_libs $LIBS"
dnl AM_CONDITIONAL(HAVE_LIBSIMAGE, true)
dnl AC_DEFINE(HAVE_SIMAGE_H, 1,
dnl   [Define this if you have simage.h])
dnl AC_DEFINE(HAVE_LIBSIMAGE, 1,
dnl   [Define this if you are going to use libsimage])
    ifelse($1, , :, $1)
  else
dnl AM_CONDITIONAL(HAVE_LIBSIMAGE, false)
    ifelse($2, , :, $2)
  fi
else
  ifelse($2, , :, $2)
fi
])

Reply via email to