123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- #! /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-2017 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);
- # 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) = @_;
- print $file_handle unindent <<EOF;
- #! /bin/sh
- # This file has been automatically generated. DO NOT EDIT BY HAND!
- . test-lib.sh
- $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 $testdir/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;
- }
- #--------------------------------------------------------------------------
- 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",
- },
- pkgconfig_macros =>
- {
- line_matcher => qr/^\s*required=.*\bpkg-config/,
- nodist_prereqs => "$testdir/pkg-config-macros.log",
- },
- use_trivial_test_driver =>
- {
- line_matcher => qr/\btrivial-test-driver\b/,
- dist_prereqs => "$testauxdir/trivial-test-driver",
- },
- 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',
- },
- );
- #--------------------------------------------------------------------------
- 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
- # A test script '$test' can possibly match more than one condition, so
- # for each tests we need to keep a list of generated wrapper tests.
- # Since what defines these wrapper scripts is the set of initializations
- # that are issued before sourcing the original, wrapped tests, these
- # initializations is what we put in our list entries.
- # The list will be saved in the hash entry '$wrapper_setups{$test}'.
- my %wrapper_setups = ();
- foreach my $test (@all_tests)
- {
- my @setups = ('');
- foreach my $x (values %test_generators)
- {
- next
- if not line_match $x->{line_matcher}, $test;
- next
- if $x->{line_rejecter} and line_match $x->{line_rejecter}, $test;
- @setups = map { ($_, "$_\n$x->{shell_setup_code}") } @setups;
- }
- @setups = grep { $_ ne '' } @setups;
- $wrapper_setups{$test} = \@setups if @setups;
- }
- # And now create all the wrapper tests.
- for my $wrapped_test (sort keys %wrapper_setups)
- {
- my $setup_list = $wrapper_setups{$wrapped_test};
- (my $base = $wrapped_test) =~ s/\.([^.]*)$//;
- my $suf = $1 or die "$me: test '$wrapped_test' lacks a suffix\n";
- my $count = 0;
- foreach my $setup (@$setup_list)
- {
- $count++;
- my $wbase = "$base-w" . ($count > 1 ? $count : '');
- my $wrapper_test = "$wbase.$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,
- $setup },
- 0444;
- # The generated test works by sourcing the original test, so that
- # it has to be re-run every time that changes ...
- print "$wbase.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 wrapper 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 "$wbase.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", "-c-o"],
- dashmstdout => ["gcc"],
- cpp => ["gcc"],
- # This was for older (pre-3.x) GCC versions (newer versions
- # have depmode "gcc3"). But other compilers use this depmode
- # as well (for example, the IMB xlc/xlC compilers, and the HP
- # C compiler, see 'lib/depcomp' for more info), so it's not
- # obsolete, and it's worth giving it some coverage.
- 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 (sort 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 = "$testdir/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"
- . test-init.sh
- plan_ $planned
- . depcomp.sh
- exit \$?
- EOF
- },
- 0444);
- }
- }
- # 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
- for my $k (sort keys %deps_extractor)
- {
- my $x = $deps_extractor{$k};
- 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__
|