(
usedevel => '',
optimize => '-g',
- cc => (`ccache --version`, $?) ? 'cc' : 'ccache cc',
ld => 'cc',
($linux64 ? (libpth => \@paths) : ()),
);
unless(GetOptions(\%options,
'target=s', 'make=s', 'jobs|j=i', 'expect-pass=i',
'expect-fail' => sub { $options{'expect-pass'} = 0; },
- 'clean!', 'one-liner|e=s', 'match=s', 'force-manifest',
- 'force-regen', 'test-build', 'A=s@', 'l', 'w',
- 'check-args', 'check-shebang!', 'usage|help|?', 'validate',
+ 'clean!', 'one-liner|e=s', 'c', 'l', 'w', 'match=s',
+ 'no-match=s' => sub {
+ $options{match} = $_[1];
+ $options{'expect-pass'} = 0;
+ },
+ 'force-manifest', 'force-regen', 'test-build', 'validate',
+ 'check-args', 'check-shebang!', 'usage|help|?', 'A=s@',
'D=s@' => sub {
my (undef, $val) = @_;
if ($val =~ /\A([^=]+)=(.*)/s) {
@ARGV = ('sh', '-c', 'cd t && ./perl TEST base/*.t')
if $options{validate} && !@ARGV;
-pod2usage(exitval => 255, verbose => 1) if $options{usage};
+pod2usage(exitval => 0, verbose => 2) if $options{usage};
pod2usage(exitval => 255, verbose => 1)
unless @ARGV || $match || $options{'test-build'} || defined $options{'one-liner'};
pod2usage(exitval => 255, verbose => 1)
if !$options{'one-liner'} && ($options{l} || $options{w});
-check_shebang($ARGV[0]) if $options{'check-shebang'} && @ARGV;
+check_shebang($ARGV[0])
+ if $options{'check-shebang'} && @ARGV && !$options{match};
exit 0 if $options{'check-args'};
.../Porting/bisect.pl --expect-fail --match '\buseithreads\b'
# When did this test program stop working?
.../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;'
=item *
-Which commit added the first to match this regex?
+Which commit added the first file to match this regex?
=item *
-Which commit removed the last to match this regex?
+Which commit removed the last file to match this regex?
=back
By default F<bisect.pl> will process all options, then use the rest of the
command line as arguments to list C<system> 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<blead>. F<bisect.pl> will use F<bisect-runner.pl> to find the
-earliest stable perl version on which the test case passes, check that it
-fails on blead, and then use F<bisect-runner.pl> with C<git bisect run> to
-find the commit which caused the failure.
+non-zero) on I<blead> (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<bisect.pl> will use F<bisect-runner.pl> to find the earliest
+stable perl version on which the test case passes, check that it fails on
+blead, and then use F<bisect-runner.pl> with C<git bisect run> to find the
+commit which caused the failure.
Because the test case is the complete argument to C<system>, it is easy to
run something other than the F<perl> built, if necessary. If you need to run
=item *
+-c
+
+Add C<-c> to the command line, to cause perl to exit after syntax checking.
+
+=item *
+
-l
Add C<-l> to the command line with C<-e>
Add C<-w> to the command line with C<-e>
-It's not valid to pass C<-l> or C<-w> to C<bisect.pl> unless you are also
-using C<-e>
+It's not valid to pass C<-c>, C<-l> or C<-w> to C<bisect.pl> unless you are
+also using C<-e>
=item *
--match pattern
-Instead of running a test program to determine I<pass> or I<fail>, pass
-if the given regex matches, and hence search for the commit that removes
-the last matching file.
+=item *
+
+--no-match pattern
+
+Instead of running a test program to determine I<pass> or I<fail>,
+C<--match> will pass if the given regex matches, and hence search for the
+commit that removes the last matching file. C<--no-match> inverts the test,
+to search for the first commit that adds files that match.
+
+The remaining command line arguments are treated as glob patterns for files
+to match against. If none are specified, then they default as follows:
+
+=over 4
+
+=item *
If no I<target> is specified, the match is against all files in the
-repository (which is fast). If a I<target> is specified, that target is
-built, and the match is against only the built files. C<--expect-fail> can
-be used with C<--match> to search for a commit that adds files that match.
+repository (which is fast).
+
+=item *
+
+If a I<target> is specified, that target is built, and the match is against
+only the built files.
+
+=back
+
+Treating the command line arguments as glob patterns should not cause
+problems, as the perl distribution has never shipped or built files with
+names that contain characters which are globbing metacharacters.
+
+Anything which is not a readable file is ignored, instead of generating an
+error. (If you want an error, run C<grep> or C<ack> as a test case). This
+permits one to easily search in a file that changed its name. For example:
+
+ .../Porting/bisect.pl --match 'Pod.*Functions' 'pod/buildtoc*'
+
+C<--no-match ...> is implemented as C<--expect-fail --match ...>
=item *
die "$0: Can't build $target" if defined $target && !grep {@targets} $target;
+unless (exists $defines{cc}) {
+ # If it fails, the heuristic of 63f9ec3008baf7d6 is noisy, and hence
+ # confusing.
+ # FIXME - really it should be replaced with a proper test of
+ # "can we build something?" and a helpful diagnostic if we can't.
+ # For now, simply move it here.
+ $defines{cc} = (`ccache -V`, $?) ? 'cc' : 'ccache cc';
+}
+
$j = "-j$j" if $j =~ /\A\d+\z/;
if (exists $options{make}) {
}
sub match_and_exit {
- my $target = shift;
+ my ($target, @globs) = @_;
my $matches = 0;
my $re = qr/$match/;
my @files;
- {
+ if (@globs) {
+ require File::Glob;
+ foreach (sort map { File::Glob::bsd_glob($_)} @globs) {
+ if (!-f $_ || !-r _) {
+ warn "Skipping matching '$_' as it is not a readable file\n";
+ } else {
+ push @files, $_;
+ }
+ }
+ } else {
local $/ = "\0";
@files = defined $target ? `git ls-files -o -z`: `git ls-files -z`;
chomp @files;
system 'git clean -dxf </dev/null' and die;
if (!defined $target) {
- match_and_exit() if $match;
+ match_and_exit(undef, @ARGV) if $match;
$target = 'test_prep';
}
}
if ($target =~ /config\.s?h/) {
- match_and_exit($target) if $match && -f $target;
+ match_and_exit($target, @ARGV) if $match && -f $target;
report_and_exit(!-f $target, 'could build', 'could not build', $target)
if $options{'test-build'};
skip("could not build $real_target");
}
-match_and_exit($real_target) if $match;
+match_and_exit($real_target, @ARGV) if $match;
if (defined $options{'one-liner'}) {
my $exe = $target =~ /^(?:perl$|test)/ ? 'perl' : 'miniperl';
unshift @ARGV, '-e', $options{'one-liner'};
- unshift @ARGV, '-l' if $options{l};
- unshift @ARGV, '-w' if $options{w};
+ foreach (qw(c l w)) {
+ unshift @ARGV, "-$_" if $options{$_};
+ }
unshift @ARGV, "./$exe", '-Ilib';
}