| Index: Tools/Scripts/bisect-builds
|
| diff --git a/Tools/Scripts/bisect-builds b/Tools/Scripts/bisect-builds
|
| deleted file mode 100755
|
| index 3750f7634461e6b62fadd3390dd670150fe320b1..0000000000000000000000000000000000000000
|
| --- a/Tools/Scripts/bisect-builds
|
| +++ /dev/null
|
| @@ -1,445 +0,0 @@
|
| -#!/usr/bin/perl -w
|
| -
|
| -# Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved.
|
| -#
|
| -# Redistribution and use in source and binary forms, with or without
|
| -# modification, are permitted provided that the following conditions
|
| -# are met:
|
| -#
|
| -# 1. Redistributions of source code must retain the above copyright
|
| -# notice, this list of conditions and the following disclaimer.
|
| -# 2. Redistributions in binary form must reproduce the above copyright
|
| -# notice, this list of conditions and the following disclaimer in the
|
| -# documentation and/or other materials provided with the distribution.
|
| -# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
|
| -# its contributors may be used to endorse or promote products derived
|
| -# from this software without specific prior written permission.
|
| -#
|
| -# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
| -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| -# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
| -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
| -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
| -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| -
|
| -# This script attempts to find the point at which a regression (or progression)
|
| -# of behavior occurred by searching WebKit nightly builds.
|
| -
|
| -# To override the location where the nightly builds are downloaded or the path
|
| -# to the Safari web browser, create a ~/.bisect-buildsrc file with one or more of
|
| -# the following lines (use "~/" to specify a path from your home directory):
|
| -#
|
| -# $branch = "branch-name";
|
| -# $nightlyDownloadDirectory = "~/path/to/nightly/downloads";
|
| -# $safariPath = "/path/to/Safari.app";
|
| -
|
| -use strict;
|
| -
|
| -use File::Basename;
|
| -use File::Path;
|
| -use File::Spec;
|
| -use File::Temp qw(tempfile);
|
| -use FindBin;
|
| -use Getopt::Long;
|
| -use Time::HiRes qw(usleep);
|
| -
|
| -use lib $FindBin::Bin;
|
| -use webkitdirs qw(safariPathFromSafariBundle);
|
| -
|
| -sub createTempFile($);
|
| -sub downloadNightly($$$);
|
| -sub findMacOSXVersion();
|
| -sub findNearestNightlyIndex(\@$$);
|
| -sub findSafariVersion($);
|
| -sub loadSettings();
|
| -sub makeNightlyList($$$$);
|
| -sub max($$) { return $_[0] > $_[1] ? $_[0] : $_[1]; }
|
| -sub mountAndRunNightly($$$$);
|
| -sub parseRevisions($$;$);
|
| -sub printStatus($$$);
|
| -sub printTracLink($$);
|
| -sub promptForTest($);
|
| -
|
| -loadSettings();
|
| -
|
| -my %validBranches = map { $_ => 1 } qw(feature-branch trunk);
|
| -my $branch = $Settings::branch;
|
| -my $nightlyDownloadDirectory = $Settings::nightlyDownloadDirectory;
|
| -my $safariPath = $Settings::safariPath;
|
| -
|
| -my @nightlies;
|
| -
|
| -my $isProgression;
|
| -my $localOnly;
|
| -my @revisions;
|
| -my $sanityCheck;
|
| -my $showHelp;
|
| -my $testURL;
|
| -
|
| -# Fix up -r switches in @ARGV
|
| -@ARGV = map { /^(-r)(.+)$/ ? ($1, $2) : $_ } @ARGV;
|
| -
|
| -my $result = GetOptions(
|
| - "b|branch=s" => \$branch,
|
| - "d|download-directory=s" => \$nightlyDownloadDirectory,
|
| - "h|help" => \$showHelp,
|
| - "l|local!" => \$localOnly,
|
| - "p|progression!" => \$isProgression,
|
| - "r|revisions=s" => \&parseRevisions,
|
| - "safari-path=s" => \$safariPath,
|
| - "s|sanity-check!" => \$sanityCheck,
|
| -);
|
| -$testURL = shift @ARGV;
|
| -
|
| -$branch = "feature-branch" if $branch eq "feature";
|
| -if (!exists $validBranches{$branch}) {
|
| - print STDERR "ERROR: Invalid branch '$branch'\n";
|
| - $showHelp = 1;
|
| -}
|
| -
|
| -if (!$result || $showHelp || scalar(@ARGV) > 0) {
|
| - print STDERR "Search WebKit nightly builds for changes in behavior.\n";
|
| - print STDERR "Usage: " . basename($0) . " [options] [url]\n";
|
| - print STDERR <<END;
|
| - [-b|--branch name] name of the nightly build branch (default: trunk)
|
| - [-d|--download-directory dir] nightly build download directory (default: ~/Library/Caches/WebKit-Nightlies)
|
| - [-h|--help] show this help message
|
| - [-l|--local] only use local (already downloaded) nightlies
|
| - [-p|--progression] searching for a progression, not a regression
|
| - [-r|--revision M[:N]] specify starting (and optional ending) revisions to search
|
| - [--safari-path path] path to Safari application bundle (default: /Applications/Safari.app)
|
| - [-s|--sanity-check] verify both starting and ending revisions before bisecting
|
| -END
|
| - exit 1;
|
| -}
|
| -
|
| -my $nightlyWebSite = "http://nightly.webkit.org";
|
| -my $nightlyBuildsURLBase = $nightlyWebSite . File::Spec->catdir("/builds", $branch, "mac");
|
| -my $nightlyFilesURLBase = $nightlyWebSite . File::Spec->catdir("/files", $branch, "mac");
|
| -
|
| -$nightlyDownloadDirectory = glob($nightlyDownloadDirectory) if $nightlyDownloadDirectory =~ /^~/;
|
| -$safariPath = glob($safariPath) if $safariPath =~ /^~/;
|
| -$safariPath = safariPathFromSafariBundle($safariPath) if $safariPath =~ m#\.app/*#;
|
| -
|
| -$nightlyDownloadDirectory = File::Spec->catdir($nightlyDownloadDirectory, $branch);
|
| -if (! -d $nightlyDownloadDirectory) {
|
| - mkpath($nightlyDownloadDirectory, 0, 0755) || die "Could not create $nightlyDownloadDirectory: $!";
|
| -}
|
| -
|
| -@nightlies = makeNightlyList($localOnly, $nightlyDownloadDirectory, findMacOSXVersion(), findSafariVersion($safariPath));
|
| -
|
| -my $startIndex = $revisions[0] ? findNearestNightlyIndex(@nightlies, $revisions[0], 'ceil') : 0;
|
| -my $endIndex = $revisions[1] ? findNearestNightlyIndex(@nightlies, $revisions[1], 'floor') : $#nightlies;
|
| -
|
| -my $tempFile = createTempFile($testURL);
|
| -
|
| -if ($sanityCheck) {
|
| - my $didReproduceBug;
|
| -
|
| - do {
|
| - printf "\nChecking starting revision r%s...\n",
|
| - $nightlies[$startIndex]->{rev};
|
| - downloadNightly($nightlies[$startIndex]->{file}, $nightlyFilesURLBase, $nightlyDownloadDirectory);
|
| - mountAndRunNightly($nightlies[$startIndex]->{file}, $nightlyDownloadDirectory, $safariPath, $tempFile);
|
| - $didReproduceBug = promptForTest($nightlies[$startIndex]->{rev});
|
| - $startIndex-- if $didReproduceBug < 0;
|
| - } while ($didReproduceBug < 0);
|
| - die "ERROR: Bug reproduced in starting revision! Do you need to test an earlier revision or for a progression?"
|
| - if $didReproduceBug && !$isProgression;
|
| - die "ERROR: Bug not reproduced in starting revision! Do you need to test an earlier revision or for a regression?"
|
| - if !$didReproduceBug && $isProgression;
|
| -
|
| - do {
|
| - printf "\nChecking ending revision r%s...\n",
|
| - $nightlies[$endIndex]->{rev};
|
| - downloadNightly($nightlies[$endIndex]->{file}, $nightlyFilesURLBase, $nightlyDownloadDirectory);
|
| - mountAndRunNightly($nightlies[$endIndex]->{file}, $nightlyDownloadDirectory, $safariPath, $tempFile);
|
| - $didReproduceBug = promptForTest($nightlies[$endIndex]->{rev});
|
| - $endIndex++ if $didReproduceBug < 0;
|
| - } while ($didReproduceBug < 0);
|
| - die "ERROR: Bug NOT reproduced in ending revision! Do you need to test a later revision or for a progression?"
|
| - if !$didReproduceBug && !$isProgression;
|
| - die "ERROR: Bug reproduced in ending revision! Do you need to test a later revision or for a regression?"
|
| - if $didReproduceBug && $isProgression;
|
| -}
|
| -
|
| -printStatus($nightlies[$startIndex]->{rev}, $nightlies[$endIndex]->{rev}, $isProgression);
|
| -
|
| -my %brokenRevisions = ();
|
| -while (abs($endIndex - $startIndex) > 1) {
|
| - my $index = $startIndex + int(($endIndex - $startIndex) / 2);
|
| -
|
| - my $didReproduceBug;
|
| - do {
|
| - if (exists $nightlies[$index]) {
|
| - my $buildsLeft = max(max(0, $endIndex - $index - 1), max(0, $index - $startIndex - 1));
|
| - my $plural = $buildsLeft == 1 ? "" : "s";
|
| - printf "\nChecking revision r%s (%d build%s left to test after this)...\n", $nightlies[$index]->{rev}, $buildsLeft, $plural;
|
| - downloadNightly($nightlies[$index]->{file}, $nightlyFilesURLBase, $nightlyDownloadDirectory);
|
| - mountAndRunNightly($nightlies[$index]->{file}, $nightlyDownloadDirectory, $safariPath, $tempFile);
|
| - $didReproduceBug = promptForTest($nightlies[$index]->{rev});
|
| - }
|
| - if ($didReproduceBug < 0) {
|
| - $brokenRevisions{$nightlies[$index]->{rev}} = $nightlies[$index]->{file};
|
| - delete $nightlies[$index];
|
| - $endIndex--;
|
| - $index = $startIndex + int(($endIndex - $startIndex) / 2);
|
| - }
|
| - } while ($didReproduceBug < 0);
|
| -
|
| - if ($didReproduceBug && !$isProgression || !$didReproduceBug && $isProgression) {
|
| - $endIndex = $index;
|
| - } else {
|
| - $startIndex = $index;
|
| - }
|
| -
|
| - print "\nBroken revisions skipped: r" . join(", r", keys %brokenRevisions) . "\n"
|
| - if scalar keys %brokenRevisions > 0;
|
| - printStatus($nightlies[$startIndex]->{rev}, $nightlies[$endIndex]->{rev}, $isProgression);
|
| -}
|
| -
|
| -printTracLink($nightlies[$startIndex]->{rev}, $nightlies[$endIndex]->{rev});
|
| -
|
| -unlink $tempFile if $tempFile;
|
| -
|
| -exit 0;
|
| -
|
| -sub createTempFile($)
|
| -{
|
| - my ($url) = @_;
|
| -
|
| - return undef if !$url;
|
| -
|
| - my ($fh, $tempFile) = tempfile(
|
| - basename($0) . "-XXXXXXXX",
|
| - DIR => File::Spec->tmpdir(),
|
| - SUFFIX => ".html",
|
| - UNLINK => 0,
|
| - );
|
| - print $fh "<meta http-equiv=\"refresh\" content=\"0; $url\">\n";
|
| - close($fh);
|
| -
|
| - return $tempFile;
|
| -}
|
| -
|
| -sub downloadNightly($$$)
|
| -{
|
| - my ($filename, $urlBase, $directory) = @_;
|
| - my $path = File::Spec->catfile($directory, $filename);
|
| - if (! -f $path) {
|
| - print "Downloading $filename to $directory...\n";
|
| - `curl -# -o '$path' '$urlBase/$filename'`;
|
| - }
|
| -}
|
| -
|
| -sub findMacOSXVersion()
|
| -{
|
| - my $version;
|
| - open(SW_VERS, "-|", "/usr/bin/sw_vers") || die;
|
| - while (<SW_VERS>) {
|
| - $version = $1 if /^ProductVersion:\s+([^\s]+)/;
|
| - }
|
| - close(SW_VERS);
|
| - return $version;
|
| -}
|
| -
|
| -sub findNearestNightlyIndex(\@$$)
|
| -{
|
| - my ($nightlies, $revision, $round) = @_;
|
| -
|
| - my $lowIndex = 0;
|
| - my $highIndex = $#{$nightlies};
|
| -
|
| - return $highIndex if uc($revision) eq 'HEAD' || $revision >= $nightlies->[$highIndex]->{rev};
|
| - return $lowIndex if $revision <= $nightlies->[$lowIndex]->{rev};
|
| -
|
| - while (abs($highIndex - $lowIndex) > 1) {
|
| - my $index = $lowIndex + int(($highIndex - $lowIndex) / 2);
|
| - if ($revision < $nightlies->[$index]->{rev}) {
|
| - $highIndex = $index;
|
| - } elsif ($revision > $nightlies->[$index]->{rev}) {
|
| - $lowIndex = $index;
|
| - } else {
|
| - return $index;
|
| - }
|
| - }
|
| -
|
| - return ($round eq "floor") ? $lowIndex : $highIndex;
|
| -}
|
| -
|
| -sub findSafariVersion($)
|
| -{
|
| - my ($path) = @_;
|
| - my $versionPlist = File::Spec->catdir(dirname(dirname($path)), "version.plist");
|
| - my $version;
|
| - open(PLIST, "< $versionPlist") || die;
|
| - while (<PLIST>) {
|
| - if (m#^\s*<key>CFBundleShortVersionString</key>#) {
|
| - $version = <PLIST>;
|
| - $version =~ s#^\s*<string>([0-9.]+)[^<]*</string>\s*[\r\n]*#$1#;
|
| - }
|
| - }
|
| - close(PLIST);
|
| - return $version;
|
| -}
|
| -
|
| -sub loadSettings()
|
| -{
|
| - package Settings;
|
| -
|
| - our $branch = "trunk";
|
| - our $nightlyDownloadDirectory = File::Spec->catdir($ENV{HOME}, "Library/Caches/WebKit-Nightlies");
|
| - our $safariPath = "/Applications/Safari.app";
|
| -
|
| - my $rcfile = File::Spec->catdir($ENV{HOME}, ".bisect-buildsrc");
|
| - return if !-f $rcfile;
|
| -
|
| - my $result = do $rcfile;
|
| - die "Could not parse $rcfile: $@" if $@;
|
| -}
|
| -
|
| -sub makeNightlyList($$$$)
|
| -{
|
| - my ($useLocalFiles, $localDirectory, $macOSXVersion, $safariVersion) = @_;
|
| - my @files;
|
| -
|
| - if ($useLocalFiles) {
|
| - opendir(DIR, $localDirectory) || die "$!";
|
| - foreach my $file (readdir(DIR)) {
|
| - if ($file =~ /^WebKit-SVN-r([0-9]+)\.dmg$/) {
|
| - push(@files, +{ rev => $1, file => $file });
|
| - }
|
| - }
|
| - closedir(DIR);
|
| - } else {
|
| - open(NIGHTLIES, "curl -s $nightlyBuildsURLBase/all |") || die;
|
| -
|
| - while (my $line = <NIGHTLIES>) {
|
| - chomp $line;
|
| - my ($revision, $timestamp, $url) = split(/,/, $line);
|
| - my $nightly = basename($url);
|
| - push(@files, +{ rev => $revision, file => $nightly });
|
| - }
|
| - close(NIGHTLIES);
|
| - }
|
| -
|
| - if (eval "v$macOSXVersion" ge v10.5) {
|
| - if ($safariVersion eq "4 Public Beta") {
|
| - @files = grep { $_->{rev} >= 39682 } @files;
|
| - } elsif (eval "v$safariVersion" ge v3.2) {
|
| - @files = grep { $_->{rev} >= 37348 } @files;
|
| - } elsif (eval "v$safariVersion" ge v3.1) {
|
| - @files = grep { $_->{rev} >= 29711 } @files;
|
| - } elsif (eval "v$safariVersion" ge v3.0) {
|
| - @files = grep { $_->{rev} >= 25124 } @files;
|
| - } elsif (eval "v$safariVersion" ge v2.0) {
|
| - @files = grep { $_->{rev} >= 19594 } @files;
|
| - } else {
|
| - die "Requires Safari 2.0 or newer";
|
| - }
|
| - } elsif (eval "v$macOSXVersion" ge v10.4) {
|
| - if ($safariVersion eq "4 Public Beta") {
|
| - @files = grep { $_->{rev} >= 39682 } @files;
|
| - } elsif (eval "v$safariVersion" ge v3.2) {
|
| - @files = grep { $_->{rev} >= 37348 } @files;
|
| - } elsif (eval "v$safariVersion" ge v3.1) {
|
| - @files = grep { $_->{rev} >= 29711 } @files;
|
| - } elsif (eval "v$safariVersion" ge v3.0) {
|
| - @files = grep { $_->{rev} >= 19992 } @files;
|
| - } elsif (eval "v$safariVersion" ge v2.0) {
|
| - @files = grep { $_->{rev} >= 11976 } @files;
|
| - } else {
|
| - die "Requires Safari 2.0 or newer";
|
| - }
|
| - } else {
|
| - die "Requires Mac OS X 10.4 (Tiger) or 10.5 (Leopard)";
|
| - }
|
| -
|
| - my $nightlycmp = sub { return $a->{rev} <=> $b->{rev}; };
|
| -
|
| - return sort $nightlycmp @files;
|
| -}
|
| -
|
| -sub mountAndRunNightly($$$$)
|
| -{
|
| - my ($filename, $directory, $safari, $tempFile) = @_;
|
| - my $mountPath = "/Volumes/WebKit";
|
| - my $webkitApp = File::Spec->catfile($mountPath, "WebKit.app");
|
| - my $diskImage = File::Spec->catfile($directory, $filename);
|
| - my $devNull = File::Spec->devnull();
|
| -
|
| - my $i = 0;
|
| - while (-e $mountPath) {
|
| - $i++;
|
| - usleep 100 if $i > 1;
|
| - `hdiutil detach '$mountPath' 2> $devNull`;
|
| - die "Could not unmount $diskImage at $mountPath" if $i > 100;
|
| - }
|
| - die "Can't mount $diskImage: $mountPath already exists!" if -e $mountPath;
|
| -
|
| - print "Mounting disk image and running WebKit...\n";
|
| - `hdiutil attach '$diskImage'`;
|
| - $i = 0;
|
| - while (! -e $webkitApp) {
|
| - usleep 100;
|
| - $i++;
|
| - die "Could not mount $diskImage at $mountPath" if $i > 100;
|
| - }
|
| -
|
| - my $frameworkPath;
|
| - if (-d "/Volumes/WebKit/WebKit.app/Contents/Frameworks") {
|
| - my $osXVersion = join('.', (split(/\./, findMacOSXVersion()))[0..1]);
|
| - $frameworkPath = "/Volumes/WebKit/WebKit.app/Contents/Frameworks/$osXVersion";
|
| - } else {
|
| - $frameworkPath = "/Volumes/WebKit/WebKit.app/Contents/Resources";
|
| - }
|
| -
|
| - $tempFile ||= "";
|
| - `DYLD_FRAMEWORK_PATH=$frameworkPath WEBKIT_UNSET_DYLD_FRAMEWORK_PATH=YES $safari $tempFile`;
|
| -
|
| - `hdiutil detach '$mountPath' 2> $devNull`;
|
| -}
|
| -
|
| -sub parseRevisions($$;$)
|
| -{
|
| - my ($optionName, $value, $ignored) = @_;
|
| -
|
| - if ($value =~ /^r?([0-9]+|HEAD):?$/i) {
|
| - push(@revisions, $1);
|
| - die "Too many revision arguments specified" if scalar @revisions > 2;
|
| - } elsif ($value =~ /^r?([0-9]+):?r?([0-9]+|HEAD)$/i) {
|
| - $revisions[0] = $1;
|
| - $revisions[1] = $2;
|
| - } else {
|
| - die "Unknown revision '$value': expected 'M' or 'M:N'";
|
| - }
|
| -}
|
| -
|
| -sub printStatus($$$)
|
| -{
|
| - my ($startRevision, $endRevision, $isProgression) = @_;
|
| - printf "\n%s: r%s %s: r%s\n",
|
| - $isProgression ? "Fails" : "Works", $startRevision,
|
| - $isProgression ? "Works" : "Fails", $endRevision;
|
| -}
|
| -
|
| -sub printTracLink($$)
|
| -{
|
| - my ($startRevision, $endRevision) = @_;
|
| - printf("http://trac.webkit.org/log/trunk/?rev=%s&stop_rev=%s\n", $endRevision, $startRevision + 1);
|
| -}
|
| -
|
| -sub promptForTest($)
|
| -{
|
| - my ($revision) = @_;
|
| - print "Did the bug reproduce in r$revision (yes/no/broken)? ";
|
| - my $answer = <STDIN>;
|
| - return 1 if $answer =~ /^(1|y.*)$/i;
|
| - return -1 if $answer =~ /^(-1|b.*)$/i; # Broken
|
| - return 0;
|
| -}
|
| -
|
|
|