Edit

kc3-lang/automake/gen-testsuite-part

Branch :

  • Show log

    Commit

  • Author : Jim Meyering
    Date : 2022-01-12 14:15:12
    Hash : 6c8ff6a8
    Message : maint: make update-copyright

  • gen-testsuite-part
  • #! /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-2022 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 <https://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__