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