X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/91bcfbd88e7ab62bbd4e0667f4361dd443dac4db..ea319c81d6264975e581fc37c96a23a66e426632:/Porting/bisect-runner.pl diff --git a/Porting/bisect-runner.pl b/Porting/bisect-runner.pl index 160c215..360c186 100755 --- a/Porting/bisect-runner.pl +++ b/Porting/bisect-runner.pl @@ -4,6 +4,8 @@ use strict; use Getopt::Long qw(:config bundling no_auto_abbrev); use Pod::Usage; use Config; +use File::Temp qw(tempdir); +use File::Spec; my @targets = qw(none config.sh config.h miniperl lib/Config.pm Fcntl perl test_prep); @@ -53,7 +55,7 @@ push @paths, qw(/usr/local/lib /lib /usr/lib) unless $linux64; unless(GetOptions(\%options, - 'target=s', 'make=s', 'jobs|j=i', 'expect-pass=i', + 'target=s', 'make=s', 'jobs|j=i', 'crash', 'expect-pass=i', 'expect-fail' => sub { $options{'expect-pass'} = 0; }, 'clean!', 'one-liner|e=s@', 'c', 'l', 'w', 'match=s', 'no-match=s' => sub { @@ -64,6 +66,7 @@ unless(GetOptions(\%options, 'test-build', 'validate', 'all-fixups', 'early-fixup=s@', 'late-fixup=s@', 'valgrind', 'check-args', 'check-shebang!', 'usage|help|?', 'gold=s', + 'module=s', 'with-module=s', 'cpan-config-dir=s', 'A=s@', 'D=s@' => sub { my (undef, $val) = @_; @@ -86,8 +89,45 @@ my ($target, $match) = @options{qw(target match)}; if $options{validate} && !@ARGV; pod2usage(exitval => 0, verbose => 2) if $options{usage}; + +# This needs to be done before the next arguments check, as it's populating +# @ARGV +if (defined $target && $target =~ /\.t\z/) { + # t/TEST don't have a reliable way to run the test script under valgrind + # The $ENV{VALGRIND} code was only added after v5.8.0, and is more + # geared to logging than to exiting on failure if errors are found. + # I guess one could fudge things by replacing the symlink t/perl with a + # wrapper script which invokes valgrind, but leave doing that until + # someone needs it. (If that's you, then patches welcome.) + foreach (qw(valgrind match validate test-build one-liner)) { + die_255("$0: Test-case targets can't be run with --$_") + if $options{$_}; + } + die_255("$0: Test-case targets can't be combined with an explicit test") + if @ARGV; + + # Needing this unless is a smell suggesting that this implementation of + # test-case targets is not really in the right place. + unless ($options{'check-args'}) { + # The top level sanity tests refuse to start or end a test run at a + # revision which skips, hence this test ensures reasonable sanity at + # automatically picking a suitable start point for both normal operation + # and --expect-fail + skip("Test case $target is not a readable file") + unless -f $target && -r _; + } + + # t/TEST runs from and takes pathnames relative to t/, so need to strip + # out a leading t, or add ../ otherwise + unless ($target =~ s!\At/!!) { + $target = "../$target"; + } + @ARGV = ('sh', '-c', "cd t && ./perl TEST " . quotemeta $target); + $target = 'test_prep'; +} + pod2usage(exitval => 255, verbose => 1) - unless @ARGV || $match || $options{'test-build'} || defined $options{'one-liner'}; + unless @ARGV || $match || $options{'test-build'} || defined $options{'one-liner'} || defined $options{module}; pod2usage(exitval => 255, verbose => 1) if !$options{'one-liner'} && ($options{l} || $options{w}); @@ -102,25 +142,33 @@ bisect.pl - use git bisect to pinpoint changes =head1 SYNOPSIS - # When did this become an error? - .../Porting/bisect.pl -e 'my $a := 2;' - # When did this stop being an error? - .../Porting/bisect.pl --expect-fail -e '1 // 2' - # When were all lines matching this pattern removed from all files? - .../Porting/bisect.pl --match '\b(?:PL_)hash_seed_set\b' - # When was some line matching this pattern added to some file? - .../Porting/bisect.pl --expect-fail --match '\buseithreads\b' - # When did this test program stop exiting 0? - .../Porting/bisect.pl -- ./perl -Ilib ../test_prog.pl - # When did this test start failing? - .../Porting/bisect.pl -- ./perl -Ilib t/TEST op/sort.t - # When did this first become valid syntax? - .../Porting/bisect.pl --target=miniperl --end=v5.10.0 \ - --expect-fail -e 'my $a := 2;' - # What was the last revision to build with these options? - .../Porting/bisect.pl --test-build -Dd_dosuid - # When did this test program start generating errors from valgrind? - .../Porting/bisect.pl --valgrind ../test_prog.pl + # When did this become an error? + .../Porting/bisect.pl -e 'my $a := 2;' + # When did this stop being an error? + .../Porting/bisect.pl --expect-fail -e '1 // 2' + # When did this test start failing? + .../Porting/bisect.pl --target t/op/sort.t + # When were all lines matching this pattern removed from all files? + .../Porting/bisect.pl --match '\b(?:PL_)hash_seed_set\b' + # When was some line matching this pattern added to some file? + .../Porting/bisect.pl --expect-fail --match '\buseithreads\b' + # When did this test program stop exiting 0? + .../Porting/bisect.pl -- ./perl -Ilib ../test_prog.pl + # When did this test program start crashing (any signal or coredump)? + .../Porting/bisect.pl --crash -- ./perl -Ilib ../test_prog.pl + # When did this first become valid syntax? + .../Porting/bisect.pl --target=miniperl --end=v5.10.0 \ + --expect-fail -e 'my $a := 2;' + # What was the last revision to build with these options? + .../Porting/bisect.pl --test-build -Dd_dosuid + # When did this test program start generating errors from valgrind? + .../Porting/bisect.pl --valgrind ../test_prog.pl + # When did these cpan modules start failing to compile/pass tests? + .../Porting/bisect.pl --module=autobox,Moose + # When did this code stop working in blead with these modules? + .../Porting/bisect.pl --with-module=Moose,Moo -e 'use Moose; 1;' + # Like the above 2 but with custom CPAN::MyConfig + .../Porting/bisect.pl --module=Moo --cpan-config-dir=/home/blah/custom/ =head1 DESCRIPTION @@ -154,12 +202,21 @@ end revisions. By default F will process all options, then use the rest of the command line as arguments to list C to run a test case. By default, the test case should pass (exit with 0) on earlier perls, and fail (exit -non-zero) on I (note that running most of perl's test files directly -won't do this, you'll need to run them through a harness to get the proper -error code). F will use F to find the earliest -stable perl version on which the test case passes, check that it fails on -blead, and then use F with C to find the -commit which caused the failure. +non-zero) on I. F will use F to find the +earliest stable perl version on which the test case passes, check that it +fails on blead, and then use F with C to +find the commit which caused the failure. + +Many of perl's own test scripts exit 0 even if their TAP reports test +failures, and some need particular setup (such as running from the right +directory, or adding C<-T> to the command line). Hence if you want to bisect +a test script, you can specify it with the I<--target> option, and it will +be invoked using F which performs all the setup, and exits non-zero +if the TAP reports failures. This works for any file ending C<.t>, so you can +use it with a file outside of the working checkout, for example to test a +particular version of a test script, as a path inside the repository will +(of course) be testing the version of the script checked out for the current +revision, which may be too early to have the test you are interested in. Because the test case is the complete argument to C, it is easy to run something other than the F built, if necessary. If you need to run @@ -285,6 +342,14 @@ is automatically substituted. For very old Fs, C is run, as there is no target provided to just get things ready, and for 5.004 and earlier the tests run very quickly. +=item * + +A file ending C<.t> + +Build everything needed to run the tests, and then run this test script using +F. This is actually implemented internally by using the target +I, and setting the test case to "sh", "-c", "cd t && ./TEST ..." + =back =item * @@ -343,6 +408,13 @@ revision. The bisect run will find the first commit where it passes. =item * +--crash + +Treat any non-crash as success, any crash as failure. (Crashing defined +as exiting with a signal or a core dump.) + +=item * + -D I =item * @@ -476,6 +548,70 @@ even link. =item * +--module module1,module2,... + +Install this (or these) module(s), die when it (the last of those) +cannot be updated to the current version. + +Misnomer. the argument can be any argument that can be passed to CPAN +shell's install command. B: since we only have the uptodate +command to verify that an install has taken place, we are unable to +determine success for arguments like +MSCHWERN/Test-Simple-1.005000_005.tar.gz. + +In so far, it is not such a misnomer. + +Note that this and I<--with-module> will both require a C. +If F<$ENV{HOME}/.cpan/CPAN/MyConfig.pm> does not exist, a CPAN shell will +be started up for you so you can configure one. Feel free to let +CPAN pick defaults for you. Enter 'quit' when you are done, and +then everything should be all set. Alternatively, you may +specify a custom C by using I<--cpan-config-dir>. + +Also, if you want to bisect a module that needs a display (like +TK) and you don't want random screens appearing and disappearing +on your computer while you're working, you can do something like +this: + +In a terminal: + + $ while true; do date ; if ! ps auxww | grep -v grep \ + | grep -q Xvfb; then Xvfb :121 & fi; echo -n 'sleeping 60 '; \ + sleep 60; done + +And then: + + DISPLAY=":121" .../Porting/bisect.pl --module=TK + +(Some display alternatives are vncserver and Xnest.) + +=item * + +--with-module module1,module2,... + +Like I<--module> above, except this simply installs the requested +modules and they can then be used in other tests. + +For example: + + .../Porting/bisect.pl --with-module=Moose -e 'use Moose; ...' + +=item * + +--cpan-config-dir /home/blah/custom + +If defined, this will cause L to look for F inside of +the specified directory, instead of using the default config of +F<$ENV{HOME}/.cpan/>. + +If no default config exists, a L shell will be fired up for you to +configure things. Letting L automatically configure things for you +should work well enough. You probably want to choose I instead of +I if it asks. When you're finished with configuration, just +type I and hit I and the bisect should continue. + +=item * + --force-manifest By default, a build will "skip" if any files listed in F are not @@ -528,7 +664,7 @@ F is run, and C and C code isn't patched until after F is built. If C<--all-fixups> is specified, all the fixups are done before running C. In rare cases adding this may cause a bisect to abort, because an inapplicable patch or other fixup is attempted -for a revision which would usually have already Ied. If this happens, +for a revision which would usually have already Iped. If this happens, please report it as a bug, giving the OS and problem revision. =item * @@ -570,7 +706,7 @@ file is fed to C on standard input. For C<=~>, the patch is applied if no lines match the pattern. As the empty pattern in Perl is a special case (it matches the most recent -sucessful match) which is not useful here, an the treatment of empty pattern +successful match) which is not useful here, the treatment of an empty pattern is special-cased. C =~ //> applies the patch if filename is present. C !~ //> applies the patch if filename missing. This makes it easy to unconditionally apply patches to files, and to use a patch @@ -1083,6 +1219,7 @@ sub run_report_and_exit { my $ret = run_with_options({setprgp => $options{setpgrp}, timeout => $options{timeout}, }, @_); + $ret &= 0xff if $options{crash}; report_and_exit(!$ret, 'zero exit from', 'non-zero exit from', "@_"); } @@ -1112,7 +1249,7 @@ sub match_and_exit { while (<$fh>) { if ($_ =~ $re) { ++$matches; - if (tr/\t\r\n -~\200-\377//c) { + if (/[^[:^cntrl:]\h\v]/) { # Matches non-spacing non-C1 controls print "Binary file $file matches\n"; } else { $_ .= "\n" unless /\n\z/; @@ -1205,7 +1342,7 @@ unless (extract_from_file('Configure', 'ignore_versioned_solibs')) { my @libs; # This is the current libswanted list from Configure, less the libs removed # by current hints/linux.sh - foreach my $lib (qw(sfio socket inet nsl nm ndbm gdbm dbm db malloc dl dld + foreach my $lib (qw(sfio socket inet nsl nm ndbm gdbm dbm db malloc dl ld sun m crypt sec util c cposix posix ucb BSD)) { foreach my $dir (@paths) { # Note the wonderful consistency of dot-or-not in the config vars: @@ -1241,6 +1378,16 @@ foreach my $key (sort keys %defines) { } push @ARGS, map {"-A$_"} @{$options{A}}; +my $prefix; + +# Testing a module? We need to install perl/cpan modules to a temp dir +if ($options{module} || $options{'with-module'}) { + $prefix = tempdir(CLEANUP => 1); + + push @ARGS, "-Dprefix=$prefix"; + push @ARGS, "-Uversiononly", "-Dinstallusrbinperl=n"; +} + # If a file in MANIFEST is missing, Configure asks if you want to # continue (the default being 'n'). With stdin closed or /dev/null, # it exits immediately and the check for config.sh below will skip. @@ -1256,7 +1403,15 @@ if (-f 'config.sh') { # Emulate noextensions if Configure doesn't support it. fake_noextensions() if $major < 10 && $defines{noextensions}; - system_or_die('./Configure -S'); + if (system './Configure -S') { + # See commit v5.23.5-89-g7a4fcb3. Configure may try to run + # ./optdef.sh instead of UU/optdef.sh. Copying the file is + # easier than patching Configure (which mentions optdef.sh multi- + # ple times). + require File::Copy; + File::Copy::copy("UU/optdef.sh", "./optdef.sh"); + system_or_die('./Configure -S'); + } } if ($target =~ /config\.s?h/) { @@ -1316,6 +1471,62 @@ if ($target ne 'miniperl') { system "$options{make} $j $real_target 1, + ) or die $!; + + # Don't ever stop to ask the user for input + $ENV{AUTOMATED_TESTING} = 1; + $ENV{PERL_MM_USE_DEFAULT} = 1; + + # Don't let these interfere with our cpan installs + delete $ENV{PERL_MB_OPT}; + delete $ENV{PERL_MM_OPT}; + + # Make sure we load up our CPAN::MyConfig and then + # override the build_dir so we have a fresh one + # every build + my $cdir = $options{'cpan-config-dir'} + || File::Spec->catfile($ENV{HOME},".cpan"); + + my @cpanshell = ( + "$prefix/bin/perl", + "-I", "$cdir", + "-MCPAN::MyConfig", + "-MCPAN", + "-e","\$CPAN::Config->{build_dir}=q{$bdir};", + "-e", + ); + + for (@m) { + s/-/::/g if /-/ and !m|/|; + } + my $install = join ",", map { "'$_'" } @m; + my $last = $m[-1]; + my $shellcmd = "install($install); die unless CPAN::Shell->expand(Module => '$last')->uptodate;"; + + if ($options{module}) { + run_report_and_exit(@cpanshell, $shellcmd); + } else { + my $ret = run_with_options({setprgp => $options{setpgrp}, + timeout => $options{timeout}, + }, @cpanshell, $shellcmd); + $ret &= 0xff if $options{crash}; + + # Failed? Give up + if ($ret) { + report_and_exit(!$ret, 'zero exit from', 'non-zero exit from', "@_"); + } + } +} + my $expected_file_found = $expected_file =~ /perl$/ ? -x $expected_file : -r $expected_file; @@ -3431,9 +3642,4 @@ sub apply_fixups { } } -# Local variables: -# cperl-indent-level: 4 -# indent-tabs-mode: nil -# End: -# # ex: set ts=8 sts=4 sw=4 et: