autoconf: Polymorphic Variables

1 
1 9.2 Support for indirect variable names
1 =======================================
1 
1 Often, it is convenient to write a macro that will emit shell code
1 operating on a shell variable.  The simplest case is when the variable
1 name is known.  But a more powerful idiom is writing shell code that can
1 work through an indirection, where another variable or command
1 substitution produces the name of the variable to actually manipulate.
1 M4sh supports the notion of polymorphic shell variables, making it easy
1 to write a macro that can deal with either literal or indirect variable
1 names and output shell code appropriate for both use cases.  Behavior is
1 undefined if expansion of an indirect variable does not result in a
1 literal variable name.
1 
1  -- Macro: AS_LITERAL_IF (EXPRESSION, [IF-LITERAL], [IF-NOT],
1           [IF-SIMPLE-REF = `IF-NOT'])
1  -- Macro: AS_LITERAL_WORD_IF (EXPRESSION, [IF-LITERAL], [IF-NOT],
1           [IF-SIMPLE-REF = `IF-NOT'])
1      If the expansion of EXPRESSION is definitely a shell literal,
1      expand IF-LITERAL.  If the expansion of EXPRESSION looks like it
1      might contain shell indirections (such as `$var' or ``expr`'),
1      then IF-NOT is expanded.  Sometimes, it is possible to output
1      optimized code if EXPRESSION consists only of shell variable
1      expansions (such as `${var}'), in which case IF-SIMPLE-REF can be
1      provided; but defaulting to IF-NOT should always be safe.
1      `AS_LITERAL_WORD_IF' only expands IF-LITERAL if EXPRESSION looks
1      like a single shell word, containing no whitespace; while
1      `AS_LITERAL_IF' allows whitespace in EXPRESSION.
1 
1      In order to reduce the time spent recognizing whether an
1      EXPRESSION qualifies as a literal or a simple indirection, the
1      implementation is somewhat conservative: EXPRESSION must be a
1      single shell word (possibly after stripping whitespace),
1      consisting only of bytes that would have the same meaning whether
1      unquoted or enclosed in double quotes (for example, `a.b' results
1      in IF-LITERAL, even though it is not a valid shell variable name;
1      while both `'a'' and `[$]' result in IF-NOT, because they behave
1      differently than `"'a'"' and `"[$]"').  This macro can be used in
1      contexts for recognizing portable file names (such as in the
1      implementation of `AC_LIBSOURCE'), or coupled with some
1      transliterations for forming valid variable names (such as in the
1      implementation of `AS_TR_SH', which uses an additional
1      `m4_translit' to convert `.' to `_').
1 
1      This example shows how to read the contents of the shell variable
1      `bar', exercising all three arguments to `AS_LITERAL_IF'.  It
1      results in a script that will output the line `hello' three times.
1 
1           AC_DEFUN([MY_ACTION],
1           [AS_LITERAL_IF([$1],
1             [echo "$$1"],
1             [AS_VAR_COPY([var], [$1])
1              echo "$var"],
1             [eval 'echo "$'"$1"\"])])
1           foo=bar bar=hello
1           MY_ACTION([bar])
1           MY_ACTION([`echo bar`])
1           MY_ACTION([$foo])
1 
1  -- Macro: AS_VAR_APPEND (VAR, TEXT)
1      Emit shell code to append the shell expansion of TEXT to the end
1      of the current contents of the polymorphic shell variable VAR,
1      taking advantage of shells that provide the `+=' extension for more
1      efficient scaling.
1 
1      For situations where the final contents of VAR are relatively
1      short (less than 256 bytes), it is more efficient to use the
1      simpler code sequence of `VAR=${VAR}TEXT' (or its polymorphic
1      equivalent of `AS_VAR_COPY([t], [VAR])' and `AS_VAR_SET([VAR],
1      ["$t"TEXT])').  But in the case when the script will be repeatedly
1      appending text into `var', issues of scaling start to become
1      apparent.  A naive implementation requires execution time linear
1      to the length of the current contents of VAR as well as the length
1      of TEXT for a single append, for an overall quadratic scaling with
1      multiple appends.  This macro takes advantage of shells which
1      provide the extension `VAR+=TEXT', which can provide amortized
1      constant time for a single append, for an overall linear scaling
1      with multiple appends.  Note that unlike `AS_VAR_SET', this macro
1      requires that TEXT be quoted properly to avoid field splitting and
1      file name expansion.
1 
1  -- Macro: AS_VAR_ARITH (VAR, EXPRESSION)
1      Emit shell code to compute the arithmetic expansion of EXPRESSION,
1      assigning the result as the contents of the polymorphic shell
1      variable VAR.  The code takes advantage of shells that provide
1      `$(())' for fewer forks, but uses `expr' as a fallback.
1      Therefore, the syntax for a valid EXPRESSION is rather limited:
1      all operators must occur as separate shell arguments and with
1      proper quoting, there is no portable equality operator, all
1      variables containing numeric values must be expanded prior to the
1      computation, all numeric values must be provided in decimal
1      without leading zeroes, and the first shell argument should not be
1      a negative number.  In the following example, this snippet will
1      print `(2+3)*4 == 20'.
1 
1           bar=3
1           AS_VAR_ARITH([foo], [\( 2 + $bar \) \* 4])
1           echo "(2+$bar)*4 == $foo"
1 
1  -- Macro: AS_VAR_COPY (DEST, SOURCE)
1      Emit shell code to assign the contents of the polymorphic shell
1      variable SOURCE to the polymorphic shell variable DEST.  For
1      example, executing this M4sh snippet will output `bar hi':
1 
1           foo=bar bar=hi
1           AS_VAR_COPY([a], [foo])
1           AS_VAR_COPY([b], [$foo])
1           echo "$a $b"
1 
1      When it is necessary to access the contents of an indirect variable
1      inside a shell double-quoted context, the recommended idiom is to
1      first copy the contents into a temporary literal shell variable.
1 
1           for header in stdint_h inttypes_h ; do
1             AS_VAR_COPY([var], [ac_cv_header_$header])
1             echo "$header detected: $var"
1           done
1 
1  -- Macro: AS_VAR_IF (VAR, [WORD], [IF-EQUAL], [IF-NOT-EQUAL])
1      Output a shell conditional statement.  If the contents of the
1      polymorphic shell variable VAR match the string WORD, execute
1      IF-EQUAL; otherwise execute IF-NOT-EQUAL.  WORD must be a single
1      shell word (typically a quoted string).  Avoids shell bugs if an
1      interrupt signal arrives while a command substitution in VAR is
1      being expanded.
1 
1  -- Macro: AS_VAR_PUSHDEF (M4-NAME, VALUE)
1  -- Macro: AS_VAR_POPDEF (M4-NAME)
1      A common M4sh idiom involves composing shell variable names from
1      an m4 argument (for example, writing a macro that uses a cache
1      variable).  VALUE can be an arbitrary string, which will be
1      transliterated into a valid shell name by `AS_TR_SH'.  In order to
1      access the composed variable name based on VALUE, it is easier to
1      declare a temporary m4 macro M4-NAME with `AS_VAR_PUSHDEF', then
1      use that macro as the argument to subsequent `AS_VAR' macros as a
1      polymorphic variable name, and finally free the temporary macro
1      with `AS_VAR_POPDEF'.  These macros are often followed with `dnl',
1      to avoid excess newlines in the output.
1 
1      Here is an involved example, that shows the power of writing
1      macros that can handle composed shell variable names:
1 
1           m4_define([MY_CHECK_HEADER],
1           [AS_VAR_PUSHDEF([my_Header], [ac_cv_header_$1])dnl
1           AS_VAR_IF([my_Header], [yes], [echo "header $1 detected"])dnl
1           AS_VAR_POPDEF([my_Header])dnl
1           ])
1           MY_CHECK_HEADER([stdint.h])
1           for header in inttypes.h stdlib.h ; do
1             MY_CHECK_HEADER([$header])
1           done
1 
1      In the above example, `MY_CHECK_HEADER' can operate on polymorphic
1      variable names.  In the first invocation, the m4 argument is
1      `stdint.h', which transliterates into a literal `stdint_h'.  As a
1      result, the temporary macro `my_Header' expands to the literal
1      shell name `ac_cv_header_stdint_h'.  In the second invocation, the
1      m4 argument to `MY_CHECK_HEADER' is `$header', and the temporary
1      macro `my_Header' expands to the indirect shell name
1      `$as_my_Header'.  During the shell execution of the for loop, when
1      `$header' contains `inttypes.h', then `$as_my_Header' contains
1      `ac_cv_header_inttypes_h'.  If this script is then run on a
1      platform where all three headers have been previously detected, the
1      output of the script will include:
1 
1           header stdint.h detected
1           header inttypes.h detected
1           header stdlib.h detected
1 
1  -- Macro: AS_VAR_SET (VAR, [VALUE])
1      Emit shell code to assign the contents of the polymorphic shell
1      variable VAR to the shell expansion of VALUE.  VALUE is not
1      subject to field splitting or file name expansion, so if command
1      substitution is used, it may be done with ``""`' rather than using
1      an intermediate variable (⇒Shell Substitutions).  However,
1      VALUE does undergo rescanning for additional macro names; behavior
1      is unspecified if late expansion results in any shell
1      meta-characters.
1 
1  -- Macro: AS_VAR_SET_IF (VAR, [IF-SET], [IF-UNDEF])
1      Emit a shell conditional statement, which executes IF-SET if the
1      polymorphic shell variable `var' is set to any value, and IF-UNDEF
1      otherwise.
1 
1  -- Macro: AS_VAR_TEST_SET (VAR)
1      Emit a shell statement that results in a successful exit status
1      only if the polymorphic shell variable `var' is set.
1