blob: da80f8124ff3a31f6bb824ab2ef1bb2d2807d234 [file] [log] [blame]
#! /usr/bin/env perl
# Automatically compute some dependencies for the hand-written tests
# of the Automake testsuite. Also, automatically generate some more
# tests from them (for particular cases/setups only).
# Copyright (C) 2011-2012 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, see <http://www.gnu.org/licenses/>.
#--------------------------------------------------------------------------
use warnings FATAL => "all";
use strict;
use File::Basename ();
use constant TRUE => 1;
use constant FALSE => 0;
my $me = File::Basename::basename $0;
# For use in VPATH builds.
my $srcdir = ".";
# The testsuite subdirectory, relative to the top-lever source directory.
my $testdir = "t";
# Where testsuite-related helper scripts, data files and shell libraries
# are placed. Relative to the top-lever source directory.
my $testauxdir = "$testdir/ax";
#--------------------------------------------------------------------------
sub unindent ($)
{
my $text = shift;
$text =~ /^(\s*)/;
my $indentation = $1;
$text =~ s/^$indentation//gm;
return $text;
}
sub atomic_write ($$;$)
{
my ($outfile, $func) = (shift, shift);
my $perms = @_ > 0 ? shift : 0777;
my $tmpfile = "$outfile-t";
foreach my $f ($outfile, $tmpfile)
{
unlink $f or die "$me: cannot unlink '$f': $!\n"
if -e $f;
}
open (my $fh, ">$tmpfile")
or die "$me: can't write to '$tmpfile': $!\n";
$func->($fh);
close $fh
or die "$me: closing '$tmpfile': $!\n";
chmod ($perms & ~umask, $tmpfile)
or die "$me: cannot change perms for '$tmpfile': $!\n";
rename ($tmpfile, $outfile)
or die "$me: renaming '$tmpfile' -> '$outfile: $!\n'";
}
sub line_match ($$)
{
my ($re, $file) = (shift, shift);
# Test scripts should always be in srcdir, whether auto-generated
# or not.
open (FH, "<$srcdir/$file")
or die "$me: cannot open file '$srcdir/$file': $!\n";
my $ret = 0;
while (defined (my $line = <FH>))
{
if ($line =~ $re)
{
$ret = 1;
last;
}
}
close FH or die "$me: cannot close file '$file': $!\n";
return $ret;
}
sub write_wrapper_script ($$$)
{
my ($file_handle, $wrapped_test, $shell_setup_code, $creator_name) = @_;
print $file_handle unindent <<EOF;
#! /bin/sh
# This file has been automatically generated. DO NOT EDIT BY HAND!
. test-lib.sh
$shell_setup_code
w="\$am_top_srcdir/$wrapped_test"
if test -f "\$w"; then
echo "\$0: will source '\$w'"
. "\$w"
exit \$?
fi
echo "\$0: cannot find wrapped test '\$w'" >&2
exit 99
EOF
}
sub get_list_of_tests ()
{
my @tests_list = split /\s+/,
`cd '$srcdir' && echo $testdir/*.sh $testdir/*.tap`;
# Tests whose names matches this pattern are already autogenerated,
# so we shouldn't re-process them.
@tests_list = grep { !/-w[0-9]*\.(sh|tap)$/ } @tests_list;
die "$me: cannot get list of tests\n" unless $? == 0 && @tests_list;
my $ok = 1;
foreach my $test (@tests_list)
{
my $test_path = "$srcdir/$test";
next if -f $test_path;
warn "$me: test '$test_path' not found\n";
$ok = 0;
}
die "$me: some test scripts not found\n" if !$ok;
return @tests_list;
}
sub parse_options (@)
{
use Getopt::Long qw/GetOptions/;
local @ARGV = @_;
GetOptions ('srcdir=s' => \$srcdir) or die "$me: usage error\n";
die "$me: too many arguments\n" if @ARGV > 0;
die "$me: srcdir '$srcdir': not a directory\n" unless -d $srcdir;
}
#--------------------------------------------------------------------------
my %deps_extractor =
(
libtool_macros =>
{
line_matcher => qr/^\s*required=.*\blibtool/,
nodist_prereqs => "$testdir/libtool-macros.log",
},
gettext_macros =>
{
line_matcher => qr/^\s*required=.*\bgettext/,
nodist_prereqs => "$testdir/gettext-macros.log",
},
use_trivial_test_driver =>
{
line_matcher => qr/\btrivial-test-driver\b/,
dist_prereqs => "$testauxdir/trivial-test-driver",
},
depcomp_shuffle =>
{
line_matcher => qr/\bdepcomp-shuffle\.sh\b/,
dist_prereqs => "$testauxdir/depcomp-shuffle.sh",
},
check_testsuite_summary =>
{
line_matcher => qr/\btestsuite-summary-checks\.sh\b/,
dist_prereqs => "$testauxdir/testsuite-summary-checks.sh",
},
extract_testsuite_summary =>
{
line_matcher => qr/\bextract-testsuite-summary\.pl\b/,
dist_prereqs => "$testauxdir/extract-testsuite-summary.pl",
},
check_tap_testsuite_summary =>
{
line_matcher => qr/\btap-summary-aux\.sh\b/,
dist_prereqs => "$testauxdir/tap-summary-aux.sh",
},
on_tap_with_common_setup =>
{
line_matcher => qr/\btap-setup\.sh\b/,
dist_prereqs => "$testauxdir/tap-setup.sh",
nodist_prereqs => "$testdir/tap-common-setup.log",
},
depcomp =>
{
line_matcher => qr/\bdepcomp\.sh\b/,
dist_prereqs => "$testauxdir/depcomp.sh",
},
);
#--------------------------------------------------------------------------
my %test_generators =
(
#
# Any test script in the Automake testsuite that checks features of
# the Automake-provided parallel testsuite harness might want to
# define a sibling test that does similar checks, but for the old
# serial testsuite harness instead.
#
# Individual tests can request the creation of such a sibling by
# making the string "try-with-serial-tests" appear any line of the
# test itself.
#
serial_testsuite_harness =>
{
line_matcher => qr/\btry-with-serial-tests\b/,
shell_setup_code => 'am_serial_tests=yes',
},
#
# For each test script in the Automake testsuite that tests features
# of one or more automake-provided shell script from the 'lib/'
# subdirectory by running those scripts directly (i.e., not thought
# make calls and automake-generated makefiles), define a sibling test
# that does likewise, but running the said script with the configure
# time $SHELL instead of the default system shell /bin/sh.
#
# A test is considered a candidate for sibling-generation if it calls
# the 'get_shell_script' function anywhere.
#
# Individual tests can prevent the creation of such a sibling by
# explicitly setting the '$am_test_prefer_config_shell' variable
# to either "yes" or "no".
# The rationale for this is that if the variable is set to "yes",
# the test already uses $SHELL, so that a sibling would be just a
# duplicate; while if the variable is set to "no", the test doesn't
# support, or is not meant to use, $SHELL to run the script under
# testing, and forcing it to do so in the sibling would likely
# cause a spurious failure.
#
prefer_config_shell =>
{
line_matcher =>
qr/(^|\s)get_shell_script\s/,
line_rejecter =>
qr/\bam_test_prefer_config_shell=/,
shell_setup_code =>
'am_test_prefer_config_shell=yes',
},
#
# Tests on tap support should be run with both the perl and awk
# implementations of the TAP driver (they run with the awk one
# by default).
#
perl_tap_driver =>
{
line_matcher =>
qr<(?:\bfetch_tap_driver\b|[\s/]tap-setup\.sh\b)>,
line_rejecter =>
qr/\bam_tap_implementation=/,
shell_setup_code =>
'am_tap_implementation=perl',
},
);
#--------------------------------------------------------------------------
parse_options @ARGV;
my @all_tests = get_list_of_tests;
print "## -*- Makefile -*-\n";
print "## Generated by $me. DO NOT EDIT BY HAND!\n";
print <<EOF;
## --------------------------------------- ##
## Dependencies for autogenerated tests. ##
## --------------------------------------- ##
EOF
# FIXME: the following is not really right, since cannot compose wrapping
# of tests matching more than one condition. Still, there should be no
# such test at the moment, so the limitation is (temporarily) acceptable.
while (my ($k, $g) = each %test_generators)
{
my @wrapped_tests = grep {
line_match ($g->{line_matcher}, $_)
&& (!$g->{line_rejecter} || !line_match ($g->{line_rejecter}, $_))
} @all_tests;
foreach my $wrapped_test (@wrapped_tests)
{
(my $base = $wrapped_test) =~ s/\.([^.]*)$//;
my $suf = $1 or die "$me: test '$wrapped_test' lacks a suffix\n";
my $wrapper_test = "$srcdir/$base-w.$suf";
# Create wrapper test.
atomic_write $wrapper_test,
sub { write_wrapper_script $_[0], $wrapped_test,
$g->{shell_setup_code} },
0555;
# The generated test works by sourcing the original test, so that
# it has to be re-run every time that changes ...
print "$base-w.log: $wrapped_test\n";
# ... but also every time the prerequisites of the wrapped test
# changes. The simpler (although suboptimal) way to do so is to
# ensure that the wrapped tests runs before the wrappee one (in
# case it needs to be re-run *at all*.
# FIXME: we could maybe refactor the script to find a more
# granular way to express such implicit dependencies.
print "$base-w.log: $base.log\n";
}
}
print <<EOF;
## ---------------------------------------------- ##
## Dependencies for ad-hoc autogenerated tests. ##
## ---------------------------------------------- ##
EOF
print "## Tests on automatic dependency tracking (see t/ax/depcomp.sh)\n";
# Key: depmode, value: list of required programs.
my %depmodes =
(
auto => ["cc"],
disabled => ["cc"],
makedepend => ["cc", "makedepend"],
dashmstdout => ["gcc"],
cpp => ["gcc"],
# This is for older (pre-3.x) GCC versions. Newer versions
# have depmode "gcc3".
gcc => ["gcc"],
# This is for older (pre-7) msvc versions. Newer versions
# have depmodes "msvc7" and "msvc7msys".
msvisualcpp => ["cl", "cygpath"],
msvcmsys => ["cl", "mingw"],
);
foreach my $lt (TRUE, FALSE)
{
foreach my $m (keys %depmodes)
{
my $planned = ($lt && $m eq "auto") ? 84 : 28;
my @required =
(
@{$depmodes{$m}},
$lt ? ("libtoolize",) : (),
);
my @vars_init =
(
"am_create_testdir=empty",
"depmode=$m",
"depcomp_with_libtool=" . ($lt ? "yes" : "no"),
);
my $testname = "depcomp" . ($lt ? "-lt-" : "-") . $m . ".tap";
# Create wrapper test.
atomic_write ("$srcdir/$testdir/$testname", sub
{
my $file_handle = shift;
print $file_handle unindent <<EOF;
#! /bin/sh
# Automatically generated test. DO NOT EDIT BY HAND!
@vars_init
required="@required"
. ./defs || exit 1
plan_ $planned
. depcomp.sh
exit \$?
EOF
},
0555);
}
}
# The test scripts are scanned for automatic dependency generation *after*
# the generated tests have been created, so they too can be scanned. To
# do so correctly, we need to update the list in '@all_tests' to make it
# comprise also the freshly-generated tests.
@all_tests = get_list_of_tests;
print <<EOF;
## ----------------------------- ##
## Autogenerated dependencies. ##
## ----------------------------- ##
EOF
while (my ($k, $x) = each %deps_extractor)
{
my $dist_prereqs = $x->{dist_prereqs} || "";
my $nodist_prereqs = $x->{nodist_prereqs} || "";
my @tests = grep { line_match $x->{line_matcher}, $_ } @all_tests;
map { s/\.[^.]*$//; s/$/\.log/; } (my @logs = @tests);
print "## Added by deps-extracting key '$k'.\n";
## The list of all tests which have a dependency detected by the
## current key.
print join(" \\\n ", "${k}_TESTS =", @tests) . "\n";
print "EXTRA_DIST += $dist_prereqs\n";
map { print "$_: $dist_prereqs $nodist_prereqs\n" } @logs;
print "\n";
}
__END__