diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
commit | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch) | |
tree | 11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebKitTools/Scripts | |
parent | 648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff) | |
download | external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebKitTools/Scripts')
61 files changed, 12213 insertions, 0 deletions
diff --git a/WebKitTools/Scripts/SpacingHeuristics.pm b/WebKitTools/Scripts/SpacingHeuristics.pm new file mode 100644 index 0000000..7de0172 --- /dev/null +++ b/WebKitTools/Scripts/SpacingHeuristics.pm @@ -0,0 +1,101 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006 Apple Computer, 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. + +# Used for helping remove extra blank lines from files when processing. +# see split-class for an example usage (or other scripts in bugzilla) + +BEGIN { + use Exporter (); + our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); + $VERSION = 1.00; + @ISA = qw(Exporter); + @EXPORT = qw(&resetSpacingHeuristics &isOnlyWhiteSpace &applySpacingHeuristicsAndPrint &setPreviousAllowedLine &setPreviousAllowedLine &printPendingEmptyLines &ignoringLine); + %EXPORT_TAGS = (); + @EXPORT_OK = (); +} + +our @EXPORT_OK; + +my $justFoundEmptyLine = 0; +my $previousLineWasDisallowed = 0; +my $previousAllowedLine = ""; +my $pendingEmptyLines = ""; + +sub resetSpacingHeuristics +{ + $justFoundEmptyLine = 0; + $previousLineWasDisallowed = 0; + $previousAllowedLine = ""; + $pendingEmptyLines = ""; +} + +sub isOnlyWhiteSpace +{ + my $line = shift; + my $isOnlyWhiteSpace = ($line =~ m/^\s+$/); + $pendingEmptyLines .= $line if ($isOnlyWhiteSpace); + return $isOnlyWhiteSpace; +} + +sub applySpacingHeuristicsAndPrint +{ + my ($out, $line) = @_; + + printPendingEmptyLines($out, $line); + $previousLineWasDisallowed = 0; + print $out $line; +} + +sub setPreviousAllowedLine +{ + my $line = shift; + $previousAllowedLine = $line; +} + +sub printPendingEmptyLines +{ + my $out = shift; + my $line = shift; + if ($previousLineWasDisallowed) { + if (!($pendingEmptyLines eq "") && !($previousAllowedLine =~ m/{\s*$/) && !($line =~ m/^\s*}/)) { + $pendingEmptyLines = "\n"; + } else { + $pendingEmptyLines = ""; + } + } + print $out $pendingEmptyLines; + $pendingEmptyLines = ""; +} + +sub ignoringLine +{ + # my $line = shift; # ignoring input argument + $previousLineWasDisallowed = 1; +} + +1;
\ No newline at end of file diff --git a/WebKitTools/Scripts/VCSUtils.pm b/WebKitTools/Scripts/VCSUtils.pm new file mode 100644 index 0000000..e09d331 --- /dev/null +++ b/WebKitTools/Scripts/VCSUtils.pm @@ -0,0 +1,119 @@ +# Copyright (C) 2007 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. + +# Module to share code to work with various version control systems. + +use strict; +use warnings; +use File::Spec; +use webkitdirs; + +BEGIN { + use Exporter (); + our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); + $VERSION = 1.00; + @ISA = qw(Exporter); + @EXPORT = qw(&isGitDirectory &isGit &isSVNDirectory &isSVN &makeFilePathRelative); + %EXPORT_TAGS = ( ); + @EXPORT_OK = (); +} + +our @EXPORT_OK; + +my $isGit; +my $isSVN; +my $gitBranch; +my $isGitBranchBuild; + +sub isGitDirectory($) +{ + my ($dir) = @_; + return system("cd $dir && git rev-parse > /dev/null 2>&1") == 0; +} + +sub isGit() +{ + return $isGit if defined $isGit; + + $isGit = isGitDirectory("."); + return $isGit; +} + +sub gitBranch() +{ + unless (defined $gitBranch) { + chomp($gitBranch = `git symbolic-ref -q HEAD`); + $gitBranch = "" if exitStatus($?); + $gitBranch =~ s#^refs/heads/##; + $gitBranch = "" if $gitBranch eq "master"; + } + + return $gitBranch; +} + +sub isGitBranchBuild() +{ + my $branch = gitBranch(); + chomp(my $override = `git config --bool branch.$branch.webKitBranchBuild`); + return 1 if $override eq "true"; + return 0 if $override eq "false"; + + unless (defined $isGitBranchBuild) { + chomp(my $gitBranchBuild = `git config --bool core.webKitBranchBuild`); + $isGitBranchBuild = $gitBranchBuild eq "true"; + } + + return $isGitBranchBuild; +} + +sub isSVNDirectory($) +{ + my ($dir) = @_; + + return -d File::Spec->catdir($dir, ".svn"); +} + +sub isSVN() +{ + return $isSVN if defined $isSVN; + + $isSVN = isSVNDirectory("."); + return $isSVN; +} + +my $gitRoot; +sub makeFilePathRelative($) +{ + my ($path) = @_; + return $path unless isGit(); + + unless (defined $gitRoot) { + chomp($gitRoot = `git rev-parse --show-cdup`); + } + return $gitRoot . $path; +} + +1; diff --git a/WebKitTools/Scripts/bisect-builds b/WebKitTools/Scripts/bisect-builds new file mode 100755 index 0000000..f77f81f --- /dev/null +++ b/WebKitTools/Scripts/bisect-builds @@ -0,0 +1,420 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2007 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 Getopt::Long; +use Time::HiRes qw(usleep); + +sub createTempFile($); +sub downloadNightly($$$); +sub findMacOSXVersion(); +sub findNearestNightlyIndex(\@$$); +sub findSafariVersion($); +sub loadSettings(); +sub makeNightlyList($$$$); +sub mountAndRunNightly($$$$); +sub parseRevisions($$;$); +sub printStatus($$$); +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 = File::Spec->catdir($safariPath, "Contents/MacOS/Safari") 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]) { + printf "\nChecking revision (r%s)...\n", $nightlies[$index]->{rev}; + 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); +} + +unlink $tempFile if $tempFile; + +exit 0; + +sub createTempFile($) +{ + my ($url) = @_; + + return undef if !$url; + + my ($fh, $tempFile) = tempfile( + basename($0) . "-XXXXXXXX", + DIR => ($ENV{'TMPDIR'} || "/tmp"), + 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>(.+)</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 (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 (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 $i = 0; + while (-e $mountPath) { + $i++; + usleep 100 if $i > 1; + `hdiutil detach '$mountPath' 2> /dev/null`; + 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> /dev/null`; +} + +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 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; +} + diff --git a/WebKitTools/Scripts/build-drawtest b/WebKitTools/Scripts/build-drawtest new file mode 100755 index 0000000..fa9b7c2 --- /dev/null +++ b/WebKitTools/Scripts/build-drawtest @@ -0,0 +1,48 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006 Apple Computer, 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. + +# Simplified build script for WebKit Open Source Project. +# Modified copy of build-dumprendertree. Perhaps these could share code. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +checkRequiredSystemConfig(); +setConfiguration(); +chdirWebKit(); +my @options = XcodeOptions(); + +# Check to see that all the frameworks are built (w/ SVG support). +checkFrameworks(); +checkWebCoreSVGSupport(1); + +# Build +chdir "WebKitTools/DrawTest" or die; +exit system "xcodebuild", "-project", "DrawTest.xcodeproj", @options; diff --git a/WebKitTools/Scripts/build-dumprendertree b/WebKitTools/Scripts/build-dumprendertree new file mode 100755 index 0000000..a11481b --- /dev/null +++ b/WebKitTools/Scripts/build-dumprendertree @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, 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. + + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +checkRequiredSystemConfig(); +setConfiguration(); +chdirWebKit(); +my @options = XcodeOptions(); + +# Build +chdir "WebKitTools/DumpRenderTree" or die; +my $result; +if (isOSX()) { + $result = system "xcodebuild", "-project", "DumpRenderTree.xcodeproj", @options, @ARGV; +} elsif (isCygwin()) { + $result = buildVisualStudioProject("DumpRenderTree.sln"); +} elsif (isQt() || isGtk()) { + # Qt and Gtk build everything in one shot. No need to build anything here. + $result = 0; +} else { + die "Building not defined for this platform!\n"; +} +exit exitStatus($result); diff --git a/WebKitTools/Scripts/build-jsc b/WebKitTools/Scripts/build-jsc new file mode 100755 index 0000000..f258ea5 --- /dev/null +++ b/WebKitTools/Scripts/build-jsc @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (C) 2007 Eric Seidel <eric@webkit.org> +# +# 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. + + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +checkRequiredSystemConfig(); +setConfiguration(); +chdirWebKit(); +my @options = XcodeOptions(); + +chdir "JavaScriptCore" or die "Can't find JavaScriptCore directory to build from"; +my $result; +if (isOSX()) { + $result = system "sh", "-c", 'xcodebuild -project JavaScriptCore.xcodeproj -target jsc "$@" | grep -v setenv && exit ${PIPESTATUS[0]}', "xcodebuild", @options, @ARGV; +} elsif (isCygwin()) { + $result = buildVisualStudioProject("JavaScriptCore.vcproj/JavaScriptCore.sln"); +} elsif (isQt() or isGtk() or isWx()) { + # Qt and Gtk build everything in one-shot. No need to build anything here. + $result = 0; +} else { + die "Building not defined for this platform!\n"; +} +exit exitStatus($result); diff --git a/WebKitTools/Scripts/build-webkit b/WebKitTools/Scripts/build-webkit new file mode 100755 index 0000000..6ed3ddc --- /dev/null +++ b/WebKitTools/Scripts/build-webkit @@ -0,0 +1,350 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006 Apple Computer, 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. + +# Simplified build script for WebKit Open Source Project. + +use strict; +use File::Basename; +use File::Spec; +use FindBin; +use Getopt::Long qw(:config pass_through); +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +my $originalWorkingDirectory = getcwd(); + +my $databaseSupport = 1; +my $domStorageSupport = 1; +my $iconDatabaseSupport = 1; +my $offlineWebApplicationSupport = 1; +my $svgSupport = 1; +my $svgExperimentalSupport = 0; +my $svgAnimationSupport = 1; +my $svgFiltersSupport = $svgExperimentalSupport; +my $svgForeignObjectSupport = 1; +my $svgUseSupport = 1; +my $svgFontsSupport = 1; +my $svgAsImageSupport = 1; +my $xpathSupport = 1; +my $xsltSupport = 1; +my $coverageSupport = 0; +my $videoSupport = (isOSX() || isCygwin()); # Enable by default on OSX and Windows +my $showHelp = 0; +my $clean = 0; +my $buildUniversal = 0; +my $buildSixtyFourBit = 0; + +my $programName = basename($0); +my $usage = <<EOF; +Usage: $programName [options] [options to pass to build system] + --help Show this help message + --cairo-win32 Build using Cairo (rather than CoreGraphics) on Windows + --clean Cleanup the build directory + --universal Build 2-way universal (PPC and Intel 32-bit) + --64-bit Build 64-bit, combine with --universal to build 4-way universal + --[no-]offline-web-applications Toggle Offline Web Application Support (default : $offlineWebApplicationSupport) + --[no-]database Toggle Database Support (default: $databaseSupport) + --[no-]dom-storage Toggle DOM Storage Support (default: $domStorageSupport) + --[no-]icon-database Toggle Icon database support (default: $iconDatabaseSupport) + --[no-]svg Toggle SVG support (default: $svgSupport) + --[no-]svg-experimental Toggle SVG experimental features support (default: $svgExperimentalSupport, + implies SVG Support) + --[no-]svg-animation Toggle SVG animation support (default: $svgAnimationSupport, implies SVG Support) + --[no-]svg-filters Toggle SVG filters support (default: $svgFiltersSupport, implies SVG Support) + --[no-]svg-foreign-object Toggle SVG foreign object support (default: $svgForeignObjectSupport, implies SVG Support) + --[no-]svg-fonts Toggle SVG fonts support (default: $svgFontsSupport, implies SVG Support) + --[no-]svg-as-image Toggle SVG as Image support (default: $svgAsImageSupport, implies SVG Support) + --[no-]svg-use Toggle SVG use element support (default: $svgUseSupport, implies SVG Support) + --[no-]xpath Toggle XPath support (default: $xpathSupport) + --[no-]xslt Toggle XSLT support (default: $xsltSupport) + --[no-]video Toggle Video support (default: $videoSupport) + --[no-]coverage Toggle code coverage support (default: $coverageSupport) +EOF + +GetOptions('database!' => \$databaseSupport, + 'dom-storage!' => \$domStorageSupport, + 'icon-database!' => \$iconDatabaseSupport, + 'offline-web-applications!' => \$offlineWebApplicationSupport, + 'svg!' => \$svgSupport, + 'svg-experimental!' => \$svgExperimentalSupport, + 'svg-animation!' => \$svgAnimationSupport, + 'svg-filters!' => \$svgFiltersSupport, + 'svg-foreign-object!' => \$svgForeignObjectSupport, + 'svg-fonts!' => \$svgFontsSupport, + 'svg-as-image!' => \$svgAsImageSupport, + 'svg-use!' => \$svgUseSupport, + 'xpath!' => \$xpathSupport, + 'xslt!' => \$xsltSupport, + 'video!' => \$videoSupport, + 'coverage!' => \$coverageSupport, + 'help' => \$showHelp, + 'universal' => \$buildUniversal, + '64-bit' => \$buildSixtyFourBit, + 'clean' => \$clean); + +if ($showHelp) { + print STDERR $usage; + exit 1; +} + +$svgExperimentalSupport = 0 unless $svgSupport; +$svgAnimationSupport = 0 unless $svgSupport; +$svgFiltersSupport = 0 unless $svgSupport; +$svgForeignObjectSupport = 0 unless $svgSupport; +$svgFontsSupport = 0 unless $svgSupport; +$svgAsImageSupport = 0 unless $svgSupport; +$svgUseSupport = 0 unless $svgSupport; + +if ($svgExperimentalSupport) { + $svgAnimationSupport = 1; + $svgFiltersSupport = 1; + $svgForeignObjectSupport = 1; + $svgFontsSupport = 1; + $svgAsImageSupport = 1; + $svgUseSupport = 1; +} + +checkRequiredSystemConfig(); +setConfiguration(); +chdirWebKit(); + +# FIXME: Migrate build-wxwebkit commands into build-webkit. +if (isWx()) { + my @opts = (); + $ENV{"WEBKITOUTPUTDIR"} = productDir(); + foreach (@ARGV) { + if ($_ eq "wxgc" || $_ eq "wxpython") { + push(@opts, $_); + } + } + if ($clean) { + push(@opts, "clean"); + } + system "WebKitTools/wx/build-wxwebkit @opts"; + exit exitStatus($?); +} + + +my $productDir = productDir(); +my @overrideFeatureDefinesOption = (); + +push @overrideFeatureDefinesOption, "ENABLE_DATABASE" if $databaseSupport; +push @overrideFeatureDefinesOption, "ENABLE_DOM_STORAGE" if $domStorageSupport; +push @overrideFeatureDefinesOption, "ENABLE_ICONDATABASE" if $iconDatabaseSupport; +push @overrideFeatureDefinesOption, "ENABLE_OFFLINE_WEB_APPLICATIONS" if $offlineWebApplicationSupport; +push @overrideFeatureDefinesOption, "ENABLE_SVG" if $svgSupport; +push @overrideFeatureDefinesOption, "ENABLE_SVG_ANIMATION" if $svgAnimationSupport; +push @overrideFeatureDefinesOption, "ENABLE_SVG_AS_IMAGE" if $svgAsImageSupport; +push @overrideFeatureDefinesOption, "ENABLE_SVG_FILTERS" if $svgFiltersSupport; +push @overrideFeatureDefinesOption, "ENABLE_SVG_FONTS" if $svgFontsSupport; +push @overrideFeatureDefinesOption, "ENABLE_SVG_FOREIGN_OBJECT" if $svgForeignObjectSupport; +push @overrideFeatureDefinesOption, "ENABLE_SVG_USE" if $svgUseSupport; +push @overrideFeatureDefinesOption, "ENABLE_VIDEO" if $videoSupport; +push @overrideFeatureDefinesOption, "ENABLE_XPATH" if $xpathSupport; +push @overrideFeatureDefinesOption, "ENABLE_XSLT" if $xsltSupport; +my $overrideFeatureDefinesString = "FEATURE_DEFINES=" . join(" ", @overrideFeatureDefinesOption); + +my @coverageSupportOption = (); +if ($coverageSupport) { + push @coverageSupportOption, "GCC_GENERATE_TEST_COVERAGE_FILES=YES"; + push @coverageSupportOption, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES"; + push @coverageSupportOption, "EXTRA_LINK= -ftest-coverage -fprofile-arcs"; + push @coverageSupportOption, "OTHER_CFLAGS= -MD"; + push @coverageSupportOption, "OTHER_LDFLAGS=\$(OTHER_LDFLAGS) -ftest-coverage -fprofile-arcs -framework AppKit"; +} + +# Check that all the project directories are there. +my @projects = ("JavaScriptCore", "JavaScriptGlue", "WebCore", "WebKit"); +my @otherDirs = ("WebKitLibraries"); +for my $dir (@projects, @otherDirs) { + if (! -d $dir) { + die "Error: No $dir directory found. Please do a fresh checkout.\n"; + } +} + +my @options = (); + +if ($clean && isOSX()) { + push(@options, "-alltargets"); + push(@options, "clean"); +} + +if ($buildSixtyFourBit && isOSX()) { + my $cpuVendor = `sysctl -n machdep.cpu.vendor`; + chomp $cpuVendor; + + if ($buildUniversal) { + push(@options, "ARCHS=ppc ppc64 i386 x86_64"); + } elsif ($cpuVendor eq "GenuineIntel") { + push(@options, "ARCHS=i386 x86_64"); + } else { + push(@options, "ARCHS=ppc ppc64"); + } +} elsif ($buildUniversal && isOSX()) { + push(@options, "ARCHS=ppc i386"); +} + +# enable autotool options accordingly +if (isGtk()) { + push @options, autotoolsFlag($databaseSupport, "database"); + push @options, autotoolsFlag($domStorageSupport, "dom-storage"); + push @options, autotoolsFlag($iconDatabaseSupport, "icon-database"); + push @options, autotoolsFlag($offlineWebApplicationSupport, "offline-web-applications"); + push @options, autotoolsFlag($svgSupport, "svg"); + push @options, autotoolsFlag($svgAnimationSupport, "svg-animation"); + push @options, autotoolsFlag($svgFiltersSupport, "svg-filters"); + push @options, autotoolsFlag($svgForeignObjectSupport, "svg-foreign-object"); + push @options, autotoolsFlag($svgFontsSupport, "svg-fonts"); + push @options, autotoolsFlag($svgAsImageSupport, "svg-as-image"); + push @options, autotoolsFlag($svgUseSupport, "svg-use-element"); + push @options, autotoolsFlag($xpathSupport, "xpath"); + push @options, autotoolsFlag($xsltSupport, "xslt"); + push @options, autotoolsFlag($videoSupport, "video"); + push @options, autotoolsFlag($coverageSupport, "coverage"); +} + +if (isOSX()) { + + push(@options, XcodeOptions()); + + # Copy library and header from WebKitLibraries to a findable place in the product directory. + my $srcLib = "WebKitLibraries/libWebKitSystemInterfaceTiger.a"; + my $lib = "$productDir/libWebKitSystemInterfaceTiger.a"; + if (!-e $lib || -M $lib > -M $srcLib) { + print "Updating $lib\n"; + system "ditto", $srcLib, $lib; + system "ranlib", $lib; + } + + $srcLib = "WebKitLibraries/libWebKitSystemInterfaceLeopard.a"; + $lib = "$productDir/libWebKitSystemInterfaceLeopard.a"; + if (!-e $lib || -M $lib > -M $srcLib) { + print "Updating $lib\n"; + system "ditto", $srcLib, $lib; + system "ranlib", $lib; + } + + my $srcHeader = "WebKitLibraries/WebKitSystemInterface.h"; + my $header = "$productDir/usr/local/include/WebKitSystemInterface.h"; + if (!-e $header || -M $header > -M $srcHeader) { + print "Updating $header\n"; + system "mkdir", "-p", "$productDir/usr/local/include"; + system "ditto", $srcHeader, $header; + } + + $srcLib = "WebKitLibraries/libWebCoreSQLite3.a"; + $lib = "$productDir/libWebCoreSQLite3.a"; + if (!-e $lib || -M $lib > -M $srcLib) { + print "Updating $lib\n"; + system "ditto", $srcLib, $lib; + system "ranlib", $lib; + } + + my $srcHeaderDir = "WebKitLibraries/WebCoreSQLite3"; + my $headerDir = "$productDir/WebCoreSQLite3"; + if (!-e $headerDir || -M $headerDir > -M $srcHeaderDir) { + print "Updating $headerDir\n"; + system "ditto", $srcHeaderDir, $headerDir; + } +} + +if (isCygwin()) { + # Copy WebKitSupportLibrary to the correct location in WebKitLibraries so it can be found. + # Will fail if WebKitSupportLibrary.zip is not in source root. + (system("perl WebKitTools/Scripts/update-webkit-support-libs") == 0) or die; +} + +# Force re-link of existing libraries if different than expected +removeLibraryDependingOnSVG("WebCore", $svgSupport); + +# Build, and abort if the build fails. +for my $dir (@projects) { + chdir $dir or die; + my $result = 0; + + if (isGtk()) { + if ($dir ne "WebKit") { + chdir ".." or die; + next; + } + + $result = buildGtkProject($dir, $clean, @options); + } elsif (isQt()) { + if ($dir ne "WebKit") { + chdir ".." or die; + next; + } + $result = buildQMakeQtProject($dir, $clean); + } elsif (isOSX()) { + $result = system "xcodebuild", "-project", "$dir.xcodeproj", @options, $overrideFeatureDefinesString, @coverageSupportOption, @ARGV; + } elsif (isCygwin()) { + if ($dir eq "WebKit") { + $result = buildVisualStudioProject("win/WebKit.vcproj/WebKit.sln", $clean); + } + } + + if (exitStatus($result)) { + if (isCygwin()) { + print "\n\n===== BUILD FAILED ======\n\n"; + my $scriptDir = relativeScriptsDir(); + print "Please ensure you have run $scriptDir/update-webkit to install depenedencies.\n\n"; + my $baseProductDir = baseProductDir(); + print "You can view build errors by checking the BuildLog.htm files located at:\n$baseProductDir/obj/<project>/<config>.\n"; + } + exit exitStatus($result); + } + chdir ".." or die; +} + +# Don't report the "WebKit is now built" message after a clean operation. +exit if $clean; + +# Write out congratulations message. + +my $launcherPath = launcherPath(); +my $launcherName = launcherName(); + +print "\n"; +print "===========================================================\n"; +print " WebKit is now built. To run $launcherName with this newly-built\n"; +print " code, use the \"$launcherPath\" script.\n"; +if ($svgSupport) { + print "\n NOTE: WebKit has been built with SVG support enabled.\n"; + print " $launcherName will have SVG viewing capabilities.\n"; +} +if ($svgAnimationSupport or $svgFiltersSupport or $svgForeignObjectSupport or $svgFontsSupport or $svgAsImageSupport or $svgUseSupport) { + print " Your build supports the following (optional) SVG features: \n"; + print " * Basic SVG animation.\n" if $svgAnimationSupport; + print " * SVG filters.\n" if $svgFiltersSupport; + print " * SVG foreign object.\n" if $svgForeignObjectSupport; + print " * SVG fonts.\n" if $svgFontsSupport; + print " * SVG as image.\n" if $svgAsImageSupport; + print " * SVG <use> support.\n" if $svgUseSupport; +} +print "===========================================================\n"; diff --git a/WebKitTools/Scripts/check-dom-results b/WebKitTools/Scripts/check-dom-results new file mode 100755 index 0000000..0b32406 --- /dev/null +++ b/WebKitTools/Scripts/check-dom-results @@ -0,0 +1,141 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, 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. + +# Script to check status of W3C DOM tests that are part of the WebKit tests. + +use strict; +use FindBin; +use Cwd; +use lib $FindBin::Bin; +use webkitdirs; + +chdirWebKit(); + +my $verbose = $ARGV[0] && $ARGV[0] eq "-v"; + +my $workingDir = getcwd(); +my $testDirectory = "$workingDir/LayoutTests"; + +my @suites = ( {"name" => "DOM Level 1 Core (html)", "directory" => "dom/html/level1/core"}, + {"name" => "DOM Level 2 Core (html)", "directory" => "dom/html/level2/core"}, + {"name" => "DOM Level 2 Events (html)", "directory" => "dom/html/level2/events"}, + {"name" => "DOM Level 2 HTML (html)", "directory" => "dom/html/level2/html"}, + {"name" => "DOM Level 1 Core (xhtml)", "directory" => "dom/xhtml/level1/core"}, + {"name" => "DOM Level 2 Core (xhtml)", "directory" => "dom/xhtml/level2/core"}, + {"name" => "DOM Level 2 Events (xhtml)", "directory" => "dom/xhtml/level2/events"}, + {"name" => "DOM Level 2 HTML (xhtml)", "directory" => "dom/xhtml/level2/html"}, + {"name" => "DOM Level 3 Core (xhtml)", "directory" => "dom/xhtml/level3/core"}, + {"name" => "DOM Level 3 XPath (svg)", "directory" => "dom/svg/level3/xpath"}); + +my $totalCount = 0; +my $totalSuccesses = 0; +my $totalDisabled = 0; +my $totalFailed = 0; + +foreach my $suite (@suites) { + + my %suite = %$suite; + my $directory = $suite{"directory"}; + my $name = $suite{"name"}; + my @results = `find "${testDirectory}/${directory}" -name "*-expected.txt"`; + my @disabled = `find "${testDirectory}/${directory}" -name "*-disabled"`; + + my @failures = (); + my $count = 0; + + foreach my $result (@results) { + $count++; + my $success = 0; + open RESULT, "<$result"; + while (<RESULT>) { + if (/Success/) { + $success = 1; + last; + } + } + close RESULT; + if (!$success) { + push @failures, $result; + } + } + + my $disabledCount = (scalar @disabled); + my $failureCount = (scalar @failures); + + $count += $disabledCount; + + my $successCount = $count - $failureCount - $disabledCount; + my $percentage = (sprintf "%.1f", ($successCount * 100.0 / $count)); + + if ($percentage == 100) { + print "${name}: all ${count} tests succeeded"; + } else { + print "${name}: ${successCount} out of ${count} tests succeeded (${percentage}%)"; + } + print " ($disabledCount disabled)" if $disabledCount; + print "\n"; + if ($verbose) { + print "\n"; + if (@disabled) { + print " Disabled:\n"; + + foreach my $failure (sort @disabled) { + $failure =~ s|.*/||; + $failure =~ s|-disabled||; + print " ${directory}/${failure}"; + } + } + if (@failures) { + print " Failed:\n"; + + foreach my $failure (sort @failures) { + $directory =~ m|^dom/(\w+)|; + my $extension = $1; + $failure =~ s|.*/||; + $failure =~ s|-expected\.txt|.${extension}|; + print " ${directory}/${failure}"; + } + } + + print "\n"; + } + + $totalCount += $count; + $totalSuccesses += $successCount; + $totalDisabled += $disabledCount; + $totalFailed += $failureCount; +} + + +my $totalPercentage = (sprintf "%.1f", ($totalSuccesses * 100.0 / $totalCount)); +my $totalDisabledPercentage = (sprintf "%.1f", ($totalDisabled * 100.0 / $totalCount)); +my $totalFailedPercentage = (sprintf "%.1f", ($totalFailed * 100.0 / $totalCount)); + +print "Total: ${totalSuccesses} out of ${totalCount} tests succeeded (${totalPercentage}%)\n"; +print " ${totalDisabled} tests disabled (${totalDisabledPercentage}%)\n"; +print " ${totalFailed} tests failed (${totalFailedPercentage}%)\n"; diff --git a/WebKitTools/Scripts/check-for-global-initializers b/WebKitTools/Scripts/check-for-global-initializers new file mode 100755 index 0000000..493b40c --- /dev/null +++ b/WebKitTools/Scripts/check-for-global-initializers @@ -0,0 +1,135 @@ +#!/usr/bin/perl + +# Copyright (C) 2006, 2007, 2008 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. + +# "check-for-global-initializers" script for WebKit Open Source Project + +# Intended to be invoked from an Xcode build step to check if there are +# any global initializers in a target. + +use warnings; +use strict; + +use File::Basename; + +sub touch($); + +my $arch = $ENV{'CURRENT_ARCH'}; +my $configuration = $ENV{'CONFIGURATION'}; +my $target = $ENV{'TARGET_NAME'}; +my $variant = $ENV{'CURRENT_VARIANT'}; +my $coverageBuild = $ENV{'WEBKIT_COVERAGE_BUILD'}; +my $debugRoot = $ENV{'WEBKIT_DEBUG_ROOT'}; + +$arch = $ENV{'NATIVE_ARCH'} if !$arch; # for Xcode 2.1, which does not have CURRENT_ARCH +$variant = "normal" if !$variant; # for Xcode 2.1, which does not have CURRENT_VARIANT + +my $executablePath = "$ENV{'TARGET_BUILD_DIR'}/$ENV{'EXECUTABLE_PATH'}"; + +my $buildTimestampPath = $ENV{'TARGET_TEMP_DIR'} . "/" . basename($0) . ".timestamp"; +my $buildTimestampAge = -M $buildTimestampPath; + +touch($buildTimestampPath); + +my $list = $ENV{"LINK_FILE_LIST_${variant}_${arch}"}; + +if (!open LIST, $list) { + print "Could not open $list\n"; + exit 1; +} + +my @files = <LIST>; +chomp @files; +close LIST; + +my $sawError = 0; + +for my $file (sort @files) { + if (defined $buildTimestampAge) { + my $fileAge = -M $file; + next if defined $fileAge && $fileAge > $buildTimestampAge; + } + if (!open NM, "(nm '$file' | sed 's/^/STDOUT:/') 2>&1 |") { + print "Could not open $file\n"; + $sawError = 1; + next; + } + my $sawGlobal = 0; + while (<NM>) { + if (/^STDOUT:/) { + $sawGlobal = 1 if /__GLOBAL__I/; + } else { + print STDERR if $_ ne "nm: no name list\n"; + } + } + close NM; + if ($sawGlobal) { + my $shortName = $file; + $shortName =~ s/.*\///; + + # Special cases for files that have initializers in debug builds. + if ($configuration eq "Debug" or $variant eq "debug" or $debugRoot) { + if ($target eq "JavaScriptCore") { + next if $shortName eq "AllInOneFile.o"; + next if $shortName eq "Opcode.o"; + next if $shortName eq "StructureID.o"; + next if $shortName eq "nodes.o"; + } + if ($target eq "WebCore") { + next if $shortName eq "CachedPage.o"; + next if $shortName eq "CachedResource.o"; + next if $shortName eq "Frame.o"; + next if $shortName eq "JSCustomSQLTransactionCallback.o"; + next if $shortName eq "JSEventListener.o"; + next if $shortName eq "Node.o"; + next if $shortName eq "Page.o"; + next if $shortName eq "Range.o"; + next if $shortName eq "RenderObject.o"; + next if $shortName eq "SubresourceLoader.o"; + next if $shortName eq "SVGElementInstance.o"; + next if $shortName eq "bidi.o"; + } + } + + print "$shortName has a global initializer in it! ($file)\n"; + $sawError = 1; + } +} + +if ($sawError and !$coverageBuild) { + unlink $executablePath; + exit 1; +} + +exit 0; + +sub touch($) +{ + my ($path) = @_; + open(TOUCH, ">", $path) or die "$!"; + close(TOUCH); +} diff --git a/WebKitTools/Scripts/check-for-weak-vtables b/WebKitTools/Scripts/check-for-weak-vtables new file mode 100755 index 0000000..d274b01 --- /dev/null +++ b/WebKitTools/Scripts/check-for-weak-vtables @@ -0,0 +1,101 @@ +#!/usr/bin/perl + +# Copyright (C) 2006, 2007, 2008 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. + +# "check-for-weak-vtables" script for WebKit Open Source Project + +# Intended to be invoked from an Xcode build step to check if there are +# any weak vtables in a target. + +use warnings; +use strict; + +use File::Basename; + +sub touch($); + +my $arch = $ENV{'CURRENT_ARCH'}; +my $configuration = $ENV{'CONFIGURATION'}; +my $target = $ENV{'TARGET_NAME'}; +my $variant = $ENV{'CURRENT_VARIANT'}; +my $coverageBuild = $ENV{'WEBKIT_COVERAGE_BUILD'}; +my $debugRoot = $ENV{'WEBKIT_DEBUG_ROOT'}; + +$arch = $ENV{'NATIVE_ARCH'} if !$arch; # for Xcode 2.1, which does not have CURRENT_ARCH +$variant = "normal" if !$variant; # for Xcode 2.1, which does not have CURRENT_VARIANT + +my $executablePath = "$ENV{'TARGET_BUILD_DIR'}/$ENV{'EXECUTABLE_PATH'}"; + +my $buildTimestampPath = $ENV{'TARGET_TEMP_DIR'} . "/" . basename($0) . ".timestamp"; +my $buildTimestampAge = -M $buildTimestampPath; +my $executablePathAge = -M $executablePath; + +my $sawError = 0; + +if (!defined $executablePathAge || !defined $buildTimestampAge || $executablePathAge > $buildTimestampAge) { + if (!open NM, "(nm -m '$executablePath' | c++filt | sed 's/^/STDOUT:/') 2>&1 |") { + print "Could not open $executablePath\n"; + $sawError = 1; + next; + } + my @weakVTableClasses = (); + while (<NM>) { + if (/^STDOUT:/) { + push @weakVTableClasses, $1 if /weak external vtable for (.*)$/; + } else { + print STDERR if $_ ne "nm: no name list\n"; + } + } + close NM; + if (@weakVTableClasses) { + my $shortName = $executablePath; + $shortName =~ s/.*\///; + + print "$shortName has a weak vtable in it ($executablePath)\n"; + print "Fix by making sure the first virtual function in each of these classes is not an inline:\n"; + for my $class (sort @weakVTableClasses) { + print " $class\n"; + } + $sawError = 1; + } +} + +if ($sawError and !$coverageBuild) { + unlink $executablePath; + exit 1; +} + +touch($buildTimestampPath); + +exit 0; + +sub touch($) +{ + my ($path) = @_; + open(TOUCH, ">", $path) or die "$!"; + close(TOUCH); +} diff --git a/WebKitTools/Scripts/clean-header-guards b/WebKitTools/Scripts/clean-header-guards new file mode 100755 index 0000000..2bad046 --- /dev/null +++ b/WebKitTools/Scripts/clean-header-guards @@ -0,0 +1,53 @@ +#!/usr/bin/ruby + +require 'find' +require 'optparse' + +options = {} +OptionParser.new do |opts| + opts.banner = "Usage: clean-header-guards [options]" + + opts.on("--prefix [PREFIX]", "Append a header prefix to all guards") do |prefix| + options[:prefix] = prefix + end +end.parse! + +IgnoredFilenamePatterns = [ + # ignore headers which are known not to have guard + /WebCorePrefix/, + /ForwardingHeaders/, + %r|bindings/objc|, + /vcproj/, # anything inside a vcproj is in the windows wasteland + + # we don't own any of these headers + %r|icu/unicode|, + %r|platform/graphics/cairo|, + %r|platform/image-decoders|, + + /config.h/ # changing this one sounds scary +].freeze + +IgnoreFileNamesPattern = Regexp.union(*IgnoredFilenamePatterns).freeze + +Find::find(".") do |filename| + next unless filename =~ /\.h$/ + next if filename.match(IgnoreFileNamesPattern) + + File.open(filename, "r+") do |file| + contents = file.read + match_results = contents.match(/#ifndef (\S+)\n#define \1/s) + if match_results + current_guard = match_results[1] + new_guard = File.basename(filename).sub('.', '_') + new_guard = options[:prefix] + '_' + new_guard if options[:prefix] + contents.gsub!(/#{current_guard}\b/, new_guard) + else + puts "Ignoring #{filename}, failed to find existing header guards." + end + tmp_filename = filename + ".tmp" + File.open(tmp_filename, "w+") do |new_file| + new_file.write(contents) + end + File.rename tmp_filename, filename + end +end diff --git a/WebKitTools/Scripts/commit-log-editor b/WebKitTools/Scripts/commit-log-editor new file mode 100755 index 0000000..939b28c --- /dev/null +++ b/WebKitTools/Scripts/commit-log-editor @@ -0,0 +1,189 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006, 2007 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. + +# Script to put change log comments in as default check-in comment. + +use strict; +use File::Basename; +use File::Spec; +use FindBin; +use lib $FindBin::Bin; +use VCSUtils; +use webkitdirs; + +my $log = $ARGV[0]; + +my $baseDir = baseProductDir(); + +my $editor = $ENV{SVN_LOG_EDITOR}; +if (!$editor) { + $editor = $ENV{CVS_LOG_EDITOR}; +} +if (!$editor) { + my $builtEditorApplication = "$baseDir/Release/Commit Log Editor.app/Contents/MacOS/Commit Log Editor"; + $editor = $builtEditorApplication if -x $builtEditorApplication; +} +if (!$editor) { + my $builtEditorApplication = "$baseDir/Debug/Commit Log Editor.app/Contents/MacOS/Commit Log Editor"; + $editor = $builtEditorApplication if -x $builtEditorApplication; +} +if (!$editor) { + my $installedEditorApplication = "$ENV{HOME}/Applications/Commit Log Editor.app/Contents/MacOS/Commit Log Editor"; + $editor = $installedEditorApplication if -x $installedEditorApplication; +} +if (!$editor) { + $editor = $ENV{EDITOR} || "/usr/bin/vi"; +} + +my $inChangesToBeCommitted = !isGit(); +my @changeLogs = (); +my $logContents = ""; +my $existingLog = 0; +open LOG, $log or die; +while (<LOG>) { + if (isGit()) { + if (/^# Changes to be committed:$/) { + $inChangesToBeCommitted = 1; + } elsif ($inChangesToBeCommitted && /^# \S/) { + $inChangesToBeCommitted = 0; + } + } + + $logContents .= $_; + $existingLog = isGit() && !(/^#/ || /^\s*$/) unless $existingLog; + + push @changeLogs, makeFilePathRelative($1) if $inChangesToBeCommitted && (/^M....(.*ChangeLog)$/ || /^#\tmodified: (.*ChangeLog)/) && !/-ChangeLog/; +} +close LOG; + +# Don't change anything if there's already a log message +# (as can happen with git-commit --amend) +exec $editor, @ARGV if $existingLog; + +my $topLevel = topLevelSourceDirectory(); + +my %changeLogSort; +my %changeLogContents; +for my $changeLog (@changeLogs) { + open CHANGELOG, $changeLog or die "Can't open $changeLog"; + my $contents = ""; + my $blankLines = ""; + while (<CHANGELOG>) { + if (/^\S/) { + last if $contents; + } + if (/\S/) { + $contents .= $blankLines if $contents; + $blankLines = ""; + $contents .= $_; + } else { + $blankLines .= $_; + } + } + close CHANGELOG; + + $changeLog = File::Spec->abs2rel(File::Spec->rel2abs($changeLog), $topLevel); + + my $label = dirname($changeLog); + $label = "top level" unless length $label; + + my $sortKey = lc $label; + if ($label eq "top level") { + $sortKey = ""; + } elsif ($label eq "Tools") { + $sortKey = "-, just after top level"; + } elsif ($label eq "WebBrowser") { + $sortKey = lc "WebKit, WebBrowser after"; + } elsif ($label eq "WebCore") { + $sortKey = lc "WebFoundation, WebCore after"; + } elsif ($label eq "LayoutTests") { + $sortKey = lc "~, LayoutTests last"; + } + + $changeLogSort{$sortKey} = $label; + $changeLogContents{$label} = $contents; +} + +my $first = 1; +open NEWLOG, ">$log.edit" or die; +for my $sortKey (sort keys %changeLogSort) { + my $label = $changeLogSort{$sortKey}; + if (keys %changeLogSort > 1) { + print NEWLOG "\n" if !$first; + $first = 0; + print NEWLOG "$label:\n\n"; + } + print NEWLOG $changeLogContents{$label}; +} +print NEWLOG $logContents; +close NEWLOG; + +system $editor, "$log.edit"; + +open NEWLOG, "$log.edit" or exit; +my $foundComment = 0; +while (<NEWLOG>) { + $foundComment = 1 if (/\S/ && !/^CVS:/); +} +close NEWLOG; + +if ($foundComment) { + open NEWLOG, "$log.edit" or die; + open LOG, ">$log" or die; + while (<NEWLOG>) { + print LOG; + } + close LOG; + close NEWLOG; +} + +unlink "$log.edit"; + +sub topLevelSourceDirectory +{ + if (isGit()) { + chomp(my $gitDir = `git rev-parse --git-dir`); + return dirname($gitDir); + } elsif (isSVN()) { + open(INFO, "-|", qw(svn info)) or die; + my ($root, $url); + while (my $line = <INFO>) { + if ($line =~ /^Repository Root: (.*)$/) { + $root = $1; + } elsif ($line =~ /^URL: (.*)$/) { + $url = $1; + } + } + close(INFO); + + my $path = $url; + $path =~ s/^\Q$root\E//; + $path =~ s/^\/?(branches\/[^\/]*|trunk)\/?//; + return File::Spec->rel2abs(File::Spec->catdir(map { ".." } File::Spec->splitdir($path))); + } +} diff --git a/WebKitTools/Scripts/compare-timing-files b/WebKitTools/Scripts/compare-timing-files new file mode 100755 index 0000000..11b470b --- /dev/null +++ b/WebKitTools/Scripts/compare-timing-files @@ -0,0 +1,88 @@ +#!/usr/bin/perl + +# Copyright (C) 2006 Apple Computer, 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 takes two files that are lists of timings and compares them. + +use warnings; +use strict; +use Getopt::Long; + +my $usage = "compare-timing-files [-c|--count results] oldFile newFile"; + +my $count = 1; +GetOptions("c|count=i" => \$count); + +my ($file1, $file2) = @ARGV; +die "$usage\n" unless ($file1 && $file2 && @ARGV == 2); + +my ($oldAverage, $oldRange, $oldRangePercent) = parseResults($file1); +my ($newAverage, $newRange, $newRangePercent) = parseResults($file2); + +print "\n===== $file1 =====\n"; +if ($count == 1) { + print("fastest run: $oldAverage\n"); +} else { + print("average of fastest $count runs: $oldAverage\n"); + printf("range of fastest $count runs: %.2f%% (%d)\n", $oldRangePercent, $oldRange); +} + +print "\n===== $file2 =====\n"; +if ($count == 1) { + print("fastest run: $newAverage\n"); +} else { + print("average of fastest $count runs: $newAverage\n"); + printf("range of fastest $count runs: %.2f%% (%d)\n", $newRangePercent, $newRange); +} + +my $gainOrLoss = $newAverage <= $oldAverage ? "GAIN" : "LOSS"; +my $difference = abs($newAverage - $oldAverage); +my $differencePercent = $difference / $oldAverage * 100; +printf("\nperformance %s of %.2f%% (%.1f / %.1f)\n", $gainOrLoss, $differencePercent, $difference, $oldAverage); +print "\n"; + +sub parseResults +{ + my ($file) = @_; + + open(FILE, $file) or die "Couldn't open file: $file"; + my @results = <FILE>; + close(FILE); + + @results = sort(@results); + my $total = 0; + for (my $i = 0; $i < $count; $i++) { + $results[$i] =~ s/\D*//; # cut out non-digits + $total += $results[$i]; + } + my $average = $total / $count; + my $range = $results[$count - 1] - $results[0]; + my $rangePercent = $range / $results[$count - 1] * 100; + + return ($average, $range, $rangePercent); +} + diff --git a/WebKitTools/Scripts/create-exports b/WebKitTools/Scripts/create-exports new file mode 100755 index 0000000..c645d55 --- /dev/null +++ b/WebKitTools/Scripts/create-exports @@ -0,0 +1,5 @@ +#!/usr/bin/perl -w + +while (<>) { + print "$1\n" if /^\s*\"(.+)\", referenced from:$/; +} diff --git a/WebKitTools/Scripts/debug-safari b/WebKitTools/Scripts/debug-safari new file mode 100755 index 0000000..52e97fe --- /dev/null +++ b/WebKitTools/Scripts/debug-safari @@ -0,0 +1,38 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2007 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. + +# Script to run Safari in the platform's debugger for the WebKit Open Source Project. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +setConfiguration(); + +exit exitStatus(runSafari(1)); diff --git a/WebKitTools/Scripts/do-file-rename b/WebKitTools/Scripts/do-file-rename new file mode 100755 index 0000000..ac5099e --- /dev/null +++ b/WebKitTools/Scripts/do-file-rename @@ -0,0 +1,115 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006, 2008 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. + +# Script to do file renaming. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; +use File::Find; + +setConfiguration(); +chdirWebKit(); + +my %words; + +# find all files we want to process + +my @paths; +find(\&wanted, "JavaScriptCore"); +find(\&wanted, "JavaScriptGlue"); +find(\&wanted, "WebCore"); +find(\&wanted, "WebKit"); + +sub wanted +{ + my $file = $_; + + if ($file eq "icu") { + $File::Find::prune = 1; + return; + } + + if ($file =~ /^\../) { + $File::Find::prune = 1; + return; + } + + return if $file =~ /^ChangeLog/; + return if -d $file; + + push @paths, $File::Find::name; +} + +my %renames = ( +); + +my %renamesContemplatedForTheFuture = ( +); + +# rename files + +my %newFile; +for my $file (sort @paths) { + my $f = $file; + $f = "$1$renames{$2}" if $f =~ /^(.*\/)(\w+\.\w+)$/ && $renames{$2}; + $newFile{$file} = $f if $f ne $file; +} + +for my $file (sort @paths) { + if ($newFile{$file}) { + my $newFile = $newFile{$file}; + print "Renaming $file to $newFile\n"; + system "svn move $file $newFile"; + } +} + +# change all file contents + +for my $file (sort @paths) { + $file = $newFile{$file} if $newFile{$file}; + my $contents; + { + local $/; + open FILE, $file or die; + $contents = <FILE>; + close FILE; + } + my $newContents = $contents; + + for my $from (keys %renames) { + $newContents =~ s/\b\Q$from\E(?!\w)/$renames{$from}/g; # this " unconfuses Xcode syntax highlighting + } + + if ($newContents ne $contents) { + open FILE, ">", $file or die; + print FILE $newContents; + close FILE; + } +} diff --git a/WebKitTools/Scripts/do-webcore-rename b/WebKitTools/Scripts/do-webcore-rename new file mode 100755 index 0000000..edfe2dc --- /dev/null +++ b/WebKitTools/Scripts/do-webcore-rename @@ -0,0 +1,199 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006, 2007, 2008 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. + +# Script to do a rename in JavaScriptCore, WebCore, and WebKit. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; +use File::Find; + +setConfiguration(); +chdirWebKit(); + +my %words; + +# find all files we want to process + +my @paths; +find(\&wanted, "JavaScriptCore"); +find(\&wanted, "JavaScriptGlue"); +find(\&wanted, "WebCore"); +find(\&wanted, "WebKit"); + +sub wanted +{ + my $file = $_; + + if ($file eq "icu") { + $File::Find::prune = 1; + return; + } + + if ($file =~ /^\../) { + $File::Find::prune = 1; + return; + } + + return if $file =~ /^ChangeLog/; + return if -d $file; + + push @paths, $File::Find::name; +} + +my %renames = ( +); + +my %renamesContemplatedForTheFuture = ( + "StructureID" => "Structure", + "StructureIDChain" => "StructureChain", + "StructureIDs" => "Structures", + "StructureID_h" => "Structure_h", + "StructureIDChain_h" => "StructureChain_h", + "activationStructureID" => "activationStructure", + "createStructureID" => "createStructure", + "currStructureID" => "currStructure", + "derefStructureIDs" => "derefStructures", + "liveStructureIDSet" => "liveStructureSet", + "m_structureID" => "mm_structure", + "newStructureID" => "newStructureID", + "numberStructureID" => "numberStructure", + "oldStructureID" => "oldStructure", + "printStructureIDs" => "printStructures", + "refStructureIDs" => "refStructures", + "repatchGetByIdDefaultStructureID" => "repatchGetByIdDefaultStructure", + "repatchOffsetGetByIdStructureID" => "repatchOffsetGetByIdStructure", + "repatchOffsetPutByIdStructureID" => "repatchOffsetPutByIdStructure", + "sIDC" => "chain", + "setCachedStructureID" => "setCachedStructureID", + "staticScopeStructureID" => "staticScopeStructure", + "stringStructureID" => "stringStructure", + "structureID" => "structure", + "structureIDAddr" => "structureAddress", + "structureIDChain" => "structureChain", + "structureIDCounter" => "structureCounter", + "structureIDInstructionIndex" => "structureInstructionIndex", + "structureIDInstructions" => "structureInstructions", + + "DOMObject" => "JSDOMObject", + + "runtimeObjectGetter" => "pluginElementGetter", + "runtimeObjectPropertyGetter" => "pluginElementPropertyGetter", + "runtimeObjectCustomGetOwnPropertySlot" => "pluginElementCustomGetOwnPropertySlot", + "runtimeObjectCustomPut" => "pluginElementCustomPut", + "runtimeObjectImplementsCall" => "pluginElementImplementsCall", + "runtimeObjectCallAsFunction" => "pluginElementCallAsFunction", + + "CLONE_CONTENTS" => "Clone", + "DELETE_CONTENTS" => "Delete", + "EXTRACT_CONTENTS" => "Extract", + + "DateInstance" => "JSDate", + "ErrorInstance" => "JSError", + + "ImageConstructorImp" => "JSImageConstructor", + "Navigator" => "JSNavigator", + + "JSHTMLOptionElementConstructor" => "JSOptionConstructor", + "XSLTProcessorPrototypeTable" => "JSXSLTProcessorPrototypeTable", + + "KURL" => "URL", + "KURLCFNet" => "URLCF", + "KURLHash" => "URLHash", + "KURLMac" => "URLMac", + "KURL_H_" => "URL_h", + + "ThreadSafeShared" => "ThreadSafeRefCounted", + "TreeShared" => "TreeRefCounted", + + "StringImpl" => "SharedString", + + "RenderView" => "RenderViewport", + + "ObjcFallbackObjectImp" => "ObjCFallbackObject", + "RuntimeObjectImp" => "ForeignObject", + + "equalIgnoringCase" => "equalFoldingCase", + + "FTPDirectoryTokenizer" => "FTPDirectoryDocumentBuilder", + "HTMLTokenizer" => "HTMLDocumentBuilder", + "ImageTokenizer" => "ImageDocumentBuilder", + "PluginTokenizer" => "PluginDocumentBuilder", + "TextTokenizer" => "TextDocumentBuilder", + "Tokenizer" => "DocumentBuilder", + "Tokenizer_h" => "DocumentBuilder_h", + "XMLTokenizer" => "XMLDocumentBuilder", + "isHTMLTokenizer" => "isHTMLDocumentBuilder", + "m_tokenizer" => "m_builder", + "createTokenizer" => "createBuilder", + "tokenizerProcessedData" => "documentBuilderProcessedData", +); + +# rename files + +my %newFile; +for my $file (sort @paths) { + my $f = $file; + $f = "$1$renames{$2}$3" if $f =~ /^(.*\/)(\w+)(\.\w+)$/ && $renames{$2}; + if ($f ne $file) { + $newFile{$file} = $f; + } +} + +for my $file (sort @paths) { + if ($newFile{$file}) { + my $newFile = $newFile{$file}; + print "Renaming $file to $newFile\n"; + system "svn move $file $newFile"; + } +} + +# change all file contents + +for my $file (sort @paths) { + $file = $newFile{$file} if $newFile{$file}; + my $contents; + { + local $/; + open FILE, $file or die; + $contents = <FILE>; + close FILE; + } + my $newContents = $contents; + + for my $from (keys %renames) { + $newContents =~ s/\b$from(?!["\w])/$renames{$from}/g; # this " unconfuses Xcode syntax highlighting + } + + if ($newContents ne $contents) { + open FILE, ">", $file or die; + print FILE $newContents; + close FILE; + } +} diff --git a/WebKitTools/Scripts/extract-localizable-strings b/WebKitTools/Scripts/extract-localizable-strings new file mode 100755 index 0000000..420624b --- /dev/null +++ b/WebKitTools/Scripts/extract-localizable-strings @@ -0,0 +1,351 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006, 2007 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 is like the genstrings tool (minus most of the options) with these differences. +# +# 1) It uses the names UI_STRING and UI_STRING_WITH_KEY for the macros, rather than the macros +# from NSBundle.h, and doesn't support tables (although they would be easy to add). +# 2) It supports UTF-8 in key strings (and hence uses "" strings rather than @"" strings; +# @"" strings only reliably support ASCII since they are decoded based on the system encoding +# at runtime, so give different results on US and Japanese systems for example). +# 3) It looks for strings that are not marked for localization, using both macro names that are +# known to be used for debugging in Intrigue source code and an exceptions file. +# 4) It finds the files to work on rather than taking them as parameters, and also uses a +# hardcoded location for both the output file and the exceptions file. +# It would have been nice to use the project to find the source files, but it's too hard to +# locate source files after parsing a .pbxproj file. + +# The exceptions file has a list of strings in quotes, filenames, and filename/string pairs separated by :. + +use strict; + +my %isDebugMacro = ( ASSERT_WITH_MESSAGE => 1, LOG_ERROR => 1, ERROR => 1, NSURL_ERROR => 1, FATAL => 1, LOG => 1, LOG_WARNING => 1, UI_STRING_LOCALIZE_LATER => 1, LPCTSTR_UI_STRING_LOCALIZE_LATER => 1, UNLOCALIZED_STRING => 1, UNLOCALIZED_LPCTSTR => 1, dprintf => 1, NSException => 1, NSLog => 1, printf => 1 ); + +@ARGV >= 1 or die "Usage: extract-localizable-strings <exceptions file> [ directory... ]\nDid you mean to run extract-webkit-localizable-strings instead?\n"; + +my $exceptionsFile = shift @ARGV; +-f $exceptionsFile or die "Couldn't find exceptions file $exceptionsFile\n"; + +my $fileToUpdate = shift @ARGV; +-f $fileToUpdate or die "Couldn't find file to update $fileToUpdate\n"; + +my @directories = (); +my @directoriesToSkip = (); +if (@ARGV < 1) { + push(@directories, "."); +} else { + for my $dir (@ARGV) { + if ($dir =~ /^-(.*)$/) { + push @directoriesToSkip, $1; + } else { + push @directories, $dir; + } + } +} + +my $sawError = 0; + +my $localizedCount = 0; +my $keyCollisionCount = 0; +my $notLocalizedCount = 0; +my $NSLocalizeCount = 0; + +my %exception; +my %usedException; + +if (open EXCEPTIONS, $exceptionsFile) { + while (<EXCEPTIONS>) { + chomp; + if (/^"([^\\"]|\\.)*"$/ or /^[-_\/\w.]+.(h|m|mm|c|cpp)$/ or /^[-_\/\w.]+.(h|m|mm|c|cpp):"([^\\"]|\\.)*"$/) { + if ($exception{$_}) { + print "$exceptionsFile:$.:exception for $_ appears twice\n"; + print "$exceptionsFile:$exception{$_}:first appearance\n"; + } else { + $exception{$_} = $.; + } + } else { + print "$exceptionsFile:$.:syntax error\n"; + } + } + close EXCEPTIONS; +} + +my $quotedDirectoriesString = '"' . join('" "', @directories) . '"'; +for my $dir (@directoriesToSkip) { + $quotedDirectoriesString .= ' -path "' . $dir . '" -prune'; +} + +my @files = ( split "\n", `find $quotedDirectoriesString -name "*.h" -o -name "*.m" -o -name "*.mm" -o -name "*.c" -o -name "*.cpp"` ); + +for my $file (sort @files) { + next if $file =~ /\/WebLocalizableStrings\.h$/; + next if $file =~ /\/icu\//; + + $file =~ s-^./--; + + open SOURCE, $file or die "can't open $file\n"; + + my $inComment = 0; + + my $expected = ""; + my $macroLine; + my $macro; + my $UIString; + my $key; + my $comment; + + my $string; + my $stringLine; + my $nestingLevel; + + my $previousToken = ""; + + while (<SOURCE>) { + chomp; + + # Handle continued multi-line comment. + if ($inComment) { + next unless s-.*\*/--; + $inComment = 0; + } + + # Handle all the tokens in the line. + while (s-^\s*([#\w]+|/\*|//|[^#\w/'"()\[\],]+|.)--) { + my $token = $1; + + if ($token eq "\"") { + if ($expected and $expected ne "a quoted string") { + print "$file:$.:ERROR:found a quoted string but expected $expected\n"; + $sawError = 1; + $expected = ""; + } + if (s-^(([^\\$token]|\\.)*?)$token--) { + if (!defined $string) { + $stringLine = $.; + $string = $1; + } else { + $string .= $1; + } + } else { + print "$file:$.:ERROR:mismatched quotes\n"; + $sawError = 1; + $_ = ""; + } + next; + } + + if (defined $string) { +handleString: + if ($expected) { + if (!defined $UIString) { + # FIXME: Validate UTF-8 here? + $UIString = $string; + $expected = ","; + } elsif (($macro =~ /UI_STRING_KEY$/) and !defined $key) { + # FIXME: Validate UTF-8 here? + $key = $string; + $expected = ","; + } elsif (!defined $comment) { + # FIXME: Validate UTF-8 here? + $comment = $string; + $expected = ")"; + } + } else { + if (defined $nestingLevel) { + # In a debug macro, no need to localize. + } elsif ($previousToken eq "#include" or $previousToken eq "#import") { + # File name, no need to localize. + } elsif ($previousToken eq "extern" and $string eq "C") { + # extern "C", no need to localize. + } elsif ($string eq "") { + # Empty string can sometimes be localized, but we need not complain if not. + } elsif ($exception{$file}) { + $usedException{$file} = 1; + } elsif ($exception{"\"$string\""}) { + $usedException{"\"$string\""} = 1; + } elsif ($exception{"$file:\"$string\""}) { + $usedException{"$file:\"$string\""} = 1; + } else { + print "$file:$stringLine:\"$string\" is not marked for localization\n"; + $notLocalizedCount++; + } + } + $string = undef; + last if !defined $token; + } + + $previousToken = $token; + + if ($token =~ /^NSLocalized/ && $token !~ /NSLocalizedDescriptionKey/ && $token !~ /NSLocalizedStringFromTableInBundle/) { + print "$file:$.:ERROR:found a use of an NSLocalized macro; not supported\n"; + $nestingLevel = 0 if !defined $nestingLevel; + $sawError = 1; + $NSLocalizeCount++; + } elsif ($token eq "/*") { + if (!s-^.*?\*/--) { + $_ = ""; # If the comment doesn't end, discard the result of the line and set flag + $inComment = 1; + } + } elsif ($token eq "//") { + $_ = ""; # Discard the rest of the line + } elsif ($token eq "'") { + if (!s-([^\\]|\\.)'--) { #' <-- that single quote makes the Project Builder editor less confused + print "$file:$.:ERROR:mismatched single quote\n"; + $sawError = 1; + $_ = ""; + } + } else { + if ($expected and $expected ne $token) { + print "$file:$.:ERROR:found $token but expected $expected\n"; + $sawError = 1; + $expected = ""; + } + if ($token =~ /UI_STRING(_KEY)?$/) { + $expected = "("; + $macro = $token; + $UIString = undef; + $key = undef; + $comment = undef; + $macroLine = $.; + } elsif ($token eq "(" or $token eq "[") { + ++$nestingLevel if defined $nestingLevel; + $expected = "a quoted string" if $expected; + } elsif ($token eq ",") { + $expected = "a quoted string" if $expected; + } elsif ($token eq ")" or $token eq "]") { + $nestingLevel = undef if defined $nestingLevel && !--$nestingLevel; + if ($expected) { + $key = $UIString if !defined $key; + HandleUIString($UIString, $key, $comment, $file, $macroLine); + $macro = ""; + $expected = ""; + $localizedCount++; + } + } elsif ($isDebugMacro{$token}) { + $nestingLevel = 0 if !defined $nestingLevel; + } + } + } + + } + + goto handleString if defined $string; + + if ($expected) { + print "$file:ERROR:reached end of file but expected $expected\n"; + $sawError = 1; + } + + close SOURCE; +} + +my %stringByKey; +my %commentByKey; +my %fileByKey; +my %lineByKey; + +sub HandleUIString +{ + my ($string, $key, $comment, $file, $line) = @_; + + my $bad = 0; + if (grep { $_ == 0xFFFD } unpack "U*", $string) { + print "$file:$line:ERROR:string for translation has illegal UTF-8 -- most likely a problem with the Text Encoding of the source file\n"; + $bad = 1; + } + if ($string ne $key && grep { $_ == 0xFFFD } unpack "U*", $key) { + print "$file:$line:ERROR:key has illegal UTF-8 -- most likely a problem with the Text Encoding of the source file\n"; + $bad = 1; + } + if (grep { $_ == 0xFFFD } unpack "U*", $comment) { + print "$file:$line:ERROR:comment for translation has illegal UTF-8 -- most likely a problem with the Text Encoding of the source file\n"; + $bad = 1; + } + if ($bad) { + $sawError = 1; + return; + } + + if ($stringByKey{$key} && $stringByKey{$key} ne $string) { + print "$file:$line:encountered the same key, \"$key\", twice, with different strings\n"; + print "$fileByKey{$key}:$lineByKey{$key}:previous occurrence\n"; + $keyCollisionCount++; + return; + } + if ($commentByKey{$key} && $commentByKey{$key} ne $comment) { + print "$file:$line:encountered the same key, \"$key\", twice, with different comments\n"; + print "$fileByKey{$key}:$lineByKey{$key}:previous occurrence\n"; + $keyCollisionCount++; + return; + } + + $fileByKey{$key} = $file; + $lineByKey{$key} = $line; + $stringByKey{$key} = $string; + $commentByKey{$key} = $comment; +} + +print "\n" if $sawError || $notLocalizedCount || $NSLocalizeCount; + +my @unusedExceptions = sort grep { !$usedException{$_} } keys %exception; +if (@unusedExceptions) { + for my $unused (@unusedExceptions) { + print "$exceptionsFile:$exception{$unused}:exception $unused not used\n"; + } + print "\n"; +} + +print "$localizedCount localizable strings\n" if $localizedCount; +print "$keyCollisionCount key collisions\n" if $keyCollisionCount; +print "$notLocalizedCount strings not marked for localization\n" if $notLocalizedCount; +print "$NSLocalizeCount uses of NSLocalize\n" if $NSLocalizeCount; +print scalar(@unusedExceptions), " unused exceptions\n" if @unusedExceptions; + +if ($sawError) { + print "\nErrors encountered. Exiting without writing to $fileToUpdate.\n"; + exit 1; +} + +my $localizedStrings = ""; + +for my $key (sort keys %commentByKey) { + $localizedStrings .= "/* $commentByKey{$key} */\n\"$key\" = \"$stringByKey{$key}\";\n\n"; +} + +# Write out the strings file in UTF-16 with a BOM. +utf8::decode($localizedStrings) if $^V ge chr(5).chr(8); +my $output = pack "n*", (0xFEFF, unpack "U*", $localizedStrings); + +if (-e "$fileToUpdate") { + open STRINGS, ">", "$fileToUpdate" or die; + print STRINGS $output; + close STRINGS; +} else { + print "$fileToUpdate does not exist\n"; + exit 1; +} diff --git a/WebKitTools/Scripts/find-extra-includes b/WebKitTools/Scripts/find-extra-includes new file mode 100755 index 0000000..1286cd5 --- /dev/null +++ b/WebKitTools/Scripts/find-extra-includes @@ -0,0 +1,102 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, 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. + +# "find-extra-includes" script for Web Kit Open Source Project + +use strict; +use File::Find; + +find(\&wanted, @ARGV ? @ARGV : "."); + +my %paths; +my %includes; + +sub wanted +{ + my $file = $_; + + if ($file eq "icu") { + $File::Find::prune = 1; + return; + } + + if ($file !~ /^\./ && $file =~ /\.(h|cpp|c|mm|m)$/) { + $paths{$file} = $File::Find::name; + open FILE, $file or die; + while (<FILE>) { + if (m-^\s*#\s*(include|import)\s+["<]((\S+/)*)(\S+)[">]-) { + my $include = ($2 eq "sys/" ? $2 : "") . $4; + $includes{$file}{$include}++; + } + } + close FILE; + } +} + +my %totalIncludes; + +sub fillOut +{ + my ($file) = @_; + + return if defined $totalIncludes{$file}; + + for my $include (keys %{ $includes{$file} }) { + $totalIncludes{$file}{$include} = 1; + fillOut($include); + for my $i (keys %{ $totalIncludes{$include} }) { + $totalIncludes{$file}{$i} = 1; + } + } +} + +sub check +{ + my ($file) = @_; + + for my $include (keys %{ $includes{$file} }) { + fillOut($include); + } + for my $i1 (sort keys %{ $includes{$file} }) { + for my $i2 (keys %{ $includes{$file} }) { + next if $i1 eq $i2; + if ($totalIncludes{$i2}{$i1}) { + my $b1 = $i1; + my $b2 = $file; + $b1 =~ s/\..+$//; + $b2 =~ s/\..+$//; + print "$paths{$file} does not need to include $i1, because $i2 does\n" if $b1 ne $b2; + last; + } + } + } +} + +for my $file (sort keys %includes) { + check($file); +} diff --git a/WebKitTools/Scripts/find-included-framework-headers b/WebKitTools/Scripts/find-included-framework-headers new file mode 100755 index 0000000..3e7aaf6 --- /dev/null +++ b/WebKitTools/Scripts/find-included-framework-headers @@ -0,0 +1,10 @@ +#!/bin/sh + +FILE_EXTENSIONS_TO_SEARCH="cpp h m mm" + +for framework in $*; do + echo -e "\n$framework\n==================" + for ext in ${FILE_EXTENSIONS_TO_SEARCH}; do + find . -name "*.$ext" -exec grep $framework {} ';' | grep '\(include\|import\)' | sed -e 's|.*/\(.*\.h\).*|\1|' + done | sort | uniq +done diff --git a/WebKitTools/Scripts/gdb-safari b/WebKitTools/Scripts/gdb-safari new file mode 100755 index 0000000..f87f965 --- /dev/null +++ b/WebKitTools/Scripts/gdb-safari @@ -0,0 +1,83 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, 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. + +# Simplified "run under gdb" script for WebKit Open Source Project. + +use strict; +use File::Temp qw/:mktemp/; +use FindBin; +use Getopt::Long; +use lib $FindBin::Bin; +use webkitdirs; + +my $programName = basename($0); +my $showHelp = 0; +my $run64Bit; + +my $usage = <<EOF; +Usage: $programName [options] + --help Show this help message + --64-bit Run in 64-bit mode +EOF + +my $getOptionsResult = GetOptions( + 'help' => \$showHelp, + '64-bit!' => \$run64Bit +); + +if (!$getOptionsResult || $showHelp) { + print STDERR $usage; + exit 1; +} + +setRun64Bit($run64Bit); +setConfiguration(); +my $productDir = productDir(); +my $safariPath = safariPath(); + +# Check to see that gdb is in the usual place. +my $gdbPath = "/usr/bin/gdb"; +die "Can't find gdb executable. Is gdb installed?\n" unless -x $gdbPath; + +# Check to see that all the frameworks are built. +checkFrameworks(); + +# Put a command to set DYLD_FRAMEWORK_PATH in a temp file. +# Also set WEBKIT_UNSET_DYLD_FRAMEWORK_PATH to YES in this environment, so that +# executables launched by Safari don't inherit using the new frameworks. +my ($fh, $path) = mkstemp("/tmp/gdb-safari-XXXX"); +print $fh "set env DYLD_FRAMEWORK_PATH $productDir\n"; +print $fh "set env WEBKIT_UNSET_DYLD_FRAMEWORK_PATH YES\n"; + +my @architectureFlags = ("-arch", preferredArchitecture()) if isOSX(); +# Start up Safari. +print "Start Safari under gdb with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; +exec $gdbPath, "-x", $path, @architectureFlags, $safariPath or die; + +# Delete the temporary file. +unlink0($fh, $path) or die "Error unlinking file $path safely"; diff --git a/WebKitTools/Scripts/generate-coverage-data b/WebKitTools/Scripts/generate-coverage-data new file mode 100644 index 0000000..7ed36aa --- /dev/null +++ b/WebKitTools/Scripts/generate-coverage-data @@ -0,0 +1,71 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. +# Copyright (C) 2007 Holger Hans Peter Freyther. 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. + +# Simple script to build, run and visualize coverage information + +use strict; +use File::Basename; +use File::Spec; +use FindBin; +use Getopt::Long qw(:config pass_through); +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +# Generate a name for our results +my $svnVersion = determineCurrentSVNRevision(); +my @timeData = localtime(time); +my $resultName = $svnVersion . "-" . join('_', @timeData); +my @otherOptions = (); + +# Move to the source directory +# Delete old gcov files +# Compile WebKit and run the tests +# Generate the coverage graph... +# Upload + +$ENV{'WEBKIT_COVERAGE_BUILD'} = 1; +chdirWebKit(); + +# Clean-up old files +print "Cleaning up\n"; +system("if [ -d WebKitBuild ]; then find WebKitBuild -name '*.gcda' -delete; fi;") == 0 or die; + + +print "Building and testing\n"; +system("WebKitTools/Scripts/build-webkit", "--coverage", @ARGV) == 0 or die; +system "WebKitTools/Scripts/run-webkit-tests", "--no-launch-safari"; +system "WebKitTools/Scripts/run-javascriptcore-tests", "--coverage", @ARGV; + +# Collect the data and generate a report +print "Collecting coverage data\n"; +system("WebKitTools/CodeCoverage/run-generate-coverage-data", $resultName, "WebKitBuild/Coverage") == 0 or die; +system("WebKitTools/CodeCoverage/regenerate-coverage-display", "WebKitBuild/Coverage", "WebKitBuild/Coverage/html") == 0 or die; + +print "Done\n"; diff --git a/WebKitTools/Scripts/make-js-test-wrappers b/WebKitTools/Scripts/make-js-test-wrappers new file mode 100755 index 0000000..4ac21ea --- /dev/null +++ b/WebKitTools/Scripts/make-js-test-wrappers @@ -0,0 +1,159 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006, 2007, 2008 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. + +# Script to generate HTML wrappers for JavaScript tests from templates + +use strict; + +use FindBin; +use lib $FindBin::Bin; + +use File::Basename; +use File::Find; +use Getopt::Long; +use webkitdirs; + +sub directoryFilter; +sub findTemplateFiles(@); + +my $showHelp; + +my $result = GetOptions( + "help" => \$showHelp, +); + +if (!$result || $showHelp) { + print STDERR basename($0) . " [-h|--help] [path ...]\n"; + exit 1; +} + +setConfiguration(); +my $productDir = productDir(); + +chdirWebKit(); + +my @templates = findTemplateFiles(@ARGV); + +for my $tfile (@templates) { + + my $tpath = $tfile; + $tpath =~ s:/resources/TEMPLATE.html$::; + + print "${tpath}\n"; + + chdirWebKit(); + chdir($tpath); + + my @files; + my $fileFilter = sub { + push @files, $File::Find::name if substr($_, -3) eq ".js"; + }; + find({ preprocess => \&directoryFilter, wanted => $fileFilter }, "resources"); + + open TEMPLATE, "<resources/TEMPLATE.html"; + my $template = do { local $/; <TEMPLATE> }; + close TEMPLATE; + + my $templateNegative = $template; + if (-e "resources/TEMPLATE-n.html") { + open TEMPLATE, "<resources/TEMPLATE-n.html"; + $templateNegative = do { local $/; <TEMPLATE> }; + close TEMPLATE; + } + + for my $file (@files) { + next if $file =~ /js-test-.*\.js$/; + next if $file =~ /SVGTestCase\.js/; + + next if $file =~ m:resources/NSResolver-exceptions\.js$:; + next if $file =~ m:resources/attr-case-sensitivity\.js$:; + next if $file =~ m:resources/codegen-temporaries-multiple-global-blocks-1\.js$:; + next if $file =~ m:resources/codegen-temporaries-multiple-global-blocks-2\.js$:; + next if $file =~ m:resources/constructors-cached-navigate\.js$:; + next if $file =~ m:resources/frame-loading-via-document-write\.js$:; + next if $file =~ m:resources/id-fastpath-almost-strict\.js$:; + next if $file =~ m:resources/id-fastpath-strict\.js$:; + next if $file =~ m:resources/intersectsNode\.js$:; + next if $file =~ m:resources/p-in-scope\.js$:; + next if $file =~ m:resources/script-element-gc\.js$:; + next if $file =~ m:resources/script-element-gc\.js$:; + next if $file =~ m:resources/script3\.js$:; + next if $file =~ m:resources/script4\.js$:; + next if $file =~ m:resources/script5\.js$:; + next if $file =~ m:resources/select-options-remove\.js$:; + next if $file =~ m:resources/shadow-offset\.js$:; + next if $file =~ m:resources/tabindex-focus-blur-all\.js$:; + next if $file =~ m:resources/use-instanceRoot-event-bubbling\.js$:; + next if $file =~ m:resources/use-instanceRoot-event-listeners\.js$:; + next if $file =~ m:resources/wrapper-identity-base\.js$:; + next if $file =~ m:resources/xhtml-scripts\.js$:; + + my $html = $file; + $html =~ s:resources/(.*)\.js:$1.html:; + next if -f "$html-disabled"; + + system("grep -q 'successfullyParsed =' $file"); + if ($? != 0) { + `echo "" >> "${file}"`; + `echo "var successfullyParsed = true;" >> "${file}"`; + } + + print " ${html}\n"; + open HTML, ">$html"; + my $output = ($file =~ /-n\.js/) ? $templateNegative : $template; + $output =~ s:YOUR_JS_FILE_HERE:$file:; + print HTML $output; + + close HTML; + } +} + +exit 0; + +sub directoryFilter +{ + return () if basename($File::Find::dir) eq ".svn"; + return @_; +} + +sub findTemplateFiles(@) { + my @args = @_; + my @templateFiles; + + push @args, "LayoutTests" if scalar(@args) == 0; + + my @paths = map { -f $_ ? dirname($_) : $_ } @args; + + my $fileFilter = sub { + push @templateFiles, $File::Find::name if $_ eq "TEMPLATE.html"; + }; + + find({ preprocess => \&directoryFilter, wanted => $fileFilter }, @paths); + + return @templateFiles; +} diff --git a/WebKitTools/Scripts/num-cpus b/WebKitTools/Scripts/num-cpus new file mode 100755 index 0000000..c5f28a1 --- /dev/null +++ b/WebKitTools/Scripts/num-cpus @@ -0,0 +1,16 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Win32API::Registry 0.21 qw( :ALL ); + + +my $key; +my $i = 0; +while (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\$i", 0, KEY_READ, $key)) { + $i++; + RegCloseKey($key); +} + +print "$i\n"; diff --git a/WebKitTools/Scripts/parallelcl b/WebKitTools/Scripts/parallelcl new file mode 100755 index 0000000..532079f --- /dev/null +++ b/WebKitTools/Scripts/parallelcl @@ -0,0 +1,224 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use File::Basename; +use File::Spec; +use File::Temp; +use POSIX; + +sub makeJob(\@$); +sub forkAndCompileFiles(\@$); +sub Exec($); +sub waitForChild(\@); +sub cleanup(\@); + +my $debug = 0; + +chomp(my $clexe = `cygpath -u '$ENV{'VS80COMNTOOLS'}/../../VC/bin/cl.exe'`); + +if ($debug) { + print STDERR "Received " . @ARGV . " arguments:\n"; + foreach my $arg (@ARGV) { + print STDERR "$arg\n"; + } +} + +my $commandFile; +foreach my $arg (@ARGV) { + if ($arg =~ /^[\/-](E|EP|P)$/) { + print STDERR "The invoking process wants preprocessed source, so let's hand off this whole command to the real cl.exe\n" if $debug; + Exec("\"$clexe\" \"" . join('" "', @ARGV) . "\""); + } elsif ($arg =~ /^@(.*)$/) { + chomp($commandFile = `cygpath -u '$1'`); + } +} + +die "No command file specified!" unless $commandFile; +die "Couldn't find $commandFile!" unless -f $commandFile; + +my @sources; + +open(COMMAND, '<:raw:encoding(UTF16-LE):crlf:utf8', $commandFile) or die "Couldn't open $commandFile!"; + +# The first line of the command file contains all the options to cl.exe plus the first (possibly quoted) filename +my $firstLine = <COMMAND>; +$firstLine =~ s/\r?\n$//; + +# To find the start of the first filename, look for either the last space on the line. +# If the filename is quoted, the last character on the line will be a quote, so look for the quote before that. +my $firstFileIndex; +print STDERR "Last character of first line = '" . substr($firstLine, -1, 1) . "'\n" if $debug; +if (substr($firstLine, -1, 1) eq '"') { + print STDERR "First file is quoted\n" if $debug; + $firstFileIndex = rindex($firstLine, '"', length($firstLine) - 2); +} else { + print STDERR "First file is NOT quoted\n" if $debug; + $firstFileIndex = rindex($firstLine, ' ') + 1; +} + +my $options = substr($firstLine, 0, $firstFileIndex) . join(' ', @ARGV[1 .. $#ARGV]); +my $possibleFirstFile = substr($firstLine, $firstFileIndex); +if ($possibleFirstFile =~ /\.(cpp|c)/) { + push(@sources, $possibleFirstFile); +} else { + $options .= " $possibleFirstFile"; +} + +print STDERR "######## Found options $options ##########\n" if $debug; +print STDERR "####### Found first source file $sources[0] ########\n" if @sources && $debug; + +# The rest of the lines of the command file just contain source files, one per line +while (my $source = <COMMAND>) { + chomp($source); + $source =~ s/^\s+//; + $source =~ s/\s+$//; + push(@sources, $source) if length($source); +} +close(COMMAND); + +my $numSources = @sources; +exit unless $numSources > 0; + +my $numJobs; +if ($options =~ s/-j\s*([0-9]+)//) { + $numJobs = $1; +} else { + chomp($numJobs = `num-cpus`); +} + +print STDERR "\n\n####### RUNNING AT MOST $numJobs PARALLEL INSTANCES OF cl.exe ###########\n\n";# if $debug; + +# Magic determination of job size +# The hope is that by splitting the source files up into 2*$numJobs pieces, we +# won't suffer too much if one job finishes much more quickly than another. +# However, we don't want to split it up too much due to cl.exe overhead, so set +# the minimum job size to 5. +my $jobSize = POSIX::ceil($numSources / (2 * $numJobs)); +$jobSize = $jobSize < 5 ? 5 : $jobSize; + +print STDERR "######## jobSize = $jobSize ##########\n" if $debug; + +# Sort the source files randomly so that we don't end up with big clumps of large files (aka SVG) +sub fisher_yates_shuffle(\@) +{ + my ($array) = @_; + for (my $i = @{$array}; --$i; ) { + my $j = int(rand($i+1)); + next if $i == $j; + @{$array}[$i,$j] = @{$array}[$j,$i]; + } +} + +fisher_yates_shuffle(@sources); # permutes @array in place + +my @children; +my @tmpFiles; +my $status = 0; +while (@sources) { + while (@sources && @children < $numJobs) { + my $pid; + my $tmpFile; + my $job = makeJob(@sources, $jobSize); + ($pid, $tmpFile) = forkAndCompileFiles(@{$job}, $options); + + print STDERR "####### Spawned child with PID $pid and tmpFile $tmpFile ##########\n" if $debug; + push(@children, $pid); + push(@tmpFiles, $tmpFile); + } + + $status |= waitForChild(@children); +} + +while (@children) { + $status |= waitForChild(@children); +} +cleanup(@tmpFiles); + +exit WEXITSTATUS($status); + + +sub makeJob(\@$) +{ + my ($files, $jobSize) = @_; + + my @job; + if (@{$files} > ($jobSize * 1.5)) { + @job = splice(@{$files}, -$jobSize); + } else { + # Compile all the remaining files in this job to avoid having a small job later + @job = splice(@{$files}); + } + + return \@job; +} + +sub forkAndCompileFiles(\@$) +{ + print STDERR "######## forkAndCompileFiles()\n" if $debug; + my ($files, $options) = @_; + + if ($debug) { + foreach my $file (@{$files}) { + print STDERR "######## $file\n"; + } + } + + my (undef, $tmpFile) = File::Temp::tempfile('clcommandXXXXX', DIR => File::Spec->tmpdir, OPEN => 0); + + my $pid = fork(); + die "Fork failed" unless defined($pid); + + unless ($pid) { + # Child process + open(TMP, '>:raw:encoding(UTF16-LE):crlf:utf8', $tmpFile) or die "Couldn't open $tmpFile"; + print TMP "$options\n"; + foreach my $file (@{$files}) { + print TMP "$file\n"; + } + close(TMP); + + chomp(my $winTmpFile = `cygpath -m $tmpFile`); + Exec "\"$clexe\" \@\"$winTmpFile\""; + } else { + return ($pid, $tmpFile); + } +} + +sub Exec($) +{ + my ($command) = @_; + + print STDERR "Exec($command)\n" if $debug; + + exec($command); +} + +sub waitForChild(\@) +{ + my ($children) = @_; + + return unless @{$children}; + + my $deceased = wait(); + my $status = $?; + print STDERR "######## Child with PID $deceased finished ###########\n" if $debug; + for (my $i = 0; $i < @{$children}; $i++) { + if ($children->[$i] == $deceased) { + splice(@{$children}, $i, 1); + last; + } + } + + return $status; +} + +sub cleanup(\@) +{ + my ($tmpFiles) = @_; + + foreach my $file (@{$tmpFiles}) { + unlink $file; + } +} diff --git a/WebKitTools/Scripts/parse-malloc-history b/WebKitTools/Scripts/parse-malloc-history new file mode 100755 index 0000000..76ca74b --- /dev/null +++ b/WebKitTools/Scripts/parse-malloc-history @@ -0,0 +1,154 @@ +#!/usr/bin/perl + +# Copyright (C) 2007 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. + +# Parses the callstacks in a file with malloc_history formatted content, sorting +# based on total number of bytes allocated, and filtering based on command-line +# parameters. + +use Getopt::Long; +use File::Basename; + +use strict; +use warnings; + +sub commify($); + +sub main() +{ + my $usage = + "Usage: " . basename($0) . " [options] malloc_history.txt\n" . + " --grep-regexp Include only call stacks that match this regular expression.\n" . + " --byte-minimum Include only call stacks with allocation sizes >= this value.\n" . + " --merge-regexp Merge all call stacks that match this regular expression.\n" . + " --merge-depth Merge all call stacks that match at this stack depth and above.\n"; + + my $grepRegexp = ""; + my $byteMinimum = ""; + my @mergeRegexps = (); + my $mergeDepth = ""; + my $getOptionsResult = GetOptions( + "grep-regexp:s" => \$grepRegexp, + "byte-minimum:i" => \$byteMinimum, + "merge-regexp:s" => \@mergeRegexps, + "merge-depth:i" => \$mergeDepth + ); + my $fileName = $ARGV[0]; + die $usage if (!$getOptionsResult || !$fileName); + + open FILE, "<$fileName" or die "bad file: $fileName"; + my @file = <FILE>; + close FILE; + + my %callstacks = (); + my $byteCountTotal = 0; + + for (my $i = 0; $i < @file; $i++) { + my $line = $file[$i]; + my ($callCount, $byteCount); + + next if $line =~ /^\-/; + + # First try malloc_history format + # 6 calls for 664 bytes thread_ffffffff |0x0 | start + ($callCount, $byteCount) = ($line =~ /(\d+) calls for (\d+) bytes/); + + # Then try leaks format + # Leak: 0x0ac3ca40 size=48 + # 0x00020001 0x00000001 0x00000000 0x00000000 ................ + # Call stack: [thread ffffffff]: | 0x0 | start + if (!$callCount || !$byteCount) { + $callCount = 1; + ($byteCount) = ($line =~ /Leak: [x[:xdigit:]]* size=(\d+)/); + + if ($byteCount) { + while (!($line =~ "Call stack: ")) { + $i++; + $line = $file[$i]; + } + } + } + + # Then give up + next if (!$callCount || !$byteCount); + + $byteCountTotal += $byteCount; + + next if ($grepRegexp && !($line =~ $grepRegexp)); + + my $callstackBegin = 0; + if ($mergeDepth) { + # count stack frames backwards from end of callstack + $callstackBegin = length($line); + for (my $pipeCount = 0; $pipeCount < $mergeDepth; $pipeCount++) { + my $rindexResult = rindex($line, "|", $callstackBegin - 1); + last if $rindexResult == -1; + $callstackBegin = $rindexResult; + } + } else { + # start at beginning of callstack + $callstackBegin = index($line, "|"); + } + + my $callstack = substr($line, $callstackBegin + 2); # + 2 skips "| " + for my $regexp (@mergeRegexps) { + if ($callstack =~ $regexp) { + $callstack = $regexp . "\n"; + last; + } + } + + if (!$callstacks{$callstack}) { + $callstacks{$callstack} = {"callCount" => 0, "byteCount" => 0}; + } + + $callstacks{$callstack}{"callCount"} += $callCount; + $callstacks{$callstack}{"byteCount"} += $byteCount; + } + + my $byteCountTotalReported = 0; + for my $callstack (sort { $callstacks{$b}{"byteCount"} <=> $callstacks{$a}{"byteCount"} } keys %callstacks) { + my $callCount = $callstacks{$callstack}{"callCount"}; + my $byteCount = $callstacks{$callstack}{"byteCount"}; + last if ($byteMinimum && $byteCount < $byteMinimum); + + $byteCountTotalReported += $byteCount; + print commify($callCount) . " calls for " . commify($byteCount) . " bytes: $callstack\n"; + } + + print "total: " . commify($byteCountTotalReported) . " bytes (" . commify($byteCountTotal - $byteCountTotalReported) . " bytes excluded).\n"; +} + +exit(main()); + +# Copied from perldoc -- please excuse the style +sub commify($) +{ + local $_ = shift; + 1 while s/^([-+]?\d+)(\d{3})/$1,$2/; + return $_; +} diff --git a/WebKitTools/Scripts/pdevenv b/WebKitTools/Scripts/pdevenv new file mode 100755 index 0000000..1931211 --- /dev/null +++ b/WebKitTools/Scripts/pdevenv @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use File::Temp qw/tempfile/; +use FindBin; + +my ($fh, $path) = tempfile(UNLINK => 0, SUFFIX => '.cmd') or die; + +chomp(my $vcBin = `cygpath -w "$FindBin::Bin/../vcbin"`); + +print $fh "\@echo off\n\n"; +print $fh "call \"\%VS80COMNTOOLS\%\\vsvars32.bat\"\n\n"; +print $fh "set PATH=$vcBin;\%PATH\%\n\n"; +print $fh "IF EXIST \"\%VSINSTALLDIR\%\\Common7\\IDE\\devenv.com\" (devenv.com /useenv " . join(" ", @ARGV) . ") ELSE "; +print $fh "VCExpress.exe /useenv " . join(" ", @ARGV) . "\n"; + +close $fh; + +chmod 0755, $path; + +chomp($path = `cygpath -w -s '$path'`); + +exec("cmd /c \"call $path\""); diff --git a/WebKitTools/Scripts/prepare-ChangeLog b/WebKitTools/Scripts/prepare-ChangeLog new file mode 100755 index 0000000..ff4ce80 --- /dev/null +++ b/WebKitTools/Scripts/prepare-ChangeLog @@ -0,0 +1,1436 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 2 -*- + +# +# Copyright (C) 2000, 2001 Eazel, Inc. +# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. +# +# prepare-ChangeLog 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 of the License, or (at your option) any later version. +# +# prepare-ChangeLog 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, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + + +# Perl script to create a ChangeLog entry with names of files +# and functions from a diff. +# +# Darin Adler <darin@bentspoon.com>, started 20 April 2000 +# Java support added by Maciej Stachowiak <mjs@eazel.com> +# Objective-C, C++ and Objective-C++ support added by Maciej Stachowiak <mjs@apple.com> +# Git support added by Adam Roben <aroben@apple.com> + + +# +# TODO: +# List functions that have been removed too. +# Decide what a good logical order is for the changed files +# other than a normal text "sort" (top level first?) +# (group directories?) (.h before .c?) +# Handle yacc source files too (other languages?). +# Help merge when there are ChangeLog conflicts or if there's +# already a partly written ChangeLog entry. +# Add command line option to put the ChangeLog into a separate +# file or just spew it out stdout. +# Add SVN version numbers for commit (can't do that until +# the changes are checked in, though). +# Work around diff stupidity where deleting a function that starts +# with a comment makes diff think that the following function +# has been changed (if the following function starts with a comment +# with the same first line, such as /**) +# Work around diff stupidity where deleting an entire function and +# the blank lines before it makes diff think you've changed the +# previous function. + +use strict; +use warnings; + +use File::Basename; +use File::Spec; +use FindBin; +use Getopt::Long; +use lib $FindBin::Bin; +use POSIX qw(strftime); +use VCSUtils; + +sub changeLogDate($); +sub firstDirectoryOrCwd(); +sub diffFromToString(); +sub diffCommand(@); +sub statusCommand(@); +sub createPatchCommand($); +sub diffHeaderFormat(); +sub findOriginalFileFromSvn($); +sub generateFileList(\@\@\%); +sub gitConfig($); +sub isModifiedStatus($); +sub isAddedStatus($); +sub isConflictStatus($); +sub statusDescription($$); +sub extractLineRange($); +sub canonicalizePath($); +sub testListForChangeLog(@); +sub get_function_line_ranges($$); +sub get_function_line_ranges_for_c($$); +sub get_function_line_ranges_for_java($$); +sub get_function_line_ranges_for_javascript($$); +sub method_decl_to_selector($); +sub processPaths(\@); +sub reviewerAndDescriptionForGitCommit($); + +# Project time zone for Cupertino, CA, US +my $changeLogTimeZone = "PST8PDT"; + +my $gitCommit = 0; +my $gitReviewer = ""; +my $openChangeLogs = 0; +my $showHelp = 0; +my $spewDiff = $ENV{"PREPARE_CHANGELOG_DIFF"}; +my $updateChangeLogs = 1; +my $parseOptionsResult = + GetOptions("diff|d!" => \$spewDiff, + "git-commit:s" => \$gitCommit, + "git-reviewer:s" => \$gitReviewer, + "help|h!" => \$showHelp, + "open|o!" => \$openChangeLogs, + "update!" => \$updateChangeLogs); +if (!$parseOptionsResult || $showHelp) { + print STDERR basename($0) . " [-d|--diff] [-h|--help] [-o|--open] [--git-commit=<committish>] [--git-reviewer=<name>] [svndir1 [svndir2 ...]]\n"; + print STDERR " -d|--diff Spew diff to stdout when running\n"; + print STDERR " --git-commit Populate the ChangeLogs from the specified git commit\n"; + print STDERR " --git-reviewer When populating the ChangeLogs from a git commit claim that the spcified name reviewed the change.\n"; + print STDERR " This option is useful when the git commit lacks a Signed-Off-By: line\n"; + print STDERR " -h|--help Show this help message\n"; + print STDERR " -o|--open Open ChangeLogs in an editor when done\n"; + print STDERR " --[no-]update Update ChangeLogs from svn before adding entry (default: update)\n"; + exit 1; +} + +my %paths = processPaths(@ARGV); + +my $isGit = isGitDirectory(firstDirectoryOrCwd()); +my $isSVN = isSVNDirectory(firstDirectoryOrCwd()); + +$isSVN || $isGit || die "Couldn't determine your version control system."; + +# Find the list of modified files +my @changed_files; +my $changed_files_string; +my %changed_line_ranges; +my %function_lists; +my @conflict_files; + +my $SVN = "svn"; +my $GIT = "git"; + +my %supportedTestExtensions = map { $_ => 1 } qw(html shtml svg xml xhtml pl php); +my @addedRegressionTests = (); +my $didChangeRegressionTests = 0; + +generateFileList(@changed_files, @conflict_files, %function_lists); + +if (!@changed_files && !@conflict_files && !keys %function_lists) { + print STDERR " No changes found.\n"; + exit 1; +} + +if (@conflict_files) { + print STDERR " The following files have conflicts. Run prepare-ChangeLog again after fixing the conflicts:\n"; + print STDERR join("\n", @conflict_files), "\n"; + exit 1; +} + +if (@changed_files) { + $changed_files_string = "'" . join ("' '", @changed_files) . "'"; + + # For each file, build a list of modified lines. + # Use line numbers from the "after" side of each diff. + print STDERR " Reviewing diff to determine which lines changed.\n"; + my $file; + open DIFF, "-|", diffCommand(@changed_files) or die "The diff failed: $!.\n"; + while (<DIFF>) { + $file = makeFilePathRelative($1) if $_ =~ diffHeaderFormat(); + if (defined $file) { + my ($start, $end) = extractLineRange($_); + if ($start >= 0 && $end >= 0) { + push @{$changed_line_ranges{$file}}, [ $start, $end ]; + } elsif (/DO_NOT_COMMIT/) { + print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n"; + } + } + } + close DIFF; +} + +# For each source file, convert line range to function list. +if (%changed_line_ranges) { + print STDERR " Extracting affected function names from source files.\n"; + foreach my $file (keys %changed_line_ranges) { + # Only look for function names in certain source files. + next unless $file =~ /\.(c|cpp|m|mm|h|java|js)/; + + # Find all the functions in the file. + open SOURCE, $file or next; + my @function_ranges = get_function_line_ranges(\*SOURCE, $file); + close SOURCE; + + # Find all the modified functions. + my @functions; + my %saw_function; + my @change_ranges = (@{$changed_line_ranges{$file}}, []); + my @change_range = (0, 0); + FUNCTION: foreach my $function_range_ref (@function_ranges) { + my @function_range = @$function_range_ref; + + # Advance to successive change ranges. + for (;; @change_range = @{shift @change_ranges}) { + last FUNCTION unless @change_range; + + # If past this function, move on to the next one. + next FUNCTION if $change_range[0] > $function_range[1]; + + # If an overlap with this function range, record the function name. + if ($change_range[1] >= $function_range[0] + and $change_range[0] <= $function_range[1]) { + if (!$saw_function{$function_range[2]}) { + $saw_function{$function_range[2]} = 1; + push @functions, $function_range[2]; + } + next FUNCTION; + } + } + } + + # Format the list of functions now. + + if (@functions) { + $function_lists{$file} = "" if !defined $function_lists{$file}; + $function_lists{$file} .= "\n (" . join("):\n (", @functions) . "):"; + } + } +} + +# Get some parameters for the ChangeLog we are about to write. +my $date = changeLogDate($changeLogTimeZone); +my $name = $ENV{CHANGE_LOG_NAME} + || $ENV{REAL_NAME} + || gitConfig("user.name") + || (split /\s*,\s*/, (getpwuid $<)[6])[0] + || "set REAL_NAME environment variable"; +my $email_address = $ENV{CHANGE_LOG_EMAIL_ADDRESS} + || $ENV{EMAIL_ADDRESS} + || gitConfig("user.email") + || "set EMAIL_ADDRESS environment variable"; + +if ($gitCommit) { + $name = `$GIT log --max-count=1 --pretty=\"format:%an\" \"$gitCommit\"`; + $email_address = `$GIT log --max-count=1 --pretty=\"format:%ae\" \"$gitCommit\"`; +} + +# Remove trailing parenthesized notes from user name (bit of hack). +$name =~ s/\(.*?\)\s*$//g; + +# Find the change logs. +my %has_log; +my %files; +foreach my $file (sort keys %function_lists) { + my $prefix = $file; + my $has_log = 0; + while ($prefix) { + $prefix =~ s-/[^/]+/?$-/- or $prefix = ""; + $has_log = $has_log{$prefix}; + if (!defined $has_log) { + $has_log = -f "${prefix}ChangeLog"; + $has_log{$prefix} = $has_log; + } + last if $has_log; + } + if (!$has_log) { + print STDERR "No ChangeLog found for $file.\n"; + } else { + push @{$files{$prefix}}, $file; + } +} + +# Get the latest ChangeLog files from svn. +my @logs = (); +foreach my $prefix (sort keys %files) { + push @logs, File::Spec->catfile($prefix || ".", "ChangeLog"); +} + +if (@logs && $updateChangeLogs && $isSVN) { + print STDERR " Running 'svn update' to update ChangeLog files.\n"; + open ERRORS, "-|", $SVN, "update", @logs + or die "The svn update of ChangeLog files failed: $!.\n"; + my @conflictedChangeLogs; + while (my $line = <ERRORS>) { + print STDERR " ", $line; + push @conflictedChangeLogs, $1 if $line =~ m/^C\s+(.+)\s*$/; + } + close ERRORS; + + if (@conflictedChangeLogs) { + print STDERR " Attempting to merge conflicted ChangeLogs.\n"; + my $resolveChangeLogsPath = File::Spec->catfile(dirname($0), "resolve-ChangeLogs"); + open RESOLVE, "-|", $resolveChangeLogsPath, "--no-warnings", @conflictedChangeLogs + or die "Could not open resolve-ChangeLogs script: $!.\n"; + print STDERR " $_" while <RESOLVE>; + close RESOLVE; + } +} + +# Write out a new ChangeLog file. +foreach my $prefix (sort keys %files) { + my $changeLogPath = File::Spec->catfile($prefix || ".", "ChangeLog"); + print STDERR " Editing the ${changeLogPath} file.\n"; + open OLD_CHANGE_LOG, ${changeLogPath} or die "Could not open ${changeLogPath} file: $!.\n"; + # It's less efficient to read the whole thing into memory than it would be + # to read it while we prepend to it later, but I like doing this part first. + my @old_change_log = <OLD_CHANGE_LOG>; + close OLD_CHANGE_LOG; + open CHANGE_LOG, "> ${changeLogPath}" or die "Could not write ${changeLogPath}\n."; + print CHANGE_LOG "$date $name <$email_address>\n\n"; + + my ($reviewer, $description) = reviewerAndDescriptionForGitCommit($gitCommit) if $gitCommit; + $reviewer = "NOBODY (OO" . "PS!)" if !$reviewer; + + print CHANGE_LOG " Reviewed by $reviewer.\n\n"; + print CHANGE_LOG $description . "\n" if $description; + + if ($prefix =~ m/WebCore/ || `pwd` =~ m/WebCore/) { + if ($didChangeRegressionTests) { + print CHANGE_LOG testListForChangeLog(sort @addedRegressionTests); + } else { + print CHANGE_LOG " WARNING: NO TEST CASES ADDED OR CHANGED\n\n"; + } + } + + foreach my $file (sort @{$files{$prefix}}) { + my $file_stem = substr $file, length $prefix; + print CHANGE_LOG " * $file_stem:$function_lists{$file}\n"; + } + print CHANGE_LOG "\n", @old_change_log; + close CHANGE_LOG; +} + +# Write out another diff. +if ($spewDiff && @changed_files) { + print STDERR " Running diff to help you write the ChangeLog entries.\n"; + local $/ = undef; # local slurp mode + open DIFF, "-|", createPatchCommand($changed_files_string) or die "The diff failed: $!.\n"; + print <DIFF>; + close DIFF; +} + +# Open ChangeLogs. +if ($openChangeLogs && @logs) { + print STDERR " Opening the edited ChangeLog files.\n"; + my $editor = $ENV{"CHANGE_LOG_EDIT_APPLICATION"}; + if ($editor) { + system "open", "-a", $editor, @logs; + } else { + system "open", "-e", @logs; + } +} + +# Done. +exit; + +sub canonicalizePath($) +{ + my ($file) = @_; + + # Remove extra slashes and '.' directories in path + $file = File::Spec->canonpath($file); + + # Remove '..' directories in path + my @dirs = (); + foreach my $dir (File::Spec->splitdir($file)) { + if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..') { + pop(@dirs); + } else { + push(@dirs, $dir); + } + } + return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : "."; +} + +sub changeLogDate($) +{ + my ($timeZone) = @_; + my $savedTimeZone = $ENV{'TZ'}; + # Set TZ temporarily so that localtime() is in that time zone + $ENV{'TZ'} = $timeZone; + my $date = strftime("%Y-%m-%d", localtime()); + if (defined $savedTimeZone) { + $ENV{'TZ'} = $savedTimeZone; + } else { + delete $ENV{'TZ'}; + } + return $date; +} + +sub get_function_line_ranges($$) +{ + my ($file_handle, $file_name) = @_; + + if ($file_name =~ /\.(c|cpp|m|mm|h)$/) { + return get_function_line_ranges_for_c ($file_handle, $file_name); + } elsif ($file_name =~ /\.java$/) { + return get_function_line_ranges_for_java ($file_handle, $file_name); + } elsif ($file_name =~ /\.js$/) { + return get_function_line_ranges_for_javascript ($file_handle, $file_name); + } + return (); +} + + +sub method_decl_to_selector($) +{ + (my $method_decl) = @_; + + $_ = $method_decl; + + if ((my $comment_stripped) = m-([^/]*)(//|/*).*-) { + $_ = $comment_stripped; + } + + s/,\s*...//; + + if (/:/) { + my @components = split /:/; + pop @components if (scalar @components > 1); + $_ = (join ':', map {s/.*[^[:word:]]//; scalar $_;} @components) . ':'; + } else { + s/\s*$//; + s/.*[^[:word:]]//; + } + + return $_; +} + + + +# Read a file and get all the line ranges of the things that look like C functions. +# A function name is the last word before an open parenthesis before the outer +# level open brace. A function starts at the first character after the last close +# brace or semicolon before the function name and ends at the close brace. +# Comment handling is simple-minded but will work for all but pathological cases. +# +# Result is a list of triples: [ start_line, end_line, function_name ]. + +sub get_function_line_ranges_for_c($$) +{ + my ($file_handle, $file_name) = @_; + + my @ranges; + + my $in_comment = 0; + my $in_macro = 0; + my $in_method_declaration = 0; + my $in_parentheses = 0; + my $in_braces = 0; + my $brace_start = 0; + my $brace_end = 0; + my $skip_til_brace_or_semicolon = 0; + + my $word = ""; + my $interface_name = ""; + + my $potential_method_char = ""; + my $potential_method_spec = ""; + + my $potential_start = 0; + my $potential_name = ""; + + my $start = 0; + my $name = ""; + + my $next_word_could_be_namespace = 0; + my $potential_namespace = ""; + my @namespaces; + + while (<$file_handle>) { + # Handle continued multi-line comment. + if ($in_comment) { + next unless s-.*\*/--; + $in_comment = 0; + } + + # Handle continued macro. + if ($in_macro) { + $in_macro = 0 unless /\\$/; + next; + } + + # Handle start of macro (or any preprocessor directive). + if (/^\s*\#/) { + $in_macro = 1 if /^([^\\]|\\.)*\\$/; + next; + } + + # Handle comments and quoted text. + while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy + my $match = $1; + if ($match eq "/*") { + if (!s-/\*.*?\*/--) { + s-/\*.*--; + $in_comment = 1; + } + } elsif ($match eq "//") { + s-//.*--; + } else { # ' or " + if (!s-$match([^\\]|\\.)*?$match--) { + warn "mismatched quotes at line $. in $file_name\n"; + s-$match.*--; + } + } + } + + + # continued method declaration + if ($in_method_declaration) { + my $original = $_; + my $method_cont = $_; + + chomp $method_cont; + $method_cont =~ s/[;\{].*//; + $potential_method_spec = "${potential_method_spec} ${method_cont}"; + + $_ = $original; + if (/;/) { + $potential_start = 0; + $potential_method_spec = ""; + $potential_method_char = ""; + $in_method_declaration = 0; + s/^[^;\{]*//; + } elsif (/{/) { + my $selector = method_decl_to_selector ($potential_method_spec); + $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]"; + + $potential_method_spec = ""; + $potential_method_char = ""; + $in_method_declaration = 0; + + $_ = $original; + s/^[^;{]*//; + } elsif (/\@end/) { + $in_method_declaration = 0; + $interface_name = ""; + $_ = $original; + } else { + next; + } + } + + + # start of method declaration + if ((my $method_char, my $method_spec) = m&^([-+])([^0-9;][^;]*);?$&) { + my $original = $_; + + if ($interface_name) { + chomp $method_spec; + $method_spec =~ s/\{.*//; + + $potential_method_char = $method_char; + $potential_method_spec = $method_spec; + $potential_start = $.; + $in_method_declaration = 1; + } else { + warn "declaring a method but don't have interface on line $. in $file_name\n"; + } + $_ = $original; + if (/\{/) { + my $selector = method_decl_to_selector ($potential_method_spec); + $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]"; + + $potential_method_spec = ""; + $potential_method_char = ""; + $in_method_declaration = 0; + $_ = $original; + s/^[^{]*//; + } elsif (/\@end/) { + $in_method_declaration = 0; + $interface_name = ""; + $_ = $original; + } else { + next; + } + } + + + # Find function, interface and method names. + while (m&((?:[[:word:]]+::)*operator(?:[ \t]*\(\)|[^()]*)|[[:word:]:~]+|[(){}:;])|\@(?:implementation|interface|protocol)\s+(\w+)[^{]*&g) { + # interface name + if ($2) { + $interface_name = $2; + next; + } + + # Open parenthesis. + if ($1 eq "(") { + $potential_name = $word unless $in_parentheses || $skip_til_brace_or_semicolon; + $in_parentheses++; + next; + } + + # Close parenthesis. + if ($1 eq ")") { + $in_parentheses--; + next; + } + + # C++ constructor initializers + if ($1 eq ":") { + $skip_til_brace_or_semicolon = 1 unless ($in_parentheses || $in_braces); + } + + # Open brace. + if ($1 eq "{") { + $skip_til_brace_or_semicolon = 0; + + if ($potential_namespace) { + push @namespaces, $potential_namespace; + $potential_namespace = ""; + next; + } + + # Promote potential name to real function name at the + # start of the outer level set of braces (function body?). + if (!$in_braces and $potential_start) { + $start = $potential_start; + $name = $potential_name; + if (@namespaces && (length($name) < 2 || substr($name,1,1) ne "[")) { + $name = join ('::', @namespaces, $name); + } + } + + $in_method_declaration = 0; + + $brace_start = $. if (!$in_braces); + $in_braces++; + next; + } + + # Close brace. + if ($1 eq "}") { + if (!$in_braces && @namespaces) { + pop @namespaces; + next; + } + + $in_braces--; + $brace_end = $. if (!$in_braces); + + # End of an outer level set of braces. + # This could be a function body. + if (!$in_braces and $name) { + push @ranges, [ $start, $., $name ]; + $name = ""; + } + + $potential_start = 0; + $potential_name = ""; + next; + } + + # Semicolon. + if ($1 eq ";") { + $skip_til_brace_or_semicolon = 0; + $potential_start = 0; + $potential_name = ""; + $in_method_declaration = 0; + next; + } + + # Ignore "const" method qualifier. + if ($1 eq "const") { + next; + } + + if ($1 eq "namespace" || $1 eq "class" || $1 eq "struct") { + $next_word_could_be_namespace = 1; + next; + } + + # Word. + $word = $1; + if (!$skip_til_brace_or_semicolon) { + if ($next_word_could_be_namespace) { + $potential_namespace = $word; + $next_word_could_be_namespace = 0; + } elsif ($potential_namespace) { + $potential_namespace = ""; + } + + if (!$in_parentheses) { + $potential_start = 0; + $potential_name = ""; + } + if (!$potential_start) { + $potential_start = $.; + $potential_name = ""; + } + } + } + } + + warn "missing close braces in $file_name (probable start at $brace_start)\n" if ($in_braces > 0); + warn "too many close braces in $file_name (probable start at $brace_end)\n" if ($in_braces < 0); + + warn "mismatched parentheses in $file_name\n" if $in_parentheses; + + return @ranges; +} + + + +# Read a file and get all the line ranges of the things that look like Java +# classes, interfaces and methods. +# +# A class or interface name is the word that immediately follows +# `class' or `interface' when followed by an open curly brace and not +# a semicolon. It can appear at the top level, or inside another class +# or interface block, but not inside a function block +# +# A class or interface starts at the first character after the first close +# brace or after the function name and ends at the close brace. +# +# A function name is the last word before an open parenthesis before +# an open brace rather than a semicolon. It can appear at top level or +# inside a class or interface block, but not inside a function block. +# +# A function starts at the first character after the first close +# brace or after the function name and ends at the close brace. +# +# Comment handling is simple-minded but will work for all but pathological cases. +# +# Result is a list of triples: [ start_line, end_line, function_name ]. + +sub get_function_line_ranges_for_java($$) +{ + my ($file_handle, $file_name) = @_; + + my @current_scopes; + + my @ranges; + + my $in_comment = 0; + my $in_macro = 0; + my $in_parentheses = 0; + my $in_braces = 0; + my $in_non_block_braces = 0; + my $class_or_interface_just_seen = 0; + + my $word = ""; + + my $potential_start = 0; + my $potential_name = ""; + my $potential_name_is_class_or_interface = 0; + + my $start = 0; + my $name = ""; + my $current_name_is_class_or_interface = 0; + + while (<$file_handle>) { + # Handle continued multi-line comment. + if ($in_comment) { + next unless s-.*\*/--; + $in_comment = 0; + } + + # Handle continued macro. + if ($in_macro) { + $in_macro = 0 unless /\\$/; + next; + } + + # Handle start of macro (or any preprocessor directive). + if (/^\s*\#/) { + $in_macro = 1 if /^([^\\]|\\.)*\\$/; + next; + } + + # Handle comments and quoted text. + while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy + my $match = $1; + if ($match eq "/*") { + if (!s-/\*.*?\*/--) { + s-/\*.*--; + $in_comment = 1; + } + } elsif ($match eq "//") { + s-//.*--; + } else { # ' or " + if (!s-$match([^\\]|\\.)*?$match--) { + warn "mismatched quotes at line $. in $file_name\n"; + s-$match.*--; + } + } + } + + # Find function names. + while (m-(\w+|[(){};])-g) { + # Open parenthesis. + if ($1 eq "(") { + if (!$in_parentheses) { + $potential_name = $word; + $potential_name_is_class_or_interface = 0; + } + $in_parentheses++; + next; + } + + # Close parenthesis. + if ($1 eq ")") { + $in_parentheses--; + next; + } + + # Open brace. + if ($1 eq "{") { + # Promote potential name to real function name at the + # start of the outer level set of braces (function/class/interface body?). + if (!$in_non_block_braces + and (!$in_braces or $current_name_is_class_or_interface) + and $potential_start) { + if ($name) { + push @ranges, [ $start, ($. - 1), + join ('.', @current_scopes) ]; + } + + + $current_name_is_class_or_interface = $potential_name_is_class_or_interface; + + $start = $potential_start; + $name = $potential_name; + + push (@current_scopes, $name); + } else { + $in_non_block_braces++; + } + + $potential_name = ""; + $potential_start = 0; + + $in_braces++; + next; + } + + # Close brace. + if ($1 eq "}") { + $in_braces--; + + # End of an outer level set of braces. + # This could be a function body. + if (!$in_non_block_braces) { + if ($name) { + push @ranges, [ $start, $., + join ('.', @current_scopes) ]; + + pop (@current_scopes); + + if (@current_scopes) { + $current_name_is_class_or_interface = 1; + + $start = $. + 1; + $name = $current_scopes[$#current_scopes-1]; + } else { + $current_name_is_class_or_interface = 0; + $start = 0; + $name = ""; + } + } + } else { + $in_non_block_braces-- if $in_non_block_braces; + } + + $potential_start = 0; + $potential_name = ""; + next; + } + + # Semicolon. + if ($1 eq ";") { + $potential_start = 0; + $potential_name = ""; + next; + } + + if ($1 eq "class" or $1 eq "interface") { + $class_or_interface_just_seen = 1; + next; + } + + # Word. + $word = $1; + if (!$in_parentheses) { + if ($class_or_interface_just_seen) { + $potential_name = $word; + $potential_start = $.; + $class_or_interface_just_seen = 0; + $potential_name_is_class_or_interface = 1; + next; + } + } + if (!$potential_start) { + $potential_start = $.; + $potential_name = ""; + } + $class_or_interface_just_seen = 0; + } + } + + warn "mismatched braces in $file_name\n" if $in_braces; + warn "mismatched parentheses in $file_name\n" if $in_parentheses; + + return @ranges; +} + + + +# Read a file and get all the line ranges of the things that look like +# JavaScript functions. +# +# A function name is the word that immediately follows `function' when +# followed by an open curly brace. It can appear at the top level, or +# inside other functions. +# +# An anonymous function name is the identifier chain immediately before +# an assignment with the equals operator or object notation that has a +# value starting with `function' followed by an open curly brace. +# +# A getter or setter name is the word that immediately follows `get' or +# `set' when followed by an open curly brace . +# +# Comment handling is simple-minded but will work for all but pathological cases. +# +# Result is a list of triples: [ start_line, end_line, function_name ]. + +sub get_function_line_ranges_for_javascript($$) +{ + my ($fileHandle, $fileName) = @_; + + my @currentScopes; + my @currentIdentifiers; + my @currentFunctionNames; + my @currentFunctionDepths; + my @currentFunctionStartLines; + + my @ranges; + + my $inComment = 0; + my $parenthesesDepth = 0; + my $bracesDepth = 0; + + my $functionJustSeen = 0; + my $getterJustSeen = 0; + my $setterJustSeen = 0; + my $assignmentJustSeen = 0; + + my $word = ""; + + while (<$fileHandle>) { + # Handle continued multi-line comment. + if ($inComment) { + next unless s-.*\*/--; + $inComment = 0; + } + + # Handle comments and quoted text. + while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy + my $match = $1; + if ($match eq '/*') { + if (!s-/\*.*?\*/--) { + s-/\*.*--; + $inComment = 1; + } + } elsif ($match eq '//') { + s-//.*--; + } else { # ' or " + if (!s-$match([^\\]|\\.)*?$match--) { + warn "mismatched quotes at line $. in $fileName\n"; + s-$match.*--; + } + } + } + + # Find function names. + while (m-(\w+|[(){}=:;])-g) { + # Open parenthesis. + if ($1 eq '(') { + $parenthesesDepth++; + next; + } + + # Close parenthesis. + if ($1 eq ')') { + $parenthesesDepth--; + next; + } + + # Open brace. + if ($1 eq '{') { + push(@currentScopes, join(".", @currentIdentifiers)); + @currentIdentifiers = (); + + $bracesDepth++; + next; + } + + # Close brace. + if ($1 eq '}') { + $bracesDepth--; + + if (@currentFunctionDepths and $bracesDepth == $currentFunctionDepths[$#currentFunctionDepths]) { + pop(@currentFunctionDepths); + + my $currentFunction = pop(@currentFunctionNames); + my $start = pop(@currentFunctionStartLines); + + push(@ranges, [$start, $., $currentFunction]); + } + + pop(@currentScopes); + @currentIdentifiers = (); + + next; + } + + # Semicolon. + if ($1 eq ';') { + @currentIdentifiers = (); + next; + } + + # Function. + if ($1 eq 'function') { + $functionJustSeen = 1; + + if ($assignmentJustSeen) { + my $currentFunction = join('.', (@currentScopes, @currentIdentifiers)); + $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods. + + push(@currentFunctionNames, $currentFunction); + push(@currentFunctionDepths, $bracesDepth); + push(@currentFunctionStartLines, $.); + } + + next; + } + + # Getter prefix. + if ($1 eq 'get') { + $getterJustSeen = 1; + next; + } + + # Setter prefix. + if ($1 eq 'set') { + $setterJustSeen = 1; + next; + } + + # Assignment operator. + if ($1 eq '=' or $1 eq ':') { + $assignmentJustSeen = 1; + next; + } + + next if $parenthesesDepth; + + # Word. + $word = $1; + $word = "get $word" if $getterJustSeen; + $word = "set $word" if $setterJustSeen; + + if (($functionJustSeen and !$assignmentJustSeen) or $getterJustSeen or $setterJustSeen) { + push(@currentIdentifiers, $word); + + my $currentFunction = join('.', (@currentScopes, @currentIdentifiers)); + $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods. + + push(@currentFunctionNames, $currentFunction); + push(@currentFunctionDepths, $bracesDepth); + push(@currentFunctionStartLines, $.); + } elsif ($word ne 'if' and $word ne 'for' and $word ne 'do' and $word ne 'while' and $word ne 'which' and $word ne 'var') { + push(@currentIdentifiers, $word); + } + + $functionJustSeen = 0; + $getterJustSeen = 0; + $setterJustSeen = 0; + $assignmentJustSeen = 0; + } + } + + warn "mismatched braces in $fileName\n" if $bracesDepth; + warn "mismatched parentheses in $fileName\n" if $parenthesesDepth; + + return @ranges; +} + + +sub processPaths(\@) +{ + my ($paths) = @_; + return ("." => 1) if (!@{$paths}); + + my %result = (); + + for my $file (@{$paths}) { + die "can't handle absolute paths like \"$file\"\n" if File::Spec->file_name_is_absolute($file); + die "can't handle empty string path\n" if $file eq ""; + die "can't handle path with single quote in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy) + + my $untouchedFile = $file; + + $file = canonicalizePath($file); + + die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|; + + $result{$file} = 1; + } + + return ("." => 1) if ($result{"."}); + + # Remove any paths that also have a parent listed. + for my $path (keys %result) { + for (my $parent = dirname($path); $parent ne '.'; $parent = dirname($parent)) { + if ($result{$parent}) { + delete $result{$path}; + last; + } + } + } + + return %result; +} + +sub diffFromToString() +{ + return "" if $isSVN; + return $gitCommit if $gitCommit =~ m/.+\.\..+/; + return "\"$gitCommit^\" \"$gitCommit\"" if $gitCommit; + return "HEAD" if $isGit; +} + +sub diffCommand(@) +{ + my @paths = @_; + + my $pathsString = "'" . join("' '", @paths) . "'"; + + my $command; + if ($isSVN) { + $command = "$SVN diff --diff-cmd diff -x -N $pathsString"; + } elsif ($isGit) { + $command = "$GIT diff -U0 " . diffFromToString(); + $command .= " -- $pathsString" unless $gitCommit; + } + + return $command; +} + +sub statusCommand(@) +{ + my @files = @_; + + my $filesString = "'" . join ("' '", @files) . "'"; + my $command; + if ($isSVN) { + $command = "$SVN stat $filesString"; + } elsif ($isGit) { + $command = "$GIT diff -r --name-status -C -C -M " . diffFromToString(); + $command .= " -- $filesString" unless $gitCommit; + } + + return "$command 2>&1"; +} + +sub createPatchCommand($) +{ + my ($changedFilesString) = @_; + + my $command; + if ($isSVN) { + $command = "'$FindBin::Bin/svn-create-patch' $changedFilesString"; + } elsif ($isGit) { + $command = "$GIT diff -C -C -M " . diffFromToString(); + $command .= " -- $changedFilesString" unless $gitCommit; + } + + return $command; +} + +sub diffHeaderFormat() +{ + return qr/^Index: (\S+)$/ if $isSVN; + return qr/^diff --git a\/.+ b\/(.+)$/ if $isGit; +} + +sub findOriginalFileFromSvn($) +{ + my ($file) = @_; + my $baseUrl; + open INFO, "$SVN info . |" or die; + while (<INFO>) { + if (/^URL: (.+)/) { + $baseUrl = $1; + last; + } + } + close INFO; + my $sourceFile; + open INFO, "$SVN info '$file' |" or die; + while (<INFO>) { + if (/^Copied From URL: (.+)/) { + $sourceFile = File::Spec->abs2rel($1, $baseUrl); + last; + } + } + close INFO; + return $sourceFile; +} + +sub generateFileList(\@\@\%) +{ + my ($changedFiles, $conflictFiles, $functionLists) = @_; + print STDERR " Running status to find changed, added, or removed files.\n"; + open STAT, "-|", statusCommand(keys %paths) or die "The status failed: $!.\n"; + my $inGitCommitSection = 0; + while (<STAT>) { + my $status; + my $original; + my $file; + + if ($isSVN) { + if (/^([ACDMR]).{5} (.+)$/) { + $status = $1; + $file = $2; + $original = findOriginalFileFromSvn($file) if substr($_, 3, 1) eq "+"; + } else { + print; # error output from svn stat + } + } elsif ($isGit) { + if (/^([ADM])\t(.+)$/) { + $status = $1; + $file = $2; + } elsif (/^([CR])[0-9]{1,3}\t([^\t]+)\t([^\t\n]+)$/) { # for example: R90% newfile oldfile + $status = $1; + $original = $2; + $file = $3; + } else { + print; # error output from git diff + } + } + + next unless $status; + + $file = makeFilePathRelative($file); + + if (isModifiedStatus($status) || isAddedStatus($status)) { + my @components = File::Spec->splitdir($file); + if ($components[0] eq "LayoutTests") { + $didChangeRegressionTests = 1; + push @addedRegressionTests, $file + if isAddedStatus($status) + && $file =~ /\.([a-zA-Z]+)$/ + && $supportedTestExtensions{lc($1)} + && !scalar(grep(/^resources$/i, @components)); + } + push @{$changedFiles}, $file if $components[$#components] ne "ChangeLog"; + } elsif (isConflictStatus($status)) { + push @{$conflictFiles}, $file; + } + if (basename($file) ne "ChangeLog") { + my $description = statusDescription($status, $original); + $functionLists->{$file} = $description if defined $description; + } + } + close STAT; +} + +sub gitConfig($) +{ + return unless $isGit; + + my ($config) = @_; + + my $result = `$GIT config $config`; + if (($? >> 8) != 0) { + $result = `$GIT repo-config $config`; + } + chomp $result; + return $result; +} + +sub isModifiedStatus($) +{ + my ($status) = @_; + + my %statusCodes = ( + "M" => 1, + ); + + return $statusCodes{$status}; +} + +sub isAddedStatus($) +{ + my ($status) = @_; + + my %statusCodes = ( + "A" => 1, + "C" => $isGit, + "R" => 1, + ); + + return $statusCodes{$status}; +} + +sub isConflictStatus($) +{ + my ($status) = @_; + + my %svn = ( + "C" => 1, + ); + + my %git = ( + "U" => 1, + ); + + return 0 if $gitCommit; # an existing commit cannot have conflicts + return $svn{$status} if $isSVN; + return $git{$status} if $isGit; +} + +sub statusDescription($$) +{ + my ($status, $original) = @_; + + my %svn = ( + "A" => defined $original ? " Copied from \%s." : " Added.", + "D" => " Removed.", + "M" => "", + "R" => defined $original ? " Replaced with \%s." : " Replaced.", + ); + + my %git = %svn; + $git{"A"} = " Added."; + $git{"C"} = " Copied from \%s."; + $git{"R"} = " Renamed from \%s."; + + return sprintf($svn{$status}, $original) if $isSVN && exists $svn{$status}; + return sprintf($git{$status}, $original) if $isGit && exists $git{$status}; + return undef; +} + +sub extractLineRange($) +{ + my ($string) = @_; + + my ($start, $end) = (-1, -1); + + if ($isSVN && $string =~ /^\d+(,\d+)?[acd](\d+)(,(\d+))?/) { + $start = $2; + $end = $4 || $2; + } elsif ($isGit && $string =~ /^@@ -\d+(,\d+)? \+(\d+)(,(\d+))? @@/) { + $start = $2; + $end = defined($4) ? $4 + $2 - 1 : $2; + } + + return ($start, $end); +} + +sub firstDirectoryOrCwd() +{ + my $dir = "."; + my @dirs = keys(%paths); + + $dir = -d $dirs[0] ? $dirs[0] : dirname($dirs[0]) if @dirs; + + return $dir; +} + +sub testListForChangeLog(@) +{ + my (@tests) = @_; + + return "" unless @tests; + + my $leadString = " Test" . (@tests == 1 ? "" : "s") . ": "; + my $list = $leadString; + foreach my $i (0..$#tests) { + $list .= " " x length($leadString) if $i; + my $test = $tests[$i]; + $test =~ s/^LayoutTests\///; + $list .= "$test\n"; + } + $list .= "\n"; + + return $list; +} + +sub reviewerAndDescriptionForGitCommit($) +{ + my ($commit) = @_; + + my $description = ''; + my $reviewer; + + my @args = qw(rev-list --pretty); + push @args, '-1' if $commit !~ m/.+\.\..+/; + my $gitLog; + { + local $/ = undef; + open(GIT, "-|", $GIT, @args, $commit) || die; + $gitLog = <GIT>; + close(GIT); + } + + my @commitLogs = split(/^[Cc]ommit [a-f0-9]{40}/m, $gitLog); + shift @commitLogs; # Remove initial blank commit log + my $commitLogCount = 0; + foreach my $commitLog (@commitLogs) { + $description .= "\n" if $commitLogCount; + $commitLogCount++; + my $inHeader = 1; + my @lines = split(/\n/, $commitLog); + shift @lines; # Remove initial blank line + foreach my $line (@lines) { + if ($inHeader) { + if (!$line) { + $inHeader = 0; + } + next; + } elsif ($line =~ /[Ss]igned-[Oo]ff-[Bb]y: (.+)/) { + if (!$reviewer) { + $reviewer = $1; + } else { + $reviewer .= ", " . $1; + } + } elsif (length $line == 0) { + $description = $description . "\n"; + } else { + $line =~ s/^\s*//; + $description = $description . " " . $line . "\n"; + } + } + } + if (!$reviewer) { + $reviewer = $gitReviewer; + } + + return ($reviewer, $description); +} diff --git a/WebKitTools/Scripts/print-msvc-project-dependencies b/WebKitTools/Scripts/print-msvc-project-dependencies new file mode 100755 index 0000000..dbc8402 --- /dev/null +++ b/WebKitTools/Scripts/print-msvc-project-dependencies @@ -0,0 +1,143 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2008 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. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR +# 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. + +use strict; +use File::Basename; + +sub printDependencyTree($); + +my $basename = basename($0); +@ARGV or die "Usage: $basename sln1 [sln2 sln3...]"; + +foreach my $sln (@ARGV) { + printDependencyTree($sln); +} + +exit; + +sub printDependencyTree($) +{ + my ($sln) = @_; + + unless (-f $sln) { + warn "Warning: Can't find $sln; skipping\n"; + return; + } + + unless (open SLN, "<", $sln) { + warn "Warning: Can't open $sln; skipping\n"; + return; + } + + my %projectsByUUID = (); + my $currentProject; + + my $state = "initial"; + foreach my $line (<SLN>) { + if ($state eq "initial") { + if ($line =~ /^Project\([^\)]+\) = "([^"]+)", "[^"]+", "([^"]+)"\r?$/) { + my $name = $1; + my $uuid = $2; + if (exists $projectsByUUID{$uuid}) { + warn "Warning: Project $name appears more than once in $sln; using first definition\n"; + next; + } + $currentProject = { + name => $name, + uuid => $uuid, + dependencies => {}, + }; + $projectsByUUID{$uuid} = $currentProject; + + $state = "inProject"; + } + + next; + } + + if ($state eq "inProject") { + defined($currentProject) or die; + + if ($line =~ /^\s*ProjectSection\(ProjectDependencies\) = postProject\r?$/) { + $state = "inDependencies"; + } elsif ($line =~ /^EndProject\r?$/) { + $currentProject = undef; + $state = "initial"; + } + + next; + } + + if ($state eq "inDependencies") { + defined($currentProject) or die; + + if ($line =~ /^\s*({[^}]+}) = ({[^}]+})\r?$/) { + my $uuid1 = $1; + my $uuid2 = $2; + if (exists $currentProject->{dependencies}->{$uuid1}) { + warn "Warning: UUID $uuid1 listed more than once as dependency of project ", $currentProject->{name}, "\n"; + next; + } + + $uuid1 eq $uuid2 or warn "Warning: UUIDs in depedency section of project ", $currentProject->{name}, " don't match: $uuid1 $uuid2; using first UUID\n"; + + $currentProject->{dependencies}->{$uuid1} = 1; + } elsif ($line =~ /^\s*EndProjectSection\r?$/) { + $state = "inProject"; + } + + next; + } + } + + close SLN or warn "Warning: Can't close $sln\n"; + + my %projectsNotDependedUpon = %projectsByUUID; + CANDIDATE: foreach my $candidateUUID (keys %projectsByUUID) { + foreach my $projectUUID (keys %projectsByUUID) { + next if $candidateUUID eq $projectUUID; + foreach my $dependencyUUID (keys %{$projectsByUUID{$projectUUID}->{dependencies}}) { + if ($candidateUUID eq $dependencyUUID) { + delete $projectsNotDependedUpon{$candidateUUID}; + next CANDIDATE; + } + } + } + } + + foreach my $project (values %projectsNotDependedUpon) { + printProjectAndDependencies($project, 0, \%projectsByUUID); + } +} + +sub printProjectAndDependencies +{ + my ($project, $indentLevel, $projectsByUUID) = @_; + + print " " x $indentLevel, $project->{name}, "\n"; + foreach my $dependencyUUID (keys %{$project->{dependencies}}) { + printProjectAndDependencies($projectsByUUID->{$dependencyUUID}, $indentLevel + 1, $projectsByUUID); + } +} diff --git a/WebKitTools/Scripts/report-include-statistics b/WebKitTools/Scripts/report-include-statistics new file mode 100755 index 0000000..f81c3c3 --- /dev/null +++ b/WebKitTools/Scripts/report-include-statistics @@ -0,0 +1,114 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006 Apple Computer, 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. + +# "report-include-statistics" script for Web Kit Open Source Project + +use strict; +use File::Find; + +find(\&wanted, @ARGV ? @ARGV : "."); + +my %paths; +my %sources; +my %includes; + +sub wanted +{ + my $file = $_; + + if ($file eq "icu") { + $File::Find::prune = 1; + return; + } + + if ($file !~ /^\./ && $file =~ /\.(h|cpp|c|mm|m)$/) { + $paths{$file} = $File::Find::name; + $sources{$file} = $File::Find::name if $file !~ /\.h/; + open FILE, $file or die; + while (<FILE>) { + if (m-^\s*#\s*(include|import)\s+["<]((\S+/)*)(\S+)[">]-) { + my $include = ($2 eq "sys/" ? $2 : "") . $4; + $includes{$file}{$include}++; + } + } + close FILE; + } +} + +my %totalIncludes; + +sub fillOut +{ + my ($file) = @_; + + return if defined $totalIncludes{$file}; + + for my $include (keys %{ $includes{$file} }) { + $totalIncludes{$file}{$include} = 1; + fillOut($include); + for my $i (keys %{ $totalIncludes{$include} }) { + $totalIncludes{$file}{$i} = 1; + } + } +} + +my %inclusionCounts; +for my $file (keys %includes) { + $inclusionCounts{$file} = 0; + fillOut($file); +} + +for my $file (keys %sources) { + for my $include (keys %{ $totalIncludes{$file} }) { + $inclusionCounts{$include}++; + } +} + +for my $file (sort mostincludedcmp keys %includes) { + next if !$paths{$file}; + my $count = $inclusionCounts{$file}; + my $numIncludes = keys %{ $includes{$file} }; + my $numTotalIncludes = keys %{ $totalIncludes{$file} }; + print "$file is included $count times, includes $numIncludes files directly, $numTotalIncludes files total.\n" +} + +# Sort most-included files first. +sub mostincludedcmp($$) +{ + my ($filea, $fileb) = @_; + + my $counta = $inclusionCounts{$filea} || 0; + my $countb = $inclusionCounts{$fileb} || 0; + return $countb <=> $counta if $counta != $countb; + + my $ta = keys %{ $totalIncludes{$filea} }; + my $tb = keys %{ $totalIncludes{$fileb} }; + return $ta <=> $tb if $ta != $tb; + + return $filea cmp $fileb; +} diff --git a/WebKitTools/Scripts/resolve-ChangeLogs b/WebKitTools/Scripts/resolve-ChangeLogs new file mode 100755 index 0000000..58471ec --- /dev/null +++ b/WebKitTools/Scripts/resolve-ChangeLogs @@ -0,0 +1,468 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2007, 2008 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. + +# Merge and resolve ChangeLog conflicts for svn and git repositories + +use strict; + +use FindBin; +use lib $FindBin::Bin; + +use File::Basename; +use File::Path; +use File::Spec; +use Getopt::Long; +use POSIX; +use VCSUtils; + +sub conflictFiles($); +sub findChangeLog($); +sub fixChangeLogPatch($); +sub fixMergedChangeLogs($;@); +sub fixOneMergedChangeLog($); +sub mergeChanges($$$); +sub parseFixMerged($$;$); +sub removeChangeLogArguments(); +sub resolveChangeLog($); +sub resolveConflict($); +sub showStatus($;$); +sub usageAndExit(); + +my $SVN = "svn"; +my $GIT = "git"; + +my $fixMerged; +my $printWarnings = 1; +my $showHelp; + +my $getOptionsResult = GetOptions( + 'f|fix-merged:s' => \&parseFixMerged, + 'h|help' => \$showHelp, + 'w|warnings!' => \$printWarnings, +); + +my @changeLogFiles = removeChangeLogArguments(); + +if (scalar(@ARGV) > 0) { + print STDERR "ERROR: Files listed on command-line that are not ChangeLogs.\n"; + undef $getOptionsResult; +} elsif (!defined $fixMerged && scalar(@changeLogFiles) == 0) { + print STDERR "ERROR: No ChangeLog files listed on command-line.\n"; + undef $getOptionsResult; +} elsif (defined $fixMerged && !isGit()) { + print STDERR "ERROR: --fix-merged may only be used with a git repository\n"; + undef $getOptionsResult; +} + +sub usageAndExit() +{ + print STDERR <<__END__; +Usage: @{[ basename($0) ]} [options] path/to/ChangeLog [path/to/another/ChangeLog ...] + -f|--fix-merged [revision-range] fix git-merged ChangeLog entries; if a revision-range + is specified, run git filter-branch on the range + -h|--help show this help message + -w|--[no-]warnings show or suppress warnings (default: show warnings) +__END__ + exit 1; +} + +if (!$getOptionsResult || $showHelp) { + usageAndExit(); +} + +if (defined $fixMerged && length($fixMerged) > 0) { + my $commitRange = $fixMerged; + $commitRange = $commitRange . "..HEAD" if index($commitRange, "..") < 0; + fixMergedChangeLogs($commitRange, @changeLogFiles); +} elsif (@changeLogFiles) { + for my $file (@changeLogFiles) { + if (defined $fixMerged) { + fixOneMergedChangeLog($file); + } else { + resolveChangeLog($file); + } + } +} else { + print STDERR "ERROR: Unknown combination of switches and arguments.\n"; + usageAndExit(); +} + +exit 0; + +sub conflictFiles($) +{ + my ($file) = @_; + my $fileMine; + my $fileOlder; + my $fileNewer; + + if (-e $file && -e "$file.orig" && -e "$file.rej") { + return ("$file.rej", "$file.orig", $file); + } + + if (isSVN()) { + open STAT, "-|", $SVN, "status", $file || die; + my $status = <STAT>; + close STAT; + if (!$status || $status !~ m/^C\s+/) { + print STDERR "WARNING: ${file} is not in a conflicted state.\n" if $printWarnings; + return (); + } + + $fileMine = "${file}.mine" if -e "${file}.mine"; + + my $currentRevision; + open INFO, "-|", $SVN, "info", $file || die; + while (my $line = <INFO>) { + $currentRevision = $1 if $line =~ m/^Revision: ([0-9]+)/; + } + close INFO; + $fileNewer = "${file}.r${currentRevision}" if -e "${file}.r${currentRevision}"; + + my @matchingFiles = grep { $_ ne $fileNewer } glob("${file}.r[0-9][0-9]*"); + if (scalar(@matchingFiles) > 1) { + print STDERR "WARNING: Too many conflict files exist for ${file}!\n" if $printWarnings; + } else { + $fileOlder = shift @matchingFiles; + } + } elsif (isGit()) { + my $gitPrefix = `$GIT rev-parse --show-prefix`; + chomp $gitPrefix; + open GIT, "-|", $GIT, "ls-files", "--unmerged", $file || die; + while (my $line = <GIT>) { + my ($mode, $hash, $stage, $fileName) = split(' ', $line); + my $outputFile; + if ($stage == 1) { + $fileOlder = "${file}.BASE.$$"; + $outputFile = $fileOlder; + } elsif ($stage == 2) { + $fileNewer = "${file}.LOCAL.$$"; + $outputFile = $fileNewer; + } elsif ($stage == 3) { + $fileMine = "${file}.REMOTE.$$"; + $outputFile = $fileMine; + } else { + die "Unknown file stage: $stage"; + } + system("$GIT cat-file blob :${stage}:${gitPrefix}${file} > $outputFile"); + } + close GIT; + } else { + die "Unknown version control system"; + } + + if (!$fileMine && !$fileOlder && !$fileNewer) { + print STDERR "WARNING: ${file} does not need merging.\n" if $printWarnings; + } elsif (!$fileMine || !$fileOlder || !$fileNewer) { + print STDERR "WARNING: ${file} is missing some conflict files.\n" if $printWarnings; + } + + return ($fileMine, $fileOlder, $fileNewer); +} + +sub findChangeLog($) { + return $_[0] if basename($_[0]) eq "ChangeLog"; + + my $file = File::Spec->catfile($_[0], "ChangeLog"); + return $file if -d $_[0] and -e $file; + + return undef; +} + +sub fixChangeLogPatch($) +{ + my $patch = shift; + my $contextLineCount = 3; + + return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; + my ($oldLineCount, $newLineCount) = ($1, $2); + return $patch if $oldLineCount <= $contextLineCount; + + # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will + # have lines of context at the top of a patch when the existing entry has the same + # date and author as the new entry. This nifty loop alters a ChangeLog patch so + # that the added lines ("+") in the patch always start at the beginning of the + # patch and there are no initial lines of context. + my $newPatch; + my $lineCountInState = 0; + my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; + my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; + my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); + my $state = $stateHeader; + foreach my $line (split(/\n/, $patch)) { + $lineCountInState++; + if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { + $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; + $lineCountInState = 0; + $state = $statePreContext; + } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { + $line = "+" . substr($line, 1); + if ($lineCountInState == $oldContentLineCountReduction) { + $lineCountInState = 0; + $state = $stateNewChanges; + } + } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { + # No changes to these lines + if ($lineCountInState == $newContentLineCountWithoutContext) { + $lineCountInState = 0; + $state = $statePostContext; + } + } elsif ($state == $statePostContext) { + if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { + $line = " " . substr($line, 1); + } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { + next; # Discard + } + } + $newPatch .= $line . "\n"; + } + + return $newPatch; +} + +sub fixMergedChangeLogs($;@) +{ + my $revisionRange = shift; + my @changedFiles = @_; + + if (scalar(@changedFiles) < 1) { + # Read in list of files changed in $revisionRange + open GIT, "-|", $GIT, "diff", "--name-only", $revisionRange || die; + push @changedFiles, <GIT>; + close GIT || die; + die "No changed files in $revisionRange" if scalar(@changedFiles) < 1; + chomp @changedFiles; + } + + my @changeLogs = grep { defined $_ } map { findChangeLog($_) } @changedFiles; + die "No changed ChangeLog files in $revisionRange" if scalar(@changeLogs) < 1; + + system("$GIT filter-branch --tree-filter 'PREVIOUS_COMMIT=\`$GIT rev-parse \$GIT_COMMIT^\` && MAPPED_PREVIOUS_COMMIT=\`map \$PREVIOUS_COMMIT\` $0 -f \"" . join('" "', @changeLogs) . "\"' $revisionRange"); + + # On success, remove the backup refs directory + if (WEXITSTATUS($?) == 0) { + rmtree(qw(.git/refs/original)); + } +} + +sub fixOneMergedChangeLog($) +{ + my $file = shift; + my $patch; + + # Read in patch for incorrectly merged ChangeLog entry + { + local $/ = undef; + open GIT, "-|", $GIT, "diff", ($ENV{GIT_COMMIT} || "HEAD") . "^", $file || die; + $patch = <GIT>; + close GIT || die; + } + + # Always checkout the previous commit's copy of the ChangeLog + system($GIT, "checkout", $ENV{MAPPED_PREVIOUS_COMMIT} || "HEAD^", $file); + + # The patch must have 0 or more lines of context, then 1 or more lines + # of additions, and then 1 or more lines of context. If not, we skip it. + if ($patch =~ /\n@@ -(\d+),(\d+) \+(\d+),(\d+) @@\n( .*\n)*((\+.*\n)+)( .*\n)+$/m) { + # Copy the header from the original patch. + my $newPatch = substr($patch, 0, index($patch, "@@ -${1},${2} +${3},${4} @@")); + + # Generate a new set of line numbers and patch lengths. Our new + # patch will start with the lines for the fixed ChangeLog entry, + # then have 3 lines of context from the top of the current file to + # make the patch apply cleanly. + $newPatch .= "@@ -1,3 +1," . ($4 - $2 + 3) . " @@\n"; + + # We assume that top few lines of the ChangeLog entry are actually + # at the bottom of the list of added lines (due to the way the patch + # algorithm works), so we simply search through the lines until we + # find the date line, then move the rest of the lines to the top. + my @patchLines = map { $_ . "\n" } split(/\n/, $6); + foreach my $i (0 .. $#patchLines) { + if ($patchLines[$i] =~ /^\+\d{4}-\d{2}-\d{2} /) { + unshift(@patchLines, splice(@patchLines, $i, scalar(@patchLines) - $i)); + last; + } + } + + $newPatch .= join("", @patchLines); + + # Add 3 lines of context to the end + open FILE, "<", $file || die; + for (my $i = 0; $i < 3; $i++) { + $newPatch .= " " . <FILE>; + } + close FILE; + + # Apply the new patch + open(PATCH, "| patch -p1 $file > /dev/null") || die; + print PATCH $newPatch; + close(PATCH) || die; + + # Run "git add" on the fixed ChangeLog file + system($GIT, "add", $file); + + showStatus($file, 1); + } elsif ($patch) { + # Restore the current copy of the ChangeLog file since we can't repatch it + system($GIT, "checkout", $ENV{GIT_COMMIT} || "HEAD", $file); + print STDERR "WARNING: Last change to ${file} could not be fixed and re-merged.\n" if $printWarnings; + } +} + +sub mergeChanges($$$) +{ + my ($fileMine, $fileOlder, $fileNewer) = @_; + + my $traditionalReject = $fileMine =~ /\.rej$/ ? 1 : 0; + + local $/ = undef; + + my $patch; + if ($traditionalReject) { + open(DIFF, "<", $fileMine); + $patch = <DIFF>; + close(DIFF); + rename($fileMine, "$fileMine.save"); + rename($fileOlder, "$fileOlder.save"); + } else { + open(DIFF, "-|", qw(diff -u), $fileOlder, $fileMine) || die; + $patch = <DIFF>; + close(DIFF); + } + + unlink("${fileNewer}.orig"); + unlink("${fileNewer}.rej"); + + open(PATCH, "| patch --fuzz=3 $fileNewer > /dev/null") || die; + print PATCH fixChangeLogPatch($patch); + close(PATCH); + + my $result; + + # Refuse to merge the patch if it did not apply cleanly + if (-e "${fileNewer}.rej") { + unlink("${fileNewer}.rej"); + unlink($fileNewer); + rename("${fileNewer}.orig", $fileNewer); + $result = 0; + } else { + unlink("${fileNewer}.orig"); + $result = 1; + } + + if ($traditionalReject) { + rename("$fileMine.save", $fileMine); + rename("$fileOlder.save", $fileOlder); + } + + return $result; +} + +sub parseFixMerged($$;$) +{ + my ($switchName, $key, $value) = @_; + if (defined $key) { + if (defined findChangeLog($key)) { + unshift(@ARGV, $key); + $fixMerged = ""; + } else { + $fixMerged = $key; + } + } else { + $fixMerged = ""; + } +} + +sub removeChangeLogArguments() +{ + my @results = (); + + for (my $i = 0; $i < scalar(@ARGV); ) { + my $file = findChangeLog($ARGV[$i]); + if (defined $file) { + splice(@ARGV, $i, 1); + push @results, $file; + } else { + $i++; + } + } + + return @results; +} + +sub resolveChangeLog($) +{ + my ($file) = @_; + + my ($fileMine, $fileOlder, $fileNewer) = conflictFiles($file); + + return unless $fileMine && $fileOlder && $fileNewer; + + if (mergeChanges($fileMine, $fileOlder, $fileNewer)) { + if ($file ne $fileNewer) { + unlink($file); + rename($fileNewer, $file) || die; + } + unlink($fileMine, $fileOlder); + resolveConflict($file); + showStatus($file, 1); + } else { + showStatus($file); + print STDERR "WARNING: ${file} could not be merged using fuzz level 3.\n" if $printWarnings; + unlink($fileMine, $fileOlder, $fileNewer) if isGit(); + } +} + +sub resolveConflict($) +{ + my ($file) = @_; + + if (isSVN()) { + system($SVN, "resolved", $file); + } elsif (isGit()) { + system($GIT, "add", $file); + } else { + die "Unknown version control system"; + } +} + +sub showStatus($;$) +{ + my ($file, $isConflictResolved) = @_; + + if (isSVN()) { + system($SVN, "status", $file); + } elsif (isGit()) { + my @args = qw(--name-status); + unshift @args, qw(--cached) if $isConflictResolved; + system($GIT, "diff", @args, $file); + } else { + die "Unknown version control system"; + } +} diff --git a/WebKitTools/Scripts/run-drawtest b/WebKitTools/Scripts/run-drawtest new file mode 100755 index 0000000..2cd61de --- /dev/null +++ b/WebKitTools/Scripts/run-drawtest @@ -0,0 +1,47 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, 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. + +# Simplified "run" script for WebKit Open Source Project. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +setConfiguration(); +my $productDir = productDir(); + +# Check to see that all the frameworks are built (w/ SVG support). +checkFrameworks(); +checkWebCoreSVGSupport(1); + +# Set up DYLD_FRAMEWORK_PATH to point to the product directory. +print "Start DrawTest with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; +$ENV{DYLD_FRAMEWORK_PATH} = $productDir; +my $drawtestPath = "$productDir/DrawTest.app/Contents/MacOS/DrawTest"; +exec $drawtestPath or die; diff --git a/WebKitTools/Scripts/run-iexploder-tests b/WebKitTools/Scripts/run-iexploder-tests new file mode 100755 index 0000000..f5e8a6c --- /dev/null +++ b/WebKitTools/Scripts/run-iexploder-tests @@ -0,0 +1,172 @@ +#!/usr/bin/perl + +# Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) +# +# 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. + +# A script to semi-automatically run iExploder tests. + +use strict; +use warnings; + +use Cwd; +use FindBin; +use Getopt::Long; +use IPC::Open2; + +use lib $FindBin::Bin; +use webkitdirs; + +sub openHTTPDIfNeeded(); +sub closeHTTPD(); +sub runSafariWithIExploder(); + +# Argument handling +my $guardMalloc = ''; +my $httpdPort = 8000; +my $downloadTest; + +GetOptions( + 'guard-malloc|g' => \$guardMalloc, + 'get=s' => \$downloadTest, + 'port=i' => \$httpdPort +); + + +setConfiguration(); +my $productDir = productDir(); + +chdirWebKit(); + +checkFrameworks(); + +my $httpdOpen = 0; +openHTTPDIfNeeded(); + +if ($downloadTest) { + system "/usr/bin/curl -o ~/Desktop/iexploder$downloadTest.html \"http://127.0.0.1:$httpdPort/iexploder.cgi?lookup=1&test=$downloadTest\""; + print "Saved the test as iexploder$downloadTest.html on the desktop\n"; +} else { + runSafariWithIExploder(); + print "Last generated tests:\n"; + system "grep 'iexploder.cgi' /tmp/WebKit/access_log.txt | tail -n -5 | awk -F'[ =&\\?]' '{if (\$8 == \"lookup\") print \$11; else print \$9}'"; +} + +closeHTTPD(); + + +sub runSafariWithIExploder() +{ + my $redirectTo; + if (@ARGV) { + $redirectTo = "http://127.0.0.1:$httpdPort/iexploder.cgi?lookup=1&test=$ARGV[0]"; + } else { + $redirectTo = "http://127.0.0.1:$httpdPort/index.html"; + } + + open REDIRECT_HTML, ">", "/tmp/WebKit/redirect.html" or die; + print REDIRECT_HTML "<html>\n"; + print REDIRECT_HTML " <head>\n"; + print REDIRECT_HTML " <meta http-equiv=\"refresh\" content=\"1;URL=$redirectTo\" />\n"; + print REDIRECT_HTML " <script type=\"text/javascript\">\n"; + print REDIRECT_HTML " document.location = \"$redirectTo\";\n"; + print REDIRECT_HTML " </script>\n"; + print REDIRECT_HTML " </head>\n"; + print REDIRECT_HTML " <body>\n"; + print REDIRECT_HTML " </body>\n"; + print REDIRECT_HTML "</html>\n"; + close REDIRECT_HTML; + + local %ENV; + $ENV{DYLD_INSERT_LIBRARIES} = "/usr/lib/libgmalloc.dylib" if $guardMalloc; + system "WebKitTools/Scripts/run-safari", "-NSOpen", "/tmp/WebKit/redirect.html"; +} + +sub openHTTPDIfNeeded() +{ + return if $httpdOpen; + + mkdir "/tmp/WebKit"; + + if (-f "/tmp/WebKit/httpd.pid") { + my $oldPid = `cat /tmp/WebKit/httpd.pid`; + chomp $oldPid; + if (0 != kill 0, $oldPid) { + print "\nhttpd is already running: pid $oldPid, killing...\n"; + kill 15, $oldPid; + + my $retryCount = 20; + while ((0 != kill 0, $oldPid) && $retryCount) { + sleep 1; + --$retryCount; + } + + die "Timed out waiting for httpd to quit" unless $retryCount; + } + } + + my $testDirectory = getcwd() . "/LayoutTests"; + my $iExploderDirectory = getcwd() . "/WebKitTools/iExploder"; + my $httpdPath = "/usr/sbin/httpd"; + my $httpdConfig = "$testDirectory/http/conf/httpd.conf"; + $httpdConfig = "$testDirectory/http/conf/apache2-httpd.conf" if `$httpdPath -v` =~ m|Apache/2|; + my $documentRoot = "$iExploderDirectory/htdocs"; + my $typesConfig = "$testDirectory/http/conf/mime.types"; + my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem"; + my $listen = "127.0.0.1:$httpdPort"; + + open2(\*HTTPDIN, \*HTTPDOUT, $httpdPath, + "-f", "$httpdConfig", + "-C", "DocumentRoot \"$documentRoot\"", + "-C", "Listen $listen", + "-c", "TypesConfig \"$typesConfig\"", + "-c", "CustomLog \"/tmp/WebKit/access_log.txt\" common", + "-c", "ErrorLog \"/tmp/WebKit/error_log.txt\"", + "-c", "SSLCertificateFile \"$sslCertificate\"", + # Apache wouldn't run CGIs with permissions==700 otherwise + "-c", "User \"#$<\""); + + my $retryCount = 20; + while (system("/usr/bin/curl -q --silent --stderr - --output /dev/null $listen") && $retryCount) { + sleep 1; + --$retryCount; + } + + die "Timed out waiting for httpd to start" unless $retryCount; + + $httpdOpen = 1; +} + +sub closeHTTPD() +{ + return if !$httpdOpen; + + close HTTPDIN; + close HTTPDOUT; + + kill 15, `cat /tmp/WebKit/httpd.pid` if -f "/tmp/WebKit/httpd.pid"; + + $httpdOpen = 0; +} diff --git a/WebKitTools/Scripts/run-javascriptcore-tests b/WebKitTools/Scripts/run-javascriptcore-tests new file mode 100755 index 0000000..afd0ab4 --- /dev/null +++ b/WebKitTools/Scripts/run-javascriptcore-tests @@ -0,0 +1,189 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (C) 2007 Eric Seidel <eric@webkit.org> +# +# 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. + +# Script to run the Web Kit Open Source Project JavaScriptCore tests (adapted from Mozilla). + +use strict; +use FindBin; +use Getopt::Long qw(:config pass_through); +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +my $coverageSupport = 0; +GetOptions('coverage!' => \$coverageSupport); + +my @coverageSupportOption = (); +if ($coverageSupport) { + push @coverageSupportOption, "GCC_GENERATE_TEST_COVERAGE_FILES=YES"; + push @coverageSupportOption, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES"; + push @coverageSupportOption, "EXTRA_LINK= -ftest-coverage -fprofile-arcs"; + push @coverageSupportOption, "OTHER_CFLAGS= -MD"; + push @coverageSupportOption, "OTHER_LDFLAGS= -ftest-coverage -fprofile-arcs -framework AppKit"; +} + +# determine configuration +my $configuration; +setConfiguration(); +$configuration = configuration(); + +my @jsArgs; +my @xcodeArgs; +my $root; +my $run64Bit; + +# pre-evaluate arguments. jsDriver args have - preceding, xcode args do not. +# special arguments +# --root=<path to webkit root> use pre-built root +# --gtk build gtk +# --no-64-bit or --64-bit +foreach my $arg(@ARGV) { + print $arg."\n"; + if( $arg =~ /root=(.*)/ ){ + $root = $1; + } elsif( $arg =~ /^--gtk$/i || $arg =~ /^--qt$/i || $arg =~ /^--wx$/i ){ + } elsif( $arg =~ /(no-)?64-bit/ ) { + $run64Bit = !defined($1); + } elsif( $arg =~ /^-/ or !($arg =~/=/)){ + push( @jsArgs, $arg ); + } else { + push( @xcodeArgs, $arg ); + } +} + +setConfigurationProductDir(Cwd::abs_path($root)) if (defined($root)); + +if (isOSX() && !isTiger()) { + my $preferredArch = preferredArchitecture("JavaScriptCore"); + setRun64Bit($run64Bit); + my $arch = preferredArchitecture("JavaScriptCore"); + if ($arch ne $preferredArch) { + push(@jsArgs, "-a", $arch); + } +} + +if(!defined($root)){ + chdirWebKit(); + push(@xcodeArgs, "--" . $configuration); + + # FIXME: These should be stored per-config and ignored here + push(@xcodeArgs, "--qt") if isQt(); + push(@xcodeArgs, "--gtk") if isGtk(); + push(@xcodeArgs, "--wx") if isWx(); + + my $arch = preferredArchitecture("JavaScriptCore"); + push(@xcodeArgs, "ARCHS=$arch") if (isOSX()); + + my $buildResult = system "perl", "WebKitTools/Scripts/build-jsc", @xcodeArgs; + if ($buildResult) { + print STDERR "Compiling jsc failed!\n"; + exit exitStatus($buildResult); + } +} + +# Find JavaScriptCore directory +chdirWebKit(); +chdir("JavaScriptCore"); + +my $productDir = productDir(); +chdir "tests/mozilla" or die; +$productDir .= "/JavaScriptCore" if isQt(); +$productDir .= "/Programs" if isGtk(); +$ENV{DYLD_FRAMEWORK_PATH} = $productDir; +setPathForRunningWebKitApp(\%ENV) if isCygwin(); + +sub jscPath($) +{ + my ($productDir) = @_; + my $jscName = "jsc"; + $jscName .= "_debug" if (isCygwin() && ($configuration eq "Debug")); + return "$productDir/$jscName"; +} + +my $result = system "perl", "jsDriver.pl", "-e", "squirrelfish", "-s", jscPath($productDir), "-f", "actual.html", @jsArgs; +exit exitStatus($result) if $result; + +my %failures; + +open EXPECTED, "expected.html" or die; +while (<EXPECTED>) { + last if /failures reported\.$/; +} +while (<EXPECTED>) { + chomp; + $failures{$_} = 1; +} +close EXPECTED; + +my %newFailures; + +open ACTUAL, "actual.html" or die; +while (<ACTUAL>) { + last if /failures reported\.$/; +} +while (<ACTUAL>) { + chomp; + if ($failures{$_}) { + delete $failures{$_}; + } else { + $newFailures{$_} = 1; + } +} +close ACTUAL; + +my $numNewFailures = keys %newFailures; +if ($numNewFailures) { + print "\n** Danger, Will Robinson! Danger! The following failures have been introduced:\n"; + foreach my $failure (sort keys %newFailures) { + print "\t$failure\n"; + } +} + +my $numOldFailures = keys %failures; +if ($numOldFailures) { + print "\nYou fixed the following test"; + print "s" if $numOldFailures != 1; + print ":\n"; + foreach my $failure (sort keys %failures) { + print "\t$failure\n"; + } +} + +print "\n"; + +print "$numNewFailures regression"; +print "s" if $numNewFailures != 1; +print " found.\n"; + +print "$numOldFailures test"; +print "s" if $numOldFailures != 1; +print " fixed.\n"; + +print "OK.\n" if $numNewFailures == 0; +exit(1) if $numNewFailures; diff --git a/WebKitTools/Scripts/run-jsc b/WebKitTools/Scripts/run-jsc new file mode 100755 index 0000000..20dc5e8 --- /dev/null +++ b/WebKitTools/Scripts/run-jsc @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +# Copyright (C) 2006 Apple Computer, 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 runs a list of scripts through jsc a specified number of times. + +use strict; +use warnings; +use FindBin; +use lib $FindBin::Bin; +use Getopt::Long; +use webkitdirs; + +my $usage = "Usage: run-jsc [--count run_count] [--verbose] shell_file [file2...]"; + +my $count = 1; +my $verbose = 0; +GetOptions("count|c=i" => \$count, + "verbose|v" => \$verbose); +die "$usage\n" if (@ARGV < 1); + +my $jsc = productDir() . "/jsc @ARGV"; +$jsc .= " 2> /dev/null" unless $verbose; + +my $dyld = productDir(); + +$ENV{"DYLD_FRAMEWORK_PATH"} = $dyld; +print STDERR "Running $count time(s): DYLD_FRAMEWORK_PATH=$dyld $jsc\n"; +while ($count--) { + if (system("$jsc") != 0) { + last; + } +} + diff --git a/WebKitTools/Scripts/run-launcher b/WebKitTools/Scripts/run-launcher new file mode 100755 index 0000000..f2b58cb --- /dev/null +++ b/WebKitTools/Scripts/run-launcher @@ -0,0 +1,70 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2007 Apple Computer, Inc. All rights reserved. +# Copyright (C) 2007 Staikos Computing Services, Inc. <info@staikos.net> +# +# 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. + +# Simplified "run" script for WebKit Open Source Project. + +use strict; +use File::Spec::Functions qw/catdir/; +use File::Temp qw/tempfile/; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +setConfiguration(); +my $productDir = productDir(); +my $launcherPath = productDir(); +my @args = @ARGV; + +# Check to see that all the frameworks are built. +checkFrameworks(); + +# Set paths according to the build system used +if (isQt()) { + my $libDir = catdir(productDir(), 'lib'); + $launcherPath = catdir($launcherPath, "bin", "QtLauncher"); + + # Set up LD_LIBRARY_PATH to point to the product directory. + print "Starting webkit launcher with LD_LIBRARY_PATH set to point to built WebKit in $libDir.\n"; + + $ENV{LD_LIBRARY_PATH} = $ENV{LD_LIBRARY_PATH} ? "$libDir:$ENV{LD_LIBRARY_PATH}" : $libDir; + $ENV{DYLD_LIBRARY_PATH} = $ENV{DYLD_LIBRARY_PATH} ? "$libDir:$ENV{DYLD_LIBRARY_PATH}" : $libDir; +} else { + + if (isGtk()) { + $launcherPath = catdir($launcherPath, "Programs", "GtkLauncher"); + # Strip --gtk from the arg-list, since otherwise GtkLauncher will try to + # interpret it as a URL. + @args = grep(!/^(--gtk)$/, @args); + } + + print "Starting webkit launcher.\n"; +} + +exec $launcherPath, @args or die; + diff --git a/WebKitTools/Scripts/run-leaks b/WebKitTools/Scripts/run-leaks new file mode 100755 index 0000000..d8f89d3 --- /dev/null +++ b/WebKitTools/Scripts/run-leaks @@ -0,0 +1,212 @@ +#!/usr/bin/perl + +# Copyright (C) 2007 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. + +# Script to run the Mac OS X leaks tool with more expressive '-exclude' lists. + +use strict; +use warnings; + +use File::Basename; +use Getopt::Long; + +sub runLeaks($); +sub parseLeaksOutput(\@); +sub removeMatchingRecords(\@$\@); +sub reportError($); + +sub main() +{ + # Read options. + my $usage = + "Usage: " . basename($0) . " [options] pid | executable name\n" . + " --exclude-callstack regexp Exclude leaks whose call stacks match the regular expression 'regexp'.\n" . + " --exclude-type regexp Exclude leaks whose data types match the regular expression 'regexp'.\n" . + " --help Show this help message.\n"; + + my @callStacksToExclude = (); + my @typesToExclude = (); + my $help = 0; + + my $getOptionsResult = GetOptions( + 'exclude-callstack:s' => \@callStacksToExclude, + 'exclude-type:s' => \@typesToExclude, + 'help' => \$help + ); + my $pidOrExecutableName = $ARGV[0]; + + if (!$getOptionsResult || $help) { + print STDERR $usage; + return 1; + } + + if (!$pidOrExecutableName) { + reportError("Missing argument: pid | executable."); + print STDERR $usage; + return 1; + } + + # Run leaks tool. + my $leaksOutput = runLeaks($pidOrExecutableName); + if (!$leaksOutput) { + return 1; + } + + my $leakList = parseLeaksOutput(@$leaksOutput); + if (!$leakList) { + return 1; + } + + # Filter output. + my $leakCount = @$leakList; + removeMatchingRecords(@$leakList, "callStack", @callStacksToExclude); + removeMatchingRecords(@$leakList, "type", @typesToExclude); + my $excludeCount = $leakCount - @$leakList; + + # Dump results. + print $leaksOutput->[0]; + print $leaksOutput->[1]; + foreach my $leak (@$leakList) { + print $leak->{"leaksOutput"}; + } + + if ($excludeCount) { + print "$excludeCount leaks excluded (not printed)\n"; + } + + return 0; +} + +exit(main()); + +# Returns the output of the leaks tool in list form. +sub runLeaks($) +{ + my ($pidOrExecutableName) = @_; + + my @leaksOutput = `leaks $pidOrExecutableName`; + if (!@leaksOutput) { + reportError("Error running leaks tool."); + return; + } + + return \@leaksOutput; +} + +# Returns a list of hash references with the keys { address, size, type, callStack, leaksOutput } +sub parseLeaksOutput(\@) +{ + my ($leaksOutput) = @_; + + # Format: + # Process 00000: 1234 nodes malloced for 1234 KB + # Process 00000: XX leaks for XXX total leaked bytes. + # Leak: 0x00000000 size=1234 [instance of 'blah'] + # 0x00000000 0x00000000 0x00000000 0x00000000 a..d.e.e + # ... + # Call stack: leak_caller() | leak() | malloc + # + # We treat every line except for Process 00000: and Leak: as optional + + my ($leakCount) = ($leaksOutput->[1] =~ /[[:blank:]]+([0-9]+)[[:blank:]]+leaks?/); + if (!defined($leakCount)) { + reportError("Could not parse leak count reported by leaks tool."); + return; + } + + my @leakList = (); + for my $line (@$leaksOutput) { + next if $line =~ /^Process/; + next if $line =~ /^node buffer added/; + + if ($line =~ /^Leak: /) { + my ($address) = ($line =~ /Leak: ([[:xdigit:]x]+)/); + if (!defined($address)) { + reportError("Could not parse Leak address."); + return; + } + + my ($size) = ($line =~ /size=([[:digit:]]+)/); + if (!defined($size)) { + reportError("Could not parse Leak size."); + return; + } + + my ($type) = ($line =~ /'([^']+)'/); #' + if (!defined($type)) { + $type = ""; # The leaks tool sometimes omits the type. + } + + my %leak = ( + "address" => $address, + "size" => $size, + "type" => $type, + "callStack" => "", # The leaks tool sometimes omits the call stack. + "leaksOutput" => $line + ); + push(@leakList, \%leak); + } else { + $leakList[$#leakList]->{"leaksOutput"} .= $line; + if ($line =~ /Call stack:/) { + $leakList[$#leakList]->{"callStack"} = $line; + } + } + } + + if (@leakList != $leakCount) { + my $parsedLeakCount = @leakList; + reportError("Parsed leak count($parsedLeakCount) does not match leak count reported by leaks tool($leakCount)."); + return; + } + + return \@leakList; +} + +sub removeMatchingRecords(\@$\@) +{ + my ($recordList, $key, $regexpList) = @_; + + RECORD: for (my $i = 0; $i < @$recordList;) { + my $record = $recordList->[$i]; + + foreach my $regexp (@$regexpList) { + if ($record->{$key} =~ $regexp) { + splice(@$recordList, $i, 1); + next RECORD; + } + } + + $i++; + } +} + +sub reportError($) +{ + my ($errorMessage) = @_; + + print STDERR basename($0) . ": $errorMessage\n"; +} diff --git a/WebKitTools/Scripts/run-mangleme-tests b/WebKitTools/Scripts/run-mangleme-tests new file mode 100755 index 0000000..93b7894 --- /dev/null +++ b/WebKitTools/Scripts/run-mangleme-tests @@ -0,0 +1,175 @@ +#!/usr/bin/perl + +# Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) +# +# 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. + +# A script to semi-automatically run mangleme tests. + +use strict; +use warnings; + +use Cwd; +use FindBin; +use Getopt::Long; +use IPC::Open2; + +use lib $FindBin::Bin; +use webkitdirs; + +sub openHTTPDIfNeeded(); +sub closeHTTPD(); +sub runSafariWithMangleme(); + +# Argument handling +my $guardMalloc = ''; +my $httpdPort = 8000; +my $downloadTest; + +GetOptions( + 'guard-malloc|g' => \$guardMalloc, + 'get=s' => \$downloadTest, + 'port=i' => \$httpdPort +); + + +setConfiguration(); +my $productDir = productDir(); + +chdirWebKit(); + +checkFrameworks(); + +mkdir "WebKitBuild/mangleme"; +(system "/usr/bin/make", "-C", "WebKitTools/mangleme") == 0 or die; + +my $httpdOpen = 0; +openHTTPDIfNeeded(); + +if ($downloadTest) { + system "/usr/bin/curl -o ~/Desktop/mangleme$downloadTest.html http://127.0.0.1:$httpdPort/remangle.cgi?$downloadTest"; + print "Saved the test as mangleme$downloadTest.html on the desktop\n"; +} else { + runSafariWithMangleme(); + print "Last generated tests:\n"; + system "grep 'Mangle attempt' /tmp/WebKit/error_log.txt | tail -n -5 | awk ' {print \$4}'"; +} + +closeHTTPD(); + + +sub runSafariWithMangleme() +{ + my $redirectTo; + if (@ARGV) { + $redirectTo = "http://127.0.0.1:$httpdPort/remangle.cgi?$ARGV[0]"; + } else { + $redirectTo = "http://127.0.0.1:$httpdPort/mangle.cgi"; + } + + open REDIRECT_HTML, ">", "/tmp/WebKit/redirect.html" or die; + print REDIRECT_HTML "<html>\n"; + print REDIRECT_HTML " <head>\n"; + print REDIRECT_HTML " <meta http-equiv=\"refresh\" content=\"1;URL=$redirectTo\" />\n"; + print REDIRECT_HTML " <script type=\"text/javascript\">\n"; + print REDIRECT_HTML " document.location = \"$redirectTo\";\n"; + print REDIRECT_HTML " </script>\n"; + print REDIRECT_HTML " </head>\n"; + print REDIRECT_HTML " <body>\n"; + print REDIRECT_HTML " </body>\n"; + print REDIRECT_HTML "</html>\n"; + close REDIRECT_HTML; + + local %ENV; + $ENV{DYLD_INSERT_LIBRARIES} = "/usr/lib/libgmalloc.dylib" if $guardMalloc; + system "WebKitTools/Scripts/run-safari", "-NSOpen", "/tmp/WebKit/redirect.html"; +} + +sub openHTTPDIfNeeded() +{ + return if $httpdOpen; + + mkdir "/tmp/WebKit"; + + if (-f "/tmp/WebKit/httpd.pid") { + my $oldPid = `cat /tmp/WebKit/httpd.pid`; + chomp $oldPid; + if (0 != kill 0, $oldPid) { + print "\nhttpd is already running: pid $oldPid, killing...\n"; + kill 15, $oldPid; + + my $retryCount = 20; + while ((0 != kill 0, $oldPid) && $retryCount) { + sleep 1; + --$retryCount; + } + + die "Timed out waiting for httpd to quit" unless $retryCount; + } + } + + my $testDirectory = getcwd() . "/LayoutTests"; + my $manglemeDirectory = getcwd() . "/WebKitBuild/mangleme"; + my $httpdPath = "/usr/sbin/httpd"; + my $httpdConfig = "$testDirectory/http/conf/httpd.conf"; + $httpdConfig = "$testDirectory/http/conf/apache2-httpd.conf" if `$httpdPath -v` =~ m|Apache/2|; + my $documentRoot = "$manglemeDirectory"; + my $typesConfig = "$testDirectory/http/conf/mime.types"; + my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem"; + my $listen = "127.0.0.1:$httpdPort"; + + open2(\*HTTPDIN, \*HTTPDOUT, $httpdPath, + "-f", "$httpdConfig", + "-C", "DocumentRoot \"$documentRoot\"", + "-C", "Listen $listen", + "-c", "TypesConfig \"$typesConfig\"", + "-c", "CustomLog \"/tmp/WebKit/access_log.txt\" common", + "-c", "ErrorLog \"/tmp/WebKit/error_log.txt\"", + "-c", "SSLCertificateFile \"$sslCertificate\"", + # Apache wouldn't run CGIs with permissions==700 otherwise + "-c", "User \"#$<\""); + + my $retryCount = 20; + while (system("/usr/bin/curl -q --silent --stderr - --output /dev/null $listen") && $retryCount) { + sleep 1; + --$retryCount; + } + + die "Timed out waiting for httpd to start" unless $retryCount; + + $httpdOpen = 1; +} + +sub closeHTTPD() +{ + return if !$httpdOpen; + + close HTTPDIN; + close HTTPDOUT; + + kill 15, `cat /tmp/WebKit/httpd.pid` if -f "/tmp/WebKit/httpd.pid"; + + $httpdOpen = 0; +} diff --git a/WebKitTools/Scripts/run-pageloadtest b/WebKitTools/Scripts/run-pageloadtest new file mode 100755 index 0000000..ad6daa1 --- /dev/null +++ b/WebKitTools/Scripts/run-pageloadtest @@ -0,0 +1,92 @@ +#!/usr/bin/perl + +# Copyright (C) 2006 Eric Seidel (eric@webkit.org) +# +# 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. + +# Script to run the WebKit Open Source Project page load tests (PLTs). + +# Run all the tests passed in on the command line. + +use strict; +use warnings; + +use File::Basename; +use File::Spec; +use FindBin; +use Getopt::Long; + +use lib $FindBin::Bin; +use webkitdirs; + +# Argument handling +my $testName = 'svg'; +my $showHelp = 0; + +my $usage = + "Usage: " . basename($0) . "[options] testName\n" . + " --help Show this help message\n"; + +my $getOptionsResult = GetOptions('help' => \$showHelp); + +if (!$getOptionsResult || $showHelp) { + print STDERR $usage; + exit 1; +} + +$testName = shift @ARGV if (@ARGV); + +my $safariExecutablePath = safariPath(); +my $safariResourcePath = File::Spec->catdir(dirname(dirname($safariExecutablePath)), "Resources"); + +# Check to see that all the frameworks are built. +checkFrameworks(); + +chdirWebKit(); + +if ($testName eq 'svg') { + my $suiteFile = "PageLoadTests/$testName/$testName.pltsuite"; + my $webkitPath = sourceDir(); + `cat "$suiteFile" | perl -pe 's|WEBKIT_PATH|$webkitPath|' > $safariResourcePath/$testName.pltsuite` +} + +die "Please copy ${testName}.pltsuite to ${safariResourcePath}/${testName}.pltsuite" + if (! -f "${safariResourcePath}/${testName}.pltsuite"); + +setConfiguration(); + +my $productDir = productDir(); + +# Set up DYLD_FRAMEWORK_PATH to point to the product directory. +print "Starting Safari with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; +$ENV{DYLD_FRAMEWORK_PATH} = $productDir; +$ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES"; + +my @testCommands = ('activate'); +# Autovicki would clear history, we skip that here as this is likely an active user account +@testCommands = (@testCommands, ("run $testName", 'emptyCache', 'wait 30')); +@testCommands = (@testCommands, (("run $testName", 'wait 10') x 3)); +my $testCommandsString = join('; ', @testCommands); +exec $safariExecutablePath, '--test-commands', $testCommandsString or die; diff --git a/WebKitTools/Scripts/run-safari b/WebKitTools/Scripts/run-safari new file mode 100755 index 0000000..f31fed3 --- /dev/null +++ b/WebKitTools/Scripts/run-safari @@ -0,0 +1,63 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2007 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. + +# Simplified "run" script for WebKit Open Source Project. + +use strict; +use FindBin; +use Getopt::Long qw(:config pass_through); +use lib $FindBin::Bin; +use webkitdirs; + +my $programName = basename($0); +my $showHelp = 0; +my $run64Bit; + +my $usage = <<EOF; +Usage: $programName [options] + --help Show this help message + --64-bit Run in 64-bit mode +EOF + +my $getOptionsResult = GetOptions( + 'help' => \$showHelp, + '64-bit!' => \$run64Bit +); + +if (!$getOptionsResult || $showHelp) { + print STDERR $usage; + exit 1; +} + +setRun64Bit($run64Bit); +setConfiguration(); + +# Check to see that all the frameworks are built. +checkFrameworks(); + +exit exitStatus(runSafari()); diff --git a/WebKitTools/Scripts/run-sunspider b/WebKitTools/Scripts/run-sunspider new file mode 100755 index 0000000..2e58418 --- /dev/null +++ b/WebKitTools/Scripts/run-sunspider @@ -0,0 +1,131 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2007 Apple Inc. All rights reserved. +# Copyright (C) 2007 Eric Seidel <eric@webkit.org> +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR +# 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. + +use strict; +use FindBin; +use Getopt::Long qw(:config pass_through); +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +# determine configuration, but default to "Release" instead of last-used configuration +setConfiguration("Release"); +setConfiguration(); +my $configuration = configuration(); + +my $root; +my $testRuns = 10; # This number may be different from what sunspider defaults to (that's OK) +my $runShark = 0; +my $runShark20 = 0; +my $runSharkCache = 0; +my $ubench = 0; +my $v8 = 0; +my $setBaseline = 0; +my $showHelp = 0; +my $testsPattern; + +my $programName = basename($0); +my $usage = <<EOF; +Usage: $programName [options] [options to pass to build system] + --help Show this help message + --set-baseline Set baseline for future comparisons + --root Path to root tools build + --runs Number of times to run tests (default: $testRuns) + --tests Only run tests matching provided pattern + --shark Sample with the Mac OS X "Shark" performance testing tool (implies --runs=1) + --shark20 Like --shark, but with a 20 microsecond sampling interval + --shark-cache Like --shark, but performs a L2 cache-miss sample instead of time sample + --ubench Use microbenchmark suite instead of regular tests (to check for core execution regressions) + --v8 Use the V8 benchmark suite. +EOF + +GetOptions('root=s' => sub { my ($x, $value) = @_; $root = $value; setConfigurationProductDir(Cwd::abs_path($root)); }, + 'runs=i' => \$testRuns, + 'set-baseline' => \$setBaseline, + 'shark' => \$runShark, + 'shark20' => \$runShark20, + 'shark-cache' => \$runSharkCache, + 'ubench' => \$ubench, + 'v8' => \$v8, + 'tests=s' => \$testsPattern, + 'help' => \$showHelp); + +if ($showHelp) { + print STDERR $usage; + exit 1; +} + +sub buildJSC +{ + if (!defined($root)){ + push(@ARGV, "--" . $configuration); + + chdirWebKit(); + my $buildResult = system "WebKitTools/Scripts/build-jsc", @ARGV; + if ($buildResult) { + print STDERR "Compiling jsc failed!\n"; + exit exitStatus($buildResult); + } + } +} + +sub setupEnvironmentForExecution($) +{ + my ($productDir) = @_; + print "Starting sunspider with DYLD_FRAMEWORK_PATH set to point to built JavaScriptCore in $productDir.\n"; + $ENV{DYLD_FRAMEWORK_PATH} = $productDir; + # FIXME: Other platforms may wish to augment this method to use LD_LIBRARY_PATH, etc. +} + +sub jscPath($) +{ + my ($productDir) = @_; + my $jscName = "jsc"; + $jscName .= "_debug" if (isCygwin() && ($configuration eq "Debug")); + return "$productDir/$jscName"; +} + +buildJSC(); + +chdirWebKit(); +chdir("SunSpider"); + +my $productDir = productDir(); +# FIXME: This hack should be pushed down into productDir() +$productDir .= "/JavaScriptCore" if (isQt() or isGtk()); + +setupEnvironmentForExecution($productDir); +my @args = ("--shell", jscPath($productDir), "--runs", $testRuns); +# This code could be removed if we chose to pass extra args to sunspider instead of Xcode +push @args, "--set-baseline" if $setBaseline; +push @args, "--shark" if $runShark; +push @args, "--shark20" if $runShark20; +push @args, "--shark-cache" if $runSharkCache; +push @args, "--ubench" if $ubench; +push @args, "--v8" if $v8; +push @args, "--tests", $testsPattern if $testsPattern; + +exec "./sunspider", @args; diff --git a/WebKitTools/Scripts/run-webkit-app b/WebKitTools/Scripts/run-webkit-app new file mode 100755 index 0000000..36b9a58 --- /dev/null +++ b/WebKitTools/Scripts/run-webkit-app @@ -0,0 +1,50 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, 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. + +# Simplified "run" script for Web Kit Open Source Project. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +setConfiguration(); +my $productDir = productDir(); + +die "Did not specify an application to open (e.g. run-webkit-app AppName).\n" unless length($ARGV[0]) > 0; + +# Check to see that all the frameworks are built. +checkFrameworks(); + +# Set up DYLD_FRAMEWORK_PATH to point to the product directory. +print "Start $ARGV[0] with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; +$ENV{DYLD_FRAMEWORK_PATH} = $productDir; +$ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES"; + +unshift(@ARGV, "-a"); +exec "open", @ARGV; diff --git a/WebKitTools/Scripts/run-webkit-httpd b/WebKitTools/Scripts/run-webkit-httpd new file mode 100755 index 0000000..a64eef6 --- /dev/null +++ b/WebKitTools/Scripts/run-webkit-httpd @@ -0,0 +1,127 @@ +#!/usr/bin/perl + +# Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. +# Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) +# +# 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 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. + +# Script to run Apache with the same configuration as used in http layout tests. + +use strict; +use warnings; + +use Cwd; +use File::Basename; +use Getopt::Long; +use FindBin; + +use lib $FindBin::Bin; +use webkitdirs; + +# Argument handling +my $httpdPort = 8000; +my $allInterfaces = 0; +my $showHelp; + +my $result = GetOptions( + 'all-interfaces|a' => \$allInterfaces, + 'help|h' => \$showHelp, + 'port=i' => \$httpdPort, +); + +if (!$result || @ARGV || $showHelp) { + print "Usage: " . basename($0) . " [options]\n"; + print " -a|--all-interfaces Bind to all interfaces\n"; + print " -h|--help Show this help message\n"; + print " -p|--port NNNN Bind to port NNNN\n"; + exit 1; +} + +setConfiguration(); +my $productDir = productDir(); +chdirWebKit(); + +mkdir "/tmp/WebKit"; + +if (-f "/tmp/WebKit/httpd.pid") { + my $oldPid = `cat /tmp/WebKit/httpd.pid`; + chomp $oldPid; + if (0 != kill 0, $oldPid) { + print "\nhttpd is already running: pid $oldPid, killing...\n"; + kill 15, $oldPid; + + my $retryCount = 20; + while ((0 != kill 0, $oldPid) && $retryCount) { + sleep 1; + --$retryCount; + } + + die "Timed out waiting for httpd to quit" unless $retryCount; + } +} + +my $testDirectory = getcwd() . "/LayoutTests"; +my $httpdPath = "/usr/sbin/httpd"; +$httpdPath = "/usr/sbin/apache2" if isDebianBased(); +my $httpdConfig = "$testDirectory/http/conf/httpd.conf"; +$httpdConfig = "$testDirectory/http/conf/cygwin-httpd.conf" if isCygwin(); +$httpdConfig = "$testDirectory/http/conf/apache2-httpd.conf" if `$httpdPath -v` =~ m|Apache/2|; +$httpdConfig = "$testDirectory/http/conf/apache2-debian-httpd.conf" if isDebianBased(); +my $documentRoot = "$testDirectory/http/tests"; +my $typesConfig = "$testDirectory/http/conf/mime.types"; +my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem"; + +my $listen = "127.0.0.1:$httpdPort"; +$listen = "$httpdPort" if ($allInterfaces); + +if ($allInterfaces) { + print "Starting httpd on port $httpdPort (all interfaces)...\n"; +} else { + print "Starting httpd on <http://$listen/>...\n"; +} +print "Press Ctrl+C to stop it.\n\n"; + +my @args = ( + "-f", "$httpdConfig", + "-C", "DocumentRoot \"$documentRoot\"", + "-C", "Listen $listen", + "-c", "TypesConfig \"$typesConfig\"", + "-c", "CustomLog |/usr/bin/tee common", + "-c", "ErrorLog |/usr/bin/tee", + # Apache wouldn't run CGIs with permissions==700 otherwise. + "-c", "User \"#$<\"", + # Run in single-process mode, do not detach from the controlling terminal. + "-X", + # Disable Keep-Alive support. Makes testing in multiple browsers easier (no need to wait + # for another browser's connection to expire). + "-c", "KeepAlive 0" +); + +# FIXME: Enable this on Windows once <rdar://problem/5345985> is fixed +push(@args, "-c", "SSLCertificateFile \"$sslCertificate\"") unless isCygwin(); + +system($httpdPath, @args); + +unlink "/tmp/WebKit/httpd.pid"; diff --git a/WebKitTools/Scripts/run-webkit-nightly.cmd b/WebKitTools/Scripts/run-webkit-nightly.cmd new file mode 100755 index 0000000..93037ab --- /dev/null +++ b/WebKitTools/Scripts/run-webkit-nightly.cmd @@ -0,0 +1,10 @@ +@echo off +set script="%TMP%\run-webkit-nightly2.cmd" +set vsvars="%VS80COMNTOOLS%\vsvars32.bat" +if exist %vsvars% ( + copy %vsvars% "%script%" +) else ( + del "%script%" +) +FindSafari.exe %1 /printSafariLauncher >> "%script%" +call %script% diff --git a/WebKitTools/Scripts/run-webkit-tests b/WebKitTools/Scripts/run-webkit-tests new file mode 100755 index 0000000..4f129b7 --- /dev/null +++ b/WebKitTools/Scripts/run-webkit-tests @@ -0,0 +1,1862 @@ +#!/usr/bin/perl + +# Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. +# Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) +# Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com) +# Copyright (C) 2007 Eric Seidel <eric@webkit.org> +# +# 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. + +# Script to run the WebKit Open Source Project layout tests. + +# Run all the tests passed in on the command line. +# If no tests are passed, find all the .html, .shtml, .xml, .xhtml, .pl, .php (and svg) files in the test directory. + +# Run each text. +# Compare against the existing file xxx-expected.txt. +# If there is a mismatch, generate xxx-actual.txt and xxx-diffs.txt. + +# At the end, report: +# the number of tests that got the expected results +# the number of tests that ran, but did not get the expected results +# the number of tests that failed to run +# the number of tests that were run but had no expected results to compare against + +use strict; +use warnings; + +use Cwd; +use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); +use File::Basename; +use File::Copy; +use File::Find; +use File::Path; +use File::Spec; +use File::Spec::Functions; +use FindBin; +use Getopt::Long; +use IPC::Open2; +use IPC::Open3; +use Time::HiRes qw(time usleep); + +use List::Util 'shuffle'; + +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +sub openDumpTool(); +sub closeDumpTool(); +sub dumpToolDidCrash(); +sub closeHTTPD(); +sub countAndPrintLeaks($$$); +sub fileNameWithNumber($$); +sub numericcmp($$); +sub openHTTPDIfNeeded(); +sub pathcmp($$); +sub processIgnoreTests($); +sub slowestcmp($$); +sub splitpath($); +sub stripExtension($); +sub isTextOnlyTest($); +sub expectedDirectoryForTest($;$;$); +sub countFinishedTest($$$$); +sub testCrashedOrTimedOut($$$$$); +sub sampleDumpTool(); +sub printFailureMessageForTest($$); +sub toURL($); +sub toWindowsPath($); +sub closeCygpaths(); +sub validateSkippedArg($$;$); +sub htmlForResultsSection(\@$&); +sub deleteExpectedAndActualResults($); +sub recordActualResultsAndDiff($$); +sub buildPlatformHierarchy(); +sub epiloguesAndPrologues($$); +sub parseLeaksandPrintUniqueLeaks(); +sub readFromDumpToolWithTimer(*;$); +sub setFileHandleNonBlocking(*$); +sub writeToFile($$); + +# Argument handling +my $addPlatformExceptions = 0; +my $complexText = 0; +my $configuration = configuration(); +my $guardMalloc = ''; +my $httpdPort = 8000; +my $httpdSSLPort = 8443; +my $ignoreTests = ''; +my $launchSafari = 1; +my $platform; +my $pixelTests = ''; +my $quiet = ''; +my $report10Slowest = 0; +my $resetResults = 0; +my $shouldCheckLeaks = 0; +my $showHelp = 0; +my $testsPerDumpTool; +my $testHTTP = 1; +my $testMedia = 1; +my $testResultsDirectory = "/tmp/layout-test-results"; +my $threaded = 0; +my $tolerance = 0; +my $treatSkipped = "default"; +my $verbose = 0; +my $useValgrind = 0; +my $strictTesting = 0; +my $generateNewResults = 1; +my $stripEditingCallbacks = isCygwin(); +my $runSample = 1; +my $root; +my $reverseTests = 0; +my $randomizeTests = 0; +my $mergeDepth; +my @leaksFilenames; +my $run64Bit; + +# Default to --no-http for Qt, Gtk and wx for now. +$testHTTP = 0 if (isQt() || isGtk() || isWx()); + +my $expectedTag = "expected"; +my $actualTag = "actual"; +my $diffsTag = "diffs"; +my $errorTag = "stderr"; + +if (isTiger()) { + $platform = "mac-tiger"; +} elsif (isLeopard()) { + $platform = "mac-leopard"; +} elsif (isSnowLeopard()) { + $platform = "mac-snowleopard"; +} elsif (isOSX()) { + $platform = "mac"; +} elsif (isQt()) { + $platform = "qt"; +} elsif (isGtk()) { + $platform = "gtk"; +} elsif (isCygwin()) { + $platform = "win"; +} + +if (!defined($platform)) { + print "WARNING: Your platform is not recognized. Any platform-specific results will be generated in platform/undefined.\n"; + $platform = "undefined"; +} + +my $programName = basename($0); +my $launchSafariDefault = $launchSafari ? "launch" : "do not launch"; +my $httpDefault = $testHTTP ? "run" : "do not run"; +my $sampleDefault = $runSample ? "run" : "do not run"; + +# FIXME: "--strict" should be renamed to qt-mac-comparison, or something along those lines. +my $usage = <<EOF; +Usage: $programName [options] [testdir|testpath ...] + --add-platform-exceptions Put new results for non-platform-specific failing tests into the platform-specific results directory + --complex-text Use the complex text code path for all text (Mac OS X and Windows only) + -c|--configuration config Set DumpRenderTree build configuration + -g|--guard-malloc Enable malloc guard + --help Show this help message + --[no-]http Run (or do not run) http tests (default: $httpDefault) + -i|--ignore-tests Comma-separated list of directories or tests to ignore + --[no-]launch-safari Launch (or do not launch) Safari to display test results (default: $launchSafariDefault) + -l|--leaks Enable leaks checking + --[no-]new-test-results Generate results for new tests + -p|--pixel-tests Enable pixel tests + --tolerance t Ignore image differences less than this percentage (implies --pixel-tests) + --platform Override the detected platform to use for tests and results (default: $platform) + --port Web server port to use with http tests + -q|--quiet Less verbose output + --reset-results Reset ALL results (including pixel tests if --pixel-tests is set) + -o|--results-directory Output results directory (default: $testResultsDirectory) + --random Run the tests in a random order + --reverse Run the tests in reverse alphabetical order + --root Path to root tools build + --[no-]sample-on-timeout Run sample on timeout (default: $sampleDefault) (Mac OS X only) + -1|--singly Isolate each test case run (implies --verbose) + --skipped=[default|ignore|only] Specifies how to treat the Skipped file + default: Tests/directories listed in the Skipped file are not tested + ignore: The Skipped file is ignored + only: Only those tests/directories listed in the Skipped file will be run + --slowest Report the 10 slowest tests + --strict Do a comparison with the output on Mac (Qt only) + --[no-]strip-editing-callbacks Remove editing callbacks from expected results + -t|--threaded Run a concurrent JavaScript thead with each test + --valgrind Run DumpRenderTree inside valgrind (Qt/Linux only) + -v|--verbose More verbose output (overrides --quiet) + -m|--merge-leak-depth arg Merges leak callStacks and prints the number of unique leaks beneath a callstack depth of arg. Defaults to 5. + --64-bit run in 64bit mode +EOF + +# Process @ARGV for configuration switches before calling GetOptions() +$configuration = passedConfiguration() || configuration(); + +my $getOptionsResult = GetOptions( + 'complex-text' => \$complexText, + 'c|configuration=s' => \$configuration, + 'guard-malloc|g' => \$guardMalloc, + 'help' => \$showHelp, + 'http!' => \$testHTTP, + 'ignore-tests|i=s' => \$ignoreTests, + 'launch-safari!' => \$launchSafari, + 'leaks|l' => \$shouldCheckLeaks, + 'pixel-tests|p' => \$pixelTests, + 'platform=s' => \$platform, + 'port=i' => \$httpdPort, + 'quiet|q' => \$quiet, + 'reset-results' => \$resetResults, + 'new-test-results!' => \$generateNewResults, + 'results-directory|o=s' => \$testResultsDirectory, + 'singly|1' => sub { $testsPerDumpTool = 1; }, + 'nthly=i' => \$testsPerDumpTool, + 'skipped=s' => \&validateSkippedArg, + 'slowest' => \$report10Slowest, + 'threaded|t' => \$threaded, + 'tolerance=f' => \$tolerance, + 'verbose|v' => \$verbose, + 'valgrind' => \$useValgrind, + 'sample-on-timeout!' => \$runSample, + 'strict' => \$strictTesting, + 'strip-editing-callbacks!' => \$stripEditingCallbacks, + 'random' => \$randomizeTests, + 'reverse' => \$reverseTests, + 'root=s' => \$root, + 'add-platform-exceptions' => \$addPlatformExceptions, + 'merge-leak-depth|m:5' => \$mergeDepth, + '64-bit!' => \$run64Bit +); + +if (!$getOptionsResult || $showHelp) { + print STDERR $usage; + exit 1; +} + +my $ignoreSkipped = $treatSkipped eq "ignore"; +my $skippedOnly = $treatSkipped eq "only"; + +!$skippedOnly || @ARGV == 0 or die "--skipped=only cannot be used when tests are specified on the command line."; + +setConfiguration($configuration); + +my $configurationOption = "--" . lc($configuration); + +$pixelTests = 1 if $tolerance > 0; + +$testsPerDumpTool = 1000 if !$testsPerDumpTool; + +$verbose = 1 if $testsPerDumpTool == 1; + +if ($shouldCheckLeaks && $testsPerDumpTool > 1000) { + print STDERR "\nWARNING: Running more than 1000 tests at a time with MallocStackLogging enabled may cause a crash.\n\n"; +} + +# Stack logging does not play well with QuickTime on Tiger (rdar://problem/5537157) +$testMedia = 0 if $shouldCheckLeaks && isTiger(); + +setConfigurationProductDir(Cwd::abs_path($root)) if (defined($root)); +my $productDir = productDir(); +$productDir .= "/bin" if isQt(); +$productDir .= "/Programs" if isGtk(); +setRun64Bit($run64Bit); + +chdirWebKit(); + +if (!defined($root)) { + # Push the parameters to build-dumprendertree as an array + my @args; + push(@args, $configurationOption); + push(@args, "--qt") if isQt(); + push(@args, "--gtk") if isGtk(); + + if (isOSX()) { + my $arch = preferredArchitecture(); + push(@args, "ARCHS=$arch"); + } + + my $buildResult = system "WebKitTools/Scripts/build-dumprendertree", @args; + if ($buildResult) { + print STDERR "Compiling DumpRenderTree failed!\n"; + exit exitStatus($buildResult); + } +} + +my $dumpToolName = "DumpRenderTree"; +$dumpToolName .= "_debug" if isCygwin() && $configuration ne "Release"; +my $dumpTool = "$productDir/$dumpToolName"; +die "can't find executable $dumpToolName (looked in $productDir)\n" unless -x $dumpTool; + +my $imageDiffTool = "$productDir/ImageDiff"; +$imageDiffTool .= "_debug" if isCygwin() && $configuration ne "Release"; +die "can't find executable $imageDiffTool (looked in $productDir)\n" if $pixelTests && !-x $imageDiffTool; + +checkFrameworks() unless isCygwin(); + +my $layoutTestsName = "LayoutTests"; +my $testDirectory = File::Spec->rel2abs($layoutTestsName); +my $expectedDirectory = $testDirectory; +my $platformBaseDirectory = catdir($testDirectory, "platform"); +my $platformTestDirectory = catdir($platformBaseDirectory, $platform); +my @platformHierarchy = buildPlatformHierarchy(); + +$expectedDirectory = $ENV{"WebKitExpectedTestResultsDirectory"} if $ENV{"WebKitExpectedTestResultsDirectory"}; + +my $testResults = catfile($testResultsDirectory, "results.html"); + +print "Running tests from $testDirectory\n"; + +my @tests = (); +my %testType = (); + +system "ln", "-s", $testDirectory, "/tmp/LayoutTests" unless -x "/tmp/LayoutTests"; + +my %ignoredFiles = (); +my %ignoredDirectories = map { $_ => 1 } qw(platform); +my %ignoredLocalDirectories = map { $_ => 1 } qw(.svn _svn resources); +my %supportedFileExtensions = map { $_ => 1 } qw(html shtml xml xhtml pl php); +if (checkWebCoreSVGSupport(0)) { + $supportedFileExtensions{'svg'} = 1; +} elsif (isCygwin()) { + # FIXME: We should fix webkitdirs.pm:hasSVGSupport() to do the correct + # check for Windows instead of forcing this here. + $supportedFileExtensions{'svg'} = 1; +} else { + $ignoredLocalDirectories{'svg'} = 1; +} +if (!$testHTTP) { + $ignoredDirectories{'http'} = 1; +} + +if (!$testMedia) { + $ignoredDirectories{'media'} = 1; + $ignoredDirectories{'http/tests/media'} = 1; +} + +if ($ignoreTests) { + processIgnoreTests($ignoreTests); +} + +if (!$ignoreSkipped) { + foreach my $level (@platformHierarchy) { + if (open SKIPPED, "<", "$level/Skipped") { + if ($verbose && !$skippedOnly) { + my ($dir, $name) = splitpath($level); + print "Skipped tests in $name:\n"; + } + + while (<SKIPPED>) { + my $skipped = $_; + chomp $skipped; + $skipped =~ s/^[ \n\r]+//; + $skipped =~ s/[ \n\r]+$//; + if ($skipped && $skipped !~ /^#/) { + if ($skippedOnly) { + push(@ARGV, $skipped); + } else { + if ($verbose) { + print " $skipped\n"; + } + processIgnoreTests($skipped); + } + } + } + close SKIPPED; + } + } +} + + +my $directoryFilter = sub { + return () if exists $ignoredLocalDirectories{basename($File::Find::dir)}; + return () if exists $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)}; + return @_; +}; + +my $fileFilter = sub { + my $filename = $_; + if ($filename =~ /\.([^.]+)$/) { + if (exists $supportedFileExtensions{$1}) { + my $path = File::Spec->abs2rel(catfile($File::Find::dir, $filename), $testDirectory); + push @tests, $path if !exists $ignoredFiles{$path}; + } + } +}; + +for my $test (@ARGV) { + $test =~ s/^($layoutTestsName|$testDirectory)\///; + my $fullPath = catfile($testDirectory, $test); + if (file_name_is_absolute($test)) { + print "can't run test $test outside $testDirectory\n"; + } elsif (-f $fullPath) { + my ($filename, $pathname, $fileExtension) = fileparse($test, qr{\.[^.]+$}); + if (!exists $supportedFileExtensions{substr($fileExtension, 1)}) { + print "test $test does not have a supported extension\n"; + } elsif ($testHTTP || $pathname !~ /^http\//) { + push @tests, $test; + } + } elsif (-d $fullPath) { + find({ preprocess => $directoryFilter, wanted => $fileFilter }, $fullPath); + + for my $level (@platformHierarchy) { + my $platformPath = catfile($level, $test); + find({ preprocess => $directoryFilter, wanted => $fileFilter }, $platformPath) if (-d $platformPath); + } + } else { + print "test $test not found\n"; + } +} +if (!scalar @ARGV) { + find({ preprocess => $directoryFilter, wanted => $fileFilter }, $testDirectory); + + for my $level (@platformHierarchy) { + find({ preprocess => $directoryFilter, wanted => $fileFilter }, $level); + } +} + +die "no tests to run\n" if !@tests; + +@tests = sort pathcmp @tests; + +my %counts; +my %tests; +my %imagesPresent; +my %imageDifferences; +my %durations; +my $count = 0; +my $leaksOutputFileNumber = 1; +my $totalLeaks = 0; + +my @toolArgs = (); +push @toolArgs, "--pixel-tests" if $pixelTests; +push @toolArgs, "--threaded" if $threaded; +push @toolArgs, "--complex-text" if $complexText; +push @toolArgs, "-"; + +my @diffToolArgs = (); +push @diffToolArgs, "--tolerance", $tolerance; + +$| = 1; + +my $imageDiffToolPID; +if ($pixelTests) { + local %ENV; + $ENV{MallocStackLogging} = 1 if $shouldCheckLeaks; + $imageDiffToolPID = open2(\*DIFFIN, \*DIFFOUT, $imageDiffTool, @diffToolArgs) or die "unable to open $imageDiffTool\n"; + $ENV{MallocStackLogging} = 0 if $shouldCheckLeaks; +} + +my $dumpToolPID; +my $isDumpToolOpen = 0; +my $dumpToolCrashed = 0; + +my $atLineStart = 1; +my $lastDirectory = ""; + +my $isHttpdOpen = 0; + +sub catch_pipe { $dumpToolCrashed = 1; } +$SIG{"PIPE"} = "catch_pipe"; + +print "Testing ", scalar @tests, " test cases.\n"; +my $overallStartTime = time; + +my %expectedResultPaths; + +# Reverse the tests +@tests = reverse @tests if $reverseTests; + +# Shuffle the array +@tests = shuffle(@tests) if $randomizeTests; + +for my $test (@tests) { + next if $test eq 'results.html'; + + my $newDumpTool = not $isDumpToolOpen; + openDumpTool(); + + my $base = stripExtension($test); + my $expectedExtension = ".txt"; + + my $dir = $base; + $dir =~ s|/[^/]+$||; + + if ($newDumpTool || $dir ne $lastDirectory) { + foreach my $logue (epiloguesAndPrologues($newDumpTool ? "" : $lastDirectory, $dir)) { + if (isCygwin()) { + $logue = toWindowsPath($logue); + } else { + $logue = canonpath($logue); + } + if ($verbose) { + print "running epilogue or prologue $logue\n"; + } + print OUT "$logue\n"; + # Throw away output from DumpRenderTree. + # Once for the test output and once for pixel results (empty) + while (<IN>) { + last if /#EOF/; + } + while (<IN>) { + last if /#EOF/; + } + } + } + + if ($verbose) { + print "running $test -> "; + $atLineStart = 0; + } elsif (!$quiet) { + if ($dir ne $lastDirectory) { + print "\n" unless $atLineStart; + print "$dir "; + } + print "."; + $atLineStart = 0; + } + + $lastDirectory = $dir; + + my $result; + + my $startTime = time if $report10Slowest; + + # Try to read expected hash file for pixel tests + my $suffixExpectedHash = ""; + if ($pixelTests && !$resetResults) { + my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png"); + if (open EXPECTEDHASH, "$expectedPixelDir/$base-$expectedTag.checksum") { + my $expectedHash = <EXPECTEDHASH>; + chomp($expectedHash); + close EXPECTEDHASH; + + # Format expected hash into a suffix string that is appended to the path / URL passed to DRT + $suffixExpectedHash = "'$expectedHash"; + } + } + + if ($test !~ /^http\//) { + my $testPath = "$testDirectory/$test"; + if (isCygwin()) { + $testPath = toWindowsPath($testPath); + } else { + $testPath = canonpath($testPath); + } + print OUT "$testPath$suffixExpectedHash\n"; + } else { + openHTTPDIfNeeded(); + if ($test !~ /^http\/tests\/local\// && $test !~ /^http\/tests\/ssl\// && $test !~ /^http\/tests\/media\//) { + my $path = canonpath($test); + $path =~ s/^http\/tests\///; + print OUT "http://127.0.0.1:$httpdPort/$path$suffixExpectedHash\n"; + } elsif ($test =~ /^http\/tests\/ssl\//) { + my $path = canonpath($test); + $path =~ s/^http\/tests\///; + print OUT "https://127.0.0.1:$httpdSSLPort/$path$suffixExpectedHash\n"; + } else { + my $testPath = "$testDirectory/$test"; + if (isCygwin()) { + $testPath = toWindowsPath($testPath); + } else { + $testPath = canonpath($testPath); + } + print OUT "$testPath$suffixExpectedHash\n"; + } + } + + # DumpRenderTree is expected to dump two "blocks" to stdout for each test. + # Each block is terminated by a #EOF on a line by itself. + # The first block is the output of the test (in text, RenderTree or other formats). + # The second block is for optional pixel data in PNG format, and may be empty if + # pixel tests are not being run, or the test does not dump pixels (e.g. text tests). + + my $actualRead = readFromDumpToolWithTimer(IN); + my $errorRead = readFromDumpToolWithTimer(ERROR, $actualRead->{status} eq "timedOut"); + + my $actual = $actualRead->{output}; + my $error = $errorRead->{output}; + + $expectedExtension = $actualRead->{extension}; + my $expectedFileName = "$base-$expectedTag.$expectedExtension"; + + my $isText = isTextOnlyTest($actual); + + my $expectedDir = expectedDirectoryForTest($base, $isText, $expectedExtension); + $expectedResultPaths{$base} = "$expectedDir/$expectedFileName"; + + unless ($actualRead->{status} eq "success" && $errorRead->{status} eq "success") { + my $crashed = $actualRead->{status} eq "crashed" || $errorRead->{status} eq "crashed"; + testCrashedOrTimedOut($test, $base, $crashed, $actual, $error); + countFinishedTest($test, $base, $crashed ? "crash" : "timedout", 0); + next; + } + + $durations{$test} = time - $startTime if $report10Slowest; + + my $expected; + + if (!$resetResults && open EXPECTED, "<", "$expectedDir/$expectedFileName") { + $expected = ""; + while (<EXPECTED>) { + next if $stripEditingCallbacks && $_ =~ /^EDITING DELEGATE:/; + $expected .= $_; + } + close EXPECTED; + } + my $expectedMac; + if (!isOSX() && $strictTesting && !$isText) { + if (!$resetResults && open EXPECTED, "<", "$testDirectory/platform/mac/$expectedFileName") { + $expectedMac = ""; + while (<EXPECTED>) { + $expectedMac .= $_; + } + close EXPECTED; + } + } + + if ($shouldCheckLeaks && $testsPerDumpTool == 1) { + print " $test -> "; + } + + my $actualPNG = ""; + my $diffPNG = ""; + my $diffPercentage = ""; + my $diffResult = "passed"; + + my $actualHash = ""; + my $expectedHash = ""; + my $actualPNGSize = 0; + + while (<IN>) { + last if /#EOF/; + if (/ActualHash: ([a-f0-9]{32})/) { + $actualHash = $1; + } elsif (/ExpectedHash: ([a-f0-9]{32})/) { + $expectedHash = $1; + } elsif (/Content-Length: (\d+)\s*/) { + $actualPNGSize = $1; + read(IN, $actualPNG, $actualPNGSize); + } + } + + if ($verbose && $pixelTests && !$resetResults && $actualPNGSize) { + if ($actualHash eq "" && $expectedHash eq "") { + printFailureMessageForTest($test, "WARNING: actual & expected pixel hashes are missing!"); + } elsif ($actualHash eq "") { + printFailureMessageForTest($test, "WARNING: actual pixel hash is missing!"); + } elsif ($expectedHash eq "") { + printFailureMessageForTest($test, "WARNING: expected pixel hash is missing!"); + } + } + + if ($actualPNGSize > 0) { + my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png"); + + if (!$resetResults && ($expectedHash ne $actualHash || ($actualHash eq "" && $expectedHash eq ""))) { + if (-f "$expectedPixelDir/$base-$expectedTag.png") { + my $expectedPNGSize = -s "$expectedPixelDir/$base-$expectedTag.png"; + my $expectedPNG = ""; + open EXPECTEDPNG, "$expectedPixelDir/$base-$expectedTag.png"; + read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize); + + print DIFFOUT "Content-Length: $actualPNGSize\n"; + print DIFFOUT $actualPNG; + + print DIFFOUT "Content-Length: $expectedPNGSize\n"; + print DIFFOUT $expectedPNG; + + while (<DIFFIN>) { + last if /^error/ || /^diff:/; + if (/Content-Length: (\d+)\s*/) { + read(DIFFIN, $diffPNG, $1); + } + } + + if (/^diff: (.+)% (passed|failed)/) { + $diffPercentage = $1; + $imageDifferences{$base} = $diffPercentage; + $diffResult = $2; + } + + if ($diffPercentage == 0) { + printFailureMessageForTest($test, "pixel hash failed (but pixel test still passes)"); + } + } elsif ($verbose) { + printFailureMessageForTest($test, "WARNING: expected image is missing!"); + } + } + + if ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.png") { + mkpath catfile($expectedPixelDir, dirname($base)) if $testDirectory ne $expectedPixelDir; + writeToFile("$expectedPixelDir/$base-$expectedTag.png", $actualPNG); + } + + if ($actualHash ne "" && ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.checksum")) { + writeToFile("$expectedPixelDir/$base-$expectedTag.checksum", $actualHash); + } + } + + if (!isOSX() && $strictTesting && !$isText) { + if (defined $expectedMac) { + my $simplified_actual; + $simplified_actual = $actual; + $simplified_actual =~ s/at \(-?[0-9]+,-?[0-9]+\) *//g; + $simplified_actual =~ s/size -?[0-9]+x-?[0-9]+ *//g; + $simplified_actual =~ s/text run width -?[0-9]+: //g; + $simplified_actual =~ s/text run width -?[0-9]+ [a-zA-Z ]+: //g; + $simplified_actual =~ s/RenderButton {BUTTON} .*/RenderButton {BUTTON}/g; + $simplified_actual =~ s/RenderImage {INPUT} .*/RenderImage {INPUT}/g; + $simplified_actual =~ s/RenderBlock {INPUT} .*/RenderBlock {INPUT}/g; + $simplified_actual =~ s/RenderTextControl {INPUT} .*/RenderTextControl {INPUT}/g; + $simplified_actual =~ s/\([0-9]+px/px/g; + $simplified_actual =~ s/ *" *\n +" */ /g; + $simplified_actual =~ s/" +$/"/g; + + $simplified_actual =~ s/- /-/g; + $simplified_actual =~ s/\n( *)"\s+/\n$1"/g; + $simplified_actual =~ s/\s+"\n/"\n/g; + + $expectedMac =~ s/at \(-?[0-9]+,-?[0-9]+\) *//g; + $expectedMac =~ s/size -?[0-9]+x-?[0-9]+ *//g; + $expectedMac =~ s/text run width -?[0-9]+: //g; + $expectedMac =~ s/text run width -?[0-9]+ [a-zA-Z ]+: //g; + $expectedMac =~ s/RenderButton {BUTTON} .*/RenderButton {BUTTON}/g; + $expectedMac =~ s/RenderImage {INPUT} .*/RenderImage {INPUT}/g; + $expectedMac =~ s/RenderBlock {INPUT} .*/RenderBlock {INPUT}/g; + $expectedMac =~ s/RenderTextControl {INPUT} .*/RenderTextControl {INPUT}/g; + $expectedMac =~ s/\([0-9]+px/px/g; + $expectedMac =~ s/ *" *\n +" */ /g; + $expectedMac =~ s/" +$/"/g; + + $expectedMac =~ s/- /-/g; + $expectedMac =~ s/\n( *)"\s+/\n$1"/g; + $expectedMac =~ s/\s+"\n/"\n/g; + + if ($simplified_actual ne $expectedMac) { + writeToFile("/tmp/actual.txt", $simplified_actual); + writeToFile("/tmp/expected.txt", $expectedMac); + system "diff -u \"/tmp/expected.txt\" \"/tmp/actual.txt\" > \"/tmp/simplified.diff\""; + + $diffResult = "failed"; + if ($verbose) { + print "\n"; + system "cat /tmp/simplified.diff"; + print "failed!!!!!"; + } + } + } + } + + if (dumpToolDidCrash()) { + $result = "crash"; + testCrashedOrTimedOut($test, $base, 1, $actual, $error); + } elsif (!defined $expected) { + if ($verbose) { + print "new " . ($resetResults ? "result" : "test") ."\n"; + $atLineStart = 1; + } + $result = "new"; + + if ($generateNewResults || $resetResults) { + mkpath catfile($expectedDir, dirname($base)) if $testDirectory ne $expectedDir; + writeToFile("$expectedDir/$expectedFileName", $actual); + } + deleteExpectedAndActualResults($base); + unless ($resetResults) { + # Always print the file name for new tests, as they will probably need some manual inspection. + # in verbose mode we already printed the test case, so no need to do it again. + unless ($verbose) { + print "\n" unless $atLineStart; + print "$test -> "; + } + my $resultsDir = catdir($expectedDir, dirname($base)); + print "new (results generated in $resultsDir)\n"; + $atLineStart = 1; + } + } elsif ($actual eq $expected && $diffResult eq "passed") { + if ($verbose) { + print "succeeded\n"; + $atLineStart = 1; + } + $result = "match"; + deleteExpectedAndActualResults($base); + } else { + $result = "mismatch"; + + my $message = $actual eq $expected ? "pixel test failed" : "failed"; + + if ($actual ne $expected && $addPlatformExceptions) { + my $testBase = catfile($testDirectory, $base); + my $expectedBase = catfile($expectedDir, $base); + my $testIsMaximallyPlatformSpecific = $testBase =~ m|^\Q$platformTestDirectory\E/|; + my $expectedResultIsMaximallyPlatformSpecific = $expectedBase =~ m|^\Q$platformTestDirectory\E/|; + if (!$testIsMaximallyPlatformSpecific && !$expectedResultIsMaximallyPlatformSpecific) { + mkpath catfile($platformTestDirectory, dirname($base)); + my $expectedFile = catfile($platformTestDirectory, "$expectedFileName"); + writeToFile("$expectedFile", $actual); + $message .= " (results generated in $platformTestDirectory)"; + } + } + + printFailureMessageForTest($test, $message); + + my $dir = "$testResultsDirectory/$base"; + $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n"; + my $testName = $1; + mkpath $dir; + + deleteExpectedAndActualResults($base); + recordActualResultsAndDiff($base, $actual); + + if ($pixelTests && $diffPNG && $diffPNG ne "") { + $imagesPresent{$base} = 1; + + writeToFile("$testResultsDirectory/$base-$actualTag.png", $actualPNG); + writeToFile("$testResultsDirectory/$base-$diffsTag.png", $diffPNG); + + my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png"); + copy("$expectedPixelDir/$base-$expectedTag.png", "$testResultsDirectory/$base-$expectedTag.png"); + + open DIFFHTML, ">$testResultsDirectory/$base-$diffsTag.html" or die; + print DIFFHTML "<html>\n"; + print DIFFHTML "<head>\n"; + print DIFFHTML "<title>$base Image Compare</title>\n"; + print DIFFHTML "<script language=\"Javascript\" type=\"text/javascript\">\n"; + print DIFFHTML "var currentImage = 0;\n"; + print DIFFHTML "var imageNames = new Array(\"Actual\", \"Expected\");\n"; + print DIFFHTML "var imagePaths = new Array(\"$testName-$actualTag.png\", \"$testName-$expectedTag.png\");\n"; + if (-f "$testDirectory/$base-w3c.png") { + copy("$testDirectory/$base-w3c.png", "$testResultsDirectory/$base-w3c.png"); + print DIFFHTML "imageNames.push(\"W3C\");\n"; + print DIFFHTML "imagePaths.push(\"$testName-w3c.png\");\n"; + } + print DIFFHTML "function animateImage() {\n"; + print DIFFHTML " var image = document.getElementById(\"animatedImage\");\n"; + print DIFFHTML " var imageText = document.getElementById(\"imageText\");\n"; + print DIFFHTML " image.src = imagePaths[currentImage];\n"; + print DIFFHTML " imageText.innerHTML = imageNames[currentImage] + \" Image\";\n"; + print DIFFHTML " currentImage = (currentImage + 1) % imageNames.length;\n"; + print DIFFHTML " setTimeout('animateImage()',2000);\n"; + print DIFFHTML "}\n"; + print DIFFHTML "</script>\n"; + print DIFFHTML "</head>\n"; + print DIFFHTML "<body onLoad=\"animateImage();\">\n"; + print DIFFHTML "<table>\n"; + if ($diffPercentage) { + print DIFFHTML "<tr>\n"; + print DIFFHTML "<td>Difference between images: <a href=\"$testName-$diffsTag.png\">$diffPercentage%</a></td>\n"; + print DIFFHTML "</tr>\n"; + } + print DIFFHTML "<tr>\n"; + print DIFFHTML "<td><a href=\"" . toURL("$testDirectory/$test") . "\">test file</a></td>\n"; + print DIFFHTML "</tr>\n"; + print DIFFHTML "<tr>\n"; + print DIFFHTML "<td id=\"imageText\" style=\"text-weight: bold;\">Actual Image</td>\n"; + print DIFFHTML "</tr>\n"; + print DIFFHTML "<tr>\n"; + print DIFFHTML "<td><img src=\"$testName-$actualTag.png\" id=\"animatedImage\"></td>\n"; + print DIFFHTML "</tr>\n"; + print DIFFHTML "</table>\n"; + print DIFFHTML "</body>\n"; + print DIFFHTML "</html>\n"; + } + } + + if ($error) { + my $dir = "$testResultsDirectory/$base"; + $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n"; + mkpath $dir; + + writeToFile("$testResultsDirectory/$base-$errorTag.txt", $error); + + $counts{error}++; + push @{$tests{error}}, $test; + } + + countFinishedTest($test, $base, $result, $isText); +} +printf "\n%0.2fs total testing time\n", (time - $overallStartTime) . ""; + +!$isDumpToolOpen || die "Failed to close $dumpToolName.\n"; + +closeHTTPD(); + +# Because multiple instances of this script are running concurrently we cannot +# safely delete this symlink. +# system "rm /tmp/LayoutTests"; + +# FIXME: Do we really want to check the image-comparison tool for leaks every time? +if ($shouldCheckLeaks && $pixelTests) { + $totalLeaks += countAndPrintLeaks("ImageDiff", $imageDiffToolPID, "$testResultsDirectory/ImageDiff-leaks.txt"); +} + +if ($totalLeaks) { + if ($mergeDepth) { + parseLeaksandPrintUniqueLeaks(); + } + else { + print "\nWARNING: $totalLeaks total leaks found!\n"; + print "See above for individual leaks results.\n" if ($leaksOutputFileNumber > 2); + } +} + +close IN; +close OUT; +close ERROR; + +if ($report10Slowest) { + print "\n\nThe 10 slowest tests:\n\n"; + my $count = 0; + for my $test (sort slowestcmp keys %durations) { + printf "%0.2f secs: %s\n", $durations{$test}, $test; + last if ++$count == 10; + } +} + +print "\n"; + +if ($skippedOnly && $counts{"match"}) { + print "The following tests are in the Skipped file (" . File::Spec->abs2rel("$platformTestDirectory/Skipped", $testDirectory) . "), but succeeded:\n"; + foreach my $test (@{$tests{"match"}}) { + print " $test\n"; + } +} + +if ($resetResults || ($counts{match} && $counts{match} == $count)) { + print "all $count test cases succeeded\n"; + unlink $testResults; + exit; +} + + +my %text = ( + match => "succeeded", + mismatch => "had incorrect layout", + new => "were new", + timedout => "timed out", + crash => "crashed", + error => "had stderr output" +); + +for my $type ("match", "mismatch", "new", "timedout", "crash", "error") { + my $c = $counts{$type}; + if ($c) { + my $t = $text{$type}; + my $message; + if ($c == 1) { + $t =~ s/were/was/; + $message = sprintf "1 test case (%d%%) %s\n", 1 * 100 / $count, $t; + } else { + $message = sprintf "%d test cases (%d%%) %s\n", $c, $c * 100 / $count, $t; + } + $message =~ s-\(0%\)-(<1%)-; + print $message; + } +} + +mkpath $testResultsDirectory; + +open HTML, ">", $testResults or die; +print HTML "<html>\n"; +print HTML "<head>\n"; +print HTML "<title>Layout Test Results</title>\n"; +print HTML "</head>\n"; +print HTML "<body>\n"; + +print HTML htmlForResultsSection(@{$tests{mismatch}}, "Tests where results did not match expected results", \&linksForMismatchTest); +print HTML htmlForResultsSection(@{$tests{timedout}}, "Tests that timed out", \&linksForErrorTest); +print HTML htmlForResultsSection(@{$tests{crash}}, "Tests that caused the DumpRenderTree tool to crash", \&linksForErrorTest); +print HTML htmlForResultsSection(@{$tests{error}}, "Tests that had stderr output", \&linksForErrorTest); +print HTML htmlForResultsSection(@{$tests{new}}, "Tests that had no expected results (probably new)", \&linksForNewTest); + +print HTML "</body>\n"; +print HTML "</html>\n"; +close HTML; + +if (isQt()) { + system "WebKitTools/Scripts/run-launcher", "--qt", $configurationOption, $testResults if $launchSafari; +} elsif (isGtk()) { + system "WebKitTools/Scripts/run-launcher", "--gtk", $configurationOption, $testResults if $launchSafari; +} elsif (isCygwin()) { + system "cygstart", $testResults if $launchSafari; +} else { + system "WebKitTools/Scripts/run-safari", $configurationOption, "-NSOpen", $testResults if $launchSafari; +} + +closeCygpaths() if isCygwin(); + +exit 1; + +sub countAndPrintLeaks($$$) +{ + my ($dumpToolName, $dumpToolPID, $leaksFilePath) = @_; + + print "\n" unless $atLineStart; + $atLineStart = 1; + + # We are excluding the following reported leaks so they don't get in our way when looking for WebKit leaks: + # This allows us ignore known leaks and only be alerted when new leaks occur. Some leaks are in the old + # versions of the system frameworks that are being used by the leaks bots. Even though a leak has been + # fixed, it will be listed here until the bot has been updated with the newer frameworks. + + my @typesToExclude = ( + ); + + my @callStacksToExclude = ( + "Flash_EnforceLocalSecurity" # leaks in Flash plug-in code, rdar://problem/4449747 + ); + + if (isTiger()) { + # Leak list for the version of Tiger used on the build bot. + push @callStacksToExclude, ( + "CFRunLoopRunSpecific \\| malloc_zone_malloc", "CFRunLoopRunSpecific \\| CFAllocatorAllocate ", # leak in CFRunLoopRunSpecific, rdar://problem/4670839 + "CGImageSourceGetPropertiesAtIndex", # leak in ImageIO, rdar://problem/4628809 + "FOGetCoveredUnicodeChars", # leak in ATS, rdar://problem/3943604 + "GetLineDirectionPreference", "InitUnicodeUtilities", # leaks tool falsely reporting leak in CFNotificationCenterAddObserver, rdar://problem/4964790 + "ICCFPrefWrapper::GetPrefDictionary", # leaks in Internet Config. code, rdar://problem/4449794 + "NSHTTPURLProtocol setResponseHeader:", # leak in multipart/mixed-replace handling in Foundation, no Radar, but fixed in Leopard + "NSURLCache cachedResponseForRequest", # leak in CFURL cache, rdar://problem/4768430 + "PCFragPrepareClosureFromFile", # leak in Code Fragment Manager, rdar://problem/3426998 + "WebCore::Selection::toRange", # bug in 'leaks', rdar://problem/4967949 + "WebCore::SubresourceLoader::create", # bug in 'leaks', rdar://problem/4985806 + "_CFPreferencesDomainDeepCopyDictionary", # leak in CFPreferences, rdar://problem/4220786 + "_objc_msgForward", # leak in NSSpellChecker, rdar://problem/4965278 + "gldGetString", # leak in OpenGL, rdar://problem/5013699 + "_setDefaultUserInfoFromURL", # leak in NSHTTPAuthenticator, rdar://problem/5546453 + "SSLHandshake", # leak in SSL, rdar://problem/5546440 + "SecCertificateCreateFromData", # leak in SSL code, rdar://problem/4464397 + ); + push @typesToExclude, ( + "THRD", # bug in 'leaks', rdar://problem/3387783 + "DRHT", # ditto (endian little hate i) + ); + } + + if (isLeopard()) { + # Leak list for the version of Leopard used on the build bot. + push @callStacksToExclude, ( + "CFHTTPMessageAppendBytes", # leak in CFNetwork, rdar://problem/5435912 + "sendDidReceiveDataCallback", # leak in CFNetwork, rdar://problem/5441619 + "_CFHTTPReadStreamReadMark", # leak in CFNetwork, rdar://problem/5441468 + "httpProtocolStart", # leak in CFNetwork, rdar://problem/5468837 + "_CFURLConnectionSendCallbacks", # leak in CFNetwork, rdar://problem/5441600 + "DispatchQTMsg", # leak in QuickTime, PPC only, rdar://problem/5667132 + "QTMovieContentView createVisualContext", # leak in QuickTime, PPC only, rdar://problem/5667132 + "_CopyArchitecturesForJVMVersion", # leak in Java, rdar://problem/5910823 + ); + } + + my $leaksTool = sourceDir() . "/WebKitTools/Scripts/run-leaks"; + my $excludeString = "--exclude-callstack '" . (join "' --exclude-callstack '", @callStacksToExclude) . "'"; + $excludeString .= " --exclude-type '" . (join "' --exclude-type '", @typesToExclude) . "'" if @typesToExclude; + + print " ? checking for leaks in $dumpToolName\n"; + my $leaksOutput = `$leaksTool $excludeString $dumpToolPID`; + my ($count, $bytes) = $leaksOutput =~ /Process $dumpToolPID: (\d+) leaks? for (\d+) total/; + my ($excluded) = $leaksOutput =~ /(\d+) leaks? excluded/; + + my $adjustedCount = $count; + $adjustedCount -= $excluded if $excluded; + + if (!$adjustedCount) { + print " - no leaks found\n"; + unlink $leaksFilePath; + return 0; + } else { + my $dir = $leaksFilePath; + $dir =~ s|/[^/]+$|| or die; + mkpath $dir; + + if ($excluded) { + print " + $adjustedCount leaks ($bytes bytes including $excluded excluded leaks) were found, details in $leaksFilePath\n"; + } else { + print " + $count leaks ($bytes bytes) were found, details in $leaksFilePath\n"; + } + + writeToFile($leaksFilePath, $leaksOutput); + + push( @leaksFilenames, $leaksFilePath ); + } + + return $adjustedCount; +} + +sub writeToFile($$) +{ + my ($filePath, $contents) = @_; + open NEWFILE, ">", "$filePath" or die "could not create $filePath\n"; + print NEWFILE $contents; + close NEWFILE; +} + +# Break up a path into the directory (with slash) and base name. +sub splitpath($) +{ + my ($path) = @_; + + my $pathSeparator = "/"; + my $dirname = dirname($path) . $pathSeparator; + $dirname = "" if $dirname eq "." . $pathSeparator; + + return ($dirname, basename($path)); +} + +# Sort first by directory, then by file, so all paths in one directory are grouped +# rather than being interspersed with items from subdirectories. +# Use numericcmp to sort directory and filenames to make order logical. +sub pathcmp($$) +{ + my ($patha, $pathb) = @_; + + my ($dira, $namea) = splitpath($patha); + my ($dirb, $nameb) = splitpath($pathb); + + return numericcmp($dira, $dirb) if $dira ne $dirb; + return numericcmp($namea, $nameb); +} + +# Sort numeric parts of strings as numbers, other parts as strings. +# Makes 1.33 come after 1.3, which is cool. +sub numericcmp($$) +{ + my ($aa, $bb) = @_; + + my @a = split /(\d+)/, $aa; + my @b = split /(\d+)/, $bb; + + # Compare one chunk at a time. + # Each chunk is either all numeric digits, or all not numeric digits. + while (@a && @b) { + my $a = shift @a; + my $b = shift @b; + + # Use numeric comparison if chunks are non-equal numbers. + return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b; + + # Use string comparison if chunks are any other kind of non-equal string. + return $a cmp $b if $a ne $b; + } + + # One of the two is now empty; compare lengths for result in this case. + return @a <=> @b; +} + +# Sort slowest tests first. +sub slowestcmp($$) +{ + my ($testa, $testb) = @_; + + my $dura = $durations{$testa}; + my $durb = $durations{$testb}; + return $durb <=> $dura if $dura != $durb; + return pathcmp($testa, $testb); +} + +sub openDumpTool() +{ + return if $isDumpToolOpen; + + # Save some requires variables for the linux environment... + my $homeDir = $ENV{'HOME'}; + my $libraryPath = $ENV{'LD_LIBRARY_PATH'}; + my $dyldLibraryPath = $ENV{'DYLD_LIBRARY_PATH'}; + my $dbusAddress = $ENV{'DBUS_SESSION_BUS_ADDRESS'}; + my $display = $ENV{'DISPLAY'}; + my $testfonts = $ENV{'WEBKIT_TESTFONTS'}; + + my $homeDrive = $ENV{'HOMEDRIVE'}; + my $homePath = $ENV{'HOMEPATH'}; + + local %ENV; + if (isQt() || isGtk()) { + if (defined $display) { + $ENV{DISPLAY} = $display; + } else { + $ENV{DISPLAY} = ":1"; + } + $ENV{'WEBKIT_TESTFONTS'} = $testfonts if defined($testfonts); + $ENV{HOME} = $homeDir; + if (defined $libraryPath) { + $ENV{LD_LIBRARY_PATH} = $libraryPath; + } + if (defined $dyldLibraryPath) { + $ENV{DYLD_LIBRARY_PATH} = $dyldLibraryPath; + } + if (defined $dbusAddress) { + $ENV{DBUS_SESSION_BUS_ADDRESS} = $dbusAddress; + } + } + $ENV{DYLD_FRAMEWORK_PATH} = $productDir; + $ENV{XML_CATALOG_FILES} = ""; # work around missing /etc/catalog <rdar://problem/4292995> + $ENV{DYLD_INSERT_LIBRARIES} = "/usr/lib/libgmalloc.dylib" if $guardMalloc; + + if (isCygwin()) { + $ENV{HOMEDRIVE} = $homeDrive; + $ENV{HOMEPATH} = $homePath; + if ($testfonts) { + $ENV{WEBKIT_TESTFONTS} = $testfonts; + } + setPathForRunningWebKitApp(\%ENV) if isCygwin(); + } + + exportArchPreference(); + + my @args = @toolArgs; + unshift @args, $dumpTool; + if (isOSX and !isTiger()) { + unshift @args, "arch"; + } + if ($useValgrind) { + unshift @args, "valgrind"; + } + + $ENV{MallocStackLogging} = 1 if $shouldCheckLeaks; + $dumpToolPID = open3(\*OUT, \*IN, \*ERROR, @args) or die "Failed to start tool: $dumpTool\n"; + $ENV{MallocStackLogging} = 0 if $shouldCheckLeaks; + $isDumpToolOpen = 1; + $dumpToolCrashed = 0; +} + +sub closeDumpTool() +{ + return if !$isDumpToolOpen; + + close IN; + close OUT; + waitpid $dumpToolPID, 0; + + # check for WebCore counter leaks. + if ($shouldCheckLeaks) { + while (<ERROR>) { + print; + } + } + close ERROR; + $isDumpToolOpen = 0; +} + +sub dumpToolDidCrash() +{ + return 1 if $dumpToolCrashed; + return 0 unless $isDumpToolOpen; + + my $pid = waitpid(-1, WNOHANG); + return 1 if ($pid == $dumpToolPID); + + # On Mac OS X, crashing may be significantly delayed by crash reporter. + return 0 unless isOSX(); + + my $tryingToExit = 0; + open PS, "ps -o state -p $dumpToolPID |"; + <PS>; # skip header + $tryingToExit = 1 if <PS> =~ /E/; + close PS; + return $tryingToExit; +} + +sub openHTTPDIfNeeded() +{ + return if $isHttpdOpen; + + mkdir "/tmp/WebKit"; + + if (-f "/tmp/WebKit/httpd.pid") { + my $oldPid = `cat /tmp/WebKit/httpd.pid`; + chomp $oldPid; + if (0 != kill 0, $oldPid) { + print "\nhttpd is already running: pid $oldPid, killing...\n"; + kill 15, $oldPid; + + my $retryCount = 20; + while ((0 != kill 0, $oldPid) && $retryCount) { + sleep 1; + --$retryCount; + } + + die "Timed out waiting for httpd to quit" unless $retryCount; + } + } + + my $httpdPath = "/usr/sbin/httpd"; + my $httpdConfig; + if (isCygwin()) { + my $windowsConfDirectory = "$testDirectory/http/conf/"; + unless (-x "/usr/lib/apache/libphp4.dll") { + copy("$windowsConfDirectory/libphp4.dll", "/usr/lib/apache/libphp4.dll"); + chmod(0755, "/usr/lib/apache/libphp4.dll"); + } + $httpdConfig = "$windowsConfDirectory/cygwin-httpd.conf"; + } elsif (isDebianBased()) { + $httpdPath = "/usr/sbin/apache2"; + $httpdConfig = "$testDirectory/http/conf/apache2-debian-httpd.conf"; + } else { + $httpdConfig = "$testDirectory/http/conf/httpd.conf"; + $httpdConfig = "$testDirectory/http/conf/apache2-httpd.conf" if `$httpdPath -v` =~ m|Apache/2|; + } + my $documentRoot = "$testDirectory/http/tests"; + my $typesConfig = "$testDirectory/http/conf/mime.types"; + my $listen = "127.0.0.1:$httpdPort"; + my $absTestResultsDirectory = File::Spec->rel2abs(glob $testResultsDirectory); + my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem"; + + mkpath $absTestResultsDirectory; + + my @args = ( + "-f", "$httpdConfig", + "-C", "DocumentRoot \"$documentRoot\"", + "-C", "Listen $listen", + "-c", "TypesConfig \"$typesConfig\"", + "-c", "CustomLog \"$absTestResultsDirectory/access_log.txt\" common", + "-c", "ErrorLog \"$absTestResultsDirectory/error_log.txt\"", + # Apache wouldn't run CGIs with permissions==700 otherwise + "-c", "User \"#$<\"" + ); + + # FIXME: Enable this on Windows once <rdar://problem/5345985> is fixed + push(@args, "-c", "SSLCertificateFile \"$sslCertificate\"") unless isCygwin(); + + open2(\*HTTPDIN, \*HTTPDOUT, $httpdPath, @args); + + my $retryCount = 20; + while (system("/usr/bin/curl -q --silent --stderr - --output /dev/null $listen") && $retryCount) { + sleep 1; + --$retryCount; + } + + die "Timed out waiting for httpd to start" unless $retryCount; + + $isHttpdOpen = 1; +} + +sub closeHTTPD() +{ + return if !$isHttpdOpen; + + close HTTPDIN; + close HTTPDOUT; + + kill 15, `cat /tmp/WebKit/httpd.pid` if -f "/tmp/WebKit/httpd.pid"; + + $isHttpdOpen = 0; +} + +sub fileNameWithNumber($$) +{ + my ($base, $number) = @_; + return "$base$number" if ($number > 1); + return $base; +} + +sub processIgnoreTests($) { + my @ignoreList = split(/\s*,\s*/, shift); + my $addIgnoredDirectories = sub { + return () if exists $ignoredLocalDirectories{basename($File::Find::dir)}; + $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)} = 1; + return @_; + }; + foreach my $item (@ignoreList) { + my $path = catfile($testDirectory, $item); + if (-d $path) { + $ignoredDirectories{$item} = 1; + find({ preprocess => $addIgnoredDirectories, wanted => sub {} }, $path); + } + elsif (-f $path) { + $ignoredFiles{$item} = 1; + } + else { + print "ignoring '$item' on ignore-tests list\n"; + } + } +} + +sub stripExtension($) +{ + my ($test) = @_; + + $test =~ s/\.[a-zA-Z]+$//; + return $test; +} + +sub isTextOnlyTest($) +{ + my ($actual) = @_; + my $isText; + if ($actual =~ /^layer at/ms) { + $isText = 0; + } else { + $isText = 1; + } + return $isText; +} + +sub expectedDirectoryForTest($;$;$) +{ + my ($base, $isText, $expectedExtension) = @_; + + my @directories = @platformHierarchy; + push @directories, map { catdir($platformBaseDirectory, $_) } qw(mac-leopard mac) if isCygwin(); + push @directories, $expectedDirectory; + + # If we already have expected results, just return their location. + foreach my $directory (@directories) { + return $directory if (-f "$directory/$base-$expectedTag.$expectedExtension"); + } + + # For platform-specific tests, the results should go right next to the test itself. + # Note: The return value of this subroutine will be concatenated with $base + # to determine the location of the new results, so returning $expectedDirectory + # will put the results right next to the test. + # FIXME: We want to allow platform/mac tests with platform/mac-leopard results, + # so this needs to be enhanced. + return $expectedDirectory if $base =~ /^platform/; + + # For cross-platform tests, text-only results should go in the cross-platform directory, + # while render tree dumps should go in the least-specific platform directory. + return $isText ? $expectedDirectory : $platformHierarchy[$#platformHierarchy]; +} + +sub countFinishedTest($$$$) { + my ($test, $base, $result, $isText) = @_; + + if (($count + 1) % $testsPerDumpTool == 0 || $count == $#tests) { + if ($shouldCheckLeaks) { + my $fileName; + if ($testsPerDumpTool == 1) { + $fileName = "$testResultsDirectory/$base-leaks.txt"; + } else { + $fileName = "$testResultsDirectory/" . fileNameWithNumber($dumpToolName, $leaksOutputFileNumber) . "-leaks.txt"; + } + my $leakCount = countAndPrintLeaks($dumpToolName, $dumpToolPID, $fileName); + $totalLeaks += $leakCount; + $leaksOutputFileNumber++ if ($leakCount); + } + + closeDumpTool(); + } + + $count++; + $counts{$result}++; + push @{$tests{$result}}, $test; + $testType{$test} = $isText; +} + +sub testCrashedOrTimedOut($$$$$) +{ + my ($test, $base, $didCrash, $actual, $error) = @_; + + printFailureMessageForTest($test, $didCrash ? "crashed" : "timed out"); + + sampleDumpTool() unless $didCrash; + + my $dir = "$testResultsDirectory/$base"; + $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n"; + mkpath $dir; + + deleteExpectedAndActualResults($base); + + if (defined($error) && length($error)) { + writeToFile("$testResultsDirectory/$base-$errorTag.txt", $error); + } + + recordActualResultsAndDiff($base, $actual); + + kill 9, $dumpToolPID unless $didCrash; + + closeDumpTool(); +} + +sub printFailureMessageForTest($$) +{ + my ($test, $description) = @_; + + unless ($verbose) { + print "\n" unless $atLineStart; + print "$test -> "; + } + print "$description\n"; + $atLineStart = 1; +} + +my %cygpaths = (); + +sub openCygpathIfNeeded($) +{ + my ($options) = @_; + + return unless isCygwin(); + return $cygpaths{$options} if $cygpaths{$options} && $cygpaths{$options}->{"open"}; + + local (*CYGPATHIN, *CYGPATHOUT); + my $pid = open2(\*CYGPATHIN, \*CYGPATHOUT, "cygpath -f - $options"); + my $cygpath = { + "pid" => $pid, + "in" => *CYGPATHIN, + "out" => *CYGPATHOUT, + "open" => 1 + }; + + $cygpaths{$options} = $cygpath; + + return $cygpath; +} + +sub closeCygpaths() +{ + return unless isCygwin(); + + foreach my $cygpath (values(%cygpaths)) { + close $cygpath->{"in"}; + close $cygpath->{"out"}; + waitpid($cygpath->{"pid"}, 0); + $cygpath->{"open"} = 0; + + } +} + +sub convertPathUsingCygpath($$) +{ + my ($path, $options) = @_; + + my $cygpath = openCygpathIfNeeded($options); + local *inFH = $cygpath->{"in"}; + local *outFH = $cygpath->{"out"}; + print outFH $path . "\n"; + chomp(my $convertedPath = <inFH>); + return $convertedPath; +} + +sub toWindowsPath($) +{ + my ($path) = @_; + return unless isCygwin(); + + return convertPathUsingCygpath($path, "-w"); +} + +sub toURL($) +{ + my ($path) = @_; + return $path unless isCygwin(); + + return "file:///" . convertPathUsingCygpath($path, "-m"); +} + +sub validateSkippedArg($$;$) +{ + my ($option, $value, $value2) = @_; + my %validSkippedValues = map { $_ => 1 } qw(default ignore only); + $value = lc($value); + die "Invalid argument '" . $value . "' for option $option" unless $validSkippedValues{$value}; + $treatSkipped = $value; +} + +sub htmlForResultsSection(\@$&) +{ + my ($tests, $description, $linkGetter) = @_; + + my @html = (); + return join("\n", @html) unless @{$tests}; + + push @html, "<p>$description:</p>"; + push @html, "<table>"; + foreach my $test (@{$tests}) { + push @html, "<tr>"; + push @html, "<td><a href=\"" . toURL("$testDirectory/$test") . "\">$test</a></td>"; + foreach my $link (@{&{$linkGetter}($test)}) { + push @html, "<td><a href=\"$link->{href}\">$link->{text}</a></td>"; + } + push @html, "</tr>"; + } + push @html, "</table>"; + + return join("\n", @html); +} + +sub linksForExpectedAndActualResults($) +{ + my ($base) = @_; + + my @links = (); + + return \@links unless -s "$testResultsDirectory/$base-$diffsTag.txt"; + + my $expectedResultPath = $expectedResultPaths{$base}; + my ($expectedResultFileName, $expectedResultsDirectory, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$}); + + push @links, { href => "$base-$expectedTag$expectedResultExtension", text => "expected" }; + push @links, { href => "$base-$actualTag$expectedResultExtension", text => "actual" }; + push @links, { href => "$base-$diffsTag.txt", text => "diffs" }; + + return \@links; +} + +sub linksForMismatchTest +{ + my ($test) = @_; + + my @links = (); + + my $base = stripExtension($test); + + push @links, @{linksForExpectedAndActualResults($base)}; + return \@links unless $pixelTests && $imagesPresent{$base}; + + push @links, { href => "$base-$expectedTag.png", text => "expected image" }; + push @links, { href => "$base-$diffsTag.html", text => "image diffs" }; + push @links, { href => "$base-$diffsTag.png", text => "$imageDifferences{$base}%" }; + + return \@links; +} + +sub linksForErrorTest +{ + my ($test) = @_; + + my @links = (); + + my $base = stripExtension($test); + + push @links, @{linksForExpectedAndActualResults($base)}; + push @links, { href => "$base-$errorTag.txt", text => "stderr" }; + + return \@links; +} + +sub linksForNewTest +{ + my ($test) = @_; + + my @links = (); + + my $base = stripExtension($test); + my $expectedResultPath = $expectedResultPaths{$base}; + my $expectedResultPathMinusExtension = stripExtension($expectedResultPath); + + push @links, { href => toURL($expectedResultPath), text => "results" }; + if ($pixelTests && -f "$expectedResultPathMinusExtension.png") { + push @links, { href => toURL("$expectedResultPathMinusExtension.png"), text => "image" }; + } + + return \@links; +} + +sub deleteExpectedAndActualResults($) +{ + my ($base) = @_; + + unlink "$testResultsDirectory/$base-$actualTag.txt"; + unlink "$testResultsDirectory/$base-$diffsTag.txt"; + unlink "$testResultsDirectory/$base-$errorTag.txt"; +} + +sub recordActualResultsAndDiff($$) +{ + my ($base, $actualResults) = @_; + + return unless defined($actualResults) && length($actualResults); + + my $expectedResultPath = $expectedResultPaths{$base}; + my ($expectedResultFileNameMinusExtension, $expectedResultDirectoryPath, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$}); + my $actualResultsPath = "$testResultsDirectory/$base-$actualTag$expectedResultExtension"; + my $copiedExpectedResultsPath = "$testResultsDirectory/$base-$expectedTag$expectedResultExtension"; + writeToFile("$actualResultsPath", $actualResults); + copy("$expectedResultPath", "$copiedExpectedResultsPath"); + + system "diff -u \"$copiedExpectedResultsPath\" \"$actualResultsPath\" > \"$testResultsDirectory/$base-$diffsTag.txt\""; +} + +sub buildPlatformHierarchy() +{ + mkpath($platformTestDirectory) if ($platform eq "undefined" && !-d "$platformTestDirectory"); + + my @platforms = split('-', $platform); + my @hierarchy; + for (my $i=0; $i < @platforms; $i++) { + my $scoped = catdir($platformBaseDirectory, join('-', @platforms[0..($#platforms - $i)])); + push(@hierarchy, $scoped) if (-d $scoped); + } + + return @hierarchy; +} + +sub epiloguesAndPrologues($$) { + my ($lastDirectory, $directory) = @_; + my @lastComponents = split('/', $lastDirectory); + my @components = split('/', $directory); + + while (@lastComponents) { + if (!defined($components[0]) || $lastComponents[0] ne $components[0]) { + last; + } + shift @components; + shift @lastComponents; + } + + my @result; + my $leaving = $lastDirectory; + foreach (@lastComponents) { + my $epilogue = $leaving . "/resources/run-webkit-tests-epilogue.html"; + foreach (@platformHierarchy) { + push @result, catdir($_, $epilogue) if (stat(catdir($_, $epilogue))); + } + push @result, catdir($testDirectory, $epilogue) if (stat(catdir($testDirectory, $epilogue))); + $leaving =~ s|(^\|/)[^/]+$||; + } + + my $entering = $leaving; + foreach (@components) { + $entering .= '/' . $_; + my $prologue = $entering . "/resources/run-webkit-tests-prologue.html"; + push @result, catdir($testDirectory, $prologue) if (stat(catdir($testDirectory, $prologue))); + foreach (reverse @platformHierarchy) { + push @result, catdir($_, $prologue) if (stat(catdir($_, $prologue))); + } + } + return @result; +} + +sub parseLeaksandPrintUniqueLeaks() { + return unless @leaksFilenames; + + my $mergedFilenames = join " ", @leaksFilenames; + my $parseMallocHistoryTool = sourceDir() . "/WebKitTools/Scripts/parse-malloc-history"; + + open MERGED_LEAKS, "cat $mergedFilenames | $parseMallocHistoryTool --merge-depth $mergeDepth - |" ; + my @leakLines = <MERGED_LEAKS>; + close MERGED_LEAKS; + + my $uniqueLeakCount = 0; + my $totalBytes; + foreach my $line (@leakLines) { + ++$uniqueLeakCount if ($line =~ /^(\d*)\scalls/); + $totalBytes = $1 if $line =~ /^total\:\s(.*)\s\(/; + } + + print "\nWARNING: $totalLeaks total leaks found for a total of $totalBytes!\n"; + print "WARNING: $uniqueLeakCount unique leaks found!\n"; + print "See above for individual leaks results.\n" if ($leaksOutputFileNumber > 2); + +} + +sub extensionForMimeType($) +{ + my ($mimeType) = @_; + + if ($mimeType eq "application/x-webarchive") { + return "webarchive"; + } elsif ($mimeType eq "application/pdf") { + return "pdf"; + } + return "txt"; +} + +# Read up to the first #EOF (the content block of the test), or until detecting crashes or timeouts. +sub readFromDumpToolWithTimer(*;$) +{ + my ($fh, $dontWaitForTimeOut) = @_; + + setFileHandleNonBlocking($fh, 1); + + my $maximumSecondsWithoutOutput = 60; + $maximumSecondsWithoutOutput *= 10 if $guardMalloc; + my $microsecondsToWaitBeforeReadingAgain = 1000; + + my $timeOfLastSuccessfulRead = time; + + my @output = (); + my $status = "success"; + my $mimeType = "text/plain"; + # We don't have a very good way to know when the "headers" stop + # and the content starts, so we use this as a hack: + my $haveSeenContentType = 0; + + while (1) { + if (time - $timeOfLastSuccessfulRead > $maximumSecondsWithoutOutput) { + $status = dumpToolDidCrash() ? "crashed" : "timedOut"; + last; + } + + my $line = readline($fh); + if (!defined($line)) { + if ($! != EAGAIN) { + $status = "crashed"; + last; + } + + if ($dontWaitForTimeOut) { + last; + } + + # No data ready + usleep($microsecondsToWaitBeforeReadingAgain); + next; + } + + $timeOfLastSuccessfulRead = time; + + if (!$haveSeenContentType && $line =~ /^Content-Type: (\S+)$/) { + $mimeType = $1; + $haveSeenContentType = 1; + next; + } + last if ($line =~ /#EOF/); + + push @output, $line; + } + + setFileHandleNonBlocking($fh, 0); + return { + output => join("", @output), + status => $status, + mimeType => $mimeType, + extension => extensionForMimeType($mimeType) + }; +} + +sub setFileHandleNonBlocking(*$) +{ + my ($fh, $nonBlocking) = @_; + + my $flags = fcntl($fh, F_GETFL, 0) or die "Couldn't get filehandle flags"; + + if ($nonBlocking) { + $flags |= O_NONBLOCK; + } else { + $flags &= ~O_NONBLOCK; + } + + fcntl($fh, F_SETFL, $flags) or die "Couldn't set filehandle flags"; + + return 1; +} + +sub sampleDumpTool() +{ + return unless isOSX(); + return unless $runSample; + + my $outputDirectory = "$ENV{HOME}/Library/Logs/DumpRenderTree"; + -d $outputDirectory or mkdir $outputDirectory; + + my $outputFile = "$outputDirectory/HangReport.txt"; + system "/usr/bin/sample", $dumpToolPID, qw(10 10 -file), $outputFile; +} diff --git a/WebKitTools/Scripts/set-webkit-configuration b/WebKitTools/Scripts/set-webkit-configuration new file mode 100755 index 0000000..8735140 --- /dev/null +++ b/WebKitTools/Scripts/set-webkit-configuration @@ -0,0 +1,41 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005 Apple Computer, 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. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +my $configuration = passedConfiguration(); +die "Please specify either --debug or --release.\n" if !$configuration; + +my $baseProductDir = baseProductDir(); +system "mkdir", "-p", "$baseProductDir"; +open CONFIGURATION, ">", "$baseProductDir/Configuration" or die; +print CONFIGURATION $configuration; +close CONFIGURATION; diff --git a/WebKitTools/Scripts/sort-Xcode-project-file b/WebKitTools/Scripts/sort-Xcode-project-file new file mode 100755 index 0000000..a35fe73 --- /dev/null +++ b/WebKitTools/Scripts/sort-Xcode-project-file @@ -0,0 +1,114 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2007 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. + +# Script to sort "files=(...);" sections in Xcode project.pbxproj files + +use strict; + +use File::Basename; +use File::Temp qw(tempfile); +use Getopt::Long; + +sub sortByFileName($$); + +my $printWarnings = 1; +my $showHelp; + +my $getOptionsResult = GetOptions( + 'h|help' => \$showHelp, + 'w|warnings!' => \$printWarnings, +); + +if (scalar(@ARGV) == 0) { + print STDERR "ERROR: No Xcode project files (project.pbxproj) listed on command-line.\n"; + undef $getOptionsResult; +} + +if (!$getOptionsResult || $showHelp) { + print STDERR <<__END__; +Usage: @{[ basename($0) ]} [options] path/to/project.pbxproj [path/to/project.pbxproj ...] + -h|--help show this help message + -w|--[no-]warnings show or suppress warnings (default: show warnings) +__END__ + exit 1; +} + +for my $projectFile (@ARGV) { + if (basename($projectFile) ne "project.pbxproj") { + print STDERR "WARNING: Not an Xcode project file: $projectFile\n" if $printWarnings; + next; + } + + my ($OUT, $tempFileName) = tempfile( + basename($projectFile) . "-XXXXXXXX", + DIR => dirname($projectFile), + UNLINK => 0, + ); + + # Clean up temp file in case of die() + $SIG{__DIE__} = sub { + close(IN); + close($OUT); + unlink($tempFileName); + }; + + open(IN, "< $projectFile") || die "Could not open $projectFile: $!"; + while (my $line = <IN>) { + if ($line =~ /^(\s*)files = \(\s*$/) { + print $OUT $line; + my $endMarker = $1 . ");"; + my @files; + while (my $fileLine = <IN>) { + if ($fileLine =~ /^\Q$endMarker\E\s*$/) { + $endMarker = $fileLine; + last; + } + push @files, $fileLine; + } + print $OUT sort sortByFileName @files; + print $OUT $endMarker; + } else { + print $OUT $line; + } + } + close(IN); + close($OUT); + + unlink($projectFile) || die "Could not delete $projectFile: $!"; + rename($tempFileName, $projectFile) || die "Could not rename $tempFileName to $projectFile: $!"; +} + +exit 0; + +sub sortByFileName($$) +{ + my ($a, $b) = @_; + my $aFileName = $1 if $a =~ /^\s*[A-Z0-9]{24} \/\* (.+) in /; + my $bFileName = $1 if $b =~ /^\s*[A-Z0-9]{24} \/\* (.+) in /; + return $aFileName cmp $bFileName; +} diff --git a/WebKitTools/Scripts/split-file-by-class b/WebKitTools/Scripts/split-file-by-class new file mode 100755 index 0000000..b6aeb68 --- /dev/null +++ b/WebKitTools/Scripts/split-file-by-class @@ -0,0 +1,159 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006 Apple Computer, 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. + +# Used for splitting a single file into multiple class files +# Usage: split-class <header file> + +use strict; +use File::Copy; +use FindBin; +use lib $FindBin::Bin; +use SpacingHeuristics; + + +for my $filename (@ARGV) { + + $filename =~ m/^(\w+)\.h$/ or die "Command line args must be .h files.\n"; + my $basename = $1; + + open(OLDFILE, "<", $filename) or die "File does not exist: $filename\n"; + print "Splitting class $filename.{h,cpp}:\n"; + + my $currentClassName = ""; + my $classIndent = ""; + my $fileContent = ""; + my %classDefs = (); + while (my $line = <OLDFILE>) { + if ($currentClassName) { + $classDefs{$currentClassName} .= $line; + if ($line =~ /^$classIndent};\s*$/) { + $currentClassName = ""; + } + } else { + if ($line =~ /^(\s*)class\s+(\w+)\s+[^;]*$/) { + $classIndent = $1; + $currentClassName = $2; + $classDefs{$currentClassName} .= $line; + $fileContent .= "###CLASS###$currentClassName\n"; + } else { + $fileContent .= $line; + } + } + } + close(OLDFILE); + + if (scalar(keys(%classDefs)) == 1) { # degenerate case + my ($classname) = keys(%classDefs); + if (!($classname eq $basename)) { + print "Skipping $filename, already correctly named.\n"; + } else { + print "$filename only includes one class, renaming to $classname.h\n"; + system("svn rm --force $classname.h") if (-r "$classname.h"); + system "svn mv $basename.h $classname.h"; + } + } else { + while (my ($classname, $classDef) = each(%classDefs)) { + if (($classname eq $basename)) { + print "Skipping $filename, already correctly named.\n"; + } else { + print "Using SVN to copy $basename.{h,cpp} to $classname.{h,cpp}\n"; + + system("svn rm --force $classname.h") if (-r "$classname.h"); + system "svn cp $basename.h $classname.h"; + + system("svn rm --force $classname.cpp") if (-r "$classname.cpp"); + system "svn cp $basename.cpp $classname.cpp"; + } + + print "Fixing $classname.h as much as possible.\n"; + open(NEWHEADER, ">", "$classname.h") or die "File does not exist: $filename\n"; + my @lines = split("\n", $fileContent); + foreach my $line (@lines) { + if ($line =~ /^###CLASS###(\w+)/) { + if ($1 eq $classname) { + print NEWHEADER $classDef . "\n"; + } + } else { + print NEWHEADER $line . "\n"; + } + } + close(NEWHEADER); + + print "Fixing $classname.cpp as much as possible.\n"; + copy("$classname.cpp", "$classname.cpp.original"); + open(OLDCPP, "<", "$classname.cpp.original") or die "Failed to copy file for reading: $filename\n"; + open(NEWCPP, ">", "$classname.cpp") or die "File does not exist: $filename\n"; + my $insideMemberFunction = 0; + my $shouldPrintMemberFunction = 0; + resetSpacingHeuristics(); + while (my $line = <OLDCPP>) { + if ($insideMemberFunction) { + if ($shouldPrintMemberFunction) { + print NEWCPP $line; + #setPreviousAllowedLine($line); + } else { + ignoringLine($line); + } + if ($line =~ /^}\s*$/) { + $insideMemberFunction = 0; + } + } elsif ($line =~ /$filename/) { + print NEWCPP "#include \"config.h\"\n"; + print NEWCPP "#include \"$classname.h\"\n"; + } elsif ($line =~ /#include/ || $line =~ /#import/) { + next; # skip includes, they're generally wrong or unecessary anyway. + } else { + $line =~ s/DOM:://; + $line =~ s/khtml:://; + $line =~ s/namespace DOM/namespace WebCore/; + $line =~ s/namespace khtml/namespace WebCore/; + + if ($line =~ /^(.*?\s+)?(\*|&)?(\w+)::(~)?\w+\s*\(/) { + $insideMemberFunction = 1; + $shouldPrintMemberFunction = ($classname eq $3); + if ($shouldPrintMemberFunction) { + printPendingEmptyLines(*NEWCPP, $line); + print NEWCPP $line; + } + } else { + next if isOnlyWhiteSpace($line); + next if ($line =~ m/------------/); + printPendingEmptyLines(*NEWCPP, $line); + applySpacingHeuristicsAndPrint(*NEWCPP, $line); + } + } + } + close(NEWCPP); + close(OLDCPP); + unlink("$classname.cpp.original"); + } + } + + print "Opening new files...\n"; + system("open " . join(".* ", keys(%classDefs)) . ".*"); +}
\ No newline at end of file diff --git a/WebKitTools/Scripts/sunspider-compare-results b/WebKitTools/Scripts/sunspider-compare-results new file mode 100755 index 0000000..ec0863a --- /dev/null +++ b/WebKitTools/Scripts/sunspider-compare-results @@ -0,0 +1,101 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2007 Apple Inc. All rights reserved. +# Copyright (C) 2007 Eric Seidel <eric@webkit.org> +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR +# 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. + +use strict; +use File::Spec; +use FindBin; +use Getopt::Long qw(:config pass_through); +use lib $FindBin::Bin; +use webkitdirs; +use POSIX; + +# determine configuration, but default to "Release" instead of last-used configuration to match run-sunspider +setConfiguration("Release"); +setConfiguration(); +my $configuration = configuration(); + +my $root; +my $showHelp = 0; + +my $programName = basename($0); +my $usage = <<EOF; +Usage: $programName [options] FILE FILE + --help Show this help message + --root Path to root tools build +EOF + +GetOptions('root=s' => sub { my ($argName, $value); setConfigurationProductDir(Cwd::abs_path($value)); }, + 'help' => \$showHelp); + +if ($showHelp) { + print STDERR $usage; + exit 1; +} + +@ARGV = map { File::Spec->rel2abs($_) } @ARGV; + +sub buildJSC +{ + if (!defined($root)){ + chdirWebKit(); + my $buildResult = system "WebKitTools/Scripts/build-jsc", "--" . $configuration; + if ($buildResult) { + print STDERR "Compiling jsc failed!\n"; + exit WEXITSTATUS($buildResult); + } + } +} + +sub setupEnvironmentForExecution($) +{ + my ($productDir) = @_; + print "Starting sunspider-compare-results with DYLD_FRAMEWORK_PATH set to point to built JavaScriptCore in $productDir.\n"; + $ENV{DYLD_FRAMEWORK_PATH} = $productDir; + # FIXME: Other platforms may wish to augment this method to use LD_LIBRARY_PATH, etc. +} + +sub jscPath($) +{ + my ($productDir) = @_; + my $jscName = "jsc"; + $jscName .= "_debug" if (isCygwin() && ($configuration eq "Debug")); + return "$productDir/$jscName"; +} + +buildJSC(); + +chdirWebKit(); +chdir("SunSpider"); + +my $productDir = productDir(); +# FIXME: This hack should be pushed down into productDir() +$productDir .= "/JavaScriptCore" if (isQt() or isGtk()); + +setupEnvironmentForExecution($productDir); +my @args = ("--shell", jscPath($productDir)); +# This code could be removed if we chose to pass extra args to sunspider instead of Xcode + +exec "./sunspider-compare-results", @args, @ARGV; diff --git a/WebKitTools/Scripts/svn-apply b/WebKitTools/Scripts/svn-apply new file mode 100755 index 0000000..d43d525 --- /dev/null +++ b/WebKitTools/Scripts/svn-apply @@ -0,0 +1,443 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006, 2007 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. + +# "patch" script for WebKit Open Source Project, used to apply patches. + +# Differences from invoking "patch -p0": +# +# Handles added files (does a svn add with logic to handle local changes). +# Handles added directories (does a svn add). +# Handles removed files (does a svn rm with logic to handle local changes). +# Handles removed directories--those with no more files or directories left in them +# (does a svn rm). +# Has mode where it will roll back to svn version numbers in the patch file so svn +# can do a 3-way merge. +# Paths from Index: lines are used rather than the paths on the patch lines, which +# makes patches generated by "cvs diff" work (increasingly unimportant since we +# use Subversion now). +# ChangeLog patches use --fuzz=3 to prevent rejects, and the entry date is set in +# the patch to today's date using $changeLogTimeZone. +# Handles binary files (requires patches made by svn-create-patch). +# Handles copied and moved files (requires patches made by svn-create-patch). +# Handles git-diff patches (without binary changes) created at the top-level directory +# +# Missing features: +# +# Handle property changes. +# Handle copied and moved directories (would require patches made by svn-create-patch). +# When doing a removal, check that old file matches what's being removed. +# Notice a patch that's being applied at the "wrong level" and make it work anyway. +# Do a dry run on the whole patch and don't do anything if part of the patch is +# going to fail (probably too strict unless we exclude ChangeLog). +# Handle git-diff patches with binary changes + +use strict; +use warnings; + +use Cwd; +use Digest::MD5; +use File::Basename; +use File::Spec; +use Getopt::Long; +use MIME::Base64; +use POSIX qw(strftime); + +sub addDirectoriesIfNeeded($); +sub applyPatch($$;$); +sub checksum($); +sub fixChangeLogPatch($); +sub gitdiff2svndiff($); +sub handleBinaryChange($$); +sub isDirectoryEmptyForRemoval($); +sub patch($); +sub removeDirectoriesIfNeeded(); +sub setChangeLogDateAndReviewer($$); +sub svnStatus($); + +# Project time zone for Cupertino, CA, US +my $changeLogTimeZone = "PST8PDT"; + +my $merge = 0; +my $showHelp = 0; +my $reviewer; +if (!GetOptions("merge!" => \$merge, "help!" => \$showHelp, "reviewer=s" => \$reviewer) || $showHelp) { + print STDERR basename($0) . " [-h|--help] [-m|--merge] [-r|--reviewer name] patch1 [patch2 ...]\n"; + exit 1; +} + +my %removeDirectoryIgnoreList = ( + '.' => 1, + '..' => 1, + '.svn' => 1, + '_svn' => 1, +); + +my %checkedDirectories; +my %copiedFiles; +my @patches; +my %versions; + +my $copiedFromPath; +my $filter; +my $indexPath; +my $patch; +while (<>) { + s/([\n\r]+)$//mg; + my $eol = $1; + if (!defined($indexPath) && m#^diff --git a/#) { + $filter = \&gitdiff2svndiff; + } + $_ = &$filter($_) if $filter; + if (/^Index: (.+)/) { + $indexPath = $1; + if ($patch) { + if (!$copiedFromPath) { + push @patches, $patch; + } + $copiedFromPath = ""; + $patch = ""; + } + } + if ($indexPath) { + # Fix paths on diff, ---, and +++ lines to match preceding Index: line. + s/\S+$/$indexPath/ if /^diff/; + s/^--- \S+/--- $indexPath/; + if (/^--- .+\(from (\S+):(\d+)\)$/) { + $copiedFromPath = $1; + $copiedFiles{$indexPath} = $copiedFromPath; + $versions{$copiedFromPath} = $2 if ($2 != 0); + } + elsif (/^--- .+\(revision (\d+)\)$/) { + $versions{$indexPath} = $1 if ($1 != 0); + } + if (s/^\+\+\+ \S+/+++ $indexPath/) { + $indexPath = ""; + } + } + $patch .= $_; + $patch .= $eol; +} + +if ($patch && !$copiedFromPath) { + push @patches, $patch; +} + +if ($merge) { + for my $file (sort keys %versions) { + print "Getting version $versions{$file} of $file\n"; + system "svn", "update", "-r", $versions{$file}, $file; + } +} + +# Handle copied and moved files first since moved files may have their source deleted before the move. +for my $file (keys %copiedFiles) { + addDirectoriesIfNeeded(dirname($file)); + system "svn", "copy", $copiedFiles{$file}, $file; +} + +for $patch (@patches) { + patch($patch); +} + +removeDirectoriesIfNeeded(); + +exit 0; + +sub addDirectoriesIfNeeded($) +{ + my ($path) = @_; + my @dirs = File::Spec->splitdir($path); + my $dir = "."; + while (scalar @dirs) { + $dir = File::Spec->catdir($dir, shift @dirs); + next if exists $checkedDirectories{$dir}; + if (! -e $dir) { + mkdir $dir or die "Failed to create required directory '$dir' for path '$path'\n"; + system "svn", "add", $dir; + $checkedDirectories{$dir} = 1; + } + elsif (-d $dir) { + my $svnOutput = svnStatus($dir); + if ($svnOutput && $svnOutput =~ m#\?\s+$dir\n#) { + system "svn", "add", $dir; + } + $checkedDirectories{$dir} = 1; + } + else { + die "'$dir' is not a directory"; + } + } +} + +sub applyPatch($$;$) +{ + my ($patch, $fullPath, $options) = @_; + $options = [] if (! $options); + my $command = "patch " . join(" ", "-p0", @{$options}); + open PATCH, "| $command" or die "Failed to patch $fullPath\n"; + print PATCH $patch; + close PATCH; +} + +sub checksum($) +{ + my $file = shift; + open(FILE, $file) or die "Can't open '$file': $!"; + binmode(FILE); + my $checksum = Digest::MD5->new->addfile(*FILE)->hexdigest(); + close(FILE); + return $checksum; +} + +sub fixChangeLogPatch($) +{ + my $patch = shift; + my $contextLineCount = 3; + + return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; + my ($oldLineCount, $newLineCount) = ($1, $2); + return $patch if $oldLineCount <= $contextLineCount; + + # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will + # have lines of context at the top of a patch when the existing entry has the same + # date and author as the new entry. This nifty loop alters a ChangeLog patch so + # that the added lines ("+") in the patch always start at the beginning of the + # patch and there are no initial lines of context. + my $newPatch; + my $lineCountInState = 0; + my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; + my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; + my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); + my $state = $stateHeader; + foreach my $line (split(/\n/, $patch)) { + $lineCountInState++; + if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { + $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; + $lineCountInState = 0; + $state = $statePreContext; + } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { + $line = "+" . substr($line, 1); + if ($lineCountInState == $oldContentLineCountReduction) { + $lineCountInState = 0; + $state = $stateNewChanges; + } + } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { + # No changes to these lines + if ($lineCountInState == $newContentLineCountWithoutContext) { + $lineCountInState = 0; + $state = $statePostContext; + } + } elsif ($state == $statePostContext) { + if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { + $line = " " . substr($line, 1); + } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { + next; # Discard + } + } + $newPatch .= $line . "\n"; + } + + return $newPatch; +} + +sub gitdiff2svndiff($) +{ + $_ = shift @_; + if (m#^diff --git a/(.+) b/(.+)#) { + return "Index: $1"; + } elsif (m/^new file.*/) { + return ""; + } elsif (m#^index [0-9a-f]{7}\.\.[0-9a-f]{7} [0-9]{6}#) { + return "==================================================================="; + } elsif (m#^--- a/(.+)#) { + return "--- $1"; + } elsif (m#^\+\+\+ b/(.+)#) { + return "+++ $1"; + } + return $_; +} + +sub handleBinaryChange($$) +{ + my ($fullPath, $contents) = @_; + if ($contents =~ m#((\n[A-Za-z0-9+/]{76})+\n[A-Za-z0-9+/=]{4,76}\n)#) { + # Addition or Modification + open FILE, ">", $fullPath or die; + print FILE decode_base64($1); + close FILE; + my $svnOutput = svnStatus($fullPath); + if ($svnOutput && substr($svnOutput, 0, 1) eq "?") { + # Addition + system "svn", "add", $fullPath; + } else { + # Modification + print $svnOutput if $svnOutput; + } + } else { + # Deletion + system "svn", "rm", $fullPath; + } +} + +sub isDirectoryEmptyForRemoval($) +{ + my ($dir) = @_; + my $directoryIsEmpty = 1; + opendir DIR, $dir or die "Could not open '$dir' to list files: $?"; + for (my $item = readdir DIR; $item && $directoryIsEmpty; $item = readdir DIR) { + next if exists $removeDirectoryIgnoreList{$item}; + if (! -d File::Spec->catdir($dir, $item)) { + $directoryIsEmpty = 0; + } else { + my $svnOutput = svnStatus(File::Spec->catdir($dir, $item)); + next if $svnOutput && substr($svnOutput, 0, 1) eq "D"; + $directoryIsEmpty = 0; + } + } + closedir DIR; + return $directoryIsEmpty; +} + +sub patch($) +{ + my ($patch) = @_; + return if !$patch; + + unless ($patch =~ m|^Index: ([^\n]+)|) { + my $separator = '-' x 67; + warn "Failed to find 'Index:' in:\n$separator\n$patch\n$separator\n"; + return; + } + my $fullPath = $1; + + my $deletion = 0; + my $addition = 0; + my $isBinary = 0; + + $addition = 1 if $patch =~ /\n--- .+\(revision 0\)\n/; + $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/; + $isBinary = 1 if $patch =~ /\nCannot display: file marked as a binary type\./; + + if (!$addition && !$deletion && !$isBinary) { + # Standard patch, patch tool can handle this. + if (basename($fullPath) eq "ChangeLog") { + my $changeLogDotOrigExisted = -f "${fullPath}.orig"; + applyPatch(setChangeLogDateAndReviewer(fixChangeLogPatch($patch), $reviewer), $fullPath, ["--fuzz=3"]); + unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted); + } else { + applyPatch($patch, $fullPath); + } + } else { + # Either a deletion, an addition or a binary change. + + addDirectoriesIfNeeded(dirname($fullPath)); + + if ($isBinary) { + # Binary change + handleBinaryChange($fullPath, $patch); + } elsif ($deletion) { + # Deletion + applyPatch($patch, $fullPath, ["--force"]); + system "svn", "rm", "--force", $fullPath; + } else { + # Addition + rename($fullPath, "$fullPath.orig") if -e $fullPath; + applyPatch($patch, $fullPath); + unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig"); + system "svn", "add", $fullPath; + system "svn", "stat", "$fullPath.orig" if -e "$fullPath.orig"; + } + } +} + +sub removeDirectoriesIfNeeded() +{ + foreach my $dir (reverse sort keys %checkedDirectories) { + if (isDirectoryEmptyForRemoval($dir)) { + my $svnOutput; + open SVN, "svn rm '$dir' |" or die; + # Only save the last line since Subversion lists all changed statuses below $dir + while (<SVN>) { + $svnOutput = $_; + } + close SVN; + print $svnOutput if $svnOutput; + } + } +} + +sub setChangeLogDateAndReviewer($$) +{ + my $patch = shift; + my $reviewer = shift; + my $savedTimeZone = $ENV{'TZ'}; + # Set TZ temporarily so that localtime() is in that time zone + $ENV{'TZ'} = $changeLogTimeZone; + my $newDate = strftime("%Y-%m-%d", localtime()); + if (defined $savedTimeZone) { + $ENV{'TZ'} = $savedTimeZone; + } else { + delete $ENV{'TZ'}; + } + $patch =~ s/(\n\+)\d{4}-[^-]{2}-[^-]{2}( )/$1$newDate$2/; + if (defined($reviewer)) { + $patch =~ s/NOBODY \(OOPS!\)/$reviewer/; + } + return $patch; +} + +sub svnStatus($) +{ + my ($fullPath) = @_; + my $svnStatus; + open SVN, "svn status --non-interactive --non-recursive '$fullPath' |" or die; + if (-d $fullPath) { + # When running "svn stat" on a directory, we can't assume that only one + # status will be returned (since any files with a status below the + # directory will be returned), and we can't assume that the directory will + # be first (since any files with unknown status will be listed first). + my $normalizedFullPath = File::Spec->catdir(File::Spec->splitdir($fullPath)); + while (<SVN>) { + chomp; + my $normalizedStatPath = File::Spec->catdir(File::Spec->splitdir(substr($_, 7))); + if ($normalizedFullPath eq $normalizedStatPath) { + $svnStatus = $_; + last; + } + } + # Read the rest of the svn command output to avoid a broken pipe warning. + local $/ = undef; + <SVN>; + } + else { + # Files will have only one status returned. + $svnStatus = <SVN>; + } + close SVN; + return $svnStatus; +} diff --git a/WebKitTools/Scripts/svn-create-patch b/WebKitTools/Scripts/svn-create-patch new file mode 100755 index 0000000..365737a --- /dev/null +++ b/WebKitTools/Scripts/svn-create-patch @@ -0,0 +1,442 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006 Apple Computer, 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. + +# Extended "svn diff" script for WebKit Open Source Project, used to make patches. + +# Differences from standard "svn diff": +# +# Uses the real diff, not svn's built-in diff. +# Always passes "-p" to diff so it will try to include function names. +# Handles binary files (encoded as a base64 chunk of text). +# Sorts the diffs alphabetically by text files, then binary files. +# Handles copied and moved files. +# +# Missing features: +# +# Handle copied and moved directories. + +use strict; +use warnings; + +use Config; +use Cwd; +use File::Basename; +use File::Spec; +use File::stat; +use Getopt::Long; +use MIME::Base64; +use POSIX qw(:errno_h); +use Time::gmtime; + +sub binarycmp($$); +sub canonicalizePath($); +sub findBaseUrl($); +sub findMimeType($;$); +sub findModificationType($); +sub findSourceFileAndRevision($); +sub fixChangeLogPatch($); +sub generateDiff($); +sub generateFileList($\%); +sub isBinaryMimeType($); +sub manufacturePatchForAdditionWithHistory($); +sub numericcmp($$); +sub outputBinaryContent($); +sub patchpathcmp($$); +sub pathcmp($$); +sub processPaths(\@); +sub splitpath($); +sub testfilecmp($$); + +$ENV{'LC_ALL'} = 'C'; + +my $showHelp; + +my $result = GetOptions( + "help" => \$showHelp, +); +if (!$result || $showHelp) { + print STDERR basename($0) . " [-h|--help] [svndir1 [svndir2 ...]]\n"; + exit 1; +} + +# Sort the diffs for easier reviewing. +my %paths = processPaths(@ARGV); + +# Generate a list of files requiring diffs. +my %diffFiles; +for my $path (keys %paths) { + generateFileList($path, %diffFiles); +} + +# Generate the diffs, in a order chosen for easy reviewing. +for my $path (sort patchpathcmp values %diffFiles) { + generateDiff($path); +} + +exit 0; + +# Overall sort, considering multiple criteria. +sub patchpathcmp($$) +{ + my ($a, $b) = @_; + + # All binary files come after all non-binary files. + my $result = binarycmp($a, $b); + return $result if $result; + + # All test files come after all non-test files. + $result = testfilecmp($a, $b); + return $result if $result; + + # Final sort is a "smart" sort by directory and file name. + return pathcmp($a, $b); +} + +# Sort so text files appear before binary files. +sub binarycmp($$) +{ + my ($fileDataA, $fileDataB) = @_; + return $fileDataA->{isBinary} <=> $fileDataB->{isBinary}; +} + +sub canonicalizePath($) +{ + my ($file) = @_; + + # Remove extra slashes and '.' directories in path + $file = File::Spec->canonpath($file); + + # Remove '..' directories in path + my @dirs = (); + foreach my $dir (File::Spec->splitdir($file)) { + if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..') { + pop(@dirs); + } else { + push(@dirs, $dir); + } + } + return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : "."; +} + +sub findBaseUrl($) +{ + my ($infoPath) = @_; + my $baseUrl; + open INFO, "svn info '$infoPath' |" or die; + while (<INFO>) { + if (/^URL: (.+)/) { + $baseUrl = $1; + last; + } + } + close INFO; + return $baseUrl; +} + +sub findMimeType($;$) +{ + my ($file, $revision) = @_; + my $args = $revision ? "--revision $revision" : ""; + open PROPGET, "svn propget svn:mime-type $args '$file' |" or die; + my $mimeType = <PROPGET>; + close PROPGET; + chomp $mimeType if $mimeType; + return $mimeType; +} + +sub findModificationType($) +{ + my ($stat) = @_; + my $fileStat = substr($stat, 0, 1); + my $propertyStat = substr($stat, 1, 1); + if ($fileStat eq "A") { + my $additionWithHistory = substr($stat, 3, 1); + return $additionWithHistory eq "+" ? "additionWithHistory" : "addition"; + } + return "modification" if ($fileStat eq "M" || $propertyStat eq "M"); + return "deletion" if ($fileStat eq "D"); + return undef; +} + +sub findSourceFileAndRevision($) +{ + my ($file) = @_; + my $baseUrl = findBaseUrl("."); + my $sourceFile; + my $sourceRevision; + open INFO, "svn info '$file' |" or die; + while (<INFO>) { + if (/^Copied From URL: (.+)/) { + $sourceFile = File::Spec->abs2rel($1, $baseUrl); + } elsif (/^Copied From Rev: ([0-9]+)/) { + $sourceRevision = $1; + } + } + close INFO; + return ($sourceFile, $sourceRevision); +} + +sub fixChangeLogPatch($) +{ + my $patch = shift; + my $contextLineCount = 3; + + return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; + my ($oldLineCount, $newLineCount) = ($1, $2); + return $patch if $oldLineCount <= $contextLineCount; + + # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will + # have lines of context at the top of a patch when the existing entry has the same + # date and author as the new entry. This nifty loop alters a ChangeLog patch so + # that the added lines ("+") in the patch always start at the beginning of the + # patch and there are no initial lines of context. + my $newPatch; + my $lineCountInState = 0; + my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; + my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; + my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); + my $state = $stateHeader; + foreach my $line (split(/\n/, $patch)) { + $lineCountInState++; + if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { + $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; + $lineCountInState = 0; + $state = $statePreContext; + } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { + $line = "+" . substr($line, 1); + if ($lineCountInState == $oldContentLineCountReduction) { + $lineCountInState = 0; + $state = $stateNewChanges; + } + } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { + # No changes to these lines + if ($lineCountInState == $newContentLineCountWithoutContext) { + $lineCountInState = 0; + $state = $statePostContext; + } + } elsif ($state == $statePostContext) { + if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { + $line = " " . substr($line, 1); + } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { + next; # Discard + } + } + $newPatch .= $line . "\n"; + } + + return $newPatch; +} + +sub generateDiff($) +{ + my ($fileData) = @_; + my $file = $fileData->{path}; + my $patch; + if ($fileData->{modificationType} eq "additionWithHistory") { + manufacturePatchForAdditionWithHistory($fileData); + } + open DIFF, "svn diff --diff-cmd diff -x -uaNp '$file' |" or die; + while (<DIFF>) { + $patch .= $_; + } + close DIFF; + $patch = fixChangeLogPatch($patch) if basename($file) eq "ChangeLog"; + print $patch if $patch; + if ($fileData->{isBinary}) { + print "\n" if ($patch && $patch =~ m/\n\S+$/m); + outputBinaryContent($file); + } +} + +sub generateFileList($\%) +{ + my ($statPath, $diffFiles) = @_; + my %testDirectories = map { $_ => 1 } qw(LayoutTests); + open STAT, "svn stat '$statPath' |" or die; + while (my $line = <STAT>) { + chomp $line; + my $stat = substr($line, 0, 7); + my $path = substr($line, 7); + next if -d $path; + my $modificationType = findModificationType($stat); + if ($modificationType) { + $diffFiles->{$path}->{path} = $path; + $diffFiles->{$path}->{modificationType} = $modificationType; + $diffFiles->{$path}->{isBinary} = isBinaryMimeType($path); + $diffFiles->{$path}->{isTestFile} = exists $testDirectories{(File::Spec->splitdir($path))[0]} ? 1 : 0; + if ($modificationType eq "additionWithHistory") { + my ($sourceFile, $sourceRevision) = findSourceFileAndRevision($path); + $diffFiles->{$path}->{sourceFile} = $sourceFile; + $diffFiles->{$path}->{sourceRevision} = $sourceRevision; + } + } else { + print STDERR $line, "\n"; + } + } + close STAT; +} + +sub isBinaryMimeType($) +{ + my ($file) = @_; + my $mimeType = findMimeType($file); + return 0 if (!$mimeType || substr($mimeType, 0, 5) eq "text/"); + return 1; +} + +sub manufacturePatchForAdditionWithHistory($) +{ + my ($fileData) = @_; + my $file = $fileData->{path}; + print "Index: ${file}\n"; + print "=" x 67, "\n"; + my $sourceFile = $fileData->{sourceFile}; + my $sourceRevision = $fileData->{sourceRevision}; + print "--- ${file}\t(revision ${sourceRevision})\t(from ${sourceFile}:${sourceRevision})\n"; + print "+++ ${file}\t(working copy)\n"; + if ($fileData->{isBinary}) { + print "\nCannot display: file marked as a binary type.\n"; + my $mimeType = findMimeType($file, $sourceRevision); + print "svn:mime-type = ${mimeType}\n\n"; + } else { + print `svn cat ${sourceFile} | diff -u /dev/null - | tail -n +3`; + } +} + +# Sort numeric parts of strings as numbers, other parts as strings. +# Makes 1.33 come after 1.3, which is cool. +sub numericcmp($$) +{ + my ($aa, $bb) = @_; + + my @a = split /(\d+)/, $aa; + my @b = split /(\d+)/, $bb; + + # Compare one chunk at a time. + # Each chunk is either all numeric digits, or all not numeric digits. + while (@a && @b) { + my $a = shift @a; + my $b = shift @b; + + # Use numeric comparison if chunks are non-equal numbers. + return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b; + + # Use string comparison if chunks are any other kind of non-equal string. + return $a cmp $b if $a ne $b; + } + + # One of the two is now empty; compare lengths for result in this case. + return @a <=> @b; +} + +sub outputBinaryContent($) +{ + my ($path) = @_; + # Deletion + return if (! -e $path); + # Addition or Modification + my $buffer; + open BINARY, $path or die; + while (read(BINARY, $buffer, 60*57)) { + print encode_base64($buffer); + } + close BINARY; + print "\n"; +} + +# Sort first by directory, then by file, so all paths in one directory are grouped +# rather than being interspersed with items from subdirectories. +# Use numericcmp to sort directory and filenames to make order logical. +# Also include a special case for ChangeLog, which comes first in any directory. +sub pathcmp($$) +{ + my ($fileDataA, $fileDataB) = @_; + + my ($dira, $namea) = splitpath($fileDataA->{path}); + my ($dirb, $nameb) = splitpath($fileDataB->{path}); + + return numericcmp($dira, $dirb) if $dira ne $dirb; + return -1 if $namea eq "ChangeLog" && $nameb ne "ChangeLog"; + return +1 if $namea ne "ChangeLog" && $nameb eq "ChangeLog"; + return numericcmp($namea, $nameb); +} + +sub processPaths(\@) +{ + my ($paths) = @_; + return ("." => 1) if (!@{$paths}); + + my %result = (); + + for my $file (@{$paths}) { + die "can't handle absolute paths like \"$file\"\n" if File::Spec->file_name_is_absolute($file); + die "can't handle empty string path\n" if $file eq ""; + die "can't handle path with single quote in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy) + + my $untouchedFile = $file; + + $file = canonicalizePath($file); + + die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|; + + $result{$file} = 1; + } + + return ("." => 1) if ($result{"."}); + + # Remove any paths that also have a parent listed. + for my $path (keys %result) { + for (my $parent = dirname($path); $parent ne '.'; $parent = dirname($parent)) { + if ($result{$parent}) { + delete $result{$path}; + last; + } + } + } + + return %result; +} + +# Break up a path into the directory (with slash) and base name. +sub splitpath($) +{ + my ($path) = @_; + + my $pathSeparator = "/"; + my $dirname = dirname($path) . $pathSeparator; + $dirname = "" if $dirname eq "." . $pathSeparator; + + return ($dirname, basename($path)); +} + +# Sort so source code files appear before test files. +sub testfilecmp($$) +{ + my ($fileDataA, $fileDataB) = @_; + return $fileDataA->{isTestFile} <=> $fileDataB->{isTestFile}; +} diff --git a/WebKitTools/Scripts/svn-unapply b/WebKitTools/Scripts/svn-unapply new file mode 100755 index 0000000..11e3ddb --- /dev/null +++ b/WebKitTools/Scripts/svn-unapply @@ -0,0 +1,374 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006, 2007 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. + +# "unpatch" script for Web Kit Open Source Project, used to remove patches. + +# Differences from invoking "patch -p0 -R": +# +# Handles added files (does a svn revert with additional logic to handle local changes). +# Handles added directories (does a svn revert and a rmdir). +# Handles removed files (does a svn revert with additional logic to handle local changes). +# Handles removed directories (does a svn revert). +# Paths from Index: lines are used rather than the paths on the patch lines, which +# makes patches generated by "cvs diff" work (increasingly unimportant since we +# use Subversion now). +# ChangeLog patches use --fuzz=3 to prevent rejects, and the entry date is reset in +# the patch before it is applied (svn-apply sets it when applying a patch). +# Handles binary files (requires patches made by svn-create-patch). +# Handles copied and moved files (requires patches made by svn-create-patch). +# Handles git-diff patches (without binary changes) created at the top-level directory +# +# Missing features: +# +# Handle property changes. +# Handle copied and moved directories (would require patches made by svn-create-patch). +# Use version numbers in the patch file and do a 3-way merge. +# When reversing an addition, check that the file matches what's being removed. +# Notice a patch that's being unapplied at the "wrong level" and make it work anyway. +# Do a dry run on the whole patch and don't do anything if part of the patch is +# going to fail (probably too strict unless we exclude ChangeLog). +# Handle git-diff patches with binary changes + +use strict; +use warnings; + +use Cwd; +use Digest::MD5; +use Fcntl qw(:DEFAULT :seek); +use File::Basename; +use File::Spec; +use File::Temp qw(tempfile); +use Getopt::Long; + +sub checksum($); +sub fixChangeLogPatch($); +sub gitdiff2svndiff($); +sub patch($); +sub revertDirectories(); +sub svnStatus($); +sub unapplyPatch($$;$); +sub unsetChangeLogDate($$); + +my $showHelp = 0; +if (!GetOptions("help!" => \$showHelp) || $showHelp) { + print STDERR basename($0) . " [-h|--help] patch1 [patch2 ...]\n"; + exit 1; +} + +my @copiedFiles; +my %directoriesToCheck; + +my $copiedFromPath; +my $filter; +my $indexPath; +my $patch; +while (<>) { + s/([\n\r]+)$//mg; + my $eol = $1; + if (!defined($indexPath) && m#^diff --git a/#) { + $filter = \&gitdiff2svndiff; + } + $_ = &$filter($_) if $filter; + if (/^Index: (.*)/) { + $indexPath = $1; + if ($patch) { + if ($copiedFromPath) { + push @copiedFiles, $patch; + } else { + patch($patch); + } + $copiedFromPath = ""; + $patch = ""; + } + } + if ($indexPath) { + # Fix paths on diff, ---, and +++ lines to match preceding Index: line. + s/^--- \S+/--- $indexPath/; + if (/^--- .+\(from (\S+):\d+\)$/) { + $copiedFromPath = $1; + } + if (s/^\+\+\+ \S+/+++ $indexPath/) { + $indexPath = ""; + } + } + $patch .= $_; + $patch .= $eol; +} + +if ($patch) { + if ($copiedFromPath) { + push @copiedFiles, $patch; + } else { + patch($patch); + } +} + +# Handle copied and moved files last since they may have had post-copy changes that have now been unapplied +for $patch (@copiedFiles) { + patch($patch); +} + +revertDirectories(); + +exit 0; + +sub checksum($) +{ + my $file = shift; + open(FILE, $file) or die "Can't open '$file': $!"; + binmode(FILE); + my $checksum = Digest::MD5->new->addfile(*FILE)->hexdigest(); + close(FILE); + return $checksum; +} + +sub fixChangeLogPatch($) +{ + my $patch = shift; + my $contextLineCount = 3; + + return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; + my ($oldLineCount, $newLineCount) = ($1, $2); + return $patch if $oldLineCount <= $contextLineCount; + + # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will + # have lines of context at the top of a patch when the existing entry has the same + # date and author as the new entry. This nifty loop alters a ChangeLog patch so + # that the added lines ("+") in the patch always start at the beginning of the + # patch and there are no initial lines of context. + my $newPatch; + my $lineCountInState = 0; + my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; + my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; + my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); + my $state = $stateHeader; + foreach my $line (split(/\n/, $patch)) { + $lineCountInState++; + if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { + $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; + $lineCountInState = 0; + $state = $statePreContext; + } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { + $line = "+" . substr($line, 1); + if ($lineCountInState == $oldContentLineCountReduction) { + $lineCountInState = 0; + $state = $stateNewChanges; + } + } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { + # No changes to these lines + if ($lineCountInState == $newContentLineCountWithoutContext) { + $lineCountInState = 0; + $state = $statePostContext; + } + } elsif ($state == $statePostContext) { + if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { + $line = " " . substr($line, 1); + } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { + next; # Discard + } + } + $newPatch .= $line . "\n"; + } + + return $newPatch; +} + +sub gitdiff2svndiff($) +{ + $_ = shift @_; + if (m#^diff --git a/(.+) b/(.+)#) { + return "Index: $1"; + } elsif (m/^new file.*/) { + return ""; + } elsif (m#^index [0-9a-f]{7}\.\.[0-9a-f]{7} [0-9]{6}#) { + return "==================================================================="; + } elsif (m#^--- a/(.+)#) { + return "--- $1"; + } elsif (m#^\+\+\+ b/(.+)#) { + return "+++ $1"; + } + return $_; +} + +sub patch($) +{ + my ($patch) = @_; + return if !$patch; + + unless ($patch =~ m|^Index: ([^\n]+)|) { + my $separator = '-' x 67; + warn "Failed to find 'Index:' in:\n$separator\n$patch\n$separator\n"; + return; + } + my $fullPath = $1; + $directoriesToCheck{dirname($fullPath)} = 1; + + my $deletion = 0; + my $addition = 0; + my $isBinary = 0; + + $addition = 1 if ($patch =~ /\n--- .+\(revision 0\)\n/ || $patch =~ /\n@@ -0,0 .* @@/); + $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/; + $isBinary = 1 if $patch =~ /\nCannot display: file marked as a binary type\./; + + if (!$addition && !$deletion && !$isBinary) { + # Standard patch, patch tool can handle this. + if (basename($fullPath) eq "ChangeLog") { + my $changeLogDotOrigExisted = -f "${fullPath}.orig"; + unapplyPatch(unsetChangeLogDate($fullPath, fixChangeLogPatch($patch)), $fullPath, ["--fuzz=3"]); + unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted); + } else { + unapplyPatch($patch, $fullPath); + } + } else { + # Either a deletion, an addition or a binary change. + + if ($isBinary) { + # Reverse binary change + unlink($fullPath) if (-e $fullPath); + system "svn", "revert", $fullPath; + } elsif ($deletion) { + # Reverse deletion + rename($fullPath, "$fullPath.orig") if -e $fullPath; + + unapplyPatch($patch, $fullPath); + + # If we don't ask for the filehandle here, we always get a warning. + my ($fh, $tempPath) = tempfile(basename($fullPath) . "-XXXXXXXX", + DIR => dirname($fullPath), UNLINK => 1); + close($fh); + + # Keep the version from the patch in case it's different from svn. + rename($fullPath, $tempPath); + system "svn", "revert", $fullPath; + rename($tempPath, $fullPath); + + # This works around a bug in the svn client. + # [Issue 1960] file modifications get lost due to FAT 2s time resolution + # http://subversion.tigris.org/issues/show_bug.cgi?id=1960 + system "touch", $fullPath; + + # Remove $fullPath.orig if it is the same as $fullPath + unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig"); + + # Show status if the file is modifed + system "svn", "stat", $fullPath; + } else { + # Reverse addition + unapplyPatch($patch, $fullPath, ["--force"]); + unlink($fullPath) if -z $fullPath; + system "svn", "revert", $fullPath; + } + } +} + +sub revertDirectories() +{ + my %checkedDirectories; + foreach my $path (reverse sort keys %directoriesToCheck) { + my @dirs = File::Spec->splitdir($path); + while (scalar @dirs) { + my $dir = File::Spec->catdir(@dirs); + pop(@dirs); + next if (exists $checkedDirectories{$dir}); + if (-d $dir) { + my $svnOutput = svnStatus($dir); + if ($svnOutput && $svnOutput =~ m#A\s+$dir\n#) { + system "svn", "revert", $dir; + rmdir $dir; + } + elsif ($svnOutput && $svnOutput =~ m#D\s+$dir\n#) { + system "svn", "revert", $dir; + } + else { + # Modification + print $svnOutput if $svnOutput; + } + $checkedDirectories{$dir} = 1; + } + else { + die "'$dir' is not a directory"; + } + } + } +} + +sub svnStatus($) +{ + my ($fullPath) = @_; + my $svnStatus; + open SVN, "svn status --non-interactive --non-recursive '$fullPath' |" or die; + if (-d $fullPath) { + # When running "svn stat" on a directory, we can't assume that only one + # status will be returned (since any files with a status below the + # directory will be returned), and we can't assume that the directory will + # be first (since any files with unknown status will be listed first). + my $normalizedFullPath = File::Spec->catdir(File::Spec->splitdir($fullPath)); + while (<SVN>) { + chomp; + my $normalizedStatPath = File::Spec->catdir(File::Spec->splitdir(substr($_, 7))); + if ($normalizedFullPath eq $normalizedStatPath) { + $svnStatus = $_; + last; + } + } + # Read the rest of the svn command output to avoid a broken pipe warning. + local $/ = undef; + <SVN>; + } + else { + # Files will have only one status returned. + $svnStatus = <SVN>; + } + close SVN; + return $svnStatus; +} + +sub unapplyPatch($$;$) +{ + my ($patch, $fullPath, $options) = @_; + $options = [] if (! $options); + my $command = "patch " . join(" ", "-p0", "-R", @{$options}); + open PATCH, "| $command" or die "Failed to patch $fullPath: $!"; + print PATCH $patch; + close PATCH; +} + +sub unsetChangeLogDate($$) +{ + my $fullPath = shift; + my $patch = shift; + my $newDate; + sysopen(CHANGELOG, $fullPath, O_RDONLY) or die "Failed to open $fullPath: $!"; + sysseek(CHANGELOG, 0, SEEK_SET); + my $byteCount = sysread(CHANGELOG, $newDate, 10); + die "Failed reading $fullPath: $!" if !$byteCount || $byteCount != 10; + close(CHANGELOG); + $patch =~ s/(\n\+)\d{4}-[^-]{2}-[^-]{2}( )/$1$newDate$2/; + return $patch; +} diff --git a/WebKitTools/Scripts/update-iexploder-cssproperties b/WebKitTools/Scripts/update-iexploder-cssproperties new file mode 100755 index 0000000..b7ae6cb --- /dev/null +++ b/WebKitTools/Scripts/update-iexploder-cssproperties @@ -0,0 +1,112 @@ +#!/usr/bin/perl + +# Copyright (C) 2007 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 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 updates WebKitTools/iExploder/htdocs/cssproperties.in based on +# WebCore/css/CSSPropertyNames.in. + +use warnings; +use strict; + +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +use File::Spec; + +sub generateSectionFromCSSPropertyNamesFile(); +sub readiExploderFile(); +sub svnRevision($); +sub writeiExploderFile(); + +my $iExploderFile = File::Spec->catfile(sourceDir(), split("/", "WebKitTools/iExploder/htdocs/cssproperties.in")); +my $cssPropertyNamesFile = File::Spec->catfile(sourceDir(), split("/", "WebCore/css/CSSPropertyNames.in")); + +my @sections = readiExploderFile(); +$sections[0] = generateSectionFromCSSPropertyNamesFile(); +writeiExploderFile(); + +print `svn stat $iExploderFile`; +print "Successfully updated!\n"; + +exit 0; + +sub generateSectionFromCSSPropertyNamesFile() +{ + my $revision = svnRevision($cssPropertyNamesFile); + my $path = File::Spec->abs2rel($cssPropertyNamesFile, sourceDir()); + my $result = "# From WebKit svn r" . $revision . " (" . $path . ")\n"; + + my @properties = (); + + open(IN, $cssPropertyNamesFile) || die "$!"; + while (my $l = <IN>) { + chomp $l; + next if $l =~ m/^\s*#/ || $l =~ m/^\s*$/; + push(@properties, $l); + } + close(IN); + + $result .= join("\n", sort { $a cmp $b } @properties) . "\n\n"; + + return $result; +} + +sub readiExploderFile() +{ + my @sections = (); + local $/ = "\n\n"; + + open(IN, $iExploderFile) || die "$!"; + @sections = <IN>; + close(IN); + + return @sections; +} + +sub svnRevision($) +{ + my ($file) = @_; + my $revision = ""; + + open INFO, "svn info '$file' |" or die; + while (<INFO>) { + if (/^Revision: (.+)/) { + $revision = $1; + } + } + close INFO; + + return $revision ? $revision : "UNKNOWN"; +} + +sub writeiExploderFile() +{ + open(OUT, "> $iExploderFile") || die "$!"; + print OUT join("", @sections); + close(OUT); +} diff --git a/WebKitTools/Scripts/update-javascriptcore-test-results b/WebKitTools/Scripts/update-javascriptcore-test-results new file mode 100755 index 0000000..dd8b9b6 --- /dev/null +++ b/WebKitTools/Scripts/update-javascriptcore-test-results @@ -0,0 +1,73 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2007 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. + +use strict; +use FindBin; +use Getopt::Long; +use lib $FindBin::Bin; +use webkitdirs; + +chdirWebKit(); +chdir "JavaScriptCore/tests/mozilla" or die; + +my $force = 0; +GetOptions('force' => \$force); + +open EXPECTED, "expected.html" or die; +while (<EXPECTED>) { + last if /failures reported\.$/; +} +my %expected; +while (<EXPECTED>) { + chomp; + $expected{$_} = 1; +} +close EXPECTED; + +open ACTUAL, "actual.html" or die; +my $actual; +while (<ACTUAL>) { + $actual .= $_; + last if /failures reported\.$/; +} +my $failed = 0; +while (<ACTUAL>) { + $actual .= $_; + chomp; + if (!$expected{$_}) { + $failed = 1; + print "failure not expected: $_\n"; + } +} +close ACTUAL; + +die "won't update, failures introduced\n" if $failed && !$force; + +open EXPECTED, ">expected.html"; +print EXPECTED $actual; +close EXPECTED; diff --git a/WebKitTools/Scripts/update-sources-list.py b/WebKitTools/Scripts/update-sources-list.py new file mode 100644 index 0000000..e565059 --- /dev/null +++ b/WebKitTools/Scripts/update-sources-list.py @@ -0,0 +1,93 @@ +#!/usr/bin/python + +# Copyright (C) 2007 Kevin Ollivier 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. +# +# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR +# 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. +# +# Make sure any port-independent files added to the Bakefile are +# added to GTK, QT, etc. so that file updates can happen in one place. + +import os, sys +from xml.dom import minidom + +scriptDir = os.path.abspath(sys.path[0]) +wkroot = os.path.abspath(os.path.join(scriptDir, "../..")) + +def getWebCoreFilesDict(): + """ + This method parses the WebCoreSources.bkl file, which has a list of all sources not specific + to any port, and returns the result as a dictionary with items of the form + (groupName, groupFiles). + """ + sources = {} + sources_prefix = "WEBCORE_" + filepath = os.path.join(wkroot, "WebCore/WebCoreSources.bkl") + assert(os.path.exists(filepath)) + + doc = minidom.parse(filepath) + for sourceGroup in doc.getElementsByTagName("set"): + groupName = "" + if sourceGroup.attributes.has_key("var"): + groupName = sourceGroup.attributes["var"].value + groupName = groupName.replace(sources_prefix, "") + + sourcesList = [] + for node in sourceGroup.childNodes: + if node.nodeType == node.TEXT_NODE: + sourcesText = node.nodeValue.strip() + sourcesList = sourcesText.split("\n") + + assert(groupName != "") + assert(sourcesList != []) + + sources[groupName] = sourcesList + + return sources + +def generateWebCoreSourcesGTKAndQT(sources): + """ + Convert the dictionary obtained from getWebCoreFilesDict() into a Unix makefile syntax, + which IIUC is suitable for both GTK and QT build systems. To take advantage of this, + QT and GTK would have to include the file "WebCore/sources.inc" into their makefiles. + """ + makefileString = "" + + for key in sources.keys(): + makefileString += key + "+=" + for source in sources[key]: + makefileString += " \\\n\t\t" + source.strip() + + makefileString += "\n\n" + + makefileString += "BASE_SOURCES +=" + for key in sources.keys(): + makefileString += " \\\n\t\t" + key + + outfile = os.path.join(wkroot, "WebCore/sources.inc") + sourcefile = open(outfile, "w") + sourcefile.write(makefileString) + sourcefile.close() + +sources = getWebCoreFilesDict() +generateWebCoreSourcesGTKAndQT(sources) + +# Coming soon - MSVC and hopefully XCode support! diff --git a/WebKitTools/Scripts/update-webkit b/WebKitTools/Scripts/update-webkit new file mode 100755 index 0000000..e0c0d82 --- /dev/null +++ b/WebKitTools/Scripts/update-webkit @@ -0,0 +1,92 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006, 2007, 2008 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. + +# Update script for Web Kit Open Source Project. + +use strict; +use FindBin; +use lib $FindBin::Bin; +use File::Basename; +use File::Spec; +use Getopt::Long; +use webkitdirs; + +sub runSvnUpdate(); + +# Handle options +my $quiet = ''; +my $showHelp; + +my $getOptionsResult = GetOptions( + 'h|help' => \$showHelp, + 'q|quiet' => \$quiet, +); + +if (!$getOptionsResult || $showHelp) { + print STDERR <<__END__; +Usage: @{[ basename($0) ]} [options] + -h|--help show the help message + -q|--quiet pass -q to svn update for quiet updates +__END__ + exit 1; +} + +my @svnOptions = (); +push @svnOptions, '-q' if $quiet; + +chdirWebKit(); +print "Updating OpenSource\n" unless $quiet; +runSvnUpdate(); + +if (-d "../Internal") { + chdir("../Internal"); + print "Updating Internal\n" unless $quiet; + runSvnUpdate(); +} elsif (isCygwin()) { + system("perl", "WebKitTools/Scripts/update-webkit-auxiliary-libs") == 0 or die; +} + +exit 0; + +sub runSvnUpdate() +{ + open UPDATE, "-|", "svn", "update", @svnOptions or die; + my @conflictedChangeLogs; + while (my $line = <UPDATE>) { + print $line; + push @conflictedChangeLogs, $1 if $line =~ m/^C\s+(.+)\s*$/ && basename($1) eq "ChangeLog"; + } + close UPDATE or die; + + if (@conflictedChangeLogs) { + print "Attempting to merge conflicted ChangeLogs.\n"; + my $resolveChangeLogsPath = File::Spec->catfile(dirname($0), "resolve-ChangeLogs"); + (system($resolveChangeLogsPath, "--no-warnings", @conflictedChangeLogs) == 0) + or die "Could not open resolve-ChangeLogs script: $!.\n"; + } +} diff --git a/WebKitTools/Scripts/update-webkit-auxiliary-libs b/WebKitTools/Scripts/update-webkit-auxiliary-libs new file mode 100755 index 0000000..1d6943c --- /dev/null +++ b/WebKitTools/Scripts/update-webkit-auxiliary-libs @@ -0,0 +1,121 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006, 2007 Apple Computer, 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. + +# Updates a development environment to the new WebKitAuxiliaryLibrary + +use strict; +use warnings; + +use HTTP::Date qw(str2time); +use File::Find; +use File::Temp; +use File::Spec; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +sub lastModifiedToUnixTime($); + +# Time in seconds that the new zip file must be newer than the old for us to +# consider them to be different. If the difference in modification time is less +# than this threshold, we assume that the files are the same. We need this +# because the zip file is served from a set of mirrors with slightly different +# Last-Modified times. +my $newnessThreshold = 30; + +my $sourceDir = sourceDir(); +my $file = "WebKitAuxiliaryLibrary"; +my $zipFile = "$file.zip"; +my $auxiliaryLibsURL = "http://developer.apple.com/opensource/internet/$zipFile"; +my $webkitLibrariesDir = toUnixPath($ENV{'WEBKITLIBRARIESDIR'}) || "$sourceDir/WebKitLibraries/win"; +my $tmpDir = File::Spec->rel2abs(File::Temp::tempdir("webkitlibsXXXXXXX", TMPDIR => 1, CLEANUP => 1)); + +print "Checking Last-Modified date of $zipFile...\n"; + +my $result = system "curl -s -I $auxiliaryLibsURL | grep Last-Modified > \"$tmpDir/$file.headers\""; +print STDERR "Couldn't check Last-Modified date of new $zipFile.\n" if $result; + +if (!$result && open NEW, "$tmpDir/$file.headers") { + my $new = lastModifiedToUnixTime(<NEW>); + close NEW; + + if (defined $new && open OLD, "$webkitLibrariesDir/$file.headers") { + my $old = lastModifiedToUnixTime(<OLD>); + close OLD; + if (defined $old && abs($new - $old) < $newnessThreshold) { + print "Current $file is up to date\n"; + exit 0; + } + } +} + +print "Downloading $zipFile...\n\n"; +$result = system "curl -o \"$tmpDir/$zipFile\" $auxiliaryLibsURL"; +die "Couldn't download $zipFile!" if $result; + +$result = system "unzip", "-q", "-d", $tmpDir, "$tmpDir/$zipFile"; +die "Couldn't unzip $zipFile." if $result; + +print "\nInstalling $file...\n"; + +sub wanted +{ + my $relativeName = File::Spec->abs2rel($File::Find::name, "$tmpDir/$file/win"); + my $destination = "$webkitLibrariesDir/$relativeName"; + + if (-d $_) { + mkdir $destination; + return; + } + + system "cp", $_, $destination; +} + +File::Find::find(\&wanted, "$tmpDir/$file"); + +$result = system "mv", "$tmpDir/$file.headers", $webkitLibrariesDir; +print STDERR "Couldn't move $file.headers to $webkitLibrariesDir" . ".\n" if $result; + +print "The $file has been sucessfully installed in\n $webkitLibrariesDir\n"; +exit; + +sub toUnixPath +{ + my $path = shift; + return unless $path; + chomp($path = `cygpath -u '$path'`); + return $path; +} + +sub lastModifiedToUnixTime($) +{ + my ($str) = @_; + + $str =~ /^Last-Modified: (.*)$/ or return; + return str2time($1); +} diff --git a/WebKitTools/Scripts/update-webkit-localizable-strings b/WebKitTools/Scripts/update-webkit-localizable-strings new file mode 100755 index 0000000..350bf21 --- /dev/null +++ b/WebKitTools/Scripts/update-webkit-localizable-strings @@ -0,0 +1,46 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2006, 2007 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. + +use strict; +use warnings; + +use File::Basename; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +my @directoriesToScan = ("WebKit/mac", "WebKit/win"); +my $fileToUpdate = "WebKit/English.lproj/Localizable.strings"; +my $exceptionsFile = "WebKit/StringsNotToBeLocalized.txt"; + +@ARGV == 0 or die "Usage: " . basename($0) . "\n"; + +chdirWebKit(); + +system "sort -u $exceptionsFile -o $exceptionsFile"; +exec "extract-localizable-strings", $exceptionsFile, $fileToUpdate, @directoriesToScan; diff --git a/WebKitTools/Scripts/update-webkit-support-libs b/WebKitTools/Scripts/update-webkit-support-libs new file mode 100755 index 0000000..e9c302b --- /dev/null +++ b/WebKitTools/Scripts/update-webkit-support-libs @@ -0,0 +1,105 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006, 2007 Apple Computer, 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. + +# Updates a development environment to the new WebKitSupportLibrary + +use strict; +use warnings; + +use File::Find; +use File::Temp; +use File::Spec; +use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + +my $sourceDir = sourceDir(); +my $file = "WebKitSupportLibrary"; +my $zipFile = "$file.zip"; +my $zipDirectory = toUnixPath($ENV{'WEBKITSUPPORTLIBRARIESZIPDIR'}) || $sourceDir; +my $pathToZip = File::Spec->catfile($zipDirectory, $zipFile); +my $webkitLibrariesDir = toUnixPath($ENV{'WEBKITLIBRARIESDIR'}) || "$sourceDir/WebKitLibraries/win"; +my $tmpDir = File::Spec->rel2abs(File::Temp::tempdir("webkitlibsXXXXXXX", TMPDIR => 1, CLEANUP => 1)); + +# Make sure the file zipfile exists before doing anything. +die "$zipFile could not be found in your root source directory. Please\n" . + "download it from http://developer.apple.com/opensource/internet/webkit_sptlib_agree.html and place it in \n" . + "$sourceDir\n and then run update-webkit again.\n" unless (-f "$pathToZip"); + +print "Checking mod-date of $zipFile...\n"; +open MOD, ">$tmpDir/$file.modified" or die "Couldn't open $tmpDir/$file.modified for writing"; +print MOD (stat $pathToZip)[9] . "\n"; +close MOD; + +if (open NEW, "$tmpDir/$file.modified") { + my $new = <NEW>; + close NEW; + + if (open OLD, "$webkitLibrariesDir/$file.modified") { + my $old = <OLD>; + close OLD; + if ($old eq $new) { + print "Current $file is up to date\n"; + exit 0; + } + } +} + +my $result = system "unzip", "-q", "-d", $tmpDir, $pathToZip; +die "Couldn't unzip $zipFile." if $result; + +print "\nInstalling $file...\n"; + +sub wanted +{ + my $relativeName = File::Spec->abs2rel($File::Find::name, "$tmpDir/$file/win"); + my $destination = "$webkitLibrariesDir/$relativeName"; + + if (-d $_) { + mkdir $destination; + return; + } + + system "cp", $_, $destination; +} + +File::Find::find(\&wanted, "$tmpDir/$file"); + +$result = system "mv", "$tmpDir/$file.modified", $webkitLibrariesDir; +print STDERR "Couldn't move $file.modified to $webkitLibrariesDir" . ".\n" if $result; + +print "The $file has been sucessfully installed in\n $webkitLibrariesDir\n"; +exit; + +sub toUnixPath +{ + my $path = shift; + return unless $path; + chomp($path = `cygpath -u '$path'`); + return $path; +} diff --git a/WebKitTools/Scripts/webkitdirs.pm b/WebKitTools/Scripts/webkitdirs.pm new file mode 100644 index 0000000..728e7c0 --- /dev/null +++ b/WebKitTools/Scripts/webkitdirs.pm @@ -0,0 +1,984 @@ +# Copyright (C) 2005, 2006, 2007 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. + +# Module to share code to get to WebKit directories. + +use strict; +use warnings; +use FindBin; +use File::Basename; +use POSIX; +use VCSUtils; + +BEGIN { + use Exporter (); + our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); + $VERSION = 1.00; + @ISA = qw(Exporter); + @EXPORT = qw(&chdirWebKit &baseProductDir &productDir &XcodeOptions &XcodeOptionString &XcodeOptionStringNoConfig &passedConfiguration &setConfiguration &safariPath &checkFrameworks ¤tSVNRevision); + %EXPORT_TAGS = ( ); + @EXPORT_OK = (); +} + +our @EXPORT_OK; + +my $baseProductDir; +my @baseProductDirOption; +my $configuration; +my $configurationForVisualStudio; +my $configurationProductDir; +my $sourceDir; +my $currentSVNRevision; +my $osXVersion; +my $isQt; +my $isGtk; +my $isWx; +my $forceRun64Bit; + +# Variables for Win32 support +my $vcBuildPath; +my $windowsTmpPath; + +sub determineSourceDir +{ + return if $sourceDir; + $sourceDir = $FindBin::Bin; + $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later + + # walks up path checking each directory to see if it is the main WebKit project dir, + # defined by containing JavaScriptCore, WebCore, and WebKit + until ((-d "$sourceDir/JavaScriptCore" && -d "$sourceDir/WebCore" && -d "$sourceDir/WebKit") || (-d "$sourceDir/Internal" && -d "$sourceDir/OpenSource")) + { + if ($sourceDir !~ s|/[^/]+$||) { + die "Could not find top level webkit directory above source directory using FindBin.\n"; + } + } + + $sourceDir = "$sourceDir/OpenSource" if -d "$sourceDir/OpenSource"; +} + +# used for scripts which are stored in a non-standard location +sub setSourceDir($) +{ + ($sourceDir) = @_; +} + +sub determineBaseProductDir +{ + return if defined $baseProductDir; + determineSourceDir(); + if (isOSX()) { + open PRODUCT, "defaults read com.apple.Xcode PBXApplicationwideBuildSettings 2> /dev/null |" or die; + $baseProductDir = join '', <PRODUCT>; + close PRODUCT; + + $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s; + undef $baseProductDir unless $baseProductDir =~ /^\//; + + if (!defined($baseProductDir)) { + open PRODUCT, "defaults read com.apple.Xcode PBXProductDirectory 2> /dev/null |" or die; + $baseProductDir = <PRODUCT>; + close PRODUCT; + if ($baseProductDir) { + chomp $baseProductDir; + undef $baseProductDir unless $baseProductDir =~ /^\//; + } + } + } else { + $baseProductDir = $ENV{"WEBKITOUTPUTDIR"}; + if (isCygwin() && $baseProductDir) { + my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`; + chomp $unixBuildPath; + $baseProductDir = $unixBuildPath; + } + } + + if ($baseProductDir && isOSX()) { + $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|; + $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|; + $baseProductDir =~ s|^~/|$ENV{HOME}/|; + die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/; + die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/; + @baseProductDirOption = (); + } + + if (!defined($baseProductDir)) { + $baseProductDir = "$sourceDir/WebKitBuild"; + + if (isGit() && isGitBranchBuild()) { + my $branch = gitBranch(); + $baseProductDir = "$baseProductDir/$branch"; + } + + @baseProductDirOption = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir") if (isOSX()); + if (isCygwin()) { + my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`; + chomp $dosBuildPath; + $ENV{"WEBKITOUTPUTDIR"} = $dosBuildPath; + } + } +} + +sub setBaseProductDir($) +{ + ($baseProductDir) = @_; +} + +sub determineConfiguration +{ + return if defined $configuration; + determineBaseProductDir(); + if (open CONFIGURATION, "$baseProductDir/Configuration") { + $configuration = <CONFIGURATION>; + close CONFIGURATION; + } + if ($configuration) { + chomp $configuration; + # compatibility for people who have old Configuration files + $configuration = "Release" if $configuration eq "Deployment"; + $configuration = "Debug" if $configuration eq "Development"; + } else { + $configuration = "Release"; + } +} + +sub determineConfigurationForVisualStudio +{ + return if defined $configurationForVisualStudio; + determineConfiguration(); + $configurationForVisualStudio = $configuration; + return unless $configuration eq "Debug"; + setupCygwinEnv(); + chomp(my $dir = `cygpath -ua '$ENV{WEBKITLIBRARIESDIR}'`); + $configurationForVisualStudio = "Debug_Internal" if -f "$dir/bin/CoreFoundation_debug.dll"; +} + +sub determineConfigurationProductDir +{ + return if defined $configurationProductDir; + determineBaseProductDir(); + determineConfiguration(); + if (isCygwin() && !isWx()) { + $configurationProductDir = "$baseProductDir/bin"; + } else { + $configurationProductDir = "$baseProductDir/$configuration"; + } +} + +sub setConfigurationProductDir($) +{ + ($configurationProductDir) = @_; +} + +sub determineCurrentSVNRevision +{ + return if defined $currentSVNRevision; + determineSourceDir(); + my $svnInfo = `LC_ALL=C svn info $sourceDir | grep Revision:`; + ($currentSVNRevision) = ($svnInfo =~ m/Revision: (\d+).*/g); + die "Unable to determine current SVN revision in $sourceDir" unless (defined $currentSVNRevision); + return $currentSVNRevision; +} + + +sub chdirWebKit +{ + determineSourceDir(); + chdir $sourceDir or die; +} + +sub baseProductDir +{ + determineBaseProductDir(); + return $baseProductDir; +} + +sub sourceDir +{ + determineSourceDir(); + return $sourceDir; +} + +sub productDir +{ + determineConfigurationProductDir(); + return $configurationProductDir; +} + +sub configuration() +{ + determineConfiguration(); + return $configuration; +} + +sub configurationForVisualStudio() +{ + determineConfigurationForVisualStudio(); + return $configurationForVisualStudio; +} + +sub currentSVNRevision +{ + determineCurrentSVNRevision(); + return $currentSVNRevision; +} + +sub XcodeOptions +{ + determineBaseProductDir(); + determineConfiguration(); + return (@baseProductDirOption, "-configuration", $configuration); +} + +sub XcodeOptionString +{ + return join " ", XcodeOptions(); +} + +sub XcodeOptionStringNoConfig +{ + return join " ", @baseProductDirOption; +} + +my $passedConfiguration; +my $searchedForPassedConfiguration; +sub determinePassedConfiguration +{ + return if $searchedForPassedConfiguration; + $searchedForPassedConfiguration = 1; + + my $isWinCairo = grep(/^--cairo-win32$/i, @ARGV); + + for my $i (0 .. $#ARGV) { + my $opt = $ARGV[$i]; + if ($opt =~ /^--debug$/i || $opt =~ /^--devel/i) { + splice(@ARGV, $i, 1); + $passedConfiguration = "Debug"; + $passedConfiguration .= "_Cairo" if ($isWinCairo && isCygwin()); + return; + } + if ($opt =~ /^--release$/i || $opt =~ /^--deploy/i) { + splice(@ARGV, $i, 1); + $passedConfiguration = "Release"; + $passedConfiguration .= "_Cairo" if ($isWinCairo && isCygwin()); + return; + } + if ($opt =~ /^--profil(e|ing)$/i) { + splice(@ARGV, $i, 1); + $passedConfiguration = "Profiling"; + $passedConfiguration .= "_Cairo" if ($isWinCairo && isCygwin()); + return; + } + } + $passedConfiguration = undef; +} + +sub passedConfiguration +{ + determinePassedConfiguration(); + return $passedConfiguration; +} + +sub setConfiguration +{ + if (my $config = shift @_) { + $configuration = $config; + return; + } + + determinePassedConfiguration(); + $configuration = $passedConfiguration if $passedConfiguration; +} + +sub safariPathFromSafariBundle +{ + my ($safariBundle) = @_; + + return "$safariBundle/Contents/MacOS/Safari" if isOSX(); + return $safariBundle if isCygwin(); +} + +sub installedSafariPath +{ + my $safariBundle; + + if (isOSX()) { + $safariBundle = "/Applications/Safari.app"; + } elsif (isCygwin()) { + $safariBundle = `"$configurationProductDir/FindSafari.exe"`; + $safariBundle =~ s/[\r\n]+$//; + $safariBundle = `cygpath -u '$safariBundle'`; + $safariBundle =~ s/[\r\n]+$//; + $safariBundle .= "Safari.exe"; + } + + return safariPathFromSafariBundle($safariBundle); +} + +# Locate Safari. +sub safariPath +{ + # Use WEBKIT_SAFARI environment variable if present. + my $safariBundle = $ENV{WEBKIT_SAFARI}; + if (!$safariBundle) { + determineConfigurationProductDir(); + # Use Safari.app in product directory if present (good for Safari development team). + if (isOSX() && -d "$configurationProductDir/Safari.app") { + $safariBundle = "$configurationProductDir/Safari.app"; + } elsif (isCygwin() && -x "$configurationProductDir/bin/Safari.exe") { + $safariBundle = "$configurationProductDir/bin/Safari.exe"; + } else { + return installedSafariPath(); + } + } + my $safariPath = safariPathFromSafariBundle($safariBundle); + die "Can't find executable at $safariPath.\n" if isOSX() && !-x $safariPath; + return $safariPath; +} + +sub builtDylibPathForName +{ + my $framework = shift; + determineConfigurationProductDir(); + if (isQt() or isGtk()) { + return "$configurationProductDir/$framework"; + } + if (isOSX()) { + return "$configurationProductDir/$framework.framework/Versions/A/$framework"; + } + if (isCygwin()) { + if ($framework eq "JavaScriptCore") { + return "$baseProductDir/lib/$framework.lib"; + } else { + return "$baseProductDir/$framework.intermediate/$configuration/$framework.intermediate/$framework.lib"; + } + } + + die "Unsupported platform, can't determine built library locations."; +} + +# Check to see that all the frameworks are built. +sub checkFrameworks +{ + return if isCygwin(); + my @frameworks = ("JavaScriptCore", "WebCore"); + push(@frameworks, "WebKit") if isOSX(); + for my $framework (@frameworks) { + my $path = builtDylibPathForName($framework); + die "Can't find built framework at \"$path\".\n" unless -x $path; + } +} + +sub hasSVGSupport +{ + return 0 if isCygwin(); + + my $path = shift; + + if (isQt()) { + return 1; + } + + if (isGtk() and $path =~ /WebCore/) { + $path .= "/../.libs/webkit-1.0.so"; + } + + my $hasSVGSupport = 0; + if (-e $path) { + open NM, "-|", "nm", $path or die; + while (<NM>) { + $hasSVGSupport = 1 if /SVGElement/; + } + close NM; + } + return $hasSVGSupport; +} + +sub removeLibraryDependingOnSVG +{ + my $frameworkName = shift; + my $shouldHaveSVG = shift; + + my $path = builtDylibPathForName($frameworkName); + return unless -x $path; + + my $hasSVG = hasSVGSupport($path); + system "rm -f $path" if ($shouldHaveSVG xor $hasSVG); +} + +sub checkWebCoreSVGSupport +{ + my $required = shift; + my $framework = "WebCore"; + my $path = builtDylibPathForName($framework); + my $hasSVG = hasSVGSupport($path); + if ($required && !$hasSVG) { + die "$framework at \"$path\" does not include SVG Support, please run build-webkit --svg\n"; + } + return $hasSVG; +} + +sub isQt() +{ + determineIsQt(); + return $isQt; +} + +sub checkArgv($) +{ + my $argToCheck = shift; + foreach my $opt (@ARGV) { + if ($opt =~ /^$argToCheck/i ) { + @ARGV = grep(!/^$argToCheck/i, @ARGV); + return 1; + } + } + return 0; +} + +sub determineIsQt() +{ + return if defined($isQt); + + # Allow override in case QTDIR is not set. + if (checkArgv("--qt")) { + $isQt = 1; + return; + } + + # The presence of QTDIR only means Qt if --gtk is not on the command-line + if (isGtk()) { + $isQt = 0; + return; + } + + $isQt = defined($ENV{'QTDIR'}); +} + +sub isGtk() +{ + determineIsGtk(); + return $isGtk; +} + +sub determineIsGtk() +{ + return if defined($isGtk); + + if (checkArgv("--gtk")) { + $isGtk = 1; + } else { + $isGtk = 0; + } +} + +sub isWx() +{ + determineIsWx(); + return $isWx; +} + +sub determineIsWx() +{ + return if defined($isWx); + + if (checkArgv("--wx")) { + $isWx = 1; + } else { + $isWx = 0; + } +} + +# Determine if this is debian, ubuntu, linspire, or something similar. +sub isDebianBased() +{ + return -e "/etc/debian_version"; +} + +sub isCygwin() +{ + return ($^O eq "cygwin") || 0; +} + +sub isDarwin() +{ + return ($^O eq "darwin") || 0; +} + +# isOSX() only returns true for Apple's port, not for other ports that can be +# built/run on OS X. +sub isOSX() +{ + return isDarwin() unless (isQt() or isGtk() or isWx()); + return 0; +} + +sub determineOSXVersion() +{ + return if $osXVersion; + + if (!isOSX()) { + $osXVersion = -1; + return; + } + + my $version = `sw_vers -productVersion`; + my @splitVersion = split(/\./, $version); + @splitVersion >= 2 or die "Invalid version $version"; + $osXVersion = { + "major" => $splitVersion[0], + "minor" => $splitVersion[1], + "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0), + }; +} + +sub osXVersion() +{ + determineOSXVersion(); + return $osXVersion; +} + +sub isTiger() +{ + return isOSX() && osXVersion()->{"minor"} == 4; +} + +sub isLeopard() +{ + return isOSX() && osXVersion()->{"minor"} == 5; +} + +sub isSnowLeopard() +{ + return isOSX() && osXVersion()->{"minor"} == 6; +} + +sub relativeScriptsDir() +{ + my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel(dirname($0), getcwd()), ""); + if ($scriptDir eq "") { + $scriptDir = "."; + } + return $scriptDir; +} + +sub launcherPath() +{ + my $relativeScriptsPath = relativeScriptsDir(); + if (isGtk() || isQt()) { + return "$relativeScriptsPath/run-launcher"; + } elsif (isOSX() || isCygwin()) { + return "$relativeScriptsPath/run-safari"; + } +} + +sub launcherName() +{ + if (isGtk()) { + return "GtkLauncher"; + } elsif (isQt()) { + return "QtLauncher"; + } elsif (isOSX() || isCygwin()) { + return "Safari"; + } +} + +sub checkRequiredSystemConfig +{ + if (isOSX()) { + chomp(my $productVersion = `sw_vers -productVersion`); + if ($productVersion lt "10.4") { + print "*************************************************************\n"; + print "Mac OS X Version 10.4.0 or later is required to build WebKit.\n"; + print "You have " . $productVersion . ", thus the build will most likely fail.\n"; + print "*************************************************************\n"; + } + my $xcodeVersion = `xcodebuild -version`; + if ($xcodeVersion !~ /DevToolsCore-(\d+)/ || $1 < 747) { + print "*************************************************************\n"; + print "Xcode Version 2.3 or later is required to build WebKit.\n"; + print "You have an earlier version of Xcode, thus the build will\n"; + print "most likely fail. The latest Xcode is available from the web:\n"; + print "http://developer.apple.com/tools/xcode\n"; + print "*************************************************************\n"; + } + } elsif (isGtk() or isQt() or isWx()) { + my @cmds = qw(flex bison gperf); + my @missing = (); + foreach my $cmd (@cmds) { + if (not `$cmd --version`) { + push @missing, $cmd; + } + } + if (@missing) { + my $list = join ", ", @missing; + die "ERROR: $list missing but required to build WebKit.\n"; + } + } + # Win32 and other platforms may want to check for minimum config +} + +sub setupCygwinEnv() +{ + return if !isCygwin(); + return if $vcBuildPath; + + my $programFilesPath = `cygpath "$ENV{'PROGRAMFILES'}"`; + chomp $programFilesPath; + $vcBuildPath = "$programFilesPath/Microsoft Visual Studio 8/Common7/IDE/devenv.com"; + if (! -e $vcBuildPath) { + # VC++ not found, try VC++ Express + my $vsInstallDir; + if ($ENV{'VSINSTALLDIR'}) { + $vsInstallDir = $ENV{'VSINSTALLDIR'}; + } else { + $programFilesPath = $ENV{'PROGRAMFILES'} || "C:\\Program Files"; + $vsInstallDir = "$programFilesPath/Microsoft Visual Studio 8"; + } + $vsInstallDir = `cygpath "$vsInstallDir"`; + chomp $vsInstallDir; + $vcBuildPath = "$vsInstallDir/Common7/IDE/VCExpress.exe"; + if (! -e $vcBuildPath) { + print "*************************************************************\n"; + print "Cannot find '$vcBuildPath'\n"; + print "Please execute the file 'vcvars32.bat' from\n"; + print "'$programFilesPath\\Microsoft Visual Studio 8\\VC\\bin\\'\n"; + print "to setup the necessary environment variables.\n"; + print "*************************************************************\n"; + die; + } + } + + my $qtSDKPath = "$programFilesPath/QuickTime SDK"; + if (0 && ! -e $qtSDKPath) { + print "*************************************************************\n"; + print "Cannot find '$qtSDKPath'\n"; + print "Please download the QuickTime SDK for Windows from\n"; + print "http://developer.apple.com/quicktime/download/\n"; + print "*************************************************************\n"; + die; + } + + chomp($ENV{'WEBKITLIBRARIESDIR'} = `cygpath -wa "$sourceDir/WebKitLibraries/win"`) unless $ENV{'WEBKITLIBRARIESDIR'}; + + $windowsTmpPath = `cygpath -w /tmp`; + chomp $windowsTmpPath; + print "Building results into: ", baseProductDir(), "\n"; + print "WEBKITOUTPUTDIR is set to: ", $ENV{"WEBKITOUTPUTDIR"}, "\n"; + print "WEBKITLIBRARIESDIR is set to: ", $ENV{"WEBKITLIBRARIESDIR"}, "\n"; +} + +sub buildVisualStudioProject +{ + my ($project, $clean) = @_; + setupCygwinEnv(); + + my $config = configurationForVisualStudio(); + + chomp(my $winProjectPath = `cygpath -w "$project"`); + + my $command = "/build"; + if ($clean) { + $command = "/clean"; + } + + print "$vcBuildPath $winProjectPath /build $config\n"; + return system $vcBuildPath, $winProjectPath, $command, $config; +} + +sub retrieveQMakespecVar +{ + my $mkspec = $_[0]; + my $varname = $_[1]; + + my $compiler = "unknown"; + #print "retrieveMakespecVar " . $mkspec . ", " . $varname . "\n"; + + local *SPEC; + open SPEC, "<$mkspec" or return "make"; + while (<SPEC>) { + if ($_ =~ /\s*include\((.+)\)/) { + # open the included mkspec + my $oldcwd = getcwd(); + (my $volume, my $directories, my $file) = File::Spec->splitpath($mkspec); + chdir "$volume$directories"; + $compiler = retrieveQMakespecVar($1, $varname); + chdir $oldcwd; + } elsif ($_ =~ /$varname\s*=\s*([^\s]+)/) { + $compiler = $1; + last; + } + } + close SPEC; + return $compiler; +} + +sub qtMakeCommand($) +{ + my ($qmakebin) = @_; + chomp(my $mkspec = `$qmakebin -query QMAKE_MKSPECS`); + $mkspec .= "/default"; + my $compiler = retrieveQMakespecVar("$mkspec/qmake.conf", "QMAKE_CC"); + + #print "default spec: " . $mkspec . "\n"; + #print "compiler found: " . $compiler . "\n"; + + if ($compiler eq "cl") { + return "nmake"; + } + + return "make"; +} + +sub autotoolsFlag($$) +{ + my ($flag, $feature) = @_; + my $prefix = $flag ? "--enable" : "--disable"; + + return $prefix . '-' . $feature; +} + +sub buildAutotoolsProject($@) +{ + my ($clean, @buildArgs) = @_; + + my $make = 'make'; + my $dir = productDir(); + my $config = passedConfiguration() || configuration(); + my $prefix = $ENV{"WebKitInstallationPrefix"}; + + # check if configuration is Debug + if ($config =~ m/debug/i) { + push @buildArgs, "--enable-debug"; + } else { + push @buildArgs, "--disable-debug"; + } + + if (! -d $dir) { + system "mkdir", "-p", "$dir"; + if (! -d $dir) { + die "Failed to create build directory " . $dir; + } + } + + chdir $dir or die "Failed to cd into " . $dir . "\n"; + + my $result; + if ($clean) { + $result = system $make, "distclean"; + return 0; + } + + print "Calling configure in " . $dir . "\n\n"; + print "Installation directory: $prefix\n" if(defined($prefix)); + + $result = system "$sourceDir/autogen.sh", @buildArgs; + if ($result ne 0) { + die "Failed to setup build environment using 'autotools'!\n"; + } + + $result = system $make; + if ($result ne 0) { + die "\nFailed to build WebKit using '$make'!\n"; + } + + chdir ".." or die; + return $result; +} + +sub buildQMakeProject($@) +{ + my ($clean, @buildArgs) = @_; + + push @buildArgs, "-r"; + + my $qmakebin = "qmake"; # Allow override of the qmake binary from $PATH + for my $i (0 .. $#ARGV) { + my $opt = $ARGV[$i]; + if ($opt =~ /^--qmake=(.*)/i ) { + $qmakebin = $1; + } elsif ($opt =~ /^--qmakearg=(.*)/i ) { + push @buildArgs, $1; + } + } + + my $make = qtMakeCommand($qmakebin); + my $config = configuration(); + my $prefix = $ENV{"WebKitInstallationPrefix"}; + + push @buildArgs, "OUTPUT_DIR=" . baseProductDir() . "/$config"; + push @buildArgs, sourceDir() . "/WebKit.pro"; + if ($config =~ m/debug/i) { + push @buildArgs, "CONFIG-=release"; + push @buildArgs, "CONFIG+=debug"; + } else { + push @buildArgs, "CONFIG+=release"; + push @buildArgs, "CONFIG-=debug"; + } + + my $dir = baseProductDir(); + if (! -d $dir) { + system "mkdir", "-p", "$dir"; + if (! -d $dir) { + die "Failed to create product directory " . $dir; + } + } + $dir = $dir . "/$config"; + if (! -d $dir) { + system "mkdir", "-p", "$dir"; + if (! -d $dir) { + die "Failed to create build directory " . $dir; + } + } + + chdir $dir or die "Failed to cd into " . $dir . "\n"; + + print "Calling '$qmakebin @buildArgs' in " . $dir . "\n\n"; + print "Installation directory: $prefix\n" if(defined($prefix)); + + my $result = system $qmakebin, @buildArgs; + if ($result ne 0) { + die "Failed to setup build environment using $qmakebin!\n"; + } + + if ($clean) { + $result = system "$make distclean"; + } else { + $result = system "$make"; + } + + chdir ".." or die; + return $result; +} + +sub buildQMakeQtProject($$) +{ + my ($project, $clean) = @_; + + my @buildArgs = ("CONFIG+=qt-port"); + return buildQMakeProject($clean, @buildArgs); +} + +sub buildGtkProject($$@) +{ + my ($project, $clean, @buildArgs) = @_; + + if ($project ne "WebKit") { + die "The Gtk port builds JavaScriptCore, WebCore and WebKit in one shot! Only call it for 'WebKit'.\n"; + } + + return buildAutotoolsProject($clean, @buildArgs); +} + +sub setPathForRunningWebKitApp +{ + my ($env) = @_; + + return unless isCygwin(); + + $env->{PATH} = join(':', productDir(), dirname(installedSafariPath()), $env->{PATH} || ""); +} + +sub exitStatus($) +{ + my ($returnvalue) = @_; + if ($^O eq "MSWin32") { + return $returnvalue >> 8; + } + return WEXITSTATUS($returnvalue); +} + +sub runSafari +{ + my ($debugger) = @_; + + if (isOSX()) { + return system "$FindBin::Bin/gdb-safari", @ARGV if $debugger; + + my $productDir = productDir(); + print "Starting Safari with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; + $ENV{DYLD_FRAMEWORK_PATH} = $productDir; + $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES"; + exportArchPreference(); + if (!isTiger()) { + return system "arch", safariPath(), @ARGV; + } else { + return system safariPath(), @ARGV; + } + } + + if (isCygwin()) { + my $script = "run-webkit-nightly.cmd"; + my $result = system "cp", "$FindBin::Bin/$script", productDir(); + return $result if $result; + + my $cwd = getcwd(); + chdir productDir(); + + my $debuggerFlag = $debugger ? "/debugger" : ""; + $result = system "cmd", "/c", "call $script $debuggerFlag"; + chdir $cwd; + return $result; + } + + return 1; +} + +sub setRun64Bit($) +{ + ($forceRun64Bit) = @_; +} + +sub preferredArchitecture +{ + return unless isOSX(); + + my $framework = shift; + $framework = "WebKit" if !defined($framework); + + my $currentArchitecture = `arch`; + chomp($currentArchitecture); + + my $run64Bit = 0; + if (!defined($forceRun64Bit)) { + my $frameworkPath = builtDylibPathForName($framework); + die "Couldn't find path for $framework" if !defined($frameworkPath); + # The binary is 64-bit if one of the architectures it contains has "64" in the name + $run64Bit = `lipo -info "$frameworkPath"` =~ /(are|architecture):.*64/; + } + + if ($forceRun64Bit or $run64Bit) { + return ($currentArchitecture eq "i386") ? "x86_64" : "ppc64"; + } + return $currentArchitecture; +} + +sub exportArchPreference +{ + $ENV{ARCHPREFERENCE} = preferredArchitecture() if isOSX(); +} + +1; diff --git a/WebKitTools/Scripts/wkstyle b/WebKitTools/Scripts/wkstyle new file mode 100755 index 0000000..4b3447f --- /dev/null +++ b/WebKitTools/Scripts/wkstyle @@ -0,0 +1,89 @@ + +# Copyright (C) 2006 Michael Emmel<mike.emmel@gmail.com> 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. + +cmdcpp="astyle \ +--unpad=paren \ +--style=linux \ +--brackets=linux \ +--indent=spaces=4 \ +--indent-switches \ +--convert-tabs" + +cmdh="astyle \ +--unpad=paren \ +--style=linux \ +--brackets=break \ +--indent=spaces=4 \ +--convert-tabs" + +#astyle does not support unpadding so we use sed +for i in $@ +do +echo $i + +ext=`echo $i|awk -F . '{print $NF}'` + +cmd=$cmdcpp + +if [ $ext == "h" ] ; then + cmd=$cmdh +fi + +$cmd $i + +#first print the changes we are making +sed -n -e ' +/( .*/p +s/( /(/gp +/*. )/p +s/ )/)/gp +#supress printing this +#/^namespace WebCore/{ +#N +#s/\n{/ {/p +#} +' $i + +#do it for real +sed -e ' +#unpad leading spaces +s/( /(/g +#unpad traling spaces +s/ )/)/g +#fixup the namspec decl +/^namespace WebCore/{ +N +s/\n{/ {/ +} +#fixup extra tab in constructor initalizer +/^ \+,/{s/^ //} +/^ \+:/{s/^ //} +' $i > $i.sed +mv $i.sed $i +done + + |