Hi,

I have spent some time on the tracing issue in Autoconf, and I have
promising results, but there are a few things on which I would like
advises, comments etc.

If people are interested to toy with it, it is attached below.  You
have to configure it as if it were autoconf, i.e.

./config.status --file autotrace:autoconf.sh.traces
chmod +x autotrace

(it is a proto autoconf, not a different prog, you can name it
autoconf if you trust me :-).

My first question is related to what we want to trace.  I find it
extremely useful to be able to trace autoconf.m4, eg the AC_DEFUNs:

~ace % ./autotrace --trace 'AC_DEFUN:$1'                         nostromo 13:10
AC_INIT_NOTICE
AC_PREFIX_DEFAULT
AC_INIT_PARSE_ARGS
AC_INIT_BINSH
AC_INIT
CUT

but most of the use will be on user's configure.in only.  Should we
introduce a switch which allows to trace on or both?

Second, it works extremely well when you specify a finite list of args
to list:

~ace % ./autotrace --trace 'AC_OUTPUT:$f:$l:$1'                  nostromo 13:14
configure.in:45:acversion.m4 Makefile m4/Makefile man/Makefile doc/Makefile
          tests/Makefile tests/atconfig

arg, there's a new line in there.  Let's make sure we can track it:

~ace % ./autotrace --trace 'AC_OUTPUT:$f:$l:[$1]'                nostromo 13:15
configure.in:45:[acversion.m4 Makefile m4/Makefile man/Makefile doc/Makefile
          tests/Makefile tests/atconfig]

No particular reason for []



Or one could fill up a more or less Perl structure:

~ace % ./autotrace --trace 'AC_SUBST:ac_subst{"$1"} = "$f:$l"'   nostromo 13:16
ac_subst{"ECHO_C"} = "configure.in:2"
ac_subst{"ECHO_N"} = "configure.in:2"
ac_subst{"ECHO_T"} = "configure.in:2"
ac_subst{"SHELL"} = "configure.in:2"
ac_subst{"CFLAGS"} = "configure.in:2"
ac_subst{"CPPFLAGS"} = "configure.in:2"
ac_subst{"CXXFLAGS"} = "configure.in:2"
ac_subst{"FFLAGS"} = "configure.in:2"
ac_subst{"DEFS"} = "configure.in:2"

what should make automake much easier (I don't know actual, just a
thought).



Now my problem is with $@:

~ace % ./autotrace --trace 'AC_SUBST:$@'                         nostromo 13:18
[configure.in],[2],[1],[ECHO_C]
[configure.in],[2],[1],[ECHO_N]
[configure.in],[2],[1],[ECHO_T]
[configure.in],[2],[1],[SHELL]
[configure.in],[2],[1],[CFLAGS]
[configure.in],[2],[1],[CPPFLAGS]
[configure.in],[2],[1],[CXXFLAGS]
[configure.in],[2],[1],[FFLAGS]
[configure.in],[2],[1],[DEFS]
[configure.in],[2],[1],[LDFLAGS]
[configure.in],[2],[1],[LIBS]
[configure.in],[2],[1],[exec_prefix]

As you can see, my technical trick which consists in giving file,
lineno and depth to the tracing macro is visible.  In order to
avoid that there are macro expansions performed on the arguments, I
did quote twice, which means I can't just
`s/$@/shift(shift(shift($@)))/' and have the right thing happen:

~ace % ./autotrace --trace 'AC_SUBST:shift(shift(shift($@)))'    nostromo 13:19
shift(shift(shift([configure.in],[2],[1],[ECHO_C])))
shift(shift(shift([configure.in],[2],[1],[ECHO_N])))
shift(shift(shift([configure.in],[2],[1],[ECHO_T])))

Also, I see no immediate means to specify another technique to
separate $@ than [], [], no means to :.:.:.: as above.

Is this a limitation with which we can live?

Are there any suggestions?

        Akim

PS/ Yes, there's a small bug which I observed here but not at home:
here all my traces were preceded with two white lines (which I removed
from the message).  It will be fixed.

#! @SHELL@
# autoconf -- create `configure' using m4 macros
# Copyright (C) 1992, 93, 94, 96, 99, 2000 Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.

# If given no args, create `configure' from template file `configure.in'.
# With one arg, create a configure script on standard output from
# the given template file.

me=`echo "$0" | sed -e 's,.*/,,'`

usage="\
Usage: autoconf [OPTION] ... [TEMPLATE-FILE]

