4 use Getopt::Long qw(:config bundling no_auto_abbrev);
7 my @targets = qw(config.sh config.h miniperl lib/Config.pm perl test_prep);
13 clean => 1, # mostly for debugging this
16 my @paths = qw(/usr/local/lib64 /lib64 /usr/lib64);
27 unless(GetOptions(\%options,
28 'target=s', 'jobs|j=i', 'expect-pass=i',
29 'expect-fail' => sub { $options{'expect-pass'} = 0; },
30 'clean!', 'one-liner|e=s', 'match=s', 'force-manifest',
31 'test-build', 'check-args', 'A=s@', 'usage|help|?',
33 my (undef, $val) = @_;
34 if ($val =~ /\A([^=]+)=(.*)/s) {
35 $defines{$1} = length $2 ? $2 : "\0";
41 $defines{$_[1]} = undef;
44 pod2usage(exitval => 255, verbose => 1);
47 my ($target, $j, $match) = @options{qw(target jobs match)};
49 pod2usage(exitval => 255, verbose => 1) if $options{usage};
50 pod2usage(exitval => 255, verbose => 1)
51 unless @ARGV || $match || $options{'test-build'} || defined $options{'one-liner'};
53 exit 0 if $options{'check-args'};
57 bisect.pl - use git bisect to pinpoint changes
61 # When did this become an error?
62 .../Porting/bisect.pl -e 'my $a := 2;'
63 # When did this become stop being an error?
64 .../Porting/bisect.pl --expect-fail -e '1 // 2'
65 # When did this stop matching?
66 .../Porting/bisect.pl --match '\b(?:PL_)hash_seed_set\b'
67 # When did this start matching?
68 .../Porting/bisect.pl --expect-fail --match '\buseithreads\b'
69 # When did this test program stop working?
70 .../Porting/bisect.pl --target=perl -- ./perl -Ilib test_prog.pl
71 # When did this first become valid syntax?
72 .../Porting/bisect.pl --target=miniperl --end=v5.10.0 \
73 --expect-fail -e 'my $a := 2;'
74 # What was the last revision to build with these options?
75 .../Porting/bisect.pl --test-build -Dd_dosuid
79 Together C<bisect.pl> and C<bisect-runner.pl> attempt to automate the use
80 of C<git bisect> as much as possible. With one command (and no other files)
87 Which commit caused this example code to break?
91 Which commit caused this example code to start working?
95 Which commit added the first to match this regex?
99 Which commit removed the last to match this regex?
103 usually without needing to know which versions of perl to use as start and
106 By default C<bisect.pl> will process all options, then use the rest of the
107 command line as arguments to list C<system> to run a test case. By default,
108 the test case should pass (exit with 0) on earlier perls, and fail (exit
109 non-zero) on I<blead>. C<bisect.pl> will use C<bisect-runner.pl> to find the
110 earliest stable perl version on which the test case passes, check that it
111 fails on blead, and then use C<bisect-runner.pl> with C<git bisect run> to
112 find the commit which caused the failure.
114 Because the test case is the complete argument to C<system>, it is easy to
115 run something other than the F<perl> built, if necessary. If you need to run
116 the perl built, you'll probably need to invoke it as C<./perl -Ilib ...>
124 --start I<commit-ish>
126 Earliest revision to test, as a I<commit-ish> (a tag, commit or anything
127 else C<git> understands as a revision. If not specified, C<bisect.pl> will
128 search stable perl releases from 5.002 to 5.14.0 until it finds one where
129 the test case passes.
135 Most recent revision to test, as a I<commit-ish>. If not specified, defaults
142 F<Makefile> target (or equivalent) needed, to run the test case. If specified,
143 this should be one of
151 Just run C<Configure>
157 Run the various F<*.SH> files to generate F<Makefile>, F<config.h>, I<etc>.
169 Use F<miniperl> to build F<lib/Config.pm>
175 Build F<perl>. This also builds pure-Perl modules in F<cpan>, F<dist> and
182 Build everything needed to run the tests. This is the default if we're
183 running test code, but is time consuming, as it means building all
184 C<XS> modules. For older F<Makefile>s, the previous name of C<test-prep>
185 is automatically substituted. For very old F<Makefile>s, C<make test> is
186 run, as there is no target provided to just get things ready, and for 5.004
187 and earlier the tests run very quickly.
193 --one-liner 'code to run'
199 Example code to run, just like you'd use with C<perl -e>.
201 This prepends C<./perl -Ilib -e 'code to run'> to the test case given,
202 or C<./miniperl> if I<target> is C<miniperl>
204 (Usually you'll use C<-e> instead of providing a test case in the
205 non-option arguments to C<bisect.pl>)
207 C<-E> intentionally isn't supported, as it's an error in 5.8.0 and earlier,
208 which interferes with detecting errors in the example code itself.
214 The test case should fail for the I<start> revision, and pass for the I<end>
215 revision. The bisect run will find the first commit where it passes.
227 -Accflags=-DNO_MATHOMS
229 Arguments to pass to F<Configure>. Repeated C<-A> arguments are passed
230 through as is. C<-D> and C<-U> are processed in order, and override
231 previous settings for the same parameter.
241 Number of C<make> jobs to run in parallel. Currently defaults to 9.
247 Instead of running a test program to determine I<pass> or I<fail>, pass
248 if the given regex matches, and hence search for the commit that removes
249 the last matching file.
251 If no I<target> is specified, the match is against all files in the
252 repository (which is fast). If a I<target> is specified, that target is
253 built, and the match is against only the built files. C<--expect-fail> can
254 be used with C<--match> to search for a commit that adds files that match.
260 Test that the build completes, without running any test case.
262 By default, if the build for the desired I<target> fails to complete,
263 F<bisect-runner.pl> reports a I<skip> back to C<git bisect>, the assumption
264 being that one wants to find a commit which changed state "builds && passes"
265 to "builds && fails". If instead one is interested in which commit broke the
266 build (possibly for particular F<Configure> options), use I<--test-build>
267 to treat a build failure as a failure, not a "skip".
271 By default, a build will "skip" if any files listed in F<MANIFEST> are not
272 present. Usually this is useful, as it avoids false-failures. However, there
273 are some long ranges of commits where listed files are missing, which can
274 cause a bisect to abort because all that remain are skipped revisions.
276 In these cases, particularly if the test case uses F<miniperl> and no modules,
277 it may be more useful to force the build to continue, even if files
278 F<MANIFEST> are missing.
284 C<--expect-pass=0> is equivalent to C<--expect-fail>. I<1> is the default.
290 Tell F<bisect-runner.pl> not to clean up after the build. This allows one
291 to use F<bisect-runner.pl> to build the current particular perl revision for
292 interactive testing, or for debugging F<bisect-runner.pl>.
294 Passing this to F<bisect.pl> will likely cause the bisect to fail badly.
300 Validate the options and arguments, and exit silently if they are valid.
314 Display the usage information and exit.
320 die "$0: Can't build $target" if defined $target && !grep {@targets} $target;
322 $j = "-j$j" if $j =~ /\A\d+\z/;
324 # Sadly, however hard we try, I don't think that it will be possible to build
325 # modules in ext/ on x86_64 Linux before commit e1666bf5602ae794 on 1999/12/29,
326 # which updated to MakeMaker 3.7, which changed from using a hard coded ld
327 # in the Makefile to $(LD). On x86_64 Linux the "linker" is gcc.
329 sub extract_from_file {
330 my ($file, $rx, $default) = @_;
331 open my $fh, '<', $file or die "Can't open $file: $!";
334 return wantarray ? @got : $got[0]
337 return $default if defined $default;
342 if ($options{clean}) {
343 # Needed, because files that are build products in this checked out
344 # version might be in git in the next desired version.
345 system 'git clean -dxf';
346 # Needed, because at some revisions the build alters checked out files.
347 # (eg pod/perlapi.pod). Also undoes any changes to makedepend.SH
348 system 'git reset --hard HEAD';
355 warn "skipping - $reason";
359 sub report_and_exit {
360 my ($ret, $pass, $fail, $desc) = @_;
364 my $got = ($options{'expect-pass'} ? !$ret : $ret) ? 'good' : 'bad';
366 print "$got - $fail $desc\n";
368 print "$got - $pass $desc\n";
382 @files = defined $target ? `git ls-files -o -z`: `git ls-files -z`;
386 foreach my $file (@files) {
387 open my $fh, '<', $file or die "Can't open $file: $!";
391 if (tr/\t\r\n -~\200-\377//c) {
392 print "Binary file $file matches\n";
394 $_ .= "\n" unless /\n\z/;
399 close $fh or die "Can't close $file: $!";
401 report_and_exit(!$matches,
402 $matches == 1 ? '1 match for' : "$matches matches for",
403 'no matches for', $match);
409 my ($file) = $patch =~ qr!^diff.*a/(\S+) b/\1!;
410 open my $fh, '|-', 'patch' or die "Can't run patch: $!";
412 close $fh or die "Can't patch $file: $?, $!";
415 # Not going to assume that system perl is yet new enough to have autodie
416 system 'git clean -dxf' and die;
418 if (!defined $target) {
419 match_and_exit() if $match;
420 $target = 'test_prep';
423 skip('no Configure - is this the //depot/perlext/Compiler branch?')
424 unless -f 'Configure';
426 # This changes to PERL_VERSION in 4d8076ea25903dcb in 1999
428 = extract_from_file('patchlevel.h',
429 qr/^#define\s+(?:PERL_VERSION|PATCHLEVEL)\s+(\d+)\s/,
433 if (extract_from_file('Configure',
434 qr/^ \*=\*\) echo "\$1" >> \$optdef;;$/)) {
435 # This is " Spaces now allowed in -D command line options.",
436 # part of commit ecfc54246c2a6f42
437 apply_patch(<<'EOPATCH');
438 diff --git a/Configure b/Configure
439 index 3d3b38d..78ffe16 100755
442 @@ -652,7 +777,8 @@ while test $# -gt 0; do
443 echo "$me: use '-U symbol=', not '-D symbol='." >&2
444 echo "$me: ignoring -D $1" >&2
446 - *=*) echo "$1" >> $optdef;;
448 + sed -e "s/'/'\"'\"'/g" -e "s/=\(.*\)/='\1'/" >> $optdef;;
449 *) echo "$1='define'" >> $optdef;;
454 if (extract_from_file('Configure', qr/^if \$contains 'd_namlen' \$xinc\b/)) {
455 # Configure's original simple "grep" for d_namlen falls foul of the
456 # approach taken by the glibc headers:
457 # #ifdef _DIRENT_HAVE_D_NAMLEN
458 # # define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
460 # where _DIRENT_HAVE_D_NAMLEN is not defined on Linux.
461 # This is also part of commit ecfc54246c2a6f42
462 apply_patch(<<'EOPATCH');
463 diff --git a/Configure b/Configure
464 index 3d3b38d..78ffe16 100755
467 @@ -3935,7 +4045,8 @@ $rm -f try.c
469 : see if the directory entry stores field length
471 -if $contains 'd_namlen' $xinc >/dev/null 2>&1; then
472 +$cppstdin $cppflags $cppminus < "$xinc" > try.c
473 +if $contains 'd_namlen' try.c >/dev/null 2>&1; then
474 echo "Good, your directory entry keeps length information in d_namlen." >&4
481 # There was a bug in makedepend.SH which was fixed in version 96a8704c.
482 # Symptom was './makedepend: 1: Syntax error: Unterminated quoted string'
483 # Remove this if you're actually bisecting a problem related to makedepend.SH
484 system 'git show blead:makedepend.SH > makedepend.SH' and die;
486 # if Encode is not needed for the test, you can speed up the bisect by
487 # excluding it from the runs with -Dnoextensions=Encode
488 # ccache is an easy win. Remove it if it causes problems.
489 # Commit 1cfa4ec74d4933da adds ignore_versioned_solibs to Configure, and sets it
490 # to true in hints/linux.sh
491 # On dromedary, from that point on, Configure (by default) fails to find any
492 # libraries, because it scans /usr/local/lib /lib /usr/lib, which only contain
493 # versioned libraries. Without -lm, the build fails.
494 # Telling /usr/local/lib64 /lib64 /usr/lib64 works from that commit onwards,
495 # until commit faae14e6e968e1c0 adds it to the hints.
496 # However, prior to 1cfa4ec74d4933da telling Configure the truth doesn't work,
497 # because it will spot versioned libraries, pass them to the compiler, and then
498 # bail out pretty early on. Configure won't let us override libswanted, but it
499 # will let us override the entire libs list.
501 unless (extract_from_file('Configure', 'ignore_versioned_solibs')) {
502 # Before 1cfa4ec74d4933da, so force the libs list.
505 # This is the current libswanted list from Configure, less the libs removed
506 # by current hints/linux.sh
507 foreach my $lib (qw(sfio socket inet nsl nm ndbm gdbm dbm db malloc dl dld
508 ld sun m crypt sec util c cposix posix ucb BSD)) {
509 foreach my $dir (@paths) {
510 next unless -f "$dir/lib$lib.so";
511 push @libs, "-l$lib";
515 $defines{libs} = \@libs unless exists $defines{libs};
518 # This seems to be necessary to avoid makedepend becoming confused, and hanging
519 # on stdin. Seems that the code after make shlist || ...here... is never run.
520 $defines{trnl} = q{'\n'}
521 if $major < 4 && !exists $defines{trnl};
523 $defines{usenm} = undef
524 if $major < 2 && !exists $defines{usenm};
526 my (@missing, @created_dirs);
528 if ($options{'force-manifest'}) {
529 open my $fh, '<', 'MANIFEST'
530 or die "Could not open MANIFEST: $!";
532 next unless /^(\S+)/;
536 close $fh or die "Can't close MANIFEST: $!";
538 foreach my $pathname (@missing) {
539 my @parts = split '/', $pathname;
540 my $leaf = pop @parts;
543 $path .= '/' . shift @parts;
545 mkdir $path, 0700 or die "Can't create $path: $!";
546 unshift @created_dirs, $path;
548 open $fh, '>', $pathname or die "Can't open $pathname: $!";
549 close $fh or die "Can't close $pathname: $!";
550 chmod 0, $pathname or die "Can't chmod 0 $pathname: $!";
554 my @ARGS = $target eq 'config.sh' ? '-dEs' : '-des';
555 foreach my $key (sort keys %defines) {
556 my $val = $defines{$key};
558 push @ARGS, "-D$key=@$val";
559 } elsif (!defined $val) {
560 push @ARGS, "-U$key";
561 } elsif (!length $val) {
562 push @ARGS, "-D$key";
564 $val = "" if $val eq "\0";
565 push @ARGS, "-D$key=$val";
568 push @ARGS, map {"-A$_"} @{$options{A}};
570 # </dev/null because it seems that some earlier versions of Configure can
571 # call commands in a way that now has them reading from stdin (and hanging)
573 die "Can't fork: $!" unless defined $pid;
575 # Before dfe9444ca7881e71, Configure would refuse to run if stdin was not a
576 # tty. With that commit, the tty requirement was dropped for -de and -dE
578 open STDIN, '<', '/dev/null';
579 } elsif (!$options{'force-manifest'}) {
580 # If a file in MANIFEST is missing, Configure asks if you want to
581 # continue (the default being 'n'). With stdin closed or /dev/null,
582 # it exit immediately and the check for config.sh below will skip.
583 # To avoid a hang, we need to check MANIFEST for ourselves, and skip
584 # if anything is missing.
585 open my $fh, '<', 'MANIFEST';
586 skip("Could not open MANIFEST: $!")
589 next unless /^(\S+)/;
590 skip("$1 from MANIFEST doesn't exist")
593 close $fh or die "Can't close MANIFEST: $!";
595 exec './Configure', @ARGS;
596 die "Failed to start Configure: $!";
599 or die "wait for Configure, pid $pid failed: $!";
601 if ($target =~ /config\.s?h/) {
602 match_and_exit($target) if $match && -f $target;
603 report_and_exit(!-f $target, 'could build', 'could not build', $target);
604 } elsif (!-f 'config.sh') {
605 # Skip if something went wrong with Configure
607 skip('could not build config.sh');
610 # This is probably way too paranoid:
614 foreach my $file (@missing) {
615 my (undef, undef, $mode, undef, undef, undef, undef, $size)
617 if (!defined $mode) {
618 push @errors, "Added file $file has been deleted by Configure";
621 if (Fcntl::S_IMODE($mode) != 0) {
623 sprintf 'Added file %s had mode changed by Configure to %03o',
628 "Added file $file had sized changed by Configure to $size";
630 unlink $file or die "Can't unlink $file: $!";
632 foreach my $dir (@created_dirs) {
633 rmdir $dir or die "Can't rmdir $dir: $!";
639 # Correct makefile for newer GNU gcc
640 # Only really needed if you comment out the use of blead's makedepend.SH
643 local @ARGV = qw(makefile x2p/makefile);
645 print unless /<(?:built-in|command|stdin)/;
649 if ($major == 2 && extract_from_file('perl.c', qr/^ fclose\(e_fp\);$/)) {
650 # need to patch perl.c to avoid calling fclose() twice on e_fp when using -e
651 # This diff is part of commit ab821d7fdc14a438. The second close was
652 # introduced with perl-5.002, commit a5f75d667838e8e7
653 # Might want a6c477ed8d4864e6 too, for the corresponding change to pp_ctl.c
654 # (likely without this, eval will have "fun")
655 apply_patch(<<'EOPATCH');
656 diff --git a/perl.c b/perl.c
657 index 03c4d48..3c814a2 100644
660 @@ -252,6 +252,7 @@ setuid perl scripts securely.\n");
661 #ifndef VMS /* VMS doesn't have environ array */
662 origenviron = environ;
664 + e_tmpname = Nullch;
668 @@ -405,6 +406,7 @@ setuid perl scripts securely.\n");
670 if (Fflush(e_fp) || ferror(e_fp) || fclose(e_fp))
671 croak("Can't write to temp file for -e: %s", Strerror(errno));
674 scriptname = e_tmpname;
676 @@ -470,10 +472,10 @@ setuid perl scripts securely.\n");
677 curcop->cop_line = 0;
684 (void)UNLINK(e_tmpname);
685 + Safefree(e_tmpname);
686 + e_tmpname = Nullch;
689 /* now that script is parsed, we can modify record separator */
690 @@ -1369,7 +1371,7 @@ SV *sv;
694 - origfilename = savepv(e_fp ? "-e" : scriptname);
695 + origfilename = savepv(e_tmpname ? "-e" : scriptname);
696 curcop->cop_filegv = gv_fetchfile(origfilename);
697 if (strEQ(origfilename,"-"))
703 # Parallel build for miniperl is safe
704 system "make $j miniperl";
706 if ($target ne 'miniperl') {
707 # Nearly all parallel build issues fixed by 5.10.0. Untrustworthy before that.
708 $j = '' if $major < 10;
710 if ($target eq 'test_prep') {
712 # test-prep was added in 5.004_01, 3e3baf6d63945cb6.
713 # renamed to test_prep in 2001 in 5fe84fd29acaf55c.
714 # earlier than that, just make test. It will be fast enough.
715 $target = extract_from_file('Makefile.SH', qr/^(test[-_]prep):/,
720 system "make $j $target";
723 my $expected = $target =~ /^test/ ? 'perl' : $target;
724 my $missing_target = $expected =~ /perl$/ ? !-x $expected : !-r $expected;
726 if ($options{'test-build'}) {
727 report_and_exit($missing_target, 'could build', 'could not build', $target);
728 } elsif ($missing_target) {
729 skip("could not build $target");
732 match_and_exit($target) if $match;
734 if (defined $options{'one-liner'}) {
735 my $exe = $target ne 'miniperl' ? 'perl' : 'miniperl';
736 unshift @ARGV, "./$exe", '-Ilib', '-e', $options{'one-liner'};
739 # This is what we came here to run:
740 my $ret = system @ARGV;
742 report_and_exit($ret, 'zero exit from', 'non-zero exit from', "@ARGV");
745 # cperl-indent-level: 4
746 # indent-tabs-mode: nil
749 # ex: set ts=8 sts=4 sw=4 et: