1#! /bin/sh
2
3# Extract macro arguments from autotools input with GNU M4.
4# Written by Gary V. Vaughan, 2010
5#
6# Copyright (C) 2010-2012 Free Software Foundation, Inc.
7# This is free software; see the source for copying conditions.  There is NO
8# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
10# Make sure we've evaluated scripts we depend on.
11test -z "$progpath" && . `echo "$0" |${SED-sed} 's|[^/]*$||'`/funclib.sh
12test extract-trace = "$progname" && . `echo "$0" |${SED-sed} 's|[^/]*$||'`/options-parser
13
14# Set a version string.
15scriptversion=2012-10-07.10; # UTC
16
17# This program is free software: you can redistribute it and/or modify
18# it under the terms of the GNU General Public License as published by
19# the Free Software Foundation, either version 3 of the License, or
20# (at your option) any later version.
21
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25# GNU General Public License for more details.
26
27# You should have received a copy of the GNU General Public License
28# along with this program.  If not, see <http://www.gnu.org/licenses/>.
29
30# Please report bugs or propose patches to gary@gnu.org.
31
32
33## ------ ##
34## Usage. ##
35## ------ ##
36
37# Run './extract-trace --help' for help with using this script from the
38# command line.
39#
40# Or source first 'options-parser' and then this file into your own
41# scripts in order to make use of the function and variable framework
42# they define, and also to avoid the overhead of forking to run this
43# script in its own process on every call.
44
45
46
47## ------------------##
48## Helper functions. ##
49## ------------------##
50
51# This section contains the helper functions used by the rest of
52# 'extract-trace'.
53
54
55# func_autoconf_configure MAYBE-CONFIGURE-FILE
56# --------------------------------------------
57# Ensure that MAYBE-CONFIGURE-FILE is the name of a file in the current
58# directory which contains an uncommented call to AC_INIT.
59func_autoconf_configure ()
60{
61    $debug_cmd
62
63    _G_sed_no_comment='
64      s|#.*$||
65      s|^dnl .*$||
66      s| dnl .*$||'
67    _G_ac_init=
68
69    # If we were passed a genuine file, make sure it calls AC_INIT.
70    test -f "$1" \
71      && _G_ac_init=`$SED "$_G_sed_no_comment" "$1" |grep AC_INIT`
72
73    # Otherwise it is not a genuine Autoconf input file.
74    test -n "$_G_ac_init"
75    _G_status=$?
76
77    test 0 -ne "$_G_status" \
78      && func_verbose "'$1' not using Autoconf"
79
80    (exit $_G_status)
81}
82
83
84# func_find_tool ENVVAR NAMES...
85# ------------------------------
86# Search for a required program.  Use the value of ENVVAR, if set,
87# otherwise find the first of the NAMES that can be run (i.e.,
88# supports --version).  If found, set ENVVAR to the program name,
89# die otherwise.
90func_find_tool ()
91{
92    $debug_cmd
93
94    _G_find_tool_envvar=$1
95    shift
96    _G_find_tool_names=$@
97    eval "_G_find_tool_res=\$$_G_find_tool_envvar"
98    if test -n "$_G_find_tool_res"; then
99      _G_find_tool_error_prefix="\$$find_tool_envvar: "
100    else
101      for _G_prog
102      do
103        if func_tool_version_output $_G_prog >/dev/null; then
104          _G_find_tool_res=$_G_prog
105          break
106        fi
107      done
108    fi
109    if test -n "$_G_find_tool_res"; then
110      func_tool_version_output >/dev/null $_G_find_tool_res "\
111${_G_find_tool_error_prefix}Cannot run '$_G_find_tool_res --version'"
112
113      # Make sure the result is exported to the environment for children
114      # to use.
115      eval "$_G_find_tool_envvar=\$_G_find_tool_res"
116      eval "export $_G_find_tool_envvar"
117    else
118      func_error "\
119One of these is required:
120       $_G_find_tool_names"
121    fi
122}
123
124
125# func_tool_version_output CMD [FATAL-ERROR-MSG]
126# ----------------------------------------------
127# Attempt to run 'CMD --version', discarding errors.  The output can be
128# ignored by redirecting stdout, and this function used simply to test
129# whether the command exists and exits normally when passed a
130# '--version' argument.
131# When FATAL-ERROR-MSG is given, then this function will display the
132# message and exit if running 'CMD --version' returns a non-zero exit
133# status.
134func_tool_version_output ()
135{
136    $debug_cmd
137
138    _G_cmd=$1
139    _G_fatal_error_msg=$2
140
141    # Some tools, like 'git2cl' produce thousands of lines of output
142    # unless stdin is /dev/null - in that case we want to return
143    # successfully without saving all of that output.  Other tools,
144    # such as 'help2man' exit with a non-zero status when stdin comes
145    # from /dev/null, so we re-execute without /dev/null if that
146    # happens.  This means that occasionally, the output from both calls
147    # ends up in the result, but the alternative would be to discard the
148    # output from one call, and hope the other produces something useful.
149    { $_G_cmd --version </dev/null || $_G_cmd --version; } 2>/dev/null
150    _G_status=$?
151
152    test 0 -ne "$_G_status" && test -n "$_G_fatal_error_msg" \
153        && func_fatal_error "$_G_fatal_error_msg"
154
155    (exit $_G_status)
156}
157
158
159## -------------------- ##
160## Resource management. ##
161## -------------------- ##
162
163# This section contains definitions for functions that each ensure a
164# particular resource (a file, or a non-empty configuration variable for
165# example) is available, and if appropriate to extract default values
166# from pertinent package files.  Where a variable already has a non-
167# empty value (as set by the package's 'bootstrap.conf'), that value is
168# used in preference to deriving the default. Call them using their
169# associated 'require_*' variable to ensure that they are executed, at
170# most, once.
171#
172# It's entirely deliberate that calling these functions can set
173# variables that don't obey the namespace limitations obeyed by the rest
174# of this file, in order that that they be as useful as possible to
175# callers.
176
177
178# require_configure_ac
179# --------------------
180# Ensure that there is a 'configure.ac' or 'configure.in' file in the
181# current directory which contains an uncommented call to AC_INIT, and
182# that '$configure_ac' contains its name.
183require_configure_ac=func_require_configure_ac
184func_require_configure_ac ()
185{
186    $debug_cmd
187
188    test -z "$configure_ac" \
189      && func_autoconf_configure configure.ac && configure_ac=configure.ac
190    test -z "$configure_ac" \
191      && func_autoconf_configure configure.in && configure_ac=configure.in
192    test -z "$configure_ac" \
193      || func_verbose "found '$configure_ac'"
194
195    require_configure_ac=:
196}
197
198
199# require_gnu_m4
200# --------------
201# Search for GNU M4, and export it in $M4.
202require_gnu_m4=func_require_gnu_m4
203func_require_gnu_m4 ()
204{
205    $debug_cmd
206
207    test -n "$M4" || {
208      # Find the first m4 binary that responds to --version.
209      func_find_tool M4 gm4 gnum4 m4
210    }
211
212    test -n "$M4" || func_fatal_error "\
213Please install GNU M4, or 'export M4=/path/to/gnu/m4'."
214
215    func_verbose "export M4='$M4'"
216
217    # Make sure the search result is visible to subshells
218    export M4
219
220    require_gnu_m4=:
221}
222
223
224## --------------- ##
225## Core functions. ##
226## --------------- ##
227
228# This section contains the high level functions used when calling this
229# file as a script. 'func_extract_trace' is probably the only one that you
230# won't want to replace if you source this file into your own script.
231
232
233# func_extract_trace MACRO_NAMES [FILENAME]...
234# --------------------------------------------
235# set '$func_extract_trace_result' to a colon delimited list of arguments
236# to any of the comma separated list of MACRO_NAMES in FILENAME. If no
237# FILENAME is given, then '$configure_ac' is assumed.
238func_extract_trace ()
239{
240    $debug_cmd
241
242    $require_configure_ac
243    $require_gnu_m4
244
245    _G_m4_traces=`$bs_echo "--trace=$1" |$SED 's%,% --trace=%g'`
246    _G_re_macros=`$bs_echo "($1)" |$SED 's%,%|%g'`
247    _G_macros="$1"; shift
248    test $# -gt 0 || {
249      set dummy $configure_ac
250      shift
251    }
252
253    # Generate an error if the first file is missing
254    <"$1"
255
256    # Sadly, we can't use 'autom4te' tracing to extract macro arguments,
257    # because it complains about things we want to ignore at bootstrap
258    # time - like missing m4_include files; AC_PREREQ being newer than
259    # the installed autoconf; and returns nothing when tracing
260    # 'AM_INIT_AUTOMAKE' when aclocal hasn't been generated yet.
261    #
262    # The following tries to emulate a less persnickety version of (and
263    # due to not having to wait for Perl startup on every invocation,
264    # it's probably faster too):
265    #
266    #    autom4te --language=Autoconf --trace=$my_macro:\$% "$@"
267    #
268    # First we give a minimal set of macro declarations to M4 to prime
269    # it for reading Autoconf macros, while still providing some of the
270    # functionality generally used at m4-time to supply dynamic
271    # arguments to Autocof functions, but without following
272    # 'm4_s?include' files.
273    _G_mini='
274        # Initialisation.
275        m4_changequote([,])
276        m4_define([m4_copy],   [m4_define([$2], m4_defn([$1]))])
277        m4_define([m4_rename], [m4_copy([$1], [$2])m4_undefine([$1])])
278
279        # Disable these macros.
280        m4_undefine([m4_dnl])
281        m4_undefine([m4_include])
282        m4_undefine([m4_m4exit])
283        m4_undefine([m4_m4wrap])
284        m4_undefine([m4_maketemp])
285
286        # Copy and rename macros not handled by "m4 --prefix".
287        m4_define([dnl],         [m4_builtin([dnl])])
288        m4_copy([m4_define],     [m4_defun])
289        m4_rename([m4_ifelse],   [m4_if])
290        m4_ifdef([m4_mkstemp],   [m4_undefine([m4_mkstemp])])
291        m4_rename([m4_patsubst], [m4_bpatsubst])
292        m4_rename([m4_regexp],   [m4_bregexp])
293
294        # "m4sugar.mini" - useful m4-time macros for dynamic arguments.
295        # If we discover packages that need more m4 macros defined in
296        # order to bootstrap correctly, add them here:
297        m4_define([m4_bmatch],
298            [m4_if([$#], 0, [], [$#], 1, [], [$#], 2, [$2],
299                   [m4_if(m4_bregexp([$1], [$2]), -1,
300                          [$0([$1], m4_shift3($@))], [$3])])])
301        m4_define([m4_ifndef], [m4_ifdef([$1], [$3], [$2])])
302        m4_define([m4_ifset],
303            [m4_ifdef([$1], [m4_ifval(m4_defn([$1]), [$2], [$3])], [$3])])
304        m4_define([m4_require], [$1])
305        m4_define([m4_shift3], [m4_shift(m4shift(m4shift($@)))])
306
307        # "autoconf.mini" - things from autoconf macros we care about.
308        m4_copy([m4_defun], [AC_DEFUN])
309
310        # Dummy definitions for the macros we want to trace.
311        # AM_INIT_AUTOMAKE at least produces no trace without this.
312    '
313
314    _G_save=$IFS
315    IFS=,
316    for _G_macro in $_G_macros; do
317      IFS=$_G_save
318      func_append _G_mini "AC_DEFUN([$_G_macro])$nl"
319    done
320    IFS=$_G_save
321
322    # We discard M4's stdout, but the M4 trace output from reading our
323    # "autoconf.mini" followed by any other files passed to this
324    # function is then scanned by sed to transform it into a colon
325    # delimited argument list assigned to a shell variable.
326    _G_transform='s|#.*$||; s|^dnl .*$||; s| dnl .*$||;'
327
328    # Unfortunately, alternation in regexp addresses doesn't work in at
329    # least BSD (and hence Mac OS X) sed, so we have to append a capture
330    # and print block for each traced macro to the sed transform script.
331    _G_save=$IFS
332    IFS=,
333    for _G_macro in $_G_macros; do
334      IFS=$_G_save
335      func_append _G_transform '
336        /^m4trace: -1- '"$_G_macro"'/ {
337          s|^m4trace: -1- '"$_G_macro"'[([]*||
338          s|], [[]|:|g
339          s|[])]*$|:|
340          s|\(.\):$|\1|
341          p
342        }'
343    done
344    IFS=$_G_save
345
346    # Save the command pipeline results for further use by callers of
347    # this function.
348    func_extract_trace_result=`$bs_echo "$_G_mini" \
349      |$M4 -daq --prefix $_G_m4_traces - "$@" 2>&1 1>/dev/null \
350      |$SED -n -e "$_G_transform"`
351}
352
353
354# func_extract_trace_first MACRO_NAMES [FILENAME]...
355# --------------------------------------------------
356# Exactly like func_extract_trace, except that only the first argument
357# to the first invocation of one of the comma separated MACRO_NAMES is
358# returned in '$func_extract_trace_first_result'.
359func_extract_trace_first ()
360{
361    $debug_cmd
362
363    func_extract_trace ${1+"$@"}
364    func_extract_trace_first_result=`$bs_echo "$func_extract_trace_result" \
365      |$SED -e 's|:.*$||g' -e 1q`
366}
367
368
369# func_main [ARG]...
370# ------------------
371func_main ()
372{
373    $debug_cmd
374
375    # Configuration.
376    usage='$progname MACRO_NAME FILE [...]'
377
378    long_help_message='
379The first argument to this program is the name of an autotools macro
380whose arguments you want to extract by examining the files listed in the
381remaining arguments using the same tool that Autoconf and Automake use,
382GNU M4.
383
384The arguments are returned separated by colons, with each traced call
385on a separate line.'
386
387    # Option processing.
388    func_options "$@"
389    eval set dummy "$func_options_result"; shift
390
391    # Validate remaining non-option arguments.
392    test $# -gt 1 \
393        || func_fatal_help "not enough arguments"
394
395    # Pass non-option arguments to extraction function.
396    func_extract_trace "$@"
397
398    # Display results.
399    test -n "$func_extract_trace_result" \
400        && $bs_echo "$func_extract_trace_result"
401
402    # The End.
403    exit $EXIT_SUCCESS
404}
405
406
407## --------------------------- ##
408## Actually perform the trace. ##
409## --------------------------- ##
410
411# Only call 'func_main' if this script was called directly.
412test extract-trace = "$progname" && func_main "$@"
413
414# Local variables:
415# mode: shell-script
416# sh-indentation: 2
417# eval: (add-hook 'write-file-hooks 'time-stamp)
418# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
419# time-stamp-time-zone: "UTC"
420# End:
421