Generate a configuration script from a TEMPLATE-FILE if given, or
\`configure.in' by default.  Output is sent to the standard output if
TEMPLATE-FILE is given, else into \`configure'.

If the option \`--trace' is used, no configuration script is created.

  -h, --help          print this help, then exit
      --version       print version number, then exit
  -m, --macrodir=DIR  directory storing Autoconf's macro files
  -l, --localdir=DIR  directory storing the \`aclocal.m4' file
  -t, --trace=MACRO   report the list of calls to MACRO
  -o, --output=FILE   save output in FILE (stdout is the default)

Report bugs to <[EMAIL PROTECTED]>."

version="\
autoconf (GNU @PACKAGE@) @VERSION@
Written by David J. MacKenzie.

Copyright (C) 1992, 93, 94, 96, 99, 2000 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="\
Try \`$me --help' for more information."

# NLS nuisances.
# Only set these to C if already set.  These must not be set unconditionally
# because not all systems understand e.g. LANG=C (notably SCO).
# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
# Non-C LC_CTYPE values break the ctype check.
if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi

# ac_LF_and_DOT
# We use echo to avoid assuming a particular line-breaking character.
# The extra dot is to prevent the shell from consuming trailing
# line-breaks from the sub-command output.  A line-break within
# single-quotes doesn't work because, if this script is created in a
# platform that uses two characters for line-breaks (e.g., DOS), tr
# would break.
ac_LF_and_DOT=`echo; echo .`

# An uncommon character, used as a separator.
separator='
'

# Find GNU m4.
# Handle the case that m4 has moved since we were configured.
# It may have been found originally in a build directory.
: ${M4=@M4@}
case "$M4" in
/*) test -f "$M4" || M4=m4 ;;
esac
# Some non-GNU m4's don't reject the --help option, so give them /dev/null.
case `$M4 --help </dev/null 2>&1` in
*reload-state*);;
*) echo "$me: Autoconf requires GNU m4 1.4 or later" >&2; exit 1 ;;
esac

# Variables.
: ${AC_MACRODIR=@datadir@}
: ${AC_ACLOCALDIR=`(aclocal --print-ac-dir) 2>/dev/null`}
: ${AWK=@AWK@}
localdir=
outfile=
# Tasks:
# - trace
#   Trace the first arguments of some macros
# - script
#   Produce the configure script (default)
task=script
: ${TMPDIR=/tmp}
tmpin=$TMPDIR/ac$$.in
tmpout=$TMPDIR/ac$$.out
silent_m4=$TMPDIR/acs$$.m4
trace_m4=$TMPDIR/act$$.m4
translate_awk=$TMPDIR/actr$$.awk
verbose=:

# Parse command line
while test $# -gt 0 ; do
  case "$1" in
    --version | --vers* )
       echo "$version" ; exit 0 ;;
    --help | --h* | -h )
       echo "$usage"; exit 0 ;;

    --localdir=* | --l*=* )
       localdir=`echo "$1" | sed -e 's/^[^=]*=//'`
       shift ;;
    --localdir | --l* | -l )
       shift
       test $# -eq 0 && { echo "$help" >&2; exit 1; }
       localdir="$1"
       shift ;;

    --macrodir=* | --m*=* )
       AC_MACRODIR=`echo "$1" | sed -e 's/^[^=]*=//'`
       shift ;;
    --macrodir | --m* | -m )
       shift
       test $# -eq 0 && { echo "$help" >&2; exit 1; }
       AC_MACRODIR="$1"
       shift ;;

    --install )
       task=install
       shift;;

    --verbose | --verb* )
       verbose=echo
       shift;;

    --trace | -t )
       task=trace
       shift
       traces="$traces$separator$1"
       shift ;;
    --trace=* )
       task=trace
       traces="$traces$separator"`echo "$1" | sed -e 's/^[^=]*=//;s/:.*//'`
       shift ;;

    --output | -o )
       shift
       outfile="$1"
       shift ;;
    --output=* )
       outfile=`echo "$1" | sed -e 's/^[^=]*=//'`
       shift ;;

    -- )     # Stop option processing
       shift; break ;;
    - ) # Use stdin as input.
       break ;;
    -* )
       exec >&2
       echo "$me: invalid option $1"
       echo "$help"
       exit 1 ;;
    * )
       break ;;
  esac
done

# Running m4.
if test -n "$localdir"; then
  use_localdir="-I$localdir -DAC_LOCALDIR=$localdir"
fi
run_m4="$M4 --reload $AC_MACRODIR/autoconf.m4f $use_localdir"


case $# in
  0) infile=configure.in
     test $task = script && test "x$outfile" = x && outfile=configure;;
  1) infile="$1" ;;
  *) exec >&2
     echo "$me: invalid number of arguments."
     echo "$help"
     exit 1 ;;
esac

trap 'rm -f $tmpin $tmpout $silent_m4 $trace_m4' 0 1 2 15
if test z$infile = z-; then
  infile=$tmpin
  cat >$infile
elif test ! -r "$infile"; then
  echo "$me: $infile: No such file or directory" >&2
  exit 1
fi

# Output is produced into FD 4.  Prepare it.
case "x$outfile" in
 x- | x )  # Output to stdout
  exec 4>&1 ;;
 * )
  exec 4>$outfile;;
esac

# Initializations are performed.  Proceed to the main task.
case $task in

  ## --------------------- ##
  ## Generate the script.  ##
  ## --------------------- ##
  script)
  $run_m4 $infile > $tmpout || exit 2

  # You could add your own prefixes to pattern if you wanted to check for
  # them too, e.g. pattern='\(AC_\|ILT_\)', except that UNIX sed doesn't do
  # alternation.
  pattern="A[CHM]_"

  status=0
  if grep "^[^#]*$pattern" $tmpout > /dev/null 2>&1; then
    echo "autoconf: Undefined macros:" >&2
    sed -n "s/^[^#]*\\($pattern[_A-Za-z0-9]*\\).*/\\1/p" $tmpout |
      while read macro; do
        grep -n "^[^#]*$macro" $infile /dev/null
        test $? -eq 1 && echo "***BUG in Autoconf--please report*** $macro"
      done | sort -u >&2
    status=1
  fi

  if test -n "$outfile"; then
    chmod +x $outfile
  fi

  # Put the real line numbers into configure to make config.log more helpful.
  # Because quoting can sometimes get really painful in m4, there are special
  # @tokens@ to substitute.
  sed -e 's/[   ]*$//' <$tmpout |
    sed -e '/^$/N;/\n$/D' |
    $AWK '
      /__oline__/ { printf "%d:", NR + 1 }
                  { print }' |
    sed '
      /__oline__/s/^\([0-9][0-9]*\):\(.*\)__oline__/\2\1/
      s/@BKL@/[/g
      s/@BKR@/]/g
      s/@DLR@/$/g
      s/@PND@/#/g
      ' >&4
  ;; # End of the task script.



  ## -------------- ##
  ## Trace macros.  ##
  ## -------------- ##
  trace)
  # errprint must be silent when we run `m4 --trace', otherwise there can be
  # warnings mixed with traces in m4's stderr.
  cat >$silent_m4 <<\EOF
define(`errprint')dnl
EOF
  # A program to trace m4 macros.
  cat >$trace_m4 <<\EOF
divert(-1)
  changequote([, ])
divert(0)dnl
EOF
  # A program to translate user tracing requests into m4 macros.
  cat >$translate_awk <<\EOF
{
  res = "";

  for (cp = $0; cp; cp = substr(cp, 2))
    {
      char = substr (cp, 1, 1);
      if (char == "$")
        {
          res = res "$";
          if (match (cp, /^\$[0-9]+/))
            {
              # $n.
              res = res (substr (cp, 2, RLENGTH - 1) + 3);
              cp = substr (cp, RLENGTH);
            }
          else if (substr (cp, 2, 1) == "f")
            {
              # File name.
              res = res "1";
              cp = substr(cp, 2);
            }
          else if (substr (cp, 2, 1) == "l")
            {
              # Line number.
              res = res "2";
              cp = substr(cp, 2);
            }
          else if (substr (cp, 2, 1) == "d")
            {
              # Depth.
              res = res "3";
              cp = substr(cp, 2);
            }
        }
      else
        res = res char;
    }
  print res;
}
EOF
  # Extract both the m4 program and the m4 options from TRACES.
  saved_IFS=$IFS
  IFS=$separator
  for trace in $traces
  do
    IFS=$saved_IFS
    trace_opt="$trace_opt -t "`echo "$trace" | sed -e 's/:.*//'`
  done
  echo "$traces" |
    sed -e 's/^\([^:]*\):\(.*\)$/define([AT_\1], [[\2]])/' |
    $AWK -f $translate_awk >>$trace_m4
  # We don't use the frozen files because we want to redefine `errprint',
  # and because we want to be to trace AC_DEFUN and others.
  $M4 $trace_opt -daflq -I $AC_MACRODIR \
      $silent_m4 autoconf.m4 $infile 2>&1 >/dev/null |
    sed -e 's/^m4trace:\([^:][^:]*\):\([0-9][0-9]*\): -\([0-9][0-9]*\)- 
\([^(][^(]*\)(\(.*\)$/AT_\4([\1], [\2], [\3], \5/' \
        -e  's/^m4trace:\([^:][^:]*\):\([0-9][0-9]*\): -\([0-9][0-9]*\)- 
\(.*\)$/AT_\4([\1], [\2], [\3])/' >>$trace_m4
  cp $trace_m4 tmpin
  m4 $trace_m4
#  cat $tmpout |
#    # No need to be too verbose
#    uniq |
#    sed -e 's/m4trace:/@&/' |
#    # Join arguments spread on several lines
#    tr "$ac_LF_and_DOT" ' .' |
#    tr @. "$ac_LF_and_DOT" |
#    # Remove m4trace and -1-
#    sed -n -e 's/^[^:]*:\([^:]*:[^:]*:\)[^a-zA-Z_]*\([a-zA-Z_]*.*\) $/\1\2/p' |
#    # Replace the first `(' by a colon, remove the last `)'.
#    sed -e 's/(/:/; s/)$//' >&4
#  # The last eof was eaten.
#  echo >&4
  ;;




  ## -------------------------------------------------------- ##
  ## Task --install.  Install links to the library m4 files.  ##
  ## -------------------------------------------------------- ##
  install)
  # An m4 program that reports what macros are requested, and where
  # they were defined.
  cat >$tmpin <<\EOF
dnl Keep the definition of the old AC_DEFUN
define([AC_DEFUN_OLD], defn([AC_DEFUN]))

dnl Define the macro so that the first time it is expanded, it reports
dnl on stderr its name, and where it was defined.
define([AC_DEFUN],
[AC_DEFUN_OLD([$1],
   [ifdef([AC_DECLARED{$1}],,
          [define([AC_DECLARED{$1}])errprint(]]__file__:__line__:[[ [$1]
)])dnl
][$2])])

dnl All the includes must be disabled.  If they are not, since people don't
dnl protect the first argument of AC_DEFUN, then, if read a second time
dnl this argument will be expanded, and we'll get pure junk out of m4.
define([AC_INCLUDE])
EOF
  # Run m4 with all the library files, save its report on strderr.
  $verbose Running $run_m4 -dipa -t m4_include -t m4_sinclude $tmpin $localdir/*.m4 
$AC_ACLOCALDIR/*.m4 $infile
  $run_m4 -dipa -t m4_include -t m4_sinclude $tmpin $localdir/*.m4 $AC_ACLOCALDIR/*.m4 
$infile >the-script 2>$tmpout
  # Keep only the good lines, there may be other outputs
  grep '^[^: ]*:[0-9][0-9]*:[^:]*$' $tmpout >$tmpin
  # Extract the files that are not in the local dir, and install the links.
  # Save in $tmpout the list of installed links.
  >$tmpout
  $verbose "Required macros:"
  $verbose "`sed -e 's/^/| /' $tmpin`"
  cat $tmpin |
    while read line
    do
      file=`echo "$line" | sed -e 's/:.*//'`
      filename=`echo "$file" | sed -e 's,.*/,,'`
      macro=`echo "$line" | sed -e 's/.*:[      ]*//'`
      if test -f "$file" && test "x$file" != "x$infile"; then
        if test -f $localdir/$filename; then
          $verbose "$filename already installed"
        else
          $verbose "installing $file which provides $macro"
          ln -s "$file" "$localdir/$filename" ||
          cp "$file" "$localdir/$filename" ||
          {
            echo "$me: cannot link from $file to $localdir/$filename" >&2
            exit 1
          }
        fi
        echo "$localdir/$filename" >>$tmpout
      fi
    done
  # Now that we have installed the links, and that we know that the
  # user needs the FILES, check that there is an exact correspondence.
  # Use yourself to get the list of the included files.
  export AC_ACLOCALDIR
  export AC_MACRODIR
  # Not m4_s?include, because it would catch acsite and aclocal, which
  # we don't care of.
  $0 -l "$localdir" -t AC_INCLUDE $inline |
    sed -e 's/^[^:]*:[^:]*:[^:]*://g' |
    sort |
    uniq >$tmpin
  # All the included files are needed.
  for file in `cat $tmpin`;
  do
    if fgrep "$file" $tmpout >/dev/null 2>&1; then :; else
      echo "\`$file' is uselessly included" >&2
    fi
  done
  # All the needed files are included.
  for file in `sort $tmpout | uniq`;
  do
    if fgrep "$file" $tmpin >/dev/null 2>&1; then :; else
      echo "\`$file' is not included" >&2
    fi
  done
  ;;



  ## ------------ ##
  ## Unknown task ##
  ## ------------ ##

  *)echo "$me: internal error: unknown task: $task" >&2
    exit 1
esac

exit $status

Reply via email to