# Remember the order into which we scanned the files.
# It's important to output the contents of aclocal.m4 in the opposite order.
+# (Definitions in first files we have scanned should override those from
+# later files. So they must appear last in the output.)
@file_order = ();
# Map macro names to file names.
# Map file names to file contents.
%file_contents = ();
+# Map file names to included files (transitively closed).
+%file_includes = ();
+
# How much to say.
$verbose = 0;
# First, scan acinclude.m4 if it exists.
if (-f 'acinclude.m4')
{
- $file_contents{'acinclude.m4'} = &scan_file ('acinclude.m4');
+ &scan_file ('acinclude.m4');
}
local ($m4dir);
next if $file eq 'aclocal.m4';
$fullfile = $m4dir . '/' . $file;
- $file_contents{$fullfile} = &scan_file ($fullfile);
+ &scan_file ($fullfile);
}
closedir (DIR);
}
s/\bdnl\b.*$//;
s/\#.*$//;
- while (/$m4_include_rx/g)
+ while (/$m4_include_rx/go)
{
push (@ilist, $1 || $2);
}
- while (/$ac_require_rx/g)
+ while (/$ac_require_rx/go)
{
push (@rlist, $1 || $2);
}
# Point to the documentation for underquoted AC_DEFUN only once.
my $underquoted_manual_once = 0;
-# Scan a single M4 file. Return contents.
+# Scan a single M4 file, and all files it includes.
+# Return the list of included files.
sub scan_file ($)
{
- local ($file) = @_;
+ my ($file) = @_;
+ my $base = dirname $file;
+
+ # Do not scan the same file twice.
+ return @$file_includes{$file} if exists $file_includes{$file};
+ # Prevent potential infinite recursion (if two files include each other).
+ return () if exists $file_contents{$file};
unshift @file_order, $file;
my $fh = new Automake::XFile $file;
my $contents = '';
+ my @inc_files = ();
while ($_ = $fh->getline)
{
# Ignore `##' lines.
$contents .= $_;
- if (/$ac_defun_rx/)
+ while (/$ac_defun_rx/go)
{
if (! defined $1)
{
unless $underquoted_manual_once;
$underquoted_manual_once = 1;
}
- if (! defined $map{$1 || $2})
+ my $macro = $1 || $2;
+ if (! defined $map{$macro})
{
- print STDERR "aclocal: found macro $1 in $file: $.\n"
+ print STDERR "aclocal: found macro $macro in $file: $.\n"
if $verbose;
- $map{$1 || $2} = $file;
+ $map{$macro} = $file;
}
else
{
# extremely unpopular. It causes actual problems which
# are hard to work around, especially when you must
# mix-and-match tool versions.
- print STDERR "aclocal: ignoring macro $1 in $file: $.\n"
+ print STDERR "aclocal: ignoring macro $macro in $file: $.\n"
if $verbose;
}
}
+
+ while (/$m4_include_rx/go)
+ {
+ my $ifile = $1 || $2;
+ # m4_include is relative to the directory of the file which
+ # perform the include, but we want paths relative to the
+ # directory where aclocal is run. Do not use
+ # File::Spec->rel2abs, because we want to store relative
+ # paths (they might be used later of aclocal outputs an
+ # m4_include for this file, or if the user itself includes
+ # this file).
+ $ifile = "$base/$ifile"
+ unless $base eq '.' || File::Spec->file_name_is_absolute ($ifile);
+ push (@inc_files, $ifile);
+ }
}
+ $file_contents{$file} = $contents;
+
+ # For some reason I don't understand, it does not work
+ # to do `map { scan_file ($_) } @inc_files' below.
+ # With Perl 5.8.2 it undefines @inc_files.
+ my @copy = @inc_files;
+ my @all_inc_files = (@inc_files, map { scan_file ($_) } @copy);
+ $file_includes{$file} = \@all_inc_files;
+ return @all_inc_files;
+}
- return $contents;
+# strip_redundant_includes (%FILES)
+# ---------------------------------
+# Each key in %FILES is a file that must be present in the output.
+# However some of these files might already include other files in %FILES,
+# so there is no point in including them another time.
+# This removes items of %FILES which are already included by another file.
+sub strip_redundant_includes (%)
+{
+ my %files = @_;
+ # Files at the end of @file_order should override those at the beginning,
+ # so it is important to preserve these trailing files. We can remove
+ # a file A if it is going to be output before a file B that includes
+ # file A, not the converse.
+ foreach my $file (reverse @file_order)
+ {
+ next unless exists $files{$file};
+ foreach my $ifile (@{$file_includes{$file}})
+ {
+ next unless exists $files{$ifile};
+ delete $files{$ifile};
+ print STDERR "$ifile is already included by $file\n"
+ if $verbose;
+ }
+ }
+ return %files;
}
sub trace_used_macros ()
{
my %files = map { $map{$_} => 1 } keys %macro_seen;
+ $files{'acinclude.m4'} = 1 if -f 'acinclude.m4';
+ %files = strip_redundant_includes %files;
my $traces = ($ENV{AUTOM4TE} || 'autom4te');
$traces .= " --language Autoconf-without-aclocal-m4 ";
my %files = map { $map{$_} => 1 } @macros;
$files{'acinclude.m4'} = 1 if -f 'acinclude.m4';
+ %files = strip_redundant_includes %files;
for $file (grep { exists $files{$_} } @file_order)
{
- my $mtime = mtime $file;
- $greatest_mtime = $mtime if $greatest_mtime < $mtime;
+ # Check the time stamp of this file, and all files it includes.
+ for my $ifile ($file, @{$file_includes{$file}})
+ {
+ my $mtime = mtime $ifile;
+ $greatest_mtime = $mtime if $greatest_mtime < $mtime;
+ }
# If the file to add looks like outside the project, copy it
# to the output. The regex catches filenames starting with
--- /dev/null
+#! /bin/sh
+# Copyright (C) 2004 Free Software Foundation, Inc.
+#
+# This file is part of GNU Automake.
+#
+# GNU Automake 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.
+#
+# GNU Automake 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 Automake; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# Make sure m4_included files are also scanned for definitions.
+# Report from Phil Edwards.
+
+required=GNUmake
+. ./defs || exit 1
+
+set -e
+
+cat >> configure.in << 'END'
+AM_PROG_LIBTOOL
+AC_OUTPUT
+END
+
+echo 'm4_include([a.m4])' > acinclude.m4
+echo 'm4_include([b.m4])' > a.m4
+cat >b.m4 <<EOF
+m4_include([c.m4])
+AC_DEFUN([AM_PROG_LIBTOOL],
+[AC_REQUIRE([SOMETHING])dnl
+AC_REQUIRE([SOMETHING_ELSE])dnl
+])
+
+AC_DEFUN([SOMETHING])
+EOF
+echo 'm4_include([d.m4])' > c.m4
+echo 'AC_DEFUN([SOMETHING_ELSE])' >d.m4
+
+mkdir defs
+echo 'AC_DEFUN([SOMETHING_ELSE])' >defs/e.m4
+echo 'AC_DEFUN([ANOTHER_MACRO])' >defs/f.m4
+
+cat >>Makefile.am<<\EOF
+ACLOCAL_AMFLAGS = -I defs
+testdist1: distdir
+ test -f $(distdir)/acinclude.m4
+ test -f $(distdir)/a.m4
+ test -f $(distdir)/b.m4
+ test -f $(distdir)/c.m4
+ test -f $(distdir)/d.m4
+ test ! -d $(distdir)/defs
+testdist2: distdir
+ test -f $(distdir)/acinclude.m4
+ test -f $(distdir)/a.m4
+ test -f $(distdir)/b.m4
+ test -f $(distdir)/c.m4
+ test -f $(distdir)/d.m4
+ test ! -f $(distdir)/defs/e.m4
+ test -f $(distdir)/defs/f.m4
+EOF
+
+$ACLOCAL -I defs
+
+$FGREP acinclude.m4 aclocal.m4
+# None of the following macro should be included. acinclude.m4
+# includes the first four, and the last two are not needed at all.
+$FGREP a.m4 aclocal.m4 && exit 1
+$FGREP b.m4 aclocal.m4 && exit 1
+$FGREP c.m4 aclocal.m4 && exit 1
+$FGREP d.m4 aclocal.m4 && exit 1
+$FGREP defs/e.m4 aclocal.m4 && exit 1
+$FGREP defs/f.m4 aclocal.m4 && exit 1
+
+$AUTOCONF
+$AUTOMAKE
+
+./configure
+$MAKE testdist1
+
+cp aclocal.m4 stamp
+$sleep
+
+cat >>c.m4 <<\EOF
+AC_DEFUN([FOO], [ANOTHER_MACRO])
+EOF
+$MAKE
+# Because c.m4 has changed, aclocal.m4 must have been rebuilt.
+test `ls -1t aclocal.m4 stamp | sed 1q` = aclocal.m4
+# However, since FOO is not used, f.m4 should not be included
+# and the contents of aclocal.m4 should remain the same
+cmp aclocal.m4 stamp
+
+
+# If FOO where to be used, that would be another story, of course.
+cat >>configure.in <<EOF
+FOO
+EOF
+cp aclocal.m4 stamp
+$sleep
+$MAKE
+grep 'defs/f.m4' aclocal.m4
+$MAKE testdist2