diff options
Diffstat (limited to 'gen-testsuite-part')
-rwxr-xr-x | gen-testsuite-part | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/gen-testsuite-part b/gen-testsuite-part new file mode 100755 index 000000000..a2db2d30b --- /dev/null +++ b/gen-testsuite-part @@ -0,0 +1,415 @@ +#! /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 = "."; + +#-------------------------------------------------------------------------- + +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); + # Try both builddir and srcdir, with builddir first, to play nice + # with VPATH builds. + open (FH, "<$file") or open (FH, "<$srcdir/$file") + or die "$me: cannot open file '$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) = @_; + # FIXME: we use some creative quoting in the generated scripts, + # FIXME: to please maintainer-check. + print $file_handle unindent <<EOF; + #! /bin/sh + # This file has been automatically generated. DO NOT EDIT BY HAND! + . ./defs-static || exit '99'; + $shell_setup_code + # In the spirit of VPATH, we prefer a test in the build tree + # over one in the source tree. + for dir in . "\$am_top_srcdir"; do + if test -f "\$dir/$wrapped_test"; then + echo "\$0: will source \$dir/$wrapped_test" + . "\$dir/$wrapped_test"; exit "\$?" + fi + done + echo "\$0: cannot find wrapped test '$wrapped_test'" >&2 + exit '99' +EOF +} + +sub get_list_of_tests () +{ + my $make = defined $ENV{MAKE} ? $ENV{MAKE} : "make"; + # Unset MAKEFLAGS, for when we are called from make itself. + my $cmd = "MAKEFLAGS= && unset MAKEFLAGS && cd '$srcdir' && " + . "$make -s -f t/list-of-tests.mk print-list-of-tests"; + my @tests_list = split /\s+/, `$cmd`; + die "$me: cannot get list of tests\n" unless $? == 0 && @tests_list; + my $ok = 1; + foreach my $test (@tests_list) + { + # Respect VPATH builds. + next if -f $test || -f "$srcdir/$test"; + warn "$me: test '$test' 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; +} + +#-------------------------------------------------------------------------- + +# Where testsuite-related helper scripts, data files and shell libraries +# are placed. Relative to the 't/' subdirectory. +my $auxdir = "ax"; + +my %deps_extractor = + ( + libtool_macros => + { + line_matcher => qr/^\s*required=.*\blibtool/, + nodist_prereqs => "t/libtool-macros.log", + }, + gettext_macros => + { + line_matcher => qr/^\s*required=.*\bgettext/, + nodist_prereqs => "t/gettext-macros.log", + }, + use_trivial_test_driver => + { + line_matcher => qr/\btrivial-test-driver\b/, + dist_prereqs => "t/$auxdir/trivial-test-driver", + }, + check_testsuite_summary => + { + line_matcher => qr/\btestsuite-summary-checks\.sh\b/, + dist_prereqs => "t/$auxdir/testsuite-summary-checks.sh", + }, + extract_testsuite_summary => + { + line_matcher => qr/\bextract-testsuite-summary\.pl\b/, + dist_prereqs => "t/$auxdir/extract-testsuite-summary.pl", + }, + check_tap_testsuite_summary => + { + line_matcher => qr/\btap-summary-aux\.sh\b/, + dist_prereqs => "t/$auxdir/tap-summary-aux.sh", + }, + on_tap_with_common_setup => + { + line_matcher => qr/\btap-setup\.sh\b/, + dist_prereqs => "t/$auxdir/tap-setup.sh", + nodist_prereqs => "t/tap-common-setup.log", + }, + depcomp => + { + line_matcher => qr/\bdepcomp\.sh\b/, + dist_prereqs => "t/$auxdir/depcomp.sh", + }, + ); + +#-------------------------------------------------------------------------- + +my %test_generators = + ( + # + # For each test script in the Automake testsuite that itself tests + # features of the TESTS automake interface, define a sibling test + # that does likewise, but with the option 'parallel-tests' enabled. + # + # A test is considered a candidate for sibling-generation if any + # Makefile.am generated by it define the TESTS variable. + # + # Individual tests can prevent the creation of such a sibling by + # explicitly setting the '$am_parallel_tests' variable to either "yes" + # or "no". The rationale for this is that if the variable is set to + # "yes", the test already uses the 'parallel-tests' option, 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 run with, the + # 'parallel-tests' option, and forcing it to do so in the sibling + # would likely cause a spurious failure. + # + parallel_testsuite_harness => + { + line_matcher => + qr/(?:^|\s)TESTS\s*=/, + line_rejecter => + qr/(?:^[^#]*\bparallel-tests\b)|\bam_parallel_tests=/, + shell_setup_code => + 'am_parallel_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; +my @generated_tests = (); # Will be updated later. + +print "## -*- Makefile -*-\n"; +print "## Generated by $me. DO NOT EDIT BY HAND!\n\n"; + +print <<EOF; + +## --------------------------------------------- ## +## Autogenerated tests and their dependencies. ## +## --------------------------------------------- ## + +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}, $_) + && !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 = "$base-w.$suf"; + # Register wrapper test as "autogenerated". + push @generated_tests, $wrapper_test; + # 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; + +## ---------------------------------------------------- ## +## Ad-hoc autogenerated tests and their dependencies. ## +## ---------------------------------------------------- ## + +EOF + +print "## Tests on automatic dependency tracking (see '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 $test = "t/depcomp" . ($lt ? "-lt-" : "-") . $m . ".tap"; + # Register wrapper test as "autogenerated" ... + push @generated_tests, $test; + # ... and create it. + atomic_write ($test, 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 + . "\$am_testauxdir/depcomp.sh"; exit "\$?" +EOF + }, + 0555); + } + } + +# Update generated makefile fragment to account for all the generated tests. +print "generated_TESTS =\n"; +map { print "generated_TESTS += $_\n" } @generated_tests; + +# 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. + +push @all_tests, @generated_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__ |