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