WebKit Bugzilla
Attachment 340316 Details for
Bug 185514
: test262/Runner.pm: isolate runner's dependency on 5.8.8
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-185514-20180514111525.patch (text/plain), 184.56 KB, created by
valerie
on 2018-05-14 08:15:26 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
valerie
Created:
2018-05-14 08:15:26 PDT
Size:
184.56 KB
patch
obsolete
>Subversion Revision: 231754 >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 8cb18d9cff772b2b6c4a975a62af39fecd83940e..439b3cf4fc79abaadcf4875cace487a5126a3d42 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,29 @@ >+2018-05-14 Valerie R Young <valerie@bocoup.com> >+ >+ test262/Runner.pm: isolate runner's dependency on 5.8.8 >+ https://bugs.webkit.org/show_bug.cgi?id=185514 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Tools/Scripts/test262-runner must run on perl 5.8.8, but relies on >+ functionality from webkitdirs.pm. webkitdirs_5008008.pm is a modified >+ version of the module with backwards compatibility to 5.8.8 but >+ identical functionality. Specifically, `use version` is removed as is >+ the use of `make_dir`. >+ >+ The imported functions for test262-runner at this time are >+ `setConfiguration` and `executableProductDir`. >+ >+ webkitdirs_5008008.pm and VCSUtils_5008008.pm both include comments with >+ their latest git commit in case they need to be updated at a later >+ time. >+ >+ * Scripts/test262/Runner.pm: >+ (getBuildPath): >+ * Scripts/test262/VCSUtils_5008008.pm: Added. >+ * Scripts/test262/webkitdirs_5008008.pm: Added. >+ >+ > 2018-05-11 Leo Balter <leonardo.balter@gmail.com> > > Test262 Runner should search for the Debug JSC by default >diff --git a/Tools/Scripts/test262/Runner.pm b/Tools/Scripts/test262/Runner.pm >index 523e006883b4c3a5c2b14c859274936e17237bd5..26483d3f0da450acf746ccda2c96bff6a61c9372 100755 >--- a/Tools/Scripts/test262/Runner.pm >+++ b/Tools/Scripts/test262/Runner.pm >@@ -65,6 +65,7 @@ BEGIN { > use YAML qw(Load LoadFile Dump DumpFile Bless); > use Parallel::ForkManager; > use Getopt::Long qw(GetOptions); >+use webkitdirs_5008008 qw(executableProductDir setConfiguration); > > # Commandline settings > my $max_process; >@@ -456,30 +457,24 @@ sub parseError { > } > > sub getBuildPath { >- my $release = shift; >+ my ($release) = @_; > >- # Try to find JSC for user, if not supplied >- my $cmd = abs_path("$Bin/../webkit-build-directory"); >- if (! -e $cmd) { >- die 'Error: cannot find webkit-build-directory, specify with JSC with --jsc <path>.'; >- } >- >- if ($release) { >- $cmd .= ' --release'; >- } else { >- $cmd .= ' --debug'; >- } >- $cmd .= ' --executablePath'; >- my $jscDir = qx($cmd); >- chomp $jscDir; >- >- my $jsc; >- $jsc = $jscDir . '/jsc'; >+ my $config = $release ? 'Release' : 'Debug'; >+ setConfiguration($config); >+ my $jscDir = executableProductDir(); > >+ my $jsc = $jscDir . '/jsc'; > $jsc = $jscDir . '/JavaScriptCore.framework/Resources/jsc' if (! -e $jsc); > $jsc = $jscDir . '/bin/jsc' if (! -e $jsc); >+ > if (! -e $jsc) { >- die 'Error: cannot find jsc, specify with --jsc <path>.'; >+ if ($release) { >+ print("Cannot find 'release' jsc, specify with --jsc <path>.\n\n"); >+ } >+ else { >+ print("Cannot find jsc, try with '--release' or specify with --jsc <path>.\n\n"); >+ } >+ die "Error: cannot find jsc"; > } > > # Sets the Env DYLD_FRAMEWORK_PATH >diff --git a/Tools/Scripts/test262/VCSUtils_5008008.pm b/Tools/Scripts/test262/VCSUtils_5008008.pm >new file mode 100644 >index 0000000000000000000000000000000000000000..0b40c0e397f7b74953125f14e670ec2fab7bb5d5 >--- /dev/null >+++ b/Tools/Scripts/test262/VCSUtils_5008008.pm >@@ -0,0 +1,2453 @@ >+# Copyright (C) 2007-2013, 2015 Apple Inc. All rights reserved. >+# Copyright (C) 2009, 2010 Chris Jerdonek (chris.jerdonek@gmail.com) >+# Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. >+# Copyright (C) 2012 Daniel Bates (dbates@intudata.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. >+ >+# Module to share code to work with various version control systems. >+ >+# THIS MODULE IS A COPY OF: VCSUtils.pm >+# COPIED AT GIT COMMIT: 76998d2b24582cef5ac7aa8fcc21706762eb73c7 >+# MODIFIED FOR COMPATIBILITY WITH: Perl v5.8.8 >+use 5.8.8; >+ >+package VCSUtils_5008008; >+ >+use strict; >+use warnings; >+ >+use Cwd qw(); # "qw()" prevents warnings about redefining getcwd() with "use POSIX;" >+use English; # for $POSTMATCH, etc. >+use File::Basename; >+use File::Spec; >+use POSIX; >+use Term::ANSIColor qw(colored); >+ >+BEGIN { >+ use Exporter (); >+ our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); >+ $VERSION = 1.00; >+ @ISA = qw(Exporter); >+ @EXPORT = qw( >+ &applyGitBinaryPatchDelta >+ &callSilently >+ &canonicalizePath >+ &changeLogEmailAddress >+ &changeLogName >+ &chdirReturningRelativePath >+ &decodeGitBinaryChunk >+ &decodeGitBinaryPatch >+ &determineSVNRoot >+ &determineVCSRoot >+ &escapeSubversionPath >+ &exitStatus >+ &fixChangeLogPatch >+ &fixSVNPatchForAdditionWithHistory >+ &gitBranch >+ &gitCommitForSVNRevision >+ &gitDirectory >+ &gitHashForDirectory >+ &gitTreeDirectory >+ &gitdiff2svndiff >+ &isGit >+ &isGitBranchBuild >+ &isGitDirectory >+ &isGitSVN >+ &isGitSVNDirectory >+ &isSVN >+ &isSVNDirectory >+ &isSVNVersion16OrNewer >+ &listOfChangedFilesBetweenRevisions >+ &makeFilePathRelative >+ &mergeChangeLogs >+ &normalizePath >+ &parseChunkRange >+ &parseDiffStartLine >+ &parseFirstEOL >+ &parsePatch >+ &pathRelativeToSVNRepositoryRootForPath >+ &possiblyColored >+ &prepareParsedPatch >+ &removeEOL >+ &runCommand >+ &runPatchCommand >+ &scmMoveOrRenameFile >+ &scmToggleExecutableBit >+ &setChangeLogDateAndReviewer >+ &svnIdentifierForPath >+ &svnInfoForPath >+ &svnRepositoryRootForPath >+ &svnRevisionForDirectory >+ &svnStatus >+ &svnURLForPath >+ &toWindowsLineEndings >+ &unixPath >+ ); >+ %EXPORT_TAGS = ( ); >+ @EXPORT_OK = (); >+} >+ >+our @EXPORT_OK; >+ >+my $gitBranch; >+my $gitRoot; >+my $isGit; >+my $isGitSVN; >+my $isGitBranchBuild; >+my $isSVN; >+my $svnVersion; >+ >+# Project time zone for Cupertino, CA, US >+my $changeLogTimeZone = "PST8PDT"; >+ >+my $unifiedDiffStartRegEx = qr#^--- ([abc]\/)?([^\r\n]+)#; >+my $gitDiffStartRegEx = qr#^diff --git [^\r\n]+#; >+my $gitDiffStartWithPrefixRegEx = qr#^diff --git \w/(.+) \w/([^\r\n]+)#; # We suppose that --src-prefix and --dst-prefix don't contain a non-word character (\W) and end with '/'. >+my $gitDiffStartWithoutPrefixNoSpaceRegEx = qr#^diff --git (\S+) (\S+)$#; >+my $svnDiffStartRegEx = qr#^Index: ([^\r\n]+)#; >+my $gitDiffStartWithoutPrefixSourceDirectoryPrefixRegExp = qr#^diff --git ([^/]+/)#; >+my $svnPropertiesStartRegEx = qr#^Property changes on: ([^\r\n]+)#; # $1 is normally the same as the index path. >+my $svnPropertyStartRegEx = qr#^(Modified|Name|Added|Deleted): ([^\r\n]+)#; # $2 is the name of the property. >+my $svnPropertyValueStartRegEx = qr#^\s*(\+|-|Merged|Reverse-merged)\s*([^\r\n]+)#; # $2 is the start of the property's value (which may span multiple lines). >+my $svnPropertyValueNoNewlineRegEx = qr#\ No newline at end of property#; >+ >+# This method is for portability. Return the system-appropriate exit >+# status of a child process. >+# >+# Args: pass the child error status returned by the last pipe close, >+# for example "$?". >+sub exitStatus($) >+{ >+ my ($returnvalue) = @_; >+ if (isWindows()) { >+ return $returnvalue >> 8; >+ } >+ if (!WIFEXITED($returnvalue)) { >+ return 254; >+ } >+ return WEXITSTATUS($returnvalue); >+} >+ >+# Call a function while suppressing STDERR, and return the return values >+# as an array. >+sub callSilently($@) { >+ my ($func, @args) = @_; >+ >+ # The following pattern was taken from here: >+ # http://www.sdsc.edu/~moreland/courses/IntroPerl/docs/manual/pod/perlfunc/open.html >+ # >+ # Also see this Perl documentation (search for "open OLDERR"): >+ # http://perldoc.perl.org/functions/open.html >+ open(OLDERR, ">&STDERR"); >+ close(STDERR); >+ my @returnValue = &$func(@args); >+ open(STDERR, ">&OLDERR"); >+ close(OLDERR); >+ >+ return @returnValue; >+} >+ >+sub toWindowsLineEndings >+{ >+ my ($text) = @_; >+ $text =~ s/\n/\r\n/g; >+ return $text; >+} >+ >+# Note, this method will not error if the file corresponding to the $source path does not exist. >+sub scmMoveOrRenameFile >+{ >+ my ($source, $destination) = @_; >+ return if ! -e $source; >+ if (isSVN()) { >+ my $escapedDestination = escapeSubversionPath($destination); >+ my $escapedSource = escapeSubversionPath($source); >+ system("svn", "move", $escapedSource, $escapedDestination); >+ } elsif (isGit()) { >+ system("git", "mv", $source, $destination); >+ } >+} >+ >+# Note, this method will not error if the file corresponding to the path does not exist. >+sub scmToggleExecutableBit >+{ >+ my ($path, $executableBitDelta) = @_; >+ return if ! -e $path; >+ if ($executableBitDelta == 1) { >+ scmAddExecutableBit($path); >+ } elsif ($executableBitDelta == -1) { >+ scmRemoveExecutableBit($path); >+ } >+} >+ >+sub scmAddExecutableBit($) >+{ >+ my ($path) = @_; >+ >+ if (isSVN()) { >+ my $escapedPath = escapeSubversionPath($path); >+ system("svn", "propset", "svn:executable", "on", $escapedPath) == 0 or die "Failed to run 'svn propset svn:executable on $escapedPath'."; >+ } elsif (isGit()) { >+ chmod(0755, $path); >+ } >+} >+ >+sub scmRemoveExecutableBit($) >+{ >+ my ($path) = @_; >+ >+ if (isSVN()) { >+ my $escapedPath = escapeSubversionPath($path); >+ system("svn", "propdel", "svn:executable", $escapedPath) == 0 or die "Failed to run 'svn propdel svn:executable $escapedPath'."; >+ } elsif (isGit()) { >+ chmod(0664, $path); >+ } >+} >+ >+sub isGitDirectory($) >+{ >+ my ($directory) = @_; >+ return system("git -C \"$directory\" rev-parse > " . File::Spec->devnull() . " 2>&1") == 0; >+} >+ >+sub isGit() >+{ >+ return $isGit if defined $isGit; >+ >+ $isGit = isGitDirectory("."); >+ return $isGit; >+} >+ >+sub isGitSVNDirectory($) >+{ >+ my ($directory) = @_; >+ >+ # There doesn't seem to be an officially documented way to determine >+ # if you're in a git-svn checkout. The best suggestions seen so far >+ # all use something like the following: >+ my $output = `git -C \"$directory\" config --get svn-remote.svn.fetch 2>&1`; >+ $isGitSVN = exitStatus($?) == 0 && $output ne ""; >+ return $isGitSVN; >+} >+ >+sub isGitSVN() >+{ >+ return $isGitSVN if defined $isGitSVN; >+ >+ $isGitSVN = isGitSVNDirectory("."); >+ return $isGitSVN; >+} >+ >+sub gitDirectory() >+{ >+ chomp(my $result = `git rev-parse --git-dir`); >+ return $result; >+} >+ >+sub gitHashForDirectory($) >+{ >+ my ($directory) = @_; >+ my $hash; >+ >+ if (isGitDirectory($directory)) { >+ my $command = "git -C \"$directory\" rev-parse HEAD"; >+ $command = "LC_ALL=C $command" if !isWindows(); >+ $hash = `$command`; >+ chomp($hash); >+ } >+ if (!defined($hash)) { >+ $hash = "unknown"; >+ warn "Unable to determine current Git hash in $directory"; >+ } >+ return $hash; >+} >+ >+sub gitTreeDirectory() >+{ >+ chomp(my $result = `git rev-parse --show-toplevel`); >+ return $result; >+} >+ >+sub gitBisectStartBranch() >+{ >+ my $bisectStartFile = File::Spec->catfile(gitDirectory(), "BISECT_START"); >+ if (!-f $bisectStartFile) { >+ return ""; >+ } >+ open(BISECT_START, $bisectStartFile) or die "Failed to open $bisectStartFile: $!"; >+ chomp(my $result = <BISECT_START>); >+ close(BISECT_START); >+ return $result; >+} >+ >+sub gitBranch() >+{ >+ unless (defined $gitBranch) { >+ chomp($gitBranch = `git symbolic-ref -q HEAD`); >+ my $hasDetachedHead = exitStatus($?); >+ if ($hasDetachedHead) { >+ # We may be in a git bisect session. >+ $gitBranch = gitBisectStartBranch(); >+ } >+ $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 system("cd $dir && svn info > " . File::Spec->devnull() . " 2>&1") == 0; >+} >+ >+sub isSVN() >+{ >+ return $isSVN if defined $isSVN; >+ >+ $isSVN = isSVNDirectory("."); >+ return $isSVN; >+} >+ >+sub svnVersion() >+{ >+ return $svnVersion if defined $svnVersion; >+ >+ if (!isSVN()) { >+ $svnVersion = 0; >+ } else { >+ chomp($svnVersion = `svn --version --quiet`); >+ } >+ return $svnVersion; >+} >+ >+sub isSVNVersion16OrNewer() >+{ >+ my $version = svnVersion(); >+ return "v$version" ge v1.6; >+} >+ >+sub chdirReturningRelativePath($) >+{ >+ my ($directory) = @_; >+ my $previousDirectory = Cwd::getcwd(); >+ chdir $directory; >+ my $newDirectory = Cwd::getcwd(); >+ return "." if $newDirectory eq $previousDirectory; >+ return File::Spec->abs2rel($previousDirectory, $newDirectory); >+} >+ >+sub determineSVNRoot() >+{ >+ my $last = ''; >+ my $path = '.'; >+ my $parent = '..'; >+ my $repositoryRoot; >+ my $repositoryUUID; >+ while (1) { >+ my $thisRoot; >+ my $thisUUID; >+ my $escapedPath = escapeSubversionPath($path); >+ # Ignore error messages in case we've run past the root of the checkout. >+ open INFO, "svn info '$escapedPath' 2> " . File::Spec->devnull() . " |" or die; >+ while (<INFO>) { >+ if (/^Repository Root: (.+)/) { >+ $thisRoot = $1; >+ } >+ if (/^Repository UUID: (.+)/) { >+ $thisUUID = $1; >+ } >+ if ($thisRoot && $thisUUID) { >+ local $/ = undef; >+ <INFO>; # Consume the rest of the input. >+ } >+ } >+ close INFO; >+ >+ # It's possible (e.g. for developers of some ports) to have a WebKit >+ # checkout in a subdirectory of another checkout. So abort if the >+ # repository root or the repository UUID suddenly changes. >+ last if !$thisUUID; >+ $repositoryUUID = $thisUUID if !$repositoryUUID; >+ last if $thisUUID ne $repositoryUUID; >+ >+ last if !$thisRoot; >+ $repositoryRoot = $thisRoot if !$repositoryRoot; >+ last if $thisRoot ne $repositoryRoot; >+ >+ $last = $path; >+ $path = File::Spec->catdir($parent, $path); >+ } >+ >+ return File::Spec->rel2abs($last); >+} >+ >+sub determineVCSRoot() >+{ >+ if (isGit()) { >+ # This is the working tree root. If WebKit is a submodule, >+ # then the relevant metadata directory is somewhere else. >+ return gitTreeDirectory(); >+ } >+ >+ if (!isSVN()) { >+ # Some users have a workflow where svn-create-patch, svn-apply and >+ # svn-unapply are used outside of multiple svn working directores, >+ # so warn the user and assume Subversion is being used in this case. >+ warn "Unable to determine VCS root for '" . Cwd::getcwd() . "'; assuming Subversion"; >+ $isSVN = 1; >+ } >+ >+ return determineSVNRoot(); >+} >+ >+sub isWindows() >+{ >+ return ($^O eq "MSWin32") || 0; >+} >+ >+sub svnRevisionForDirectory($) >+{ >+ my ($directory) = @_; >+ my $revision; >+ >+ if (isSVNDirectory($directory)) { >+ my $escapedDir = escapeSubversionPath($directory); >+ my $command = "svn info $escapedDir | grep Revision:"; >+ $command = "LC_ALL=C $command" if !isWindows(); >+ my $svnInfo = `$command`; >+ ($revision) = ($svnInfo =~ m/Revision: (\d+).*/g); >+ } elsif (isGitDirectory($directory)) { >+ my $command = "git -C \"$directory\" log --grep=\"git-svn-id: \" -n 1 | grep git-svn-id:"; >+ $command = "LC_ALL=C $command" if !isWindows(); >+ my $gitLog = `$command`; >+ ($revision) = ($gitLog =~ m/ +git-svn-id: .+@(\d+) /g); >+ } >+ if (!defined($revision)) { >+ $revision = "unknown"; >+ warn "Unable to determine current SVN revision in $directory"; >+ } >+ return $revision; >+} >+ >+sub svnInfoForPath($) >+{ >+ my ($file) = @_; >+ my $relativePath = File::Spec->abs2rel($file); >+ >+ my $svnInfo; >+ if (isSVNDirectory($file)) { >+ my $escapedRelativePath = escapeSubversionPath($relativePath); >+ my $command = "svn info $escapedRelativePath"; >+ $command = "LC_ALL=C $command" if !isWindows(); >+ $svnInfo = `$command`; >+ } elsif (isGitDirectory($file)) { >+ my $command = "git -C \"$file\" svn info"; >+ $command = "LC_ALL=C $command" if !isWindows(); >+ $svnInfo = `$command`; >+ } >+ >+ return $svnInfo; >+} >+ >+sub svnURLForPath($) >+{ >+ my ($file) = @_; >+ my $svnInfo = svnInfoForPath($file); >+ >+ $svnInfo =~ /.*^URL: (.*?)$/m; >+ return $1; >+} >+ >+sub svnRepositoryRootForPath($) >+{ >+ my ($file) = @_; >+ my $svnInfo = svnInfoForPath($file); >+ >+ $svnInfo =~ /.*^Repository Root: (.*?)$/m; >+ return $1; >+} >+ >+sub pathRelativeToSVNRepositoryRootForPath($) >+{ >+ my ($file) = @_; >+ >+ my $svnURL = svnURLForPath($file); >+ my $svnRepositoryRoot = svnRepositoryRootForPath($file); >+ >+ $svnURL =~ s/$svnRepositoryRoot\///; >+ return $svnURL; >+} >+ >+sub svnIdentifierForPath($) >+{ >+ my ($file) = @_; >+ my $path = pathRelativeToSVNRepositoryRootForPath($file); >+ >+ $path =~ /^(trunk)|tags\/([\w\.\-]*)|branches\/([\w\.\-]*).*$/m; >+ return $1 || $2 || $3; >+} >+ >+sub makeFilePathRelative($) >+{ >+ my ($path) = @_; >+ return $path unless isGit(); >+ >+ unless (defined $gitRoot) { >+ chomp($gitRoot = `git rev-parse --show-cdup`); >+ } >+ return $gitRoot . $path; >+} >+ >+sub normalizePath($) >+{ >+ my ($path) = @_; >+ if (isWindows()) { >+ $path =~ s/\//\\/g; >+ } else { >+ $path =~ s/\\/\//g; >+ } >+ return $path; >+} >+ >+sub unixPath($) >+{ >+ my ($path) = @_; >+ $path =~ s/\\/\//g; >+ return $path; >+} >+ >+sub possiblyColored($$) >+{ >+ my ($colors, $string) = @_; >+ >+ if (-t STDOUT) { >+ return colored([$colors], $string); >+ } else { >+ return $string; >+ } >+} >+ >+sub adjustPathForRecentRenamings($) >+{ >+ my ($fullPath) = @_; >+ >+ $fullPath =~ s|WebCore/webaudio|WebCore/Modules/webaudio|g; >+ $fullPath =~ s|JavaScriptCore/wtf|WTF/wtf|g; >+ $fullPath =~ s|test_expectations.txt|TestExpectations|g; >+ >+ return $fullPath; >+} >+ >+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 removeEOL($) >+{ >+ my ($line) = @_; >+ return "" unless $line; >+ >+ $line =~ s/[\r\n]+$//g; >+ return $line; >+} >+ >+sub parseFirstEOL($) >+{ >+ my ($fileHandle) = @_; >+ >+ # Make input record separator the new-line character to simplify regex matching below. >+ my $savedInputRecordSeparator = $INPUT_RECORD_SEPARATOR; >+ $INPUT_RECORD_SEPARATOR = "\n"; >+ my $firstLine = <$fileHandle>; >+ $INPUT_RECORD_SEPARATOR = $savedInputRecordSeparator; >+ >+ return unless defined($firstLine); >+ >+ my $eol; >+ if ($firstLine =~ /\r\n/) { >+ $eol = "\r\n"; >+ } elsif ($firstLine =~ /\r/) { >+ $eol = "\r"; >+ } elsif ($firstLine =~ /\n/) { >+ $eol = "\n"; >+ } >+ return $eol; >+} >+ >+sub firstEOLInFile($) >+{ >+ my ($file) = @_; >+ my $eol; >+ if (open(FILE, $file)) { >+ $eol = parseFirstEOL(*FILE); >+ close(FILE); >+ } >+ return $eol; >+} >+ >+# Parses a chunk range line into its components. >+# >+# A chunk range line has the form: @@ -L_1,N_1 +L_2,N_2 @@, where the pairs (L_1, N_1), >+# (L_2, N_2) are ranges that represent the starting line number and line count in the >+# original file and new file, respectively. >+# >+# Note, some versions of GNU diff may omit the comma and trailing line count (e.g. N_1), >+# in which case the omitted line count defaults to 1. For example, GNU diff may output >+# @@ -1 +1 @@, which is equivalent to @@ -1,1 +1,1 @@. >+# >+# This subroutine returns undef if given an invalid or malformed chunk range. >+# >+# Args: >+# $line: the line to parse. >+# $chunkSentinel: the sentinel that surrounds the chunk range information (defaults to "@@"). >+# >+# Returns $chunkRangeHashRef >+# $chunkRangeHashRef: a hash reference representing the parts of a chunk range, as follows-- >+# startingLine: the starting line in the original file. >+# lineCount: the line count in the original file. >+# newStartingLine: the new starting line in the new file. >+# newLineCount: the new line count in the new file. >+sub parseChunkRange($;$) >+{ >+ my ($line, $chunkSentinel) = @_; >+ $chunkSentinel = "@@" if !$chunkSentinel; >+ my $chunkRangeRegEx = qr#^\Q$chunkSentinel\E -(\d+)(,(\d+))? \+(\d+)(,(\d+))? \Q$chunkSentinel\E#; >+ if ($line !~ /$chunkRangeRegEx/) { >+ return; >+ } >+ my %chunkRange; >+ $chunkRange{startingLine} = $1; >+ $chunkRange{lineCount} = defined($2) ? $3 : 1; >+ $chunkRange{newStartingLine} = $4; >+ $chunkRange{newLineCount} = defined($5) ? $6 : 1; >+ return \%chunkRange; >+} >+ >+sub svnStatus($) >+{ >+ my ($fullPath) = @_; >+ my $escapedFullPath = escapeSubversionPath($fullPath); >+ my $svnStatus; >+ open SVN, "svn status --non-interactive --non-recursive '$escapedFullPath' |" 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>) { >+ # Input may use a different EOL sequence than $/, so avoid chomp. >+ $_ = removeEOL($_); >+ my $normalizedStatPath = File::Spec->catdir(File::Spec->splitdir(substr($_, 7))); >+ if ($normalizedFullPath eq $normalizedStatPath) { >+ $svnStatus = "$_\n"; >+ 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 = removeEOL(<SVN>) . "\n"; >+ } >+ close SVN; >+ return $svnStatus; >+} >+ >+# Return whether the given file mode is executable in the source control >+# sense. We make this determination based on whether the executable bit >+# is set for "others" rather than the stronger condition that it be set >+# for the user, group, and others. This is sufficient for distinguishing >+# the default behavior in Git and SVN. >+# >+# Args: >+# $fileMode: A number or string representing a file mode in octal notation. >+sub isExecutable($) >+{ >+ my $fileMode = shift; >+ >+ return $fileMode % 2; >+} >+ >+# Parses an SVN or Git diff header start line. >+# >+# Args: >+# $line: "Index: " line or "diff --git" line >+# >+# Returns the path of the target file or undef if the $line is unrecognized. >+sub parseDiffStartLine($) >+{ >+ my ($line) = @_; >+ return $1 if $line =~ /$svnDiffStartRegEx/; >+ return parseGitDiffStartLine($line) if $line =~ /$gitDiffStartRegEx/; >+} >+ >+# Parse the Git diff header start line. >+# >+# Args: >+# $line: "diff --git" line. >+# >+# Returns the path of the target file. >+sub parseGitDiffStartLine($) >+{ >+ my $line = shift; >+ $_ = $line; >+ if (/$gitDiffStartWithPrefixRegEx/ || /$gitDiffStartWithoutPrefixNoSpaceRegEx/) { >+ return $2; >+ } >+ # Assume the diff was generated with --no-prefix (e.g. git diff --no-prefix). >+ if (!/$gitDiffStartWithoutPrefixSourceDirectoryPrefixRegExp/) { >+ # FIXME: Moving top directory file is not supported (e.g diff --git A.txt B.txt). >+ die("Could not find '/' in \"diff --git\" line: \"$line\"; only non-prefixed git diffs (i.e. not generated with --no-prefix) that move a top-level directory file are supported."); >+ } >+ my $pathPrefix = $1; >+ if (!/^diff --git \Q$pathPrefix\E.+ (\Q$pathPrefix\E.+)$/) { >+ # FIXME: Moving a file through sub directories of top directory is not supported (e.g diff --git A/B.txt C/B.txt). >+ die("Could not find '/' in \"diff --git\" line: \"$line\"; only non-prefixed git diffs (i.e. not generated with --no-prefix) that move a file between top-level directories are supported."); >+ } >+ return $1; >+} >+ >+# Parse the next Git diff header from the given file handle, and advance >+# the handle so the last line read is the first line after the header. >+# >+# This subroutine dies if given leading junk. >+# >+# Args: >+# $fileHandle: advanced so the last line read from the handle is the first >+# line of the header to parse. This should be a line >+# beginning with "diff --git". >+# $line: the line last read from $fileHandle >+# >+# Returns ($headerHashRef, $lastReadLine): >+# $headerHashRef: a hash reference representing a diff header, as follows-- >+# copiedFromPath: the path from which the file was copied or moved if >+# the diff is a copy or move. >+# executableBitDelta: the value 1 or -1 if the executable bit was added or >+# removed, respectively. New and deleted files have >+# this value only if the file is executable, in which >+# case the value is 1 and -1, respectively. >+# indexPath: the path of the target file. >+# isBinary: the value 1 if the diff is for a binary file. >+# isDeletion: the value 1 if the diff is a file deletion. >+# isCopyWithChanges: the value 1 if the file was copied or moved and >+# the target file was changed in some way after being >+# copied or moved (e.g. if its contents or executable >+# bit were changed). >+# isNew: the value 1 if the diff is for a new file. >+# shouldDeleteSource: the value 1 if the file was copied or moved and >+# the source file was deleted -- i.e. if the copy >+# was actually a move. >+# svnConvertedText: the header text with some lines converted to SVN >+# format. Git-specific lines are preserved. >+# $lastReadLine: the line last read from $fileHandle. >+sub parseGitDiffHeader($$) >+{ >+ my ($fileHandle, $line) = @_; >+ >+ $_ = $line; >+ >+ my $indexPath; >+ if (/$gitDiffStartRegEx/) { >+ # Use $POSTMATCH to preserve the end-of-line character. >+ my $eol = $POSTMATCH; >+ >+ # The first and second paths can differ in the case of copies >+ # and renames. We use the second file path because it is the >+ # destination path. >+ $indexPath = adjustPathForRecentRenamings(parseGitDiffStartLine($_)); >+ >+ $_ = "Index: $indexPath$eol"; # Convert to SVN format. >+ } else { >+ die("Could not parse leading \"diff --git\" line: \"$line\"."); >+ } >+ >+ my $copiedFromPath; >+ my $foundHeaderEnding; >+ my $isBinary; >+ my $isDeletion; >+ my $isNew; >+ my $newExecutableBit = 0; >+ my $oldExecutableBit = 0; >+ my $shouldDeleteSource = 0; >+ my $similarityIndex = 0; >+ my $svnConvertedText; >+ while (1) { >+ # Temporarily strip off any end-of-line characters to simplify >+ # regex matching below. >+ s/([\n\r]+)$//; >+ my $eol = $1; >+ >+ if (/^(deleted file|old) mode (\d+)/) { >+ $oldExecutableBit = (isExecutable($2) ? 1 : 0); >+ $isDeletion = 1 if $1 eq "deleted file"; >+ } elsif (/^new( file)? mode (\d+)/) { >+ $newExecutableBit = (isExecutable($2) ? 1 : 0); >+ $isNew = 1 if $1; >+ } elsif (/^similarity index (\d+)%/) { >+ $similarityIndex = $1; >+ } elsif (/^copy from ([^\t\r\n]+)/) { >+ $copiedFromPath = $1; >+ } elsif (/^rename from ([^\t\r\n]+)/) { >+ # FIXME: Record this as a move rather than as a copy-and-delete. >+ # This will simplify adding rename support to svn-unapply. >+ # Otherwise, the hash for a deletion would have to know >+ # everything about the file being deleted in order to >+ # support undoing itself. Recording as a move will also >+ # permit us to use "svn move" and "git move". >+ $copiedFromPath = $1; >+ $shouldDeleteSource = 1; >+ } elsif (/^--- \S+/) { >+ # Convert to SVN format. >+ # We emit the suffix "\t(revision 0)" to handle $indexPath which contains a space character. >+ # The patch(1) command thinks a file path is characters before a tab. >+ # This suffix make our diff more closely match the SVN diff format. >+ $_ = "--- $indexPath\t(revision 0)"; >+ } elsif (/^\+\+\+ \S+/) { >+ # Convert to SVN format. >+ # We emit the suffix "\t(working copy)" to handle $indexPath which contains a space character. >+ # The patch(1) command thinks a file path is characters before a tab. >+ # This suffix make our diff more closely match the SVN diff format. >+ $_ = "+++ $indexPath\t(working copy)"; >+ $foundHeaderEnding = 1; >+ } elsif (/^GIT binary patch$/ ) { >+ $isBinary = 1; >+ $foundHeaderEnding = 1; >+ # The "git diff" command includes a line of the form "Binary files >+ # <path1> and <path2> differ" if the --binary flag is not used. >+ } elsif (/^Binary files / ) { >+ die("Error: the Git diff contains a binary file without the binary data in ". >+ "line: \"$_\". Be sure to use the --binary flag when invoking \"git diff\" ". >+ "with diffs containing binary files."); >+ } >+ >+ $svnConvertedText .= "$_$eol"; # Also restore end-of-line characters. >+ >+ $_ = <$fileHandle>; # Not defined if end-of-file reached. >+ >+ last if (!defined($_) || /$gitDiffStartRegEx/ || $foundHeaderEnding); >+ } >+ >+ my $executableBitDelta = $newExecutableBit - $oldExecutableBit; >+ >+ my %header; >+ >+ $header{copiedFromPath} = $copiedFromPath if $copiedFromPath; >+ $header{executableBitDelta} = $executableBitDelta if $executableBitDelta; >+ $header{indexPath} = $indexPath; >+ $header{isBinary} = $isBinary if $isBinary; >+ $header{isCopyWithChanges} = 1 if ($copiedFromPath && ($similarityIndex != 100 || $executableBitDelta)); >+ $header{isDeletion} = $isDeletion if $isDeletion; >+ $header{isNew} = $isNew if $isNew; >+ $header{shouldDeleteSource} = $shouldDeleteSource if $shouldDeleteSource; >+ $header{svnConvertedText} = $svnConvertedText; >+ >+ return (\%header, $_); >+} >+ >+# Parse the next SVN diff header from the given file handle, and advance >+# the handle so the last line read is the first line after the header. >+# >+# This subroutine dies if given leading junk or if it could not detect >+# the end of the header block. >+# >+# Args: >+# $fileHandle: advanced so the last line read from the handle is the first >+# line of the header to parse. This should be a line >+# beginning with "Index:". >+# $line: the line last read from $fileHandle >+# >+# Returns ($headerHashRef, $lastReadLine): >+# $headerHashRef: a hash reference representing a diff header, as follows-- >+# copiedFromPath: the path from which the file was copied if the diff >+# is a copy. >+# indexPath: the path of the target file, which is the path found in >+# the "Index:" line. >+# isBinary: the value 1 if the diff is for a binary file. >+# isNew: the value 1 if the diff is for a new file. >+# sourceRevision: the revision number of the source, if it exists. This >+# is the same as the revision number the file was copied >+# from, in the case of a file copy. >+# svnConvertedText: the header text converted to a header with the paths >+# in some lines corrected. >+# $lastReadLine: the line last read from $fileHandle. >+sub parseSvnDiffHeader($$) >+{ >+ my ($fileHandle, $line) = @_; >+ >+ $_ = $line; >+ >+ my $indexPath; >+ if (/$svnDiffStartRegEx/) { >+ $indexPath = adjustPathForRecentRenamings($1); >+ } else { >+ die("First line of SVN diff does not begin with \"Index \": \"$_\""); >+ } >+ >+ my $copiedFromPath; >+ my $foundHeaderEnding; >+ my $isBinary; >+ my $isNew; >+ my $sourceRevision; >+ my $svnConvertedText; >+ while (1) { >+ # Temporarily strip off any end-of-line characters to simplify >+ # regex matching below. >+ s/([\n\r]+)$//; >+ my $eol = $1; >+ >+ # Fix paths on "---" and "+++" lines to match the leading >+ # index line. >+ if (s/^--- [^\t\n\r]+/--- $indexPath/) { >+ # --- >+ if (/^--- .+\(revision (\d+)\)/) { >+ $sourceRevision = $1; >+ $isNew = 1 if !$sourceRevision; # if revision 0. >+ if (/\(from (\S+):(\d+)\)$/) { >+ # The "from" clause is created by svn-create-patch, in >+ # which case there is always also a "revision" clause. >+ $copiedFromPath = $1; >+ die("Revision number \"$2\" in \"from\" clause does not match " . >+ "source revision number \"$sourceRevision\".") if ($2 != $sourceRevision); >+ } >+ } >+ } elsif (s/^\+\+\+ [^\t\n\r]+/+++ $indexPath/ || $isBinary && /^$/) { >+ $foundHeaderEnding = 1; >+ } elsif (/^Cannot display: file marked as a binary type.$/) { >+ $isBinary = 1; >+ # SVN 1.7 has an unusual display format for a binary diff. It repeats the first >+ # two lines of the diff header. For example: >+ # Index: test_file.swf >+ # =================================================================== >+ # Cannot display: file marked as a binary type. >+ # svn:mime-type = application/octet-stream >+ # Index: test_file.swf >+ # =================================================================== >+ # --- test_file.swf >+ # +++ test_file.swf >+ # >+ # ... >+ # Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== >+ # Therefore, we continue reading the diff header until we either encounter a line >+ # that begins with "+++" (SVN 1.7 or greater) or an empty line (SVN version less >+ # than 1.7). >+ } >+ >+ $svnConvertedText .= "$_$eol"; # Also restore end-of-line characters. >+ >+ $_ = <$fileHandle>; # Not defined if end-of-file reached. >+ >+ last if (!defined($_) || !$isBinary && /$svnDiffStartRegEx/ || $foundHeaderEnding); >+ } >+ >+ if (!$foundHeaderEnding) { >+ die("Did not find end of header block corresponding to index path \"$indexPath\"."); >+ } >+ >+ my %header; >+ >+ $header{copiedFromPath} = $copiedFromPath if $copiedFromPath; >+ $header{indexPath} = $indexPath; >+ $header{isBinary} = $isBinary if $isBinary; >+ $header{isNew} = $isNew if $isNew; >+ $header{sourceRevision} = $sourceRevision if $sourceRevision; >+ $header{svnConvertedText} = $svnConvertedText; >+ >+ return (\%header, $_); >+} >+ >+# Parse the next Unified diff header from the given file handle, and advance >+# the handle so the last line read is the first line after the header. >+# >+# This subroutine dies if given leading junk. >+# >+# Args: >+# $fileHandle: advanced so the last line read from the handle is the first >+# line of the header to parse. This should be a line >+# beginning with "Index:". >+# $line: the line last read from $fileHandle >+# >+# Returns ($headerHashRef, $lastReadLine): >+# $headerHashRef: a hash reference representing a diff header, as follows-- >+# indexPath: the path of the target file, which is the path found in >+# the "Index:" line. >+# isNew: the value 1 if the diff is for a new file. >+# isDeletion: the value 1 if the diff is a file deletion. >+# svnConvertedText: the header text converted to a header with the paths >+# in some lines corrected. >+# $lastReadLine: the line last read from $fileHandle. >+sub parseUnifiedDiffHeader($$) >+{ >+ my ($fileHandle, $line) = @_; >+ >+ $_ = $line; >+ >+ my $currentPosition = tell($fileHandle); >+ my $indexLine; >+ my $indexPath; >+ if (/$unifiedDiffStartRegEx/) { >+ # Use $POSTMATCH to preserve the end-of-line character. >+ my $eol = $POSTMATCH; >+ >+ $indexPath = $2; >+ >+ # In the case of an addition, we look at the next line for the index path >+ if ($indexPath eq "/dev/null") { >+ $_ = <$fileHandle>; >+ if (/^\+\+\+ ([abc]\/)?([^\t\n\r]+)/) { >+ $indexPath = $2; >+ } else { >+ die "Unrecognized unified diff format."; >+ } >+ $_ = $line; >+ } >+ >+ $indexLine = "Index: $indexPath$eol"; # Convert to SVN format. >+ } else { >+ die("Could not parse leading \"---\" line: \"$line\"."); >+ } >+ >+ seek($fileHandle, $currentPosition, 0); >+ >+ my $isDeletion; >+ my $isHeaderEnding; >+ my $isNew; >+ my $svnConvertedText = $indexLine; >+ while (1) { >+ # Temporarily strip off any end-of-line characters to simplify >+ # regex matching below. >+ s/([\n\r]+)$//; >+ my $eol = $1; >+ >+ if (/^--- \/dev\/null/) { >+ $isNew = 1; >+ } elsif (/^\+\+\+ \/dev\/null/) { >+ $isDeletion = 1; >+ } >+ >+ if (/^(---|\+\+\+) ([abc]\/)?([^\t\n\r]+)/) { >+ if ($1 eq "---") { >+ my $prependText = ""; >+ $prependText = "new file mode 100644\n" if $isNew; >+ $_ = "${prependText}index 0000000..0000000\n$1 $3"; >+ } else { >+ $_ = "$1 $3"; >+ $isHeaderEnding = 1; >+ } >+ } >+ >+ $svnConvertedText .= "$_$eol"; # Also restore end-of-line characters. >+ >+ $currentPosition = tell($fileHandle); >+ $_ = <$fileHandle>; # Not defined if end-of-file reached. >+ last if (!defined($_) || /$unifiedDiffStartRegEx/ || $isHeaderEnding); >+ } >+ >+ my %header; >+ >+ $header{indexPath} = $indexPath; >+ $header{isDeletion} = $isDeletion if $isDeletion; >+ $header{isNew} = $isNew if $isNew; >+ $header{svnConvertedText} = $svnConvertedText; >+ >+ return (\%header, $_); >+} >+ >+# Parse the next diff header from the given file handle, and advance >+# the handle so the last line read is the first line after the header. >+# >+# This subroutine dies if given leading junk or if it could not detect >+# the end of the header block. >+# >+# Args: >+# $fileHandle: advanced so the last line read from the handle is the first >+# line of the header to parse. For SVN-formatted diffs, this >+# is a line beginning with "Index:". For Git, this is a line >+# beginning with "diff --git". >+# $line: the line last read from $fileHandle >+# >+# Returns ($headerHashRef, $lastReadLine): >+# $headerHashRef: a hash reference representing a diff header >+# copiedFromPath: the path from which the file was copied if the diff >+# is a copy. >+# executableBitDelta: the value 1 or -1 if the executable bit was added or >+# removed, respectively. New and deleted files have >+# this value only if the file is executable, in which >+# case the value is 1 and -1, respectively. >+# indexPath: the path of the target file. >+# isBinary: the value 1 if the diff is for a binary file. >+# isGit: the value 1 if the diff is Git-formatted. >+# isSvn: the value 1 if the diff is SVN-formatted. >+# sourceRevision: the revision number of the source, if it exists. This >+# is the same as the revision number the file was copied >+# from, in the case of a file copy. >+# svnConvertedText: the header text with some lines converted to SVN >+# format. Git-specific lines are preserved. >+# $lastReadLine: the line last read from $fileHandle. >+sub parseDiffHeader($$) >+{ >+ my ($fileHandle, $line) = @_; >+ >+ my $header; # This is a hash ref. >+ my $isGit; >+ my $isSvn; >+ my $isUnified; >+ my $lastReadLine; >+ >+ if ($line =~ $svnDiffStartRegEx) { >+ $isSvn = 1; >+ ($header, $lastReadLine) = parseSvnDiffHeader($fileHandle, $line); >+ } elsif ($line =~ $gitDiffStartRegEx) { >+ $isGit = 1; >+ ($header, $lastReadLine) = parseGitDiffHeader($fileHandle, $line); >+ } elsif ($line =~ $unifiedDiffStartRegEx) { >+ $isUnified = 1; >+ ($header, $lastReadLine) = parseUnifiedDiffHeader($fileHandle, $line); >+ } else { >+ die("First line of diff does not begin with \"Index:\" or \"diff --git\": \"$line\""); >+ } >+ >+ $header->{isGit} = $isGit if $isGit; >+ $header->{isSvn} = $isSvn if $isSvn; >+ $header->{isUnified} = $isUnified if $isUnified; >+ >+ return ($header, $lastReadLine); >+} >+ >+# FIXME: The %diffHash "object" should not have an svnConvertedText property. >+# Instead, the hash object should store its information in a >+# structured way as properties. This should be done in a way so >+# that, if necessary, the text of an SVN or Git patch can be >+# reconstructed from the information in those hash properties. >+# >+# A %diffHash is a hash representing a source control diff of a single >+# file operation (e.g. a file modification, copy, or delete). >+# >+# These hashes appear, for example, in the parseDiff(), parsePatch(), >+# and prepareParsedPatch() subroutines of this package. >+# >+# The corresponding values are-- >+# >+# copiedFromPath: the path from which the file was copied if the diff >+# is a copy. >+# executableBitDelta: the value 1 or -1 if the executable bit was added or >+# removed from the target file, respectively. >+# indexPath: the path of the target file. For SVN-formatted diffs, >+# this is the same as the path in the "Index:" line. >+# isBinary: the value 1 if the diff is for a binary file. >+# isDeletion: the value 1 if the diff is known from the header to be a deletion. >+# isGit: the value 1 if the diff is Git-formatted. >+# isNew: the value 1 if the dif is known from the header to be a new file. >+# isSvn: the value 1 if the diff is SVN-formatted. >+# sourceRevision: the revision number of the source, if it exists. This >+# is the same as the revision number the file was copied >+# from, in the case of a file copy. >+# svnConvertedText: the diff with some lines converted to SVN format. >+# Git-specific lines are preserved. >+ >+# Parse one diff from a patch file created by svn-create-patch, and >+# advance the file handle so the last line read is the first line >+# of the next header block. >+# >+# This subroutine preserves any leading junk encountered before the header. >+# >+# Composition of an SVN diff >+# >+# There are three parts to an SVN diff: the header, the property change, and >+# the binary contents, in that order. Either the header or the property change >+# may be ommitted, but not both. If there are binary changes, then you always >+# have all three. >+# >+# Args: >+# $fileHandle: a file handle advanced to the first line of the next >+# header block. Leading junk is okay. >+# $line: the line last read from $fileHandle. >+# $optionsHashRef: a hash reference representing optional options to use >+# when processing a diff. >+# shouldNotUseIndexPathEOL: whether to use the line endings in the diff instead >+# instead of the line endings in the target file; the >+# value of 1 if svnConvertedText should use the line >+# endings in the diff. >+# >+# Returns ($diffHashRefs, $lastReadLine): >+# $diffHashRefs: A reference to an array of references to %diffHash hashes. >+# See the %diffHash documentation above. >+# $lastReadLine: the line last read from $fileHandle >+sub parseDiff($$;$) >+{ >+ # FIXME: Adjust this method so that it dies if the first line does not >+ # match the start of a diff. This will require a change to >+ # parsePatch() so that parsePatch() skips over leading junk. >+ my ($fileHandle, $line, $optionsHashRef) = @_; >+ >+ my $headerStartRegEx = $svnDiffStartRegEx; # SVN-style header for the default >+ >+ my $headerHashRef; # Last header found, as returned by parseDiffHeader(). >+ my $svnPropertiesHashRef; # Last SVN properties diff found, as returned by parseSvnDiffProperties(). >+ my $svnText; >+ my $indexPathEOL; >+ my $numTextChunks = 0; >+ while (defined($line)) { >+ if (!$headerHashRef && ($line =~ $gitDiffStartRegEx)) { >+ # Then assume all diffs in the patch are Git-formatted. This >+ # block was made to be enterable at most once since we assume >+ # all diffs in the patch are formatted the same (SVN or Git). >+ $headerStartRegEx = $gitDiffStartRegEx; >+ } >+ >+ if (!$headerHashRef && ($line =~ $unifiedDiffStartRegEx)) { >+ $headerStartRegEx = $unifiedDiffStartRegEx; >+ } >+ >+ if ($line =~ $svnPropertiesStartRegEx) { >+ my $propertyPath = $1; >+ if ($svnPropertiesHashRef || $headerHashRef && ($propertyPath ne $headerHashRef->{indexPath})) { >+ # This is the start of the second diff in the while loop, which happens to >+ # be a property diff. If $svnPropertiesHasRef is defined, then this is the >+ # second consecutive property diff, otherwise it's the start of a property >+ # diff for a file that only has property changes. >+ last; >+ } >+ ($svnPropertiesHashRef, $line) = parseSvnDiffProperties($fileHandle, $line); >+ next; >+ } >+ if ($line !~ $headerStartRegEx) { >+ # Then we are in the body of the diff. >+ my $isChunkRange = defined(parseChunkRange($line)); >+ $numTextChunks += 1 if $isChunkRange; >+ my $nextLine = <$fileHandle>; >+ my $willAddNewLineAtEndOfFile = defined($nextLine) && $nextLine =~ /^\\ No newline at end of file$/; >+ if ($willAddNewLineAtEndOfFile) { >+ # Diff(1) always emits a LF character preceeding the line "\ No newline at end of file". >+ # We must preserve both the added LF character and the line ending of this sentinel line >+ # or patch(1) will complain. >+ $svnText .= $line . $nextLine; >+ $line = <$fileHandle>; >+ next; >+ } >+ if ($indexPathEOL && !$isChunkRange) { >+ # The chunk range is part of the body of the diff, but its line endings should't be >+ # modified or patch(1) will complain. So, we only modify non-chunk range lines. >+ $line =~ s/\r\n|\r|\n/$indexPathEOL/g; >+ } >+ $svnText .= $line; >+ $line = $nextLine; >+ next; >+ } # Otherwise, we found a diff header. >+ >+ if ($svnPropertiesHashRef || $headerHashRef) { >+ # Then either we just processed an SVN property change or this >+ # is the start of the second diff header of this while loop. >+ last; >+ } >+ >+ ($headerHashRef, $line) = parseDiffHeader($fileHandle, $line); >+ if (!$optionsHashRef || !$optionsHashRef->{shouldNotUseIndexPathEOL}) { >+ # FIXME: We shouldn't query the file system (via firstEOLInFile()) to determine the >+ # line endings of the file indexPath. Instead, either the caller to parseDiff() >+ # should provide this information or parseDiff() should take a delegate that it >+ # can use to query for this information. >+ $indexPathEOL = firstEOLInFile($headerHashRef->{indexPath}) if !$headerHashRef->{isNew} && !$headerHashRef->{isBinary}; >+ } >+ >+ $svnText .= $headerHashRef->{svnConvertedText}; >+ } >+ >+ my @diffHashRefs; >+ >+ if ($headerHashRef->{shouldDeleteSource}) { >+ my %deletionHash; >+ $deletionHash{indexPath} = $headerHashRef->{copiedFromPath}; >+ $deletionHash{isDeletion} = 1; >+ push @diffHashRefs, \%deletionHash; >+ } >+ if ($headerHashRef->{copiedFromPath}) { >+ my %copyHash; >+ $copyHash{copiedFromPath} = $headerHashRef->{copiedFromPath}; >+ $copyHash{indexPath} = $headerHashRef->{indexPath}; >+ $copyHash{sourceRevision} = $headerHashRef->{sourceRevision} if $headerHashRef->{sourceRevision}; >+ if ($headerHashRef->{isSvn}) { >+ $copyHash{executableBitDelta} = $svnPropertiesHashRef->{executableBitDelta} if $svnPropertiesHashRef->{executableBitDelta}; >+ } >+ push @diffHashRefs, \%copyHash; >+ } >+ >+ # Note, the order of evaluation for the following if conditional has been explicitly chosen so that >+ # it evaluates to false when there is no headerHashRef (e.g. a property change diff for a file that >+ # only has property changes). >+ if ($headerHashRef->{isCopyWithChanges} || (%$headerHashRef && !$headerHashRef->{copiedFromPath})) { >+ # Then add the usual file modification. >+ my %diffHash; >+ # FIXME: We should expand this code to support other properties. In the future, >+ # parseSvnDiffProperties may return a hash whose keys are the properties. >+ if ($headerHashRef->{isSvn}) { >+ # SVN records the change to the executable bit in a separate property change diff >+ # that follows the contents of the diff, except for binary diffs. For binary >+ # diffs, the property change diff follows the diff header. >+ $diffHash{executableBitDelta} = $svnPropertiesHashRef->{executableBitDelta} if $svnPropertiesHashRef->{executableBitDelta}; >+ } elsif ($headerHashRef->{isGit}) { >+ # Git records the change to the executable bit in the header of a diff. >+ $diffHash{executableBitDelta} = $headerHashRef->{executableBitDelta} if $headerHashRef->{executableBitDelta}; >+ } >+ $diffHash{indexPath} = $headerHashRef->{indexPath}; >+ $diffHash{isBinary} = $headerHashRef->{isBinary} if $headerHashRef->{isBinary}; >+ $diffHash{isDeletion} = $headerHashRef->{isDeletion} if $headerHashRef->{isDeletion}; >+ $diffHash{isGit} = $headerHashRef->{isGit} if $headerHashRef->{isGit}; >+ $diffHash{isNew} = $headerHashRef->{isNew} if $headerHashRef->{isNew}; >+ $diffHash{isSvn} = $headerHashRef->{isSvn} if $headerHashRef->{isSvn}; >+ if (!$headerHashRef->{copiedFromPath}) { >+ # If the file was copied, then we have already incorporated the >+ # sourceRevision information into the change. >+ $diffHash{sourceRevision} = $headerHashRef->{sourceRevision} if $headerHashRef->{sourceRevision}; >+ } >+ # FIXME: Remove the need for svnConvertedText. See the %diffHash >+ # code comments above for more information. >+ # >+ # Note, we may not always have SVN converted text since we intend >+ # to deprecate it in the future. For example, a property change >+ # diff for a file that only has property changes will not return >+ # any SVN converted text. >+ $diffHash{svnConvertedText} = $svnText if $svnText; >+ $diffHash{numTextChunks} = $numTextChunks if $svnText && !$headerHashRef->{isBinary}; >+ push @diffHashRefs, \%diffHash; >+ } >+ >+ if (!%$headerHashRef && $svnPropertiesHashRef) { >+ # A property change diff for a file that only has property changes. >+ my %propertyChangeHash; >+ $propertyChangeHash{executableBitDelta} = $svnPropertiesHashRef->{executableBitDelta} if $svnPropertiesHashRef->{executableBitDelta}; >+ $propertyChangeHash{indexPath} = $svnPropertiesHashRef->{propertyPath}; >+ $propertyChangeHash{isSvn} = 1; >+ push @diffHashRefs, \%propertyChangeHash; >+ } >+ >+ return (\@diffHashRefs, $line); >+} >+ >+# Parse an SVN property change diff from the given file handle, and advance >+# the handle so the last line read is the first line after this diff. >+# >+# For the case of an SVN binary diff, the binary contents will follow the >+# the property changes. >+# >+# This subroutine dies if the first line does not begin with "Property changes on" >+# or if the separator line that follows this line is missing. >+# >+# Args: >+# $fileHandle: advanced so the last line read from the handle is the first >+# line of the footer to parse. This line begins with >+# "Property changes on". >+# $line: the line last read from $fileHandle. >+# >+# Returns ($propertyHashRef, $lastReadLine): >+# $propertyHashRef: a hash reference representing an SVN diff footer. >+# propertyPath: the path of the target file. >+# executableBitDelta: the value 1 or -1 if the executable bit was added or >+# removed from the target file, respectively. >+# $lastReadLine: the line last read from $fileHandle. >+sub parseSvnDiffProperties($$) >+{ >+ my ($fileHandle, $line) = @_; >+ >+ $_ = $line; >+ >+ my %footer; >+ if (/$svnPropertiesStartRegEx/) { >+ $footer{propertyPath} = $1; >+ } else { >+ die("Failed to find start of SVN property change, \"Property changes on \": \"$_\""); >+ } >+ >+ # We advance $fileHandle two lines so that the next line that >+ # we process is $svnPropertyStartRegEx in a well-formed footer. >+ # A well-formed footer has the form: >+ # Property changes on: FileA >+ # ___________________________________________________________________ >+ # Added: svn:executable >+ # + * >+ $_ = <$fileHandle>; # Not defined if end-of-file reached. >+ my $separator = "_" x 67; >+ if (defined($_) && /^$separator[\r\n]+$/) { >+ $_ = <$fileHandle>; >+ } else { >+ die("Failed to find separator line: \"$_\"."); >+ } >+ >+ # FIXME: We should expand this to support other SVN properties >+ # (e.g. return a hash of property key-values that represents >+ # all properties). >+ # >+ # Notice, we keep processing until we hit end-of-file or some >+ # line that does not resemble $svnPropertyStartRegEx, such as >+ # the empty line that precedes the start of the binary contents >+ # of a patch, or the start of the next diff (e.g. "Index:"). >+ my $propertyHashRef; >+ while (defined($_) && /$svnPropertyStartRegEx/) { >+ ($propertyHashRef, $_) = parseSvnProperty($fileHandle, $_); >+ if ($propertyHashRef->{name} eq "svn:executable") { >+ # Notice, for SVN properties, propertyChangeDelta is always non-zero >+ # because a property can only be added or removed. >+ $footer{executableBitDelta} = $propertyHashRef->{propertyChangeDelta}; >+ } >+ } >+ >+ return(\%footer, $_); >+} >+ >+# Parse the next SVN property from the given file handle, and advance the handle so the last >+# line read is the first line after the property. >+# >+# This subroutine dies if the first line is not a valid start of an SVN property, >+# or the property is missing a value, or the property change type (e.g. "Added") >+# does not correspond to the property value type (e.g. "+"). >+# >+# Args: >+# $fileHandle: advanced so the last line read from the handle is the first >+# line of the property to parse. This should be a line >+# that matches $svnPropertyStartRegEx. >+# $line: the line last read from $fileHandle. >+# >+# Returns ($propertyHashRef, $lastReadLine): >+# $propertyHashRef: a hash reference representing a SVN property. >+# name: the name of the property. >+# value: the last property value. For instance, suppose the property is "Modified". >+# Then it has both a '-' and '+' property value in that order. Therefore, >+# the value of this key is the value of the '+' property by ordering (since >+# it is the last value). >+# propertyChangeDelta: the value 1 or -1 if the property was added or >+# removed, respectively. >+# $lastReadLine: the line last read from $fileHandle. >+sub parseSvnProperty($$) >+{ >+ my ($fileHandle, $line) = @_; >+ >+ $_ = $line; >+ >+ my $propertyName; >+ my $propertyChangeType; >+ if (/$svnPropertyStartRegEx/) { >+ $propertyChangeType = $1; >+ $propertyName = $2; >+ } else { >+ die("Failed to find SVN property: \"$_\"."); >+ } >+ >+ $_ = <$fileHandle>; # Not defined if end-of-file reached. >+ >+ if (defined($_) && defined(parseChunkRange($_, "##"))) { >+ # FIXME: We should validate the chunk range line that is part of an SVN 1.7 >+ # property diff. For now, we ignore this line. >+ $_ = <$fileHandle>; >+ } >+ >+ # The "svn diff" command neither inserts newline characters between property values >+ # nor between successive properties. >+ # >+ # As of SVN 1.7, "svn diff" may insert "\ No newline at end of property" after a >+ # property value that doesn't end in a newline. >+ # >+ # FIXME: We do not support property values that contain tailing newline characters >+ # as it is difficult to disambiguate these trailing newlines from the empty >+ # line that precedes the contents of a binary patch. >+ my $propertyValue; >+ my $propertyValueType; >+ while (defined($_) && /$svnPropertyValueStartRegEx/) { >+ # Note, a '-' property may be followed by a '+' property in the case of a "Modified" >+ # or "Name" property. We only care about the ending value (i.e. the '+' property) >+ # in such circumstances. So, we take the property value for the property to be its >+ # last parsed property value. >+ # >+ # FIXME: We may want to consider strictly enforcing a '-', '+' property ordering or >+ # add error checking to prevent '+', '+', ..., '+' and other invalid combinations. >+ $propertyValueType = $1; >+ ($propertyValue, $_) = parseSvnPropertyValue($fileHandle, $_); >+ $_ = <$fileHandle> if defined($_) && /$svnPropertyValueNoNewlineRegEx/; >+ } >+ >+ if (!$propertyValue) { >+ die("Failed to find the property value for the SVN property \"$propertyName\": \"$_\"."); >+ } >+ >+ my $propertyChangeDelta; >+ if ($propertyValueType eq "+" || $propertyValueType eq "Merged") { >+ $propertyChangeDelta = 1; >+ } elsif ($propertyValueType eq "-" || $propertyValueType eq "Reverse-merged") { >+ $propertyChangeDelta = -1; >+ } else { >+ die("Not reached."); >+ } >+ >+ # We perform a simple validation that an "Added" or "Deleted" property >+ # change type corresponds with a "+" and "-" value type, respectively. >+ my $expectedChangeDelta; >+ if ($propertyChangeType eq "Added") { >+ $expectedChangeDelta = 1; >+ } elsif ($propertyChangeType eq "Deleted") { >+ $expectedChangeDelta = -1; >+ } >+ >+ if ($expectedChangeDelta && $propertyChangeDelta != $expectedChangeDelta) { >+ die("The final property value type found \"$propertyValueType\" does not " . >+ "correspond to the property change type found \"$propertyChangeType\"."); >+ } >+ >+ my %propertyHash; >+ $propertyHash{name} = $propertyName; >+ $propertyHash{propertyChangeDelta} = $propertyChangeDelta; >+ $propertyHash{value} = $propertyValue; >+ return (\%propertyHash, $_); >+} >+ >+# Parse the value of an SVN property from the given file handle, and advance >+# the handle so the last line read is the first line after the property value. >+# >+# This subroutine dies if the first line is an invalid SVN property value line >+# (i.e. a line that does not begin with " +" or " -"). >+# >+# Args: >+# $fileHandle: advanced so the last line read from the handle is the first >+# line of the property value to parse. This should be a line >+# beginning with " +" or " -". >+# $line: the line last read from $fileHandle. >+# >+# Returns ($propertyValue, $lastReadLine): >+# $propertyValue: the value of the property. >+# $lastReadLine: the line last read from $fileHandle. >+sub parseSvnPropertyValue($$) >+{ >+ my ($fileHandle, $line) = @_; >+ >+ $_ = $line; >+ >+ my $propertyValue; >+ my $eol; >+ if (/$svnPropertyValueStartRegEx/) { >+ $propertyValue = $2; # Does not include the end-of-line character(s). >+ $eol = $POSTMATCH; >+ } else { >+ die("Failed to find property value beginning with '+', '-', 'Merged', or 'Reverse-merged': \"$_\"."); >+ } >+ >+ while (<$fileHandle>) { >+ if (/^[\r\n]+$/ || /$svnPropertyValueStartRegEx/ || /$svnPropertyStartRegEx/ || /$svnPropertyValueNoNewlineRegEx/ || /$svnDiffStartRegEx/) { >+ # Note, we may encounter an empty line before the contents of a binary patch. >+ # Also, we check for $svnPropertyValueStartRegEx because a '-' property may be >+ # followed by a '+' property in the case of a "Modified" or "Name" property. >+ # We check for $svnPropertyStartRegEx because it indicates the start of the >+ # next property to parse. >+ last; >+ } >+ >+ # Temporarily strip off any end-of-line characters. We add the end-of-line characters >+ # from the previously processed line to the start of this line so that the last line >+ # of the property value does not end in end-of-line characters. >+ s/([\n\r]+)$//; >+ $propertyValue .= "$eol$_"; >+ $eol = $1; >+ } >+ >+ return ($propertyValue, $_); >+} >+ >+# Parse a patch file created by svn-create-patch. >+# >+# Args: >+# $fileHandle: A file handle to the patch file that has not yet been >+# read from. >+# $optionsHashRef: a hash reference representing optional options to use >+# when processing a diff. >+# shouldNotUseIndexPathEOL: whether to use the line endings in the diff instead >+# instead of the line endings in the target file; the >+# value of 1 if svnConvertedText should use the line >+# endings in the diff. >+# >+# Returns: >+# @diffHashRefs: an array of diff hash references. >+# See the %diffHash documentation above. >+sub parsePatch($;$) >+{ >+ my ($fileHandle, $optionsHashRef) = @_; >+ >+ my $newDiffHashRefs; >+ my @diffHashRefs; # return value >+ >+ my $line = <$fileHandle>; >+ >+ while (defined($line)) { # Otherwise, at EOF. >+ >+ ($newDiffHashRefs, $line) = parseDiff($fileHandle, $line, $optionsHashRef); >+ >+ push @diffHashRefs, @$newDiffHashRefs; >+ } >+ >+ return @diffHashRefs; >+} >+ >+# Prepare the results of parsePatch() for use in svn-apply and svn-unapply. >+# >+# Args: >+# $shouldForce: Whether to continue processing if an unexpected >+# state occurs. >+# @diffHashRefs: An array of references to %diffHashes. >+# See the %diffHash documentation above. >+# >+# Returns $preparedPatchHashRef: >+# copyDiffHashRefs: A reference to an array of the $diffHashRefs in >+# @diffHashRefs that represent file copies. The original >+# ordering is preserved. >+# nonCopyDiffHashRefs: A reference to an array of the $diffHashRefs in >+# @diffHashRefs that do not represent file copies. >+# The original ordering is preserved. >+# sourceRevisionHash: A reference to a hash of source path to source >+# revision number. >+sub prepareParsedPatch($@) >+{ >+ my ($shouldForce, @diffHashRefs) = @_; >+ >+ my %copiedFiles; >+ >+ # Return values >+ my @copyDiffHashRefs = (); >+ my @nonCopyDiffHashRefs = (); >+ my %sourceRevisionHash = (); >+ for my $diffHashRef (@diffHashRefs) { >+ my $copiedFromPath = $diffHashRef->{copiedFromPath}; >+ my $indexPath = $diffHashRef->{indexPath}; >+ my $sourceRevision = $diffHashRef->{sourceRevision}; >+ my $sourcePath; >+ >+ if (defined($copiedFromPath)) { >+ # Then the diff is a copy operation. >+ $sourcePath = $copiedFromPath; >+ >+ # FIXME: Consider printing a warning or exiting if >+ # exists($copiedFiles{$indexPath}) is true -- i.e. if >+ # $indexPath appears twice as a copy target. >+ $copiedFiles{$indexPath} = $sourcePath; >+ >+ push @copyDiffHashRefs, $diffHashRef; >+ } else { >+ # Then the diff is not a copy operation. >+ $sourcePath = $indexPath; >+ >+ push @nonCopyDiffHashRefs, $diffHashRef; >+ } >+ >+ if (defined($sourceRevision)) { >+ if (exists($sourceRevisionHash{$sourcePath}) && >+ ($sourceRevisionHash{$sourcePath} != $sourceRevision)) { >+ if (!$shouldForce) { >+ die "Two revisions of the same file required as a source:\n". >+ " $sourcePath:$sourceRevisionHash{$sourcePath}\n". >+ " $sourcePath:$sourceRevision"; >+ } >+ } >+ $sourceRevisionHash{$sourcePath} = $sourceRevision; >+ } >+ } >+ >+ my %preparedPatchHash; >+ >+ $preparedPatchHash{copyDiffHashRefs} = \@copyDiffHashRefs; >+ $preparedPatchHash{nonCopyDiffHashRefs} = \@nonCopyDiffHashRefs; >+ $preparedPatchHash{sourceRevisionHash} = \%sourceRevisionHash; >+ >+ return \%preparedPatchHash; >+} >+ >+# Return localtime() for the project's time zone, given an integer time as >+# returned by Perl's time() function. >+sub localTimeInProjectTimeZone($) >+{ >+ my $epochTime = shift; >+ >+ # Change the time zone temporarily for the localtime() call. >+ my $savedTimeZone = $ENV{'TZ'}; >+ $ENV{'TZ'} = $changeLogTimeZone; >+ my @localTime = localtime($epochTime); >+ if (defined $savedTimeZone) { >+ $ENV{'TZ'} = $savedTimeZone; >+ } else { >+ delete $ENV{'TZ'}; >+ } >+ >+ return @localTime; >+} >+ >+# Set the reviewer and date in a ChangeLog patch, and return the new patch. >+# >+# Args: >+# $patch: a ChangeLog patch as a string. >+# $reviewer: the name of the reviewer, or undef if the reviewer should not be set. >+# $epochTime: an integer time as returned by Perl's time() function. >+sub setChangeLogDateAndReviewer($$$) >+{ >+ my ($patch, $reviewer, $epochTime) = @_; >+ >+ my @localTime = localTimeInProjectTimeZone($epochTime); >+ my $newDate = strftime("%Y-%m-%d", @localTime); >+ >+ my $firstChangeLogLineRegEx = qr#(\n\+)\d{4}-[^-]{2}-[^-]{2}( )#; >+ $patch =~ s/$firstChangeLogLineRegEx/$1$newDate$2/; >+ >+ if (defined($reviewer)) { >+ # We include a leading plus ("+") in the regular expression to make >+ # the regular expression less likely to match text in the leading junk >+ # for the patch, if the patch has leading junk. >+ $patch =~ s/(\n\+.*)NOBODY \(OOPS!\)/$1$reviewer/; >+ } >+ >+ return $patch; >+} >+ >+# Removes a leading Subversion header without an associated diff if one exists. >+# >+# This subroutine dies if the specified patch does not begin with an "Index:" line. >+# >+# In SVN 1.9 or newer, "svn diff" of a moved/copied file without post changes always >+# emits a leading header without an associated diff: >+# Index: B.txt >+# =================================================================== >+# (end of file or next header) >+# >+# If the same file has a property change then the patch has the form: >+# Index: B.txt >+# =================================================================== >+# Index: B.txt >+# =================================================================== >+# --- B.txt (revision 1) >+# +++ B.txt (working copy) >+# >+# Property change on B.txt >+# ___________________________________________________________________ >+# Added: svn:executable >+# ## -0,0 +1 ## >+# +* >+# \ No newline at end of property >+# >+# We need to apply this function to the ouput of "svn diff" for an addition with history >+# to remove a duplicate header so that svn-apply can apply the resulting patch. >+sub fixSVNPatchForAdditionWithHistory($) >+{ >+ my ($patch) = @_; >+ >+ $patch =~ /(\r?\n)/; >+ my $lineEnding = $1; >+ my @lines = split(/$lineEnding/, $patch); >+ >+ if ($lines[0] !~ /$svnDiffStartRegEx/) { >+ die("First line of SVN diff does not begin with \"Index \": \"$lines[0]\""); >+ } >+ if (@lines <= 2) { >+ return ""; >+ } >+ splice(@lines, 0, 2) if $lines[2] =~ /$svnDiffStartRegEx/; >+ return join($lineEnding, @lines) . "\n"; # patch(1) expects an extra trailing newline. >+} >+ >+# If possible, returns a ChangeLog patch equivalent to the given one, >+# but with the newest ChangeLog entry inserted at the top of the >+# file -- i.e. no leading context and all lines starting with "+". >+# >+# If given a patch string not representable as a patch with the above >+# properties, it returns the input back unchanged. >+# >+# WARNING: This subroutine can return an inequivalent patch string if >+# both the beginning of the new ChangeLog file matches the beginning >+# of the source ChangeLog, and the source beginning was modified. >+# Otherwise, it is guaranteed to return an equivalent patch string, >+# if it returns. >+# >+# Applying this subroutine to ChangeLog patches allows svn-apply to >+# insert new ChangeLog entries at the top of the ChangeLog file. >+# svn-apply uses patch with --fuzz=3 to do this. We need to apply >+# this subroutine because the diff(1) command is greedy when matching >+# lines. A new ChangeLog entry with the same date and author as the >+# previous will match and cause the diff to have lines of starting >+# context. >+# >+# This subroutine has unit tests in VCSUtils_unittest.pl. >+# >+# Returns $changeLogHashRef: >+# $changeLogHashRef: a hash reference representing a change log patch. >+# patch: a ChangeLog patch equivalent to the given one, but with the >+# newest ChangeLog entry inserted at the top of the file, if possible. >+sub fixChangeLogPatch($) >+{ >+ my $patch = shift; # $patch will only contain patch fragments for ChangeLog. >+ >+ $patch =~ s|test_expectations.txt:|TestExpectations:|g; >+ >+ $patch =~ /(\r?\n)/; >+ my $lineEnding = $1; >+ my @lines = split(/$lineEnding/, $patch); >+ >+ my $i = 0; # We reuse the same index throughout. >+ >+ # Skip to beginning of first chunk. >+ for (; $i < @lines; ++$i) { >+ if (substr($lines[$i], 0, 1) eq "@") { >+ last; >+ } >+ } >+ my $chunkStartIndex = ++$i; >+ my %changeLogHashRef; >+ >+ # Optimization: do not process if new lines already begin the chunk. >+ if (substr($lines[$i], 0, 1) eq "+") { >+ $changeLogHashRef{patch} = $patch; >+ return \%changeLogHashRef; >+ } >+ >+ # Skip to first line of newly added ChangeLog entry. >+ # For example, +2009-06-03 Eric Seidel <eric@webkit.org> >+ my $dateStartRegEx = '^\+(\d{4}-\d{2}-\d{2})' # leading "+" and date >+ . '\s+(.+)\s+' # name >+ . '<([^<>]+)>$'; # e-mail address >+ >+ for (; $i < @lines; ++$i) { >+ my $line = $lines[$i]; >+ my $firstChar = substr($line, 0, 1); >+ if ($line =~ /$dateStartRegEx/) { >+ last; >+ } elsif ($firstChar eq " " or $firstChar eq "+") { >+ next; >+ } >+ $changeLogHashRef{patch} = $patch; # Do not change if, for example, "-" or "@" found. >+ return \%changeLogHashRef; >+ } >+ if ($i >= @lines) { >+ $changeLogHashRef{patch} = $patch; # Do not change if date not found. >+ return \%changeLogHashRef; >+ } >+ my $dateStartIndex = $i; >+ >+ # Rewrite overlapping lines to lead with " ". >+ my @overlappingLines = (); # These will include a leading "+". >+ for (; $i < @lines; ++$i) { >+ my $line = $lines[$i]; >+ if (substr($line, 0, 1) ne "+") { >+ last; >+ } >+ push(@overlappingLines, $line); >+ $lines[$i] = " " . substr($line, 1); >+ } >+ >+ # Remove excess ending context, if necessary. >+ my $shouldTrimContext = 1; >+ for (; $i < @lines; ++$i) { >+ my $firstChar = substr($lines[$i], 0, 1); >+ if ($firstChar eq " ") { >+ next; >+ } elsif ($firstChar eq "@") { >+ last; >+ } >+ $shouldTrimContext = 0; # For example, if "+" or "-" encountered. >+ last; >+ } >+ my $deletedLineCount = 0; >+ if ($shouldTrimContext) { # Also occurs if end of file reached. >+ splice(@lines, $i - @overlappingLines, @overlappingLines); >+ $deletedLineCount = @overlappingLines; >+ } >+ >+ # Work backwards, shifting overlapping lines towards front >+ # while checking that patch stays equivalent. >+ for ($i = $dateStartIndex - 1; @overlappingLines && $i >= $chunkStartIndex; --$i) { >+ my $line = $lines[$i]; >+ if (substr($line, 0, 1) ne " ") { >+ next; >+ } >+ my $text = substr($line, 1); >+ my $newLine = pop(@overlappingLines); >+ if ($text ne substr($newLine, 1)) { >+ $changeLogHashRef{patch} = $patch; # Unexpected difference. >+ return \%changeLogHashRef; >+ } >+ $lines[$i] = "+$text"; >+ } >+ >+ # If @overlappingLines > 0, this is where we make use of the >+ # assumption that the beginning of the source file was not modified. >+ splice(@lines, $chunkStartIndex, 0, @overlappingLines); >+ >+ # Update the date start index as it may have changed after shifting >+ # the overlapping lines towards the front. >+ for ($i = $chunkStartIndex; $i < $dateStartIndex; ++$i) { >+ $dateStartIndex = $i if $lines[$i] =~ /$dateStartRegEx/; >+ } >+ splice(@lines, $chunkStartIndex, $dateStartIndex - $chunkStartIndex); # Remove context of later entry. >+ $deletedLineCount += $dateStartIndex - $chunkStartIndex; >+ >+ # Update the initial chunk range. >+ my $chunkRangeHashRef = parseChunkRange($lines[$chunkStartIndex - 1]); >+ if (!$chunkRangeHashRef) { >+ # FIXME: Handle errors differently from ChangeLog files that >+ # are okay but should not be altered. That way we can find out >+ # if improvements to the script ever become necessary. >+ $changeLogHashRef{patch} = $patch; # Error: unexpected patch string format. >+ return \%changeLogHashRef; >+ } >+ my $oldSourceLineCount = $chunkRangeHashRef->{lineCount}; >+ my $oldTargetLineCount = $chunkRangeHashRef->{newLineCount}; >+ >+ my $sourceLineCount = $oldSourceLineCount + @overlappingLines - $deletedLineCount; >+ my $targetLineCount = $oldTargetLineCount + @overlappingLines - $deletedLineCount; >+ $lines[$chunkStartIndex - 1] = "@@ -1,$sourceLineCount +1,$targetLineCount @@"; >+ >+ $changeLogHashRef{patch} = join($lineEnding, @lines) . "\n"; # patch(1) expects an extra trailing newline. >+ return \%changeLogHashRef; >+} >+ >+# This is a supporting method for runPatchCommand. >+# >+# Arg: the optional $args parameter passed to runPatchCommand (can be undefined). >+# >+# Returns ($patchCommand, $isForcing). >+# >+# This subroutine has unit tests in VCSUtils_unittest.pl. >+sub generatePatchCommand($) >+{ >+ my ($passedArgsHashRef) = @_; >+ >+ my $argsHashRef = { # Defaults >+ ensureForce => 0, >+ shouldReverse => 0, >+ options => [] >+ }; >+ >+ # Merges hash references. It's okay here if passed hash reference is undefined. >+ @{$argsHashRef}{keys %{$passedArgsHashRef}} = values %{$passedArgsHashRef}; >+ >+ my $ensureForce = $argsHashRef->{ensureForce}; >+ my $shouldReverse = $argsHashRef->{shouldReverse}; >+ my $options = $argsHashRef->{options}; >+ >+ if (! $options) { >+ $options = []; >+ } else { >+ $options = [@{$options}]; # Copy to avoid side effects. >+ } >+ >+ my $isForcing = 0; >+ if (grep /^--force$/, @{$options}) { >+ $isForcing = 1; >+ } elsif ($ensureForce) { >+ push @{$options}, "--force"; >+ $isForcing = 1; >+ } >+ >+ if ($shouldReverse) { # No check: --reverse should never be passed explicitly. >+ push @{$options}, "--reverse"; >+ } >+ >+ @{$options} = sort(@{$options}); # For easier testing. >+ >+ my $patchCommand = join(" ", "patch -p0", @{$options}); >+ >+ return ($patchCommand, $isForcing); >+} >+ >+# Apply the given patch using the patch(1) command. >+# >+# On success, return the resulting exit status. Otherwise, exit with the >+# exit status. If "--force" is passed as an option, however, then never >+# exit and always return the exit status. >+# >+# Args: >+# $patch: a patch string. >+# $repositoryRootPath: an absolute path to the repository root. >+# $pathRelativeToRoot: the path of the file to be patched, relative to the >+# repository root. This should normally be the path >+# found in the patch's "Index:" line. It is passed >+# explicitly rather than reparsed from the patch >+# string for optimization purposes. >+# This is used only for error reporting. The >+# patch command gleans the actual file to patch >+# from the patch string. >+# $args: a reference to a hash of optional arguments. The possible >+# keys are -- >+# ensureForce: whether to ensure --force is passed (defaults to 0). >+# shouldReverse: whether to pass --reverse (defaults to 0). >+# options: a reference to an array of options to pass to the >+# patch command. The subroutine passes the -p0 option >+# no matter what. This should not include --reverse. >+# >+# This subroutine has unit tests in VCSUtils_unittest.pl. >+sub runPatchCommand($$$;$) >+{ >+ my ($patch, $repositoryRootPath, $pathRelativeToRoot, $args) = @_; >+ >+ my ($patchCommand, $isForcing) = generatePatchCommand($args); >+ >+ # Temporarily change the working directory since the path found >+ # in the patch's "Index:" line is relative to the repository root >+ # (i.e. the same as $pathRelativeToRoot). >+ my $cwd = Cwd::getcwd(); >+ chdir $repositoryRootPath; >+ >+ open PATCH, "| $patchCommand" or die "Could not call \"$patchCommand\" for file \"$pathRelativeToRoot\": $!"; >+ print PATCH $patch; >+ close PATCH; >+ my $exitStatus = exitStatus($?); >+ >+ chdir $cwd; >+ >+ if ($exitStatus && !$isForcing) { >+ print "Calling \"$patchCommand\" for file \"$pathRelativeToRoot\" returned " . >+ "status $exitStatus. Pass --force to ignore patch failures.\n"; >+ exit $exitStatus; >+ } >+ >+ return $exitStatus; >+} >+ >+# Merge ChangeLog patches using a three-file approach. >+# >+# This is used by resolve-ChangeLogs when it's operated as a merge driver >+# and when it's used to merge conflicts after a patch is applied or after >+# an svn update. >+# >+# It's also used for traditional rejected patches. >+# >+# Args: >+# $fileMine: The merged version of the file. Also known in git as the >+# other branch's version (%B) or "ours". >+# For traditional patch rejects, this is the *.rej file. >+# $fileOlder: The base version of the file. Also known in git as the >+# ancestor version (%O) or "base". >+# For traditional patch rejects, this is the *.orig file. >+# $fileNewer: The current version of the file. Also known in git as the >+# current version (%A) or "theirs". >+# For traditional patch rejects, this is the original-named >+# file. >+# >+# Returns 1 if merge was successful, else 0. >+sub mergeChangeLogs($$$) >+{ >+ my ($fileMine, $fileOlder, $fileNewer) = @_; >+ >+ my $traditionalReject = $fileMine =~ /\.rej$/ ? 1 : 0; >+ >+ local $/ = undef; >+ >+ my $patch; >+ if ($traditionalReject) { >+ open(DIFF, "<", $fileMine) or die $!; >+ $patch = <DIFF>; >+ close(DIFF); >+ rename($fileMine, "$fileMine.save"); >+ rename($fileOlder, "$fileOlder.save"); >+ } else { >+ open(DIFF, "diff -u -a --binary \"$fileOlder\" \"$fileMine\" |") or die $!; >+ $patch = <DIFF>; >+ close(DIFF); >+ } >+ >+ unlink("${fileNewer}.orig"); >+ unlink("${fileNewer}.rej"); >+ >+ open(PATCH, "| patch --force --fuzz=3 --binary \"$fileNewer\" > " . File::Spec->devnull()) or die $!; >+ if ($traditionalReject) { >+ print PATCH $patch; >+ } else { >+ my $changeLogHash = fixChangeLogPatch($patch); >+ print PATCH $changeLogHash->{patch}; >+ } >+ close(PATCH); >+ >+ my $result = !exitStatus($?); >+ >+ # Refuse to merge the patch if it did not apply cleanly >+ if (-e "${fileNewer}.rej") { >+ unlink("${fileNewer}.rej"); >+ if (-f "${fileNewer}.orig") { >+ unlink($fileNewer); >+ rename("${fileNewer}.orig", $fileNewer); >+ } >+ } else { >+ unlink("${fileNewer}.orig"); >+ } >+ >+ if ($traditionalReject) { >+ rename("$fileMine.save", $fileMine); >+ rename("$fileOlder.save", $fileOlder); >+ } >+ >+ return $result; >+} >+ >+sub gitConfig($) >+{ >+ return unless isGit(); >+ >+ my ($config) = @_; >+ >+ my $result = `git config $config`; >+ chomp $result; >+ return $result; >+} >+ >+sub changeLogNameError($) >+{ >+ my ($message) = @_; >+ print STDERR "$message\nEither:\n"; >+ print STDERR " set CHANGE_LOG_NAME in your environment\n"; >+ print STDERR " OR pass --name= on the command line\n"; >+ print STDERR " OR set REAL_NAME in your environment"; >+ print STDERR " OR git users can set 'git config user.name'\n"; >+ exit(1); >+} >+ >+sub changeLogName() >+{ >+ my $name = $ENV{CHANGE_LOG_NAME} || $ENV{REAL_NAME} || gitConfig("user.name"); >+ if (not $name and !isWindows()) { >+ $name = (split /\s*,\s*/, (getpwuid $<)[6])[0]; >+ } >+ >+ changeLogNameError("Failed to determine ChangeLog name.") unless $name; >+ # getpwuid seems to always succeed on windows, returning the username instead of the full name. This check will catch that case. >+ changeLogNameError("'$name' does not contain a space! ChangeLogs should contain your full name.") unless ($name =~ /\S\s\S/); >+ >+ return $name; >+} >+ >+sub changeLogEmailAddressError($) >+{ >+ my ($message) = @_; >+ print STDERR "$message\nEither:\n"; >+ print STDERR " set CHANGE_LOG_EMAIL_ADDRESS in your environment\n"; >+ print STDERR " OR pass --email= on the command line\n"; >+ print STDERR " OR set EMAIL_ADDRESS in your environment\n"; >+ print STDERR " OR git users can set 'git config user.email'\n"; >+ exit(1); >+} >+ >+sub changeLogEmailAddress() >+{ >+ my $emailAddress = $ENV{CHANGE_LOG_EMAIL_ADDRESS} || $ENV{EMAIL_ADDRESS} || gitConfig("user.email"); >+ >+ changeLogEmailAddressError("Failed to determine email address for ChangeLog.") unless $emailAddress; >+ changeLogEmailAddressError("Email address '$emailAddress' does not contain '\@' and is likely invalid.") unless ($emailAddress =~ /\@/); >+ >+ return $emailAddress; >+} >+ >+# http://tools.ietf.org/html/rfc1924 >+sub decodeBase85($) >+{ >+ my ($encoded) = @_; >+ my %table; >+ my @characters = ('0'..'9', 'A'..'Z', 'a'..'z', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', '>', '?', '@', '^', '_', '`', '{', '|', '}', '~'); >+ for (my $i = 0; $i < 85; $i++) { >+ $table{$characters[$i]} = $i; >+ } >+ >+ my $decoded = ''; >+ my @encodedChars = $encoded =~ /./g; >+ >+ for (my $encodedIter = 0; defined($encodedChars[$encodedIter]);) { >+ my $digit = 0; >+ for (my $i = 0; $i < 5; $i++) { >+ $digit *= 85; >+ my $char = $encodedChars[$encodedIter]; >+ $digit += $table{$char}; >+ $encodedIter++; >+ } >+ >+ for (my $i = 0; $i < 4; $i++) { >+ $decoded .= chr(($digit >> (3 - $i) * 8) & 255); >+ } >+ } >+ >+ return $decoded; >+} >+ >+sub decodeGitBinaryChunk($$) >+{ >+ my ($contents, $fullPath) = @_; >+ >+ # Load this module lazily in case the user don't have this module >+ # and won't handle git binary patches. >+ require Compress::Zlib; >+ >+ my $encoded = ""; >+ my $compressedSize = 0; >+ while ($contents =~ /^([A-Za-z])(.*)$/gm) { >+ my $line = $2; >+ next if $line eq ""; >+ die "$fullPath: unexpected size of a line: $&" if length($2) % 5 != 0; >+ my $actualSize = length($2) / 5 * 4; >+ my $encodedExpectedSize = ord($1); >+ my $expectedSize = $encodedExpectedSize <= ord("Z") ? $encodedExpectedSize - ord("A") + 1 : $encodedExpectedSize - ord("a") + 27; >+ >+ die "$fullPath: unexpected size of a line: $&" if int(($expectedSize + 3) / 4) * 4 != $actualSize; >+ $compressedSize += $expectedSize; >+ $encoded .= $line; >+ } >+ >+ my $compressed = decodeBase85($encoded); >+ $compressed = substr($compressed, 0, $compressedSize); >+ return Compress::Zlib::uncompress($compressed); >+} >+ >+sub decodeGitBinaryPatch($$) >+{ >+ my ($contents, $fullPath) = @_; >+ >+ # Git binary patch has two chunks. One is for the normal patching >+ # and another is for the reverse patching. >+ # >+ # Each chunk a line which starts from either "literal" or "delta", >+ # followed by a number which specifies decoded size of the chunk. >+ # >+ # Then, content of the chunk comes. To decode the content, we >+ # need decode it with base85 first, and then zlib. >+ my $gitPatchRegExp = '(literal|delta) ([0-9]+)\n([A-Za-z0-9!#$%&()*+-;<=>?@^_`{|}~\\n]*?)\n\n'; >+ if ($contents !~ m"\nGIT binary patch\n$gitPatchRegExp$gitPatchRegExp(\Z|-- \n)") { >+ return (); >+ } >+ >+ my $binaryChunkType = $1; >+ my $binaryChunkExpectedSize = $2; >+ my $encodedChunk = $3; >+ my $reverseBinaryChunkType = $4; >+ my $reverseBinaryChunkExpectedSize = $5; >+ my $encodedReverseChunk = $6; >+ >+ my $binaryChunk = decodeGitBinaryChunk($encodedChunk, $fullPath); >+ my $binaryChunkActualSize = length($binaryChunk); >+ my $reverseBinaryChunk = decodeGitBinaryChunk($encodedReverseChunk, $fullPath); >+ my $reverseBinaryChunkActualSize = length($reverseBinaryChunk); >+ >+ die "$fullPath: unexpected size of the first chunk (expected $binaryChunkExpectedSize but was $binaryChunkActualSize" if ($binaryChunkType eq "literal" and $binaryChunkExpectedSize != $binaryChunkActualSize); >+ die "$fullPath: unexpected size of the second chunk (expected $reverseBinaryChunkExpectedSize but was $reverseBinaryChunkActualSize" if ($reverseBinaryChunkType eq "literal" and $reverseBinaryChunkExpectedSize != $reverseBinaryChunkActualSize); >+ >+ return ($binaryChunkType, $binaryChunk, $reverseBinaryChunkType, $reverseBinaryChunk); >+} >+ >+sub readByte($$) >+{ >+ my ($data, $location) = @_; >+ >+ # Return the byte at $location in $data as a numeric value. >+ return ord(substr($data, $location, 1)); >+} >+ >+# The git binary delta format is undocumented, except in code: >+# - https://github.com/git/git/blob/master/delta.h:get_delta_hdr_size is the source >+# of the algorithm in decodeGitBinaryPatchDeltaSize. >+# - https://github.com/git/git/blob/master/patch-delta.c:patch_delta is the source >+# of the algorithm in applyGitBinaryPatchDelta. >+sub decodeGitBinaryPatchDeltaSize($) >+{ >+ my ($binaryChunk) = @_; >+ >+ # Source and destination buffer sizes are stored in 7-bit chunks at the >+ # start of the binary delta patch data. The highest bit in each byte >+ # except the last is set; the remaining 7 bits provide the next >+ # chunk of the size. The chunks are stored in ascending significance >+ # order. >+ my $cmd; >+ my $size = 0; >+ my $shift = 0; >+ for (my $i = 0; $i < length($binaryChunk);) { >+ $cmd = readByte($binaryChunk, $i++); >+ $size |= ($cmd & 0x7f) << $shift; >+ $shift += 7; >+ if (!($cmd & 0x80)) { >+ return ($size, $i); >+ } >+ } >+} >+ >+sub applyGitBinaryPatchDelta($$) >+{ >+ my ($binaryChunk, $originalContents) = @_; >+ >+ # Git delta format consists of two headers indicating source buffer size >+ # and result size, then a series of commands. Each command is either >+ # a copy-from-old-version (the 0x80 bit is set) or a copy-from-delta >+ # command. Commands are applied sequentially to generate the result. >+ # >+ # A copy-from-old-version command encodes an offset and size to copy >+ # from in subsequent bits, while a copy-from-delta command consists only >+ # of the number of bytes to copy from the delta. >+ >+ # We don't use these values, but we need to know how big they are so that >+ # we can skip to the diff data. >+ my ($size, $bytesUsed) = decodeGitBinaryPatchDeltaSize($binaryChunk); >+ $binaryChunk = substr($binaryChunk, $bytesUsed); >+ ($size, $bytesUsed) = decodeGitBinaryPatchDeltaSize($binaryChunk); >+ $binaryChunk = substr($binaryChunk, $bytesUsed); >+ >+ my $out = ""; >+ for (my $i = 0; $i < length($binaryChunk); ) { >+ my $cmd = ord(substr($binaryChunk, $i++, 1)); >+ if ($cmd & 0x80) { >+ # Extract an offset and size from the delta data, then copy >+ # $size bytes from $offset in the original data into the output. >+ my $offset = 0; >+ my $size = 0; >+ if ($cmd & 0x01) { $offset = readByte($binaryChunk, $i++); } >+ if ($cmd & 0x02) { $offset |= readByte($binaryChunk, $i++) << 8; } >+ if ($cmd & 0x04) { $offset |= readByte($binaryChunk, $i++) << 16; } >+ if ($cmd & 0x08) { $offset |= readByte($binaryChunk, $i++) << 24; } >+ if ($cmd & 0x10) { $size = readByte($binaryChunk, $i++); } >+ if ($cmd & 0x20) { $size |= readByte($binaryChunk, $i++) << 8; } >+ if ($cmd & 0x40) { $size |= readByte($binaryChunk, $i++) << 16; } >+ if ($size == 0) { $size = 0x10000; } >+ $out .= substr($originalContents, $offset, $size); >+ } elsif ($cmd) { >+ # Copy $cmd bytes from the delta data into the output. >+ $out .= substr($binaryChunk, $i, $cmd); >+ $i += $cmd; >+ } else { >+ die "unexpected delta opcode 0"; >+ } >+ } >+ >+ return $out; >+} >+ >+sub escapeSubversionPath($) >+{ >+ my ($path) = @_; >+ $path .= "@" if $path =~ /@/; >+ return $path; >+} >+ >+sub runCommand(@) >+{ >+ my @args = @_; >+ my $pid = open(CHILD, "-|"); >+ if (!defined($pid)) { >+ die "Failed to fork(): $!"; >+ } >+ if ($pid) { >+ # Parent process >+ my $childStdout; >+ while (<CHILD>) { >+ $childStdout .= $_; >+ } >+ close(CHILD); >+ my %childOutput; >+ $childOutput{exitStatus} = exitStatus($?); >+ $childOutput{stdout} = $childStdout if $childStdout; >+ return \%childOutput; >+ } >+ # Child process >+ # FIXME: Consider further hardening of this function, including sanitizing the environment. >+ exec { $args[0] } @args or die "Failed to exec(): $!"; >+} >+ >+sub gitCommitForSVNRevision >+{ >+ my ($svnRevision) = @_; >+ my $command = "git svn find-rev r" . $svnRevision; >+ $command = "LC_ALL=C $command" if !isWindows(); >+ my $gitHash = `$command`; >+ if (!defined($gitHash)) { >+ $gitHash = "unknown"; >+ warn "Unable to determine GIT commit from SVN revision"; >+ } else { >+ chop($gitHash); >+ } >+ return $gitHash; >+} >+ >+sub listOfChangedFilesBetweenRevisions >+{ >+ my ($sourceDir, $firstRevision, $lastRevision) = @_; >+ my $command; >+ >+ if ($firstRevision eq "unknown" or $lastRevision eq "unknown") { >+ return (); >+ } >+ >+ # Some VCS functions don't work from within the build dir, so always >+ # go to the source dir first. >+ my $cwd = Cwd::getcwd(); >+ chdir $sourceDir; >+ >+ if (isGit()) { >+ my $firstCommit = gitCommitForSVNRevision($firstRevision); >+ my $lastCommit = gitCommitForSVNRevision($lastRevision); >+ $command = "git diff --name-status $firstCommit..$lastCommit"; >+ } elsif (isSVN()) { >+ $command = "svn diff --summarize -r $firstRevision:$lastRevision"; >+ } >+ >+ my @result = (); >+ >+ if ($command) { >+ my $diffOutput = `$command`; >+ $diffOutput =~ s/^[A-Z]\s+//gm; >+ @result = split(/[\r\n]+/, $diffOutput); >+ } >+ >+ chdir $cwd; >+ >+ return @result; >+} >+ >+ >+1; >diff --git a/Tools/Scripts/test262/webkitdirs_5008008.pm b/Tools/Scripts/test262/webkitdirs_5008008.pm >new file mode 100644 >index 0000000000000000000000000000000000000000..13daa30ea9c522e9ad969d878e950fa90f7eb022 >--- /dev/null >+++ b/Tools/Scripts/test262/webkitdirs_5008008.pm >@@ -0,0 +1,2868 @@ >+# Copyright (C) 2005-2007, 2010-2016 Apple Inc. All rights reserved. >+# Copyright (C) 2009 Google Inc. All rights reserved. >+# Copyright (C) 2011 Research In Motion Limited. All rights reserved. >+# Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies). >+# >+# 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. >+ >+# Module to share code to get to WebKit directories. >+ >+# THIS MODULE IS A COPY OF: webkitdirs.pm >+# COPIED AT GIT COMMIT: ddff164f466329a9d6cee64de802291e307f6ced >+# MODIFIED FOR COMPATIBILITY WITH: Perl v5.8.8 >+use 5.8.8; >+ >+use strict; >+use warnings; >+use Config; >+use Cwd qw(realpath); >+use Digest::MD5 qw(md5_hex); >+use FindBin; >+use File::Basename; >+use File::Find; >+use File::Path qw(mkpath rmtree); >+use File::Spec; >+use File::Temp qw(tempdir); >+use File::stat; >+use List::Util; >+use POSIX; >+use Time::HiRes qw(usleep); >+use VCSUtils_5008008; >+ >+BEGIN { >+ use Exporter (); >+ our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); >+ $VERSION = 1.00; >+ @ISA = qw(Exporter); >+ @EXPORT = qw( >+ &XcodeCoverageSupportOptions >+ &XcodeOptionString >+ &XcodeOptionStringNoConfig >+ &XcodeOptions >+ &XcodeStaticAnalyzerOption >+ &appDisplayNameFromBundle >+ &appendToEnvironmentVariableList >+ &archCommandLineArgumentsForRestrictedEnvironmentVariables >+ &baseProductDir >+ &chdirWebKit >+ &checkFrameworks >+ &cmakeBasedPortArguments >+ ¤tSVNRevision >+ &debugSafari >+ &executableProductDir >+ &extractNonHostConfiguration >+ &findOrCreateSimulatorForIOSDevice >+ &iosSimulatorDeviceByName >+ &iosVersion >+ &nmPath >+ &passedConfiguration >+ &prependToEnvironmentVariableList >+ &printHelpAndExitForRunAndDebugWebKitAppIfNeeded >+ &productDir >+ &quitIOSSimulator >+ &relaunchIOSSimulator >+ &restartIOSSimulatorDevice >+ &runIOSWebKitApp >+ &runMacWebKitApp >+ &safariPath >+ &sdkDirectory >+ &sdkPlatformDirectory >+ &setConfiguration >+ &setupMacWebKitEnvironment >+ &sharedCommandLineOptions >+ &sharedCommandLineOptionsUsage >+ &shutDownIOSSimulatorDevice >+ &willUseIOSDeviceSDK >+ &willUseIOSSimulatorSDK >+ DO_NOT_USE_OPEN_COMMAND >+ SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT >+ USE_OPEN_COMMAND >+ ); >+ %EXPORT_TAGS = ( ); >+ @EXPORT_OK = (); >+} >+ >+# Ports >+use constant { >+ AppleWin => "AppleWin", >+ GTK => "GTK", >+ iOS => "iOS", >+ tvOS => "tvOS", >+ watchOS => "watchOS", >+ Mac => "Mac", >+ JSCOnly => "JSCOnly", >+ WinCairo => "WinCairo", >+ WPE => "WPE", >+ Unknown => "Unknown" >+}; >+ >+use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp(). >+use constant DO_NOT_USE_OPEN_COMMAND => 2; >+use constant SIMULATOR_DEVICE_STATE_SHUTDOWN => "1"; >+use constant SIMULATOR_DEVICE_STATE_BOOTED => "3"; >+use constant SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT => "For WebKit Development"; >+ >+# See table "Certificate types and names" on <https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html#//apple_ref/doc/uid/TP40012582-CH31-SW41>. >+use constant IOS_DEVELOPMENT_CERTIFICATE_NAME_PREFIX => "iPhone Developer: "; >+ >+our @EXPORT_OK; >+ >+my $architecture; >+my $asanIsEnabled; >+my $numberOfCPUs; >+my $maxCPULoad; >+my $baseProductDir; >+my @baseProductDirOption; >+my $configuration; >+my $xcodeSDK; >+my $configurationForVisualStudio; >+my $configurationProductDir; >+my $sourceDir; >+my $currentSVNRevision; >+my $didLoadIPhoneSimulatorNotification; >+my $nmPath; >+my $osXVersion; >+my $iosVersion; >+my $generateDsym; >+my $isCMakeBuild; >+my $isGenerateProjectOnly; >+my $shouldBuild32Bit; >+my $isWin64; >+my $isInspectorFrontend; >+my $portName; >+my $shouldUseGuardMalloc; >+my $shouldNotUseNinja; >+my $xcodeVersion; >+ >+my $unknownPortProhibited = 0; >+ >+# Variables for Win32 support >+my $programFilesPath; >+my $vcBuildPath; >+my $vsInstallDir; >+my $msBuildInstallDir; >+my $vsVersion; >+my $windowsSourceDir; >+my $winVersion; >+my $willUseVCExpressWhenBuilding = 0; >+my $vsWhereFoundInstallation; >+ >+# Defined in VCSUtils. >+sub exitStatus($); >+ >+sub findMatchingArguments($$); >+sub hasArgument($$); >+ >+sub sdkDirectory($) >+{ >+ my ($sdkName) = @_; >+ chomp(my $sdkDirectory = `xcrun --sdk '$sdkName' --show-sdk-path`); >+ die "Failed to get SDK path from xcrun: $!" if exitStatus($?); >+ return $sdkDirectory; >+} >+ >+sub sdkPlatformDirectory($) >+{ >+ my ($sdkName) = @_; >+ chomp(my $sdkPlatformDirectory = `xcrun --sdk '$sdkName' --show-sdk-platform-path`); >+ die "Failed to get SDK platform path from xcrun: $!" if exitStatus($?); >+ return $sdkPlatformDirectory; >+} >+ >+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 Sources, WebCore, and JavaScriptCore. >+ until ((-d File::Spec->catdir($sourceDir, "Source") && -d File::Spec->catdir($sourceDir, "Source", "WebCore") && -d File::Spec->catdir($sourceDir, "Source", "JavaScriptCore")) || (-d File::Spec->catdir($sourceDir, "Internal") && -d File::Spec->catdir($sourceDir, "OpenSource"))) >+ { >+ if ($sourceDir !~ s|/[^/]+$||) { >+ die "Could not find top level webkit directory above source directory using FindBin.\n"; >+ } >+ } >+ >+ $sourceDir = File::Spec->catdir($sourceDir, "OpenSource") if -d File::Spec->catdir($sourceDir, "OpenSource"); >+} >+ >+sub currentPerlPath() >+{ >+ my $thisPerl = $^X; >+ if ($^O ne 'VMS') { >+ $thisPerl .= $Config{_exe} unless $thisPerl =~ m/$Config{_exe}$/i; >+ } >+ return $thisPerl; >+} >+ >+# used for scripts which are stored in a non-standard location >+sub setSourceDir($) >+{ >+ ($sourceDir) = @_; >+} >+ >+sub determineNinjaVersion >+{ >+ chomp(my $ninjaVersion = `ninja --version`); >+ return $ninjaVersion; >+} >+ >+sub determineXcodeVersion >+{ >+ return if defined $xcodeVersion; >+ my $xcodebuildVersionOutput = `xcodebuild -version`; >+ $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : "3.0"; >+} >+ >+sub readXcodeUserDefault($) >+{ >+ my ($key) = @_; >+ >+ my $devnull = File::Spec->devnull(); >+ >+ my $value = `defaults read com.apple.dt.Xcode ${key} 2> ${devnull}`; >+ return if $?; >+ >+ chomp $value; >+ return $value; >+} >+ >+sub determineBaseProductDir >+{ >+ return if defined $baseProductDir; >+ determineSourceDir(); >+ >+ my $setSharedPrecompsDir; >+ my $indexDataStoreDir; >+ $baseProductDir = $ENV{"WEBKIT_OUTPUTDIR"}; >+ >+ if (!defined($baseProductDir) and isAppleCocoaWebKit()) { >+ # Silently remove ~/Library/Preferences/xcodebuild.plist which can >+ # cause build failure. The presence of >+ # ~/Library/Preferences/xcodebuild.plist can prevent xcodebuild from >+ # respecting global settings such as a custom build products directory >+ # (<rdar://problem/5585899>). >+ my $personalPlistFile = $ENV{HOME} . "/Library/Preferences/xcodebuild.plist"; >+ if (-e $personalPlistFile) { >+ unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!"; >+ } >+ >+ my $buildLocationStyle = join '', readXcodeUserDefault("IDEBuildLocationStyle"); >+ if ($buildLocationStyle eq "Custom") { >+ my $buildLocationType = join '', readXcodeUserDefault("IDECustomBuildLocationType"); >+ # FIXME: Read CustomBuildIntermediatesPath and set OBJROOT accordingly. >+ if ($buildLocationType eq "Absolute") { >+ $baseProductDir = readXcodeUserDefault("IDECustomBuildProductsPath"); >+ $indexDataStoreDir = readXcodeUserDefault("IDECustomIndexStorePath"); >+ } >+ } >+ >+ # DeterminedByTargets corresponds to a setting of "Legacy" in Xcode. >+ # It is the only build location style for which SHARED_PRECOMPS_DIR is not >+ # overridden when building from within Xcode. >+ $setSharedPrecompsDir = 1 if $buildLocationStyle ne "DeterminedByTargets"; >+ >+ if (!defined($baseProductDir)) { >+ $baseProductDir = join '', readXcodeUserDefault("IDEApplicationwideBuildSettings"); >+ $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s; >+ } >+ >+ undef $baseProductDir unless $baseProductDir =~ /^\//; >+ } >+ >+ if (!defined($baseProductDir)) { # Port-specific checks failed, use default >+ $baseProductDir = File::Spec->catdir($sourceDir, "WebKitBuild"); >+ } >+ >+ if (isGit() && isGitBranchBuild()) { >+ my $branch = gitBranch(); >+ $baseProductDir = "$baseProductDir/$branch"; >+ } >+ >+ if (isAppleCocoaWebKit()) { >+ $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 = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir"); >+ push(@baseProductDirOption, "SHARED_PRECOMPS_DIR=${baseProductDir}/PrecompiledHeaders") if $setSharedPrecompsDir; >+ push(@baseProductDirOption, "INDEX_ENABLE_DATA_STORE=YES", "INDEX_DATA_STORE_DIR=${indexDataStoreDir}") if $indexDataStoreDir; >+ } >+ >+ if (isCygwin()) { >+ my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`; >+ chomp $dosBuildPath; >+ $ENV{"WEBKIT_OUTPUTDIR"} = $dosBuildPath; >+ my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`; >+ chomp $unixBuildPath; >+ $baseProductDir = $dosBuildPath; >+ } >+} >+ >+sub systemVerbose { >+ print "+ @_\n"; >+ return system(@_); >+} >+ >+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 determineArchitecture >+{ >+ return if defined $architecture; >+ # make sure $architecture is defined in all cases >+ $architecture = ""; >+ >+ determineBaseProductDir(); >+ determineXcodeSDK(); >+ >+ if (isAppleCocoaWebKit()) { >+ if (open ARCHITECTURE, "$baseProductDir/Architecture") { >+ $architecture = <ARCHITECTURE>; >+ close ARCHITECTURE; >+ } >+ if ($architecture) { >+ chomp $architecture; >+ } else { >+ if (not defined $xcodeSDK or $xcodeSDK =~ /^(\/$|macosx)/) { >+ my $supports64Bit = `sysctl -n hw.optional.x86_64`; >+ chomp $supports64Bit; >+ $architecture = 'x86_64' if $supports64Bit; >+ } elsif ($xcodeSDK =~ /^iphonesimulator/) { >+ $architecture = 'x86_64'; >+ } elsif ($xcodeSDK =~ /^iphoneos/) { >+ $architecture = 'arm64'; >+ } >+ } >+ } elsif (isCMakeBuild()) { >+ if (isCrossCompilation()) { >+ my $compiler = "gcc"; >+ $compiler = $ENV{'CC'} if (defined($ENV{'CC'})); >+ my @compiler_machine = split('-', `$compiler -dumpmachine`); >+ $architecture = $compiler_machine[0]; >+ } elsif (open my $cmake_sysinfo, "cmake --system-information |") { >+ while (<$cmake_sysinfo>) { >+ next unless index($_, 'CMAKE_SYSTEM_PROCESSOR') == 0; >+ if (/^CMAKE_SYSTEM_PROCESSOR \"([^"]+)\"/) { >+ $architecture = $1; >+ last; >+ } >+ } >+ close $cmake_sysinfo; >+ } >+ } >+ >+ if (!isAnyWindows()) { >+ if (!$architecture) { >+ # Fall back to output of `uname -m', if it is present. >+ $architecture = `uname -m`; >+ chomp $architecture; >+ } >+ } >+ >+ $architecture = 'x86_64' if $architecture =~ /amd64/i; >+ $architecture = 'arm64' if $architecture =~ /aarch64/i; >+} >+ >+sub determineASanIsEnabled >+{ >+ return if defined $asanIsEnabled; >+ determineBaseProductDir(); >+ >+ $asanIsEnabled = 0; >+ my $asanConfigurationValue; >+ >+ if (open ASAN, "$baseProductDir/ASan") { >+ $asanConfigurationValue = <ASAN>; >+ close ASAN; >+ chomp $asanConfigurationValue; >+ $asanIsEnabled = 1 if $asanConfigurationValue eq "YES"; >+ } >+} >+ >+sub determineNumberOfCPUs >+{ >+ return if defined $numberOfCPUs; >+ if (defined($ENV{NUMBER_OF_PROCESSORS})) { >+ $numberOfCPUs = $ENV{NUMBER_OF_PROCESSORS}; >+ } elsif (isLinux()) { >+ # First try the nproc utility, if it exists. If we get no >+ # results fall back to just interpretting /proc directly. >+ chomp($numberOfCPUs = `nproc --all 2> /dev/null`); >+ if ($numberOfCPUs eq "") { >+ $numberOfCPUs = (grep /processor/, `cat /proc/cpuinfo`); >+ } >+ } elsif (isAnyWindows()) { >+ # Assumes cygwin >+ $numberOfCPUs = `ls /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor | wc -w`; >+ } elsif (isDarwin() || isBSD()) { >+ chomp($numberOfCPUs = `sysctl -n hw.ncpu`); >+ } else { >+ $numberOfCPUs = 1; >+ } >+} >+ >+sub determineMaxCPULoad >+{ >+ return if defined $maxCPULoad; >+ if (defined($ENV{MAX_CPU_LOAD})) { >+ $maxCPULoad = $ENV{MAX_CPU_LOAD}; >+ } >+} >+ >+sub jscPath($) >+{ >+ my ($productDir) = @_; >+ my $jscName = "jsc"; >+ $jscName .= "_debug" if configuration() eq "Debug_All"; >+ $jscName .= ".exe" if (isAnyWindows()); >+ return "$productDir/$jscName" if -e "$productDir/$jscName"; >+ return "$productDir/JavaScriptCore.framework/Resources/$jscName"; >+} >+ >+sub argumentsForConfiguration() >+{ >+ determineConfiguration(); >+ determineArchitecture(); >+ determineXcodeSDK(); >+ >+ my @args = (); >+ # FIXME: Is it necessary to pass --debug, --release, --32-bit or --64-bit? >+ # These are determined automatically from stored configuration. >+ push(@args, '--debug') if ($configuration =~ "^Debug"); >+ push(@args, '--release') if ($configuration =~ "^Release"); >+ push(@args, '--ios-device') if (defined $xcodeSDK && $xcodeSDK =~ /^iphoneos/); >+ push(@args, '--ios-simulator') if (defined $xcodeSDK && $xcodeSDK =~ /^iphonesimulator/); >+ push(@args, '--32-bit') if ($architecture ne "x86_64" and !isWin64()); >+ push(@args, '--64-bit') if (isWin64()); >+ push(@args, '--gtk') if isGtk(); >+ push(@args, '--wpe') if isWPE(); >+ push(@args, '--jsc-only') if isJSCOnly(); >+ push(@args, '--wincairo') if isWinCairo(); >+ push(@args, '--inspector-frontend') if isInspectorFrontend(); >+ return @args; >+} >+ >+sub extractNonMacOSHostConfiguration >+{ >+ my @args = (); >+ my @extract = ('--device', '--gtk', '--ios', '--platform', '--sdk', '--simulator', '--wincairo', 'SDKROOT', 'ARCHS'); >+ foreach (@{$_[0]}) { >+ my $line = $_; >+ my $flag = 0; >+ foreach (@extract) { >+ if (length($line) >= length($_) && substr($line, 0, length($_)) eq $_ >+ && index($line, 'i386') == -1 && index($line, 'x86_64') == -1) { >+ $flag = 1; >+ } >+ } >+ if (!$flag) { >+ push @args, $_; >+ } >+ } >+ return @args; >+} >+ >+sub determineXcodeSDK >+{ >+ return if defined $xcodeSDK; >+ my $sdk; >+ if (checkForArgumentAndRemoveFromARGVGettingValue("--sdk", \$sdk)) { >+ $xcodeSDK = $sdk; >+ } >+ if (checkForArgumentAndRemoveFromARGV("--device") || checkForArgumentAndRemoveFromARGV("--ios-device")) { >+ my $hasInternalSDK = exitStatus(system("xcrun --sdk iphoneos.internal --show-sdk-version > /dev/null 2>&1")) == 0; >+ $xcodeSDK ||= $hasInternalSDK ? "iphoneos.internal" : "iphoneos"; >+ } >+ if (checkForArgumentAndRemoveFromARGV("--simulator") || checkForArgumentAndRemoveFromARGV("--ios-simulator")) { >+ $xcodeSDK ||= 'iphonesimulator'; >+ } >+ if (checkForArgumentAndRemoveFromARGV("--tvos-device")) { >+ my $hasInternalSDK = exitStatus(system("xcrun --sdk appletvos.internal --show-sdk-version > /dev/null 2>&1")) == 0; >+ $xcodeSDK ||= $hasInternalSDK ? "appletvos.internal" : "appletvos"; >+ } >+ if (checkForArgumentAndRemoveFromARGV("--tvos-simulator")) { >+ $xcodeSDK ||= "appletvsimulator"; >+ } >+ if (checkForArgumentAndRemoveFromARGV("--watchos-device")) { >+ my $hasInternalSDK = exitStatus(system("xcrun --sdk watchos.internal --show-sdk-version > /dev/null 2>&1")) == 0; >+ $xcodeSDK ||= $hasInternalSDK ? "watchos.internal" : "watchos"; >+ } >+ if (checkForArgumentAndRemoveFromARGV("--watchos-simulator")) { >+ $xcodeSDK ||= "watchsimulator"; >+ } >+} >+ >+sub xcodeSDK >+{ >+ determineXcodeSDK(); >+ return $xcodeSDK; >+} >+ >+sub setXcodeSDK($) >+{ >+ ($xcodeSDK) = @_; >+} >+ >+ >+sub xcodeSDKPlatformName() >+{ >+ determineXcodeSDK(); >+ return "" if !defined $xcodeSDK; >+ return "appletvos" if $xcodeSDK =~ /appletvos/i; >+ return "appletvsimulator" if $xcodeSDK =~ /appletvsimulator/i; >+ return "iphoneos" if $xcodeSDK =~ /iphoneos/i; >+ return "iphonesimulator" if $xcodeSDK =~ /iphonesimulator/i; >+ return "macosx" if $xcodeSDK =~ /macosx/i; >+ return "watchos" if $xcodeSDK =~ /watchos/i; >+ return "watchsimulator" if $xcodeSDK =~ /watchsimulator/i; >+ die "Couldn't determine platform name from Xcode SDK"; >+} >+ >+sub XcodeSDKPath >+{ >+ determineXcodeSDK(); >+ >+ die "Can't find the SDK path because no Xcode SDK was specified" if not $xcodeSDK; >+ return sdkDirectory($xcodeSDK); >+} >+ >+sub xcodeSDKVersion >+{ >+ determineXcodeSDK(); >+ >+ die "Can't find the SDK version because no Xcode SDK was specified" if !$xcodeSDK; >+ >+ chomp(my $sdkVersion = `xcrun --sdk $xcodeSDK --show-sdk-version`); >+ die "Failed to get SDK version from xcrun" if exitStatus($?); >+ >+ return $sdkVersion; >+} >+ >+sub programFilesPath >+{ >+ return $programFilesPath if defined $programFilesPath; >+ >+ $programFilesPath = $ENV{'PROGRAMFILES(X86)'} || $ENV{'PROGRAMFILES'} || "C:\\Program Files"; >+ >+ return $programFilesPath; >+} >+ >+sub programFilesPathX86 >+{ >+ my $programFilesPathX86 = $ENV{'PROGRAMFILES(X86)'} || "C:\\Program Files (x86)"; >+ >+ return $programFilesPathX86; >+} >+ >+sub requireModulesForVSWhere >+{ >+ require Encode; >+ require Encode::Locale; >+ require JSON::PP; >+} >+ >+sub pickCurrentVisualStudioInstallation >+{ >+ return $vsWhereFoundInstallation if defined $vsWhereFoundInstallation; >+ >+ requireModulesForVSWhere(); >+ determineSourceDir(); >+ >+ # Prefer Enterprise, then Professional, then Community, then >+ # anything else that provides MSBuild. >+ foreach my $productType (( >+ 'Microsoft.VisualStudio.Product.Enterprise', >+ 'Microsoft.VisualStudio.Product.Professional', >+ 'Microsoft.VisualStudio.Product.Community', >+ undef >+ )) { >+ my $command = "$sourceDir/WebKitLibraries/win/tools/vswhere -nologo -latest -format json -requires Microsoft.Component.MSBuild"; >+ if (defined $productType) { >+ $command .= " -products $productType"; >+ } >+ my $vsWhereOut = `$command`; >+ my $installations = []; >+ eval { >+ $installations = JSON::PP::decode_json(Encode::encode('UTF-8' => Encode::decode(console_in => $vsWhereOut))); >+ }; >+ print "Error getting Visual Studio Location: $@\n" if $@; >+ undef $@; >+ >+ if (scalar @$installations) { >+ my $installation = $installations->[0]; >+ $vsWhereFoundInstallation = $installation; >+ return $installation; >+ } >+ } >+ return undef; >+} >+ >+sub visualStudioInstallDir >+{ >+ return $vsInstallDir if defined $vsInstallDir; >+ >+ if ($ENV{'VSINSTALLDIR'}) { >+ $vsInstallDir = $ENV{'VSINSTALLDIR'}; >+ $vsInstallDir =~ s|[\\/]$||; >+ } else { >+ $vsInstallDir = visualStudioInstallDirVSWhere(); >+ if (not -e $vsInstallDir) { >+ $vsInstallDir = visualStudioInstallDirFallback(); >+ } >+ } >+ chomp($vsInstallDir = `cygpath "$vsInstallDir"`) if isCygwin(); >+ >+ print "Using Visual Studio: $vsInstallDir\n"; >+ return $vsInstallDir; >+} >+ >+sub visualStudioInstallDirVSWhere >+{ >+ pickCurrentVisualStudioInstallation(); >+ if (defined($vsWhereFoundInstallation)) { >+ return $vsWhereFoundInstallation->{installationPath}; >+ } >+ return undef; >+} >+ >+sub visualStudioInstallDirFallback >+{ >+ foreach my $productType (( >+ 'Enterprise', >+ 'Professional', >+ 'Community', >+ )) { >+ my $installdir = File::Spec->catdir(programFilesPathX86(), >+ "Microsoft Visual Studio", "2017", $productType); >+ my $msbuilddir = File::Spec->catdir($installdir, >+ "MSBuild", "15.0", "bin"); >+ if (-e $installdir && -e $msbuilddir) { >+ return $installdir; >+ } >+ } >+ return undef; >+} >+ >+sub msBuildInstallDir >+{ >+ return $msBuildInstallDir if defined $msBuildInstallDir; >+ >+ my $installDir = visualStudioInstallDir(); >+ $msBuildInstallDir = File::Spec->catdir($installDir, >+ "MSBuild", "15.0", "bin"); >+ >+ chomp($msBuildInstallDir = `cygpath "$msBuildInstallDir"`) if isCygwin(); >+ >+ print "Using MSBuild: $msBuildInstallDir\n"; >+ return $msBuildInstallDir; >+} >+ >+sub determineConfigurationForVisualStudio >+{ >+ return if defined $configurationForVisualStudio; >+ determineConfiguration(); >+ # FIXME: We should detect when Debug_All or Production has been chosen. >+ $configurationForVisualStudio = "/p:Configuration=" . $configuration; >+} >+ >+sub usesPerConfigurationBuildDirectory >+{ >+ # [Gtk] We don't have Release/Debug configurations in straight >+ # autotool builds (non build-webkit). In this case and if >+ # WEBKIT_OUTPUTDIR exist, use that as our configuration dir. This will >+ # allows us to run run-webkit-tests without using build-webkit. >+ return ($ENV{"WEBKIT_OUTPUTDIR"} && isGtk()) || isAppleWinWebKit(); >+} >+ >+sub determineConfigurationProductDir >+{ >+ return if defined $configurationProductDir; >+ determineBaseProductDir(); >+ determineConfiguration(); >+ if (isAppleWinWebKit() || isWinCairo()) { >+ $configurationProductDir = File::Spec->catdir($baseProductDir, $configuration); >+ } else { >+ if (usesPerConfigurationBuildDirectory()) { >+ $configurationProductDir = "$baseProductDir"; >+ } else { >+ $configurationProductDir = "$baseProductDir/$configuration"; >+ $configurationProductDir .= "-" . xcodeSDKPlatformName() if isEmbeddedWebKit(); >+ } >+ } >+} >+ >+sub setConfigurationProductDir($) >+{ >+ ($configurationProductDir) = @_; >+} >+ >+sub determineCurrentSVNRevision >+{ >+ # We always update the current SVN revision here, and leave the caching >+ # to currentSVNRevision(), so that changes to the SVN revision while the >+ # script is running can be picked up by calling this function again. >+ determineSourceDir(); >+ $currentSVNRevision = svnRevisionForDirectory($sourceDir); >+ 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 executableProductDir >+{ >+ my $productDirectory = productDir(); >+ >+ my $binaryDirectory; >+ if (isGtk() || isJSCOnly() || isWPE()) { >+ $binaryDirectory = "bin"; >+ } elsif (isAnyWindows()) { >+ $binaryDirectory = isWin64() ? "bin64" : "bin32"; >+ } else { >+ return $productDirectory; >+ } >+ >+ return File::Spec->catdir($productDirectory, $binaryDirectory); >+} >+ >+sub jscProductDir >+{ >+ return executableProductDir(); >+} >+ >+sub configuration() >+{ >+ determineConfiguration(); >+ return $configuration; >+} >+ >+sub asanIsEnabled() >+{ >+ determineASanIsEnabled(); >+ return $asanIsEnabled; >+} >+ >+sub configurationForVisualStudio() >+{ >+ determineConfigurationForVisualStudio(); >+ return $configurationForVisualStudio; >+} >+ >+sub currentSVNRevision >+{ >+ determineCurrentSVNRevision() if not defined $currentSVNRevision; >+ return $currentSVNRevision; >+} >+ >+sub generateDsym() >+{ >+ determineGenerateDsym(); >+ return $generateDsym; >+} >+ >+sub determineGenerateDsym() >+{ >+ return if defined($generateDsym); >+ $generateDsym = checkForArgumentAndRemoveFromARGV("--dsym"); >+} >+ >+sub hasIOSDevelopmentCertificate() >+{ >+ return !exitStatus(system("security find-identity -p codesigning | grep '" . IOS_DEVELOPMENT_CERTIFICATE_NAME_PREFIX . "' > /dev/null 2>&1")); >+} >+ >+sub argumentsForXcode() >+{ >+ my @args = (); >+ push @args, "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym" if generateDsym(); >+ return @args; >+} >+ >+sub XcodeOptions >+{ >+ determineBaseProductDir(); >+ determineConfiguration(); >+ determineArchitecture(); >+ determineASanIsEnabled(); >+ determineXcodeSDK(); >+ >+ my @options; >+ push @options, "-UseSanitizedBuildSystemEnvironment=YES"; >+ push @options, ("-configuration", $configuration); >+ push @options, ("-xcconfig", sourceDir() . "/Tools/asan/asan.xcconfig", "ASAN_IGNORE=" . sourceDir() . "/Tools/asan/webkit-asan-ignore.txt") if $asanIsEnabled; >+ push @options, @baseProductDirOption; >+ push @options, "ARCHS=$architecture" if $architecture; >+ push @options, "SDKROOT=$xcodeSDK" if $xcodeSDK; >+ if (willUseIOSDeviceSDK()) { >+ push @options, "ENABLE_BITCODE=NO"; >+ if (hasIOSDevelopmentCertificate()) { >+ # FIXME: May match more than one installed development certificate. >+ push @options, "CODE_SIGN_IDENTITY=" . IOS_DEVELOPMENT_CERTIFICATE_NAME_PREFIX; >+ } else { >+ push @options, "CODE_SIGN_IDENTITY="; # No identity >+ push @options, "CODE_SIGNING_REQUIRED=NO"; >+ } >+ } >+ push @options, argumentsForXcode(); >+ return @options; >+} >+ >+sub XcodeOptionString >+{ >+ return join " ", XcodeOptions(); >+} >+ >+sub XcodeOptionStringNoConfig >+{ >+ return join " ", @baseProductDirOption; >+} >+ >+sub XcodeCoverageSupportOptions() >+{ >+ my @coverageSupportOptions = (); >+ push @coverageSupportOptions, "GCC_GENERATE_TEST_COVERAGE_FILES=YES"; >+ push @coverageSupportOptions, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES"; >+ return @coverageSupportOptions; >+} >+ >+sub XcodeStaticAnalyzerOption() >+{ >+ return "RUN_CLANG_STATIC_ANALYZER=YES"; >+} >+ >+my $passedConfiguration; >+my $searchedForPassedConfiguration; >+sub determinePassedConfiguration >+{ >+ return if $searchedForPassedConfiguration; >+ $searchedForPassedConfiguration = 1; >+ $passedConfiguration = undef; >+ >+ if (checkForArgumentAndRemoveFromARGV("--debug")) { >+ $passedConfiguration = "Debug"; >+ } elsif(checkForArgumentAndRemoveFromARGV("--release")) { >+ $passedConfiguration = "Release"; >+ } elsif (checkForArgumentAndRemoveFromARGV("--profile") || checkForArgumentAndRemoveFromARGV("--profiling")) { >+ $passedConfiguration = "Profiling"; >+ } >+} >+ >+sub passedConfiguration >+{ >+ determinePassedConfiguration(); >+ return $passedConfiguration; >+} >+ >+sub setConfiguration >+{ >+ setArchitecture(); >+ >+ if (my $config = shift @_) { >+ $configuration = $config; >+ return; >+ } >+ >+ determinePassedConfiguration(); >+ $configuration = $passedConfiguration if $passedConfiguration; >+} >+ >+ >+my $passedArchitecture; >+my $searchedForPassedArchitecture; >+sub determinePassedArchitecture >+{ >+ return if $searchedForPassedArchitecture; >+ $searchedForPassedArchitecture = 1; >+ >+ $passedArchitecture = undef; >+ if (shouldBuild32Bit()) { >+ if (isAppleCocoaWebKit()) { >+ # PLATFORM_IOS: Don't run `arch` command inside Simulator environment >+ local %ENV = %ENV; >+ delete $ENV{DYLD_ROOT_PATH}; >+ delete $ENV{DYLD_FRAMEWORK_PATH}; >+ >+ $passedArchitecture = `arch`; >+ chomp $passedArchitecture; >+ } >+ } >+} >+ >+sub passedArchitecture >+{ >+ determinePassedArchitecture(); >+ return $passedArchitecture; >+} >+ >+sub architecture() >+{ >+ determineArchitecture(); >+ return $architecture; >+} >+ >+sub numberOfCPUs() >+{ >+ determineNumberOfCPUs(); >+ return $numberOfCPUs; >+} >+ >+sub maxCPULoad() >+{ >+ determineMaxCPULoad(); >+ return $maxCPULoad; >+} >+ >+sub setArchitecture >+{ >+ if (my $arch = shift @_) { >+ $architecture = $arch; >+ return; >+ } >+ >+ determinePassedArchitecture(); >+ $architecture = $passedArchitecture if $passedArchitecture; >+} >+ >+# Locate Safari. >+sub safariPath >+{ >+ die "Safari path is only relevant on Apple Mac platform\n" unless isAppleCocoaWebKit(); >+ >+ my $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 (-d "$configurationProductDir/Safari.app") { >+ $safariBundle = "$configurationProductDir/Safari.app"; >+ } >+ } >+ >+ if ($safariBundle) { >+ $safariPath = "$safariBundle/Contents/MacOS/Safari"; >+ } else { >+ $safariPath = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"; >+ } >+ >+ die "Can't find executable at $safariPath.\n" if !-x $safariPath; >+ return $safariPath; >+} >+ >+sub builtDylibPathForName >+{ >+ my $libraryName = shift; >+ determineConfigurationProductDir(); >+ >+ if (isGtk()) { >+ my $extension = isDarwin() ? ".dylib" : ".so"; >+ return "$configurationProductDir/lib/libwebkit2gtk-4.0" . $extension; >+ } >+ if (isIOSWebKit()) { >+ return "$configurationProductDir/$libraryName.framework/$libraryName"; >+ } >+ if (isAppleCocoaWebKit()) { >+ return "$configurationProductDir/$libraryName.framework/Versions/A/$libraryName"; >+ } >+ if (isAppleWinWebKit()) { >+ if ($libraryName eq "JavaScriptCore") { >+ return "$baseProductDir/lib/$libraryName.lib"; >+ } else { >+ return "$baseProductDir/$libraryName.intermediate/$configuration/$libraryName.intermediate/$libraryName.lib"; >+ } >+ } >+ if (isWPE()) { >+ return "$configurationProductDir/lib/libWPEWebKit-0.1.so"; >+ } >+ >+ die "Unsupported platform, can't determine built library locations.\nTry `build-webkit --help` for more information.\n"; >+} >+ >+# Check to see that all the frameworks are built. >+sub checkFrameworks # FIXME: This is a poor name since only the Mac calls built WebCore a Framework. >+{ >+ return if isAnyWindows(); >+ my @frameworks = ("JavaScriptCore", "WebCore"); >+ push(@frameworks, "WebKit") if isAppleCocoaWebKit(); # FIXME: This seems wrong, all ports should have a WebKit these days. >+ for my $framework (@frameworks) { >+ my $path = builtDylibPathForName($framework); >+ die "Can't find built framework at \"$path\".\n" unless -e $path; >+ } >+} >+ >+sub isInspectorFrontend() >+{ >+ determineIsInspectorFrontend(); >+ return $isInspectorFrontend; >+} >+ >+sub determineIsInspectorFrontend() >+{ >+ return if defined($isInspectorFrontend); >+ $isInspectorFrontend = checkForArgumentAndRemoveFromARGV("--inspector-frontend"); >+} >+ >+sub commandExists($) >+{ >+ my $command = shift; >+ my $devnull = File::Spec->devnull(); >+ >+ if (isAnyWindows()) { >+ return exitStatus(system("where /q $command >$devnull 2>&1")) == 0; >+ } >+ return exitStatus(system("which $command >$devnull 2>&1")) == 0; >+} >+ >+sub checkForArgumentAndRemoveFromARGV($) >+{ >+ my $argToCheck = shift; >+ return checkForArgumentAndRemoveFromArrayRef($argToCheck, \@ARGV); >+} >+ >+sub checkForArgumentAndRemoveFromArrayRefGettingValue($$$) >+{ >+ my ($argToCheck, $valueRef, $arrayRef) = @_; >+ my $argumentStartRegEx = qr#^$argToCheck(?:=\S|$)#; >+ my $i = 0; >+ for (; $i < @$arrayRef; ++$i) { >+ last if $arrayRef->[$i] =~ $argumentStartRegEx; >+ } >+ if ($i >= @$arrayRef) { >+ return $$valueRef = undef; >+ } >+ my ($key, $value) = split("=", $arrayRef->[$i]); >+ splice(@$arrayRef, $i, 1); >+ if (defined($value)) { >+ # e.g. --sdk=iphonesimulator >+ return $$valueRef = $value; >+ } >+ return $$valueRef = splice(@$arrayRef, $i, 1); # e.g. --sdk iphonesimulator >+} >+ >+sub checkForArgumentAndRemoveFromARGVGettingValue($$) >+{ >+ my ($argToCheck, $valueRef) = @_; >+ return checkForArgumentAndRemoveFromArrayRefGettingValue($argToCheck, $valueRef, \@ARGV); >+} >+ >+sub findMatchingArguments($$) >+{ >+ my ($argToCheck, $arrayRef) = @_; >+ my @matchingIndices; >+ foreach my $index (0 .. $#$arrayRef) { >+ my $opt = $$arrayRef[$index]; >+ if ($opt =~ /^$argToCheck$/i ) { >+ push(@matchingIndices, $index); >+ } >+ } >+ return @matchingIndices; >+} >+ >+sub hasArgument($$) >+{ >+ my ($argToCheck, $arrayRef) = @_; >+ my @matchingIndices = findMatchingArguments($argToCheck, $arrayRef); >+ return scalar @matchingIndices > 0; >+} >+ >+sub checkForArgumentAndRemoveFromArrayRef >+{ >+ my ($argToCheck, $arrayRef) = @_; >+ my @indicesToRemove = findMatchingArguments($argToCheck, $arrayRef); >+ my $removeOffset = 0; >+ foreach my $index (@indicesToRemove) { >+ splice(@$arrayRef, $index - $removeOffset++, 1); >+ } >+ return scalar @indicesToRemove > 0; >+} >+ >+sub prohibitUnknownPort() >+{ >+ $unknownPortProhibited = 1; >+} >+ >+sub determinePortName() >+{ >+ return if defined $portName; >+ >+ my %argToPortName = ( >+ gtk => GTK, >+ 'jsc-only' => JSCOnly, >+ wincairo => WinCairo, >+ wpe => WPE >+ ); >+ >+ for my $arg (sort keys %argToPortName) { >+ if (checkForArgumentAndRemoveFromARGV("--$arg")) { >+ die "Argument '--$arg' conflicts with selected port '$portName'\n" >+ if defined $portName; >+ >+ $portName = $argToPortName{$arg}; >+ } >+ } >+ >+ return if defined $portName; >+ >+ # Port was not selected via command line, use appropriate default value >+ >+ if (isAnyWindows()) { >+ $portName = AppleWin; >+ } elsif (isDarwin()) { >+ determineXcodeSDK(); >+ if (willUseIOSDeviceSDK() || willUseIOSSimulatorSDK()) { >+ $portName = iOS; >+ } elsif (willUseAppleTVDeviceSDK() || willUseAppleTVSimulatorSDK()) { >+ $portName = tvOS; >+ } elsif (willUseWatchDeviceSDK() || willUseWatchSimulatorSDK()) { >+ $portName = watchOS; >+ } else { >+ $portName = Mac; >+ } >+ } else { >+ if ($unknownPortProhibited) { >+ my $portsChoice = join "\n\t", qw( >+ --gtk >+ --jsc-only >+ --wpe >+ ); >+ die "Please specify which WebKit port to build using one of the following options:" >+ . "\n\t$portsChoice\n"; >+ } >+ >+ # If script is run without arguments we cannot determine port >+ # TODO: This state should be outlawed >+ $portName = Unknown; >+ } >+} >+ >+sub portName() >+{ >+ determinePortName(); >+ return $portName; >+} >+ >+sub isGtk() >+{ >+ return portName() eq GTK; >+} >+ >+sub isJSCOnly() >+{ >+ return portName() eq JSCOnly; >+} >+ >+sub isWPE() >+{ >+ return portName() eq WPE; >+} >+ >+# Determine if this is debian, ubuntu, linspire, or something similar. >+sub isDebianBased() >+{ >+ return -e "/etc/debian_version"; >+} >+ >+sub isFedoraBased() >+{ >+ return -e "/etc/fedora-release"; >+} >+ >+sub isWinCairo() >+{ >+ return portName() eq WinCairo; >+} >+ >+sub shouldBuild32Bit() >+{ >+ determineShouldBuild32Bit(); >+ return $shouldBuild32Bit; >+} >+ >+sub determineShouldBuild32Bit() >+{ >+ return if defined($shouldBuild32Bit); >+ $shouldBuild32Bit = checkForArgumentAndRemoveFromARGV("--32-bit"); >+} >+ >+sub isWin64() >+{ >+ determineIsWin64(); >+ return $isWin64; >+} >+ >+sub determineIsWin64() >+{ >+ return if defined($isWin64); >+ $isWin64 = checkForArgumentAndRemoveFromARGV("--64-bit") || isWinCairo() && !shouldBuild32Bit(); >+} >+ >+sub determineIsWin64FromArchitecture($) >+{ >+ my $arch = shift; >+ $isWin64 = ($arch eq "x86_64"); >+ return $isWin64; >+} >+ >+sub isCygwin() >+{ >+ return ($^O eq "cygwin") || 0; >+} >+ >+sub isAnyWindows() >+{ >+ return isWindows() || isCygwin(); >+} >+ >+sub determineWinVersion() >+{ >+ return if $winVersion; >+ >+ if (!isAnyWindows()) { >+ $winVersion = -1; >+ return; >+ } >+ >+ my $versionString = `cmd /c ver`; >+ $versionString =~ /(\d)\.(\d)\.(\d+)/; >+ >+ $winVersion = { >+ major => $1, >+ minor => $2, >+ build => $3, >+ }; >+} >+ >+sub winVersion() >+{ >+ determineWinVersion(); >+ return $winVersion; >+} >+ >+sub isWindows7SP0() >+{ >+ return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 1 && winVersion()->{build} == 7600; >+} >+ >+sub isWindowsVista() >+{ >+ return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 0; >+} >+ >+sub isWindowsXP() >+{ >+ return isAnyWindows() && winVersion()->{major} == 5 && winVersion()->{minor} == 1; >+} >+ >+sub isDarwin() >+{ >+ return ($^O eq "darwin") || 0; >+} >+ >+sub isWindows() >+{ >+ return ($^O eq "MSWin32") || 0; >+} >+ >+sub isLinux() >+{ >+ return ($^O eq "linux") || 0; >+} >+ >+sub isBSD() >+{ >+ return ($^O eq "freebsd") || ($^O eq "openbsd") || ($^O eq "netbsd") || 0; >+} >+ >+sub isX86_64() >+{ >+ return (architecture() eq "x86_64") || 0; >+} >+ >+sub isARM64() >+{ >+ return (architecture() eq "arm64") || 0; >+} >+ >+sub isCrossCompilation() >+{ >+ my $compiler = ""; >+ $compiler = $ENV{'CC'} if (defined($ENV{'CC'})); >+ if ($compiler =~ /gcc/) { >+ my $compilerOptions = `$compiler -v 2>&1`; >+ my @host = $compilerOptions =~ m/--host=(.*?)\s/; >+ my @target = $compilerOptions =~ m/--target=(.*?)\s/; >+ if ($target[0] ne "" && $host[0] ne "") { >+ return ($host[0] ne $target[0]); >+ } else { >+ # $tempDir gets automatically deleted when goes out of scope >+ my $tempDir = File::Temp->newdir(); >+ my $testProgramSourcePath = File::Spec->catfile($tempDir, "testcross.c"); >+ my $testProgramBinaryPath = File::Spec->catfile($tempDir, "testcross"); >+ open(my $testProgramSourceHandler, ">", $testProgramSourcePath); >+ print $testProgramSourceHandler "int main() { return 0; }\n"; >+ system("$compiler $testProgramSourcePath -o $testProgramBinaryPath > /dev/null 2>&1") == 0 or return 0; >+ # Crosscompiling if the program fails to run (because it was built for other arch) >+ system("$testProgramBinaryPath > /dev/null 2>&1") == 0 or return 1; >+ return 0; >+ } >+ } >+ return 0; >+} >+ >+sub isAppleWebKit() >+{ >+ return isAppleCocoaWebKit() || isAppleWinWebKit(); >+} >+ >+sub isAppleCocoaWebKit() >+{ >+ return (portName() eq Mac) || isIOSWebKit() || isTVOSWebKit() || isWatchOSWebKit(); >+} >+ >+sub isAppleWinWebKit() >+{ >+ return portName() eq AppleWin; >+} >+ >+sub iOSSimulatorDevicesPath >+{ >+ return "$ENV{HOME}/Library/Developer/CoreSimulator/Devices"; >+} >+ >+sub iOSSimulatorDevices >+{ >+ eval "require Foundation"; >+ my $devicesPath = iOSSimulatorDevicesPath(); >+ opendir(DEVICES, $devicesPath); >+ my @udids = grep { >+ $_ =~ m/^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$/; >+ } readdir(DEVICES); >+ close(DEVICES); >+ >+ # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known >+ # dictionary keys so as to decouple our representation of the plist from the actual structure >+ # of the plist, which may change. >+ my @devices = map { >+ Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_("$devicesPath/$_/device.plist")); >+ } @udids; >+ >+ return @devices; >+} >+ >+sub createiOSSimulatorDevice >+{ >+ my $name = shift; >+ my $deviceTypeId = shift; >+ my $runtimeId = shift; >+ >+ my $created = system("xcrun", "--sdk", "iphonesimulator", "simctl", "create", $name, $deviceTypeId, $runtimeId) == 0; >+ die "Couldn't create simulator device: $name $deviceTypeId $runtimeId" if not $created; >+ >+ system("xcrun", "--sdk", "iphonesimulator", "simctl", "list"); >+ >+ print "Waiting for device to be created ...\n"; >+ sleep 5; >+ for (my $tries = 0; $tries < 5; $tries++){ >+ my @devices = iOSSimulatorDevices(); >+ foreach my $device (@devices) { >+ return $device if $device->{name} eq $name and $device->{deviceType} eq $deviceTypeId and $device->{runtime} eq $runtimeId; >+ } >+ sleep 5; >+ } >+ die "Device $name $deviceTypeId $runtimeId wasn't found in " . iOSSimulatorDevicesPath(); >+} >+ >+sub willUseIOSDeviceSDK() >+{ >+ return xcodeSDKPlatformName() eq "iphoneos"; >+} >+ >+sub willUseIOSSimulatorSDK() >+{ >+ return xcodeSDKPlatformName() eq "iphonesimulator"; >+} >+ >+sub willUseAppleTVDeviceSDK() >+{ >+ return xcodeSDKPlatformName() eq "appletvos"; >+} >+ >+sub willUseAppleTVSimulatorSDK() >+{ >+ return xcodeSDKPlatformName() eq "appletvsimulator"; >+} >+ >+sub willUseWatchDeviceSDK() >+{ >+ return xcodeSDKPlatformName() eq "watchos"; >+} >+ >+sub willUseWatchSimulatorSDK() >+{ >+ return xcodeSDKPlatformName() eq "watchsimulator"; >+} >+ >+sub isIOSWebKit() >+{ >+ return portName() eq iOS; >+} >+ >+sub isTVOSWebKit() >+{ >+ return portName() eq tvOS; >+} >+ >+sub isWatchOSWebKit() >+{ >+ return portName() eq watchOS; >+} >+ >+sub isEmbeddedWebKit() >+{ >+ return isIOSWebKit() || isTVOSWebKit() || isWatchOSWebKit(); >+} >+ >+sub determineNmPath() >+{ >+ return if $nmPath; >+ >+ if (isAppleCocoaWebKit()) { >+ $nmPath = `xcrun -find nm`; >+ chomp $nmPath; >+ } >+ $nmPath = "nm" if !$nmPath; >+} >+ >+sub nmPath() >+{ >+ determineNmPath(); >+ return $nmPath; >+} >+ >+sub splitVersionString >+{ >+ my $versionString = shift; >+ my @splitVersion = split(/\./, $versionString); >+ @splitVersion >= 2 or die "Invalid version $versionString"; >+ $osXVersion = { >+ "major" => $splitVersion[0], >+ "minor" => $splitVersion[1], >+ "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0), >+ }; >+} >+ >+sub determineOSXVersion() >+{ >+ return if $osXVersion; >+ >+ if (!isDarwin()) { >+ $osXVersion = -1; >+ return; >+ } >+ >+ my $versionString = `sw_vers -productVersion`; >+ $osXVersion = splitVersionString($versionString); >+} >+ >+sub osXVersion() >+{ >+ determineOSXVersion(); >+ return $osXVersion; >+} >+ >+sub determineIOSVersion() >+{ >+ return if $iosVersion; >+ >+ if (!isIOSWebKit()) { >+ $iosVersion = -1; >+ return; >+ } >+ >+ my $versionString = xcodeSDKVersion(); >+ $iosVersion = splitVersionString($versionString); >+} >+ >+sub iosVersion() >+{ >+ determineIOSVersion(); >+ return $iosVersion; >+} >+ >+sub isWindowsNT() >+{ >+ return $ENV{'OS'} eq 'Windows_NT'; >+} >+ >+sub appendToEnvironmentVariableList($$) >+{ >+ my ($name, $value) = @_; >+ >+ if (defined($ENV{$name})) { >+ $ENV{$name} .= $Config{path_sep} . $value; >+ } else { >+ $ENV{$name} = $value; >+ } >+} >+ >+sub prependToEnvironmentVariableList($$) >+{ >+ my ($name, $value) = @_; >+ >+ if (defined($ENV{$name})) { >+ $ENV{$name} = $value . $Config{path_sep} . $ENV{$name}; >+ } else { >+ $ENV{$name} = $value; >+ } >+} >+ >+sub sharedCommandLineOptions() >+{ >+ return ( >+ "g|guard-malloc" => \$shouldUseGuardMalloc, >+ ); >+} >+ >+sub sharedCommandLineOptionsUsage >+{ >+ my %opts = @_; >+ >+ my %switches = ( >+ '-g|--guard-malloc' => 'Use guardmalloc when running executable', >+ ); >+ >+ my $indent = " " x ($opts{indent} || 2); >+ my $switchWidth = List::Util::max(int($opts{switchWidth}), List::Util::max(map { length($_) } keys %switches) + ($opts{brackets} ? 2 : 0)); >+ >+ my $result = "Common switches:\n"; >+ >+ for my $switch (keys %switches) { >+ my $switchName = $opts{brackets} ? "[" . $switch . "]" : $switch; >+ $result .= sprintf("%s%-" . $switchWidth . "s %s\n", $indent, $switchName, $switches{$switch}); >+ } >+ >+ return $result; >+} >+ >+sub setUpGuardMallocIfNeeded >+{ >+ if (!isDarwin()) { >+ return; >+ } >+ >+ if (!defined($shouldUseGuardMalloc)) { >+ $shouldUseGuardMalloc = checkForArgumentAndRemoveFromARGV("-g") || checkForArgumentAndRemoveFromARGV("--guard-malloc"); >+ } >+ >+ if ($shouldUseGuardMalloc) { >+ appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib"); >+ appendToEnvironmentVariableList("__XPC_DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib"); >+ } >+} >+ >+sub relativeScriptsDir() >+{ >+ my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel($FindBin::Bin, getcwd()), ""); >+ if ($scriptDir eq "") { >+ $scriptDir = "."; >+ } >+ return $scriptDir; >+} >+ >+sub launcherPath() >+{ >+ my $relativeScriptsPath = relativeScriptsDir(); >+ if (isGtk() || isWPE()) { >+ return "$relativeScriptsPath/run-minibrowser"; >+ } elsif (isAppleWebKit()) { >+ return "$relativeScriptsPath/run-safari"; >+ } >+} >+ >+sub launcherName() >+{ >+ if (isGtk()) { >+ return "MiniBrowser"; >+ } elsif (isAppleCocoaWebKit()) { >+ return "Safari"; >+ } elsif (isAppleWinWebKit()) { >+ return "MiniBrowser"; >+ } elsif (isWPE()) { >+ return "dyz"; >+ } >+} >+ >+sub checkRequiredSystemConfig >+{ >+ if (isDarwin()) { >+ chomp(my $productVersion = `sw_vers -productVersion`); >+ if (eval "v$productVersion" lt v10.10.5) { >+ print "*************************************************************\n"; >+ print "OS X Yosemite v10.10.5 or later is required to build WebKit.\n"; >+ print "You have " . $productVersion . ", thus the build will most likely fail.\n"; >+ print "*************************************************************\n"; >+ } >+ determineXcodeVersion(); >+ if (eval "v$xcodeVersion" lt v7.0) { >+ print "*************************************************************\n"; >+ print "Xcode 7.0 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 App Store.\n"; >+ print "*************************************************************\n"; >+ } >+ } >+} >+ >+sub determineWindowsSourceDir() >+{ >+ return if $windowsSourceDir; >+ $windowsSourceDir = sourceDir(); >+ chomp($windowsSourceDir = `cygpath -w '$windowsSourceDir'`) if isCygwin(); >+} >+ >+sub windowsSourceDir() >+{ >+ determineWindowsSourceDir(); >+ return $windowsSourceDir; >+} >+ >+sub windowsSourceSourceDir() >+{ >+ return File::Spec->catdir(windowsSourceDir(), "Source"); >+} >+ >+sub windowsLibrariesDir() >+{ >+ return File::Spec->catdir(windowsSourceDir(), "WebKitLibraries", "win"); >+} >+ >+sub windowsOutputDir() >+{ >+ return File::Spec->catdir(windowsSourceDir(), "WebKitBuild"); >+} >+ >+sub fontExists($) >+{ >+ my $font = shift; >+ my $cmd = "reg query \"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\" /v \"$font\" 2>&1"; >+ my $val = `$cmd`; >+ return $? == 0; >+} >+ >+sub checkInstalledTools() >+{ >+ # environment variables. Avoid until this is corrected. >+ my $pythonVer = `python --version 2>&1`; >+ die "You must have Python installed to build WebKit.\n" if ($?); >+ >+ # cURL 7.34.0 has a bug that prevents authentication with opensource.apple.com (and other things using SSL3). >+ my $curlVer = `curl --version 2> NUL`; >+ if (!$? and $curlVer =~ "(.*curl.*)") { >+ $curlVer = $1; >+ if ($curlVer =~ /libcurl\/7\.34\.0/) { >+ print "cURL version 7.34.0 has a bug that prevents authentication with SSL v2 or v3.\n"; >+ print "cURL 7.33.0 is known to work. The cURL projects is preparing an update to\n"; >+ print "correct this problem.\n\n"; >+ die "Please install a working cURL and try again.\n"; >+ } >+ } >+ >+ # MathML requires fonts that may not ship with Windows. >+ # Warn the user if they are missing. >+ my @fonts = ('Cambria & Cambria Math (TrueType)', 'LatinModernMath-Regular (TrueType)', 'STIXMath-Regular (TrueType)'); >+ my @missing = (); >+ foreach my $font (@fonts) { >+ push @missing, $font if not fontExists($font); >+ } >+ >+ if (scalar @missing > 0) { >+ print "*************************************************************\n"; >+ print "Mathematical fonts, such as Latin Modern Math are needed to\n"; >+ print "use the MathML feature. You do not appear to have these fonts\n"; >+ print "on your system.\n\n"; >+ print "You can download a suitable set of fonts from the following URL:\n"; >+ print "https://trac.webkit.org/wiki/MathML/Fonts\n"; >+ print "*************************************************************\n"; >+ } >+ >+ print "Installed tools are correct for the WebKit build.\n"; >+} >+ >+sub setupAppleWinEnv() >+{ >+ return unless isAppleWinWebKit(); >+ >+ checkInstalledTools(); >+ >+ if (isWindowsNT()) { >+ my $restartNeeded = 0; >+ my %variablesToSet = (); >+ >+ # FIXME: We should remove this explicit version check for cygwin once we stop supporting Cygwin 1.7.9 or older versions. >+ # https://bugs.webkit.org/show_bug.cgi?id=85791 >+ my $uname_version = (POSIX::uname())[2]; >+ $uname_version =~ s/\(.*\)//; # Remove the trailing cygwin version, if any. >+ $uname_version =~ s/\-.*$//; # Remove trailing dash-version content, if any >+ if (version->parse($uname_version) < version->parse("1.7.10")) { >+ # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios) >+ # for UNIX-like ttys in the Windows console >+ $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN}; >+ } >+ >+ # Those environment variables must be set to be able to build inside Visual Studio. >+ $variablesToSet{WEBKIT_LIBRARIES} = windowsLibrariesDir() unless $ENV{WEBKIT_LIBRARIES}; >+ $variablesToSet{WEBKIT_OUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKIT_OUTPUTDIR}; >+ $variablesToSet{MSBUILDDISABLENODEREUSE} = "1" unless $ENV{MSBUILDDISABLENODEREUSE}; >+ $variablesToSet{_IsNativeEnvironment} = "true" unless $ENV{_IsNativeEnvironment}; >+ $variablesToSet{PreferredToolArchitecture} = "x64" unless $ENV{PreferredToolArchitecture}; >+ >+ foreach my $variable (keys %variablesToSet) { >+ print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n"; >+ my $ret = system "setx", $variable, $variablesToSet{$variable}; >+ if ($ret != 0) { >+ system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable}; >+ } >+ $restartNeeded ||= $variable eq "WEBKIT_LIBRARIES" || $variable eq "WEBKIT_OUTPUTDIR"; >+ } >+ >+ if ($restartNeeded) { >+ print "Please restart your computer before attempting to build inside Visual Studio.\n\n"; >+ } >+ } else { >+ if (!defined $ENV{'WEBKIT_LIBRARIES'} || !$ENV{'WEBKIT_LIBRARIES'}) { >+ print "Warning: You must set the 'WebKit_Libraries' environment variable\n"; >+ print " to be able build WebKit from within Visual Studio 2017 and newer.\n"; >+ print " Make sure that 'WebKit_Libraries' points to the\n"; >+ print " 'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n"; >+ } >+ if (!defined $ENV{'WEBKIT_OUTPUTDIR'} || !$ENV{'WEBKIT_OUTPUTDIR'}) { >+ print "Warning: You must set the 'WebKit_OutputDir' environment variable\n"; >+ print " to be able build WebKit from within Visual Studio 2017 and newer.\n\n"; >+ } >+ if (!defined $ENV{'MSBUILDDISABLENODEREUSE'} || !$ENV{'MSBUILDDISABLENODEREUSE'}) { >+ print "Warning: You should set the 'MSBUILDDISABLENODEREUSE' environment variable to '1'\n"; >+ print " to avoid periodic locked log files when building.\n\n"; >+ } >+ } >+ # FIXME (125180): Remove the following temporary 64-bit support once official support is available. >+ if (isWin64() and !$ENV{'WEBKIT_64_SUPPORT'}) { >+ print "Warning: You must set the 'WEBKIT_64_SUPPORT' environment variable\n"; >+ print " to be able run WebKit or JavaScriptCore tests.\n\n"; >+ } >+} >+ >+sub setupCygwinEnv() >+{ >+ return if !isAnyWindows(); >+ return if $vcBuildPath; >+ >+ my $programFilesPath = programFilesPath(); >+ my $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.com)); >+ if (!-e $visualStudioPath) { >+ # Visual Studio not found, try VC++ Express >+ $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE WDExpress.exe)); >+ if (! -e $visualStudioPath) { >+ print "*************************************************************\n"; >+ print "Cannot find '$visualStudioPath'\n"; >+ print "Please execute the file 'vcvars32.bat' from\n"; >+ print "your Visual Studio 2017 installation\n"; >+ print "to setup the necessary environment variables.\n"; >+ print "*************************************************************\n"; >+ die; >+ } >+ $willUseVCExpressWhenBuilding = 1; >+ } >+ >+ print "Building results into: ", baseProductDir(), "\n"; >+ print "WEBKIT_OUTPUTDIR is set to: ", $ENV{"WEBKIT_OUTPUTDIR"}, "\n"; >+ print "WEBKIT_LIBRARIES is set to: ", $ENV{"WEBKIT_LIBRARIES"}, "\n"; >+ # FIXME (125180): Remove the following temporary 64-bit support once official support is available. >+ print "WEBKIT_64_SUPPORT is set to: ", $ENV{"WEBKIT_64_SUPPORT"}, "\n" if isWin64(); >+ >+ # We will actually use MSBuild to build WebKit, but we need to find the Visual Studio install (above) to make >+ # sure we use the right options. >+ $vcBuildPath = File::Spec->catfile(msBuildInstallDir(), qw(MSBuild.exe)); >+ if (! -e $vcBuildPath) { >+ print "*************************************************************\n"; >+ print "Cannot find '$vcBuildPath'\n"; >+ print "Please make sure execute that the Microsoft .NET Framework SDK\n"; >+ print "is installed on this machine.\n"; >+ print "*************************************************************\n"; >+ die; >+ } >+} >+ >+sub dieIfWindowsPlatformSDKNotInstalled >+{ >+ my $registry32Path = "/proc/registry/"; >+ my $registry64Path = "/proc/registry64/"; >+ my @windowsPlatformSDKRegistryEntries = ( >+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0A", >+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0", >+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.1A", >+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.0A", >+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", >+ ); >+ >+ # FIXME: It would be better to detect whether we are using 32- or 64-bit Windows >+ # and only check the appropriate entry. But for now we just blindly check both. >+ my $recommendedPlatformSDK = $windowsPlatformSDKRegistryEntries[0]; >+ >+ while (@windowsPlatformSDKRegistryEntries) { >+ my $windowsPlatformSDKRegistryEntry = shift @windowsPlatformSDKRegistryEntries; >+ return if (-e $registry32Path . $windowsPlatformSDKRegistryEntry) || (-e $registry64Path . $windowsPlatformSDKRegistryEntry); >+ } >+ >+ print "*************************************************************\n"; >+ print "Cannot find registry entry '$recommendedPlatformSDK'.\n"; >+ print "Please download and install the Microsoft Windows SDK\n"; >+ print "from <http://www.microsoft.com/en-us/download/details.aspx?id=8279>.\n\n"; >+ print "Then follow step 2 in the Windows section of the \"Installing Developer\n"; >+ print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n"; >+ print "*************************************************************\n"; >+ die; >+} >+ >+sub buildXCodeProject($$@) >+{ >+ my ($project, $clean, @extraOptions) = @_; >+ >+ if ($clean) { >+ push(@extraOptions, "-alltargets"); >+ push(@extraOptions, "clean"); >+ } >+ >+ chomp($ENV{DSYMUTIL_NUM_THREADS} = `sysctl -n hw.activecpu`); >+ return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions; >+} >+ >+sub usingVisualStudioExpress() >+{ >+ setupCygwinEnv(); >+ return $willUseVCExpressWhenBuilding; >+} >+ >+sub buildVisualStudioProject >+{ >+ my ($project, $clean) = @_; >+ setupCygwinEnv(); >+ >+ my $config = configurationForVisualStudio(); >+ >+ dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding; >+ >+ chomp($project = `cygpath -w "$project"`) if isCygwin(); >+ >+ my $action = "/t:build"; >+ if ($clean) { >+ $action = "/t:clean"; >+ } >+ >+ my $platform = "/p:Platform=" . (isWin64() ? "x64" : "Win32"); >+ my $logPath = File::Spec->catdir($baseProductDir, $configuration); >+ mkpath($logPath) unless -d $logPath or $logPath eq "."; >+ >+ my $errorLogFile = File::Spec->catfile($logPath, "webkit_errors.log"); >+ chomp($errorLogFile = `cygpath -w "$errorLogFile"`) if isCygwin(); >+ my $errorLogging = "/flp:LogFile=" . $errorLogFile . ";ErrorsOnly"; >+ >+ my $warningLogFile = File::Spec->catfile($logPath, "webkit_warnings.log"); >+ chomp($warningLogFile = `cygpath -w "$warningLogFile"`) if isCygwin(); >+ my $warningLogging = "/flp1:LogFile=" . $warningLogFile . ";WarningsOnly"; >+ >+ my $maxCPUCount = '/maxcpucount:' . numberOfCPUs(); >+ >+ my @command = ($vcBuildPath, "/verbosity:minimal", $project, $action, $config, $platform, "/fl", $errorLogging, "/fl1", $warningLogging, $maxCPUCount); >+ print join(" ", @command), "\n"; >+ return system @command; >+} >+ >+sub getJhbuildPath() >+{ >+ my @jhbuildPath = File::Spec->splitdir(baseProductDir()); >+ if (isGit() && isGitBranchBuild() && gitBranch()) { >+ pop(@jhbuildPath); >+ } >+ if (isGtk()) { >+ push(@jhbuildPath, "DependenciesGTK"); >+ } elsif (isWPE()) { >+ push(@jhbuildPath, "DependenciesWPE"); >+ } else { >+ die "Cannot get JHBuild path for platform that isn't GTK+.\n"; >+ } >+ return File::Spec->catdir(@jhbuildPath); >+} >+ >+sub isCachedArgumentfileOutOfDate($@) >+{ >+ my ($filename, $currentContents) = @_; >+ >+ if (! -e $filename) { >+ return 1; >+ } >+ >+ open(CONTENTS_FILE, $filename); >+ chomp(my $previousContents = <CONTENTS_FILE>); >+ close(CONTENTS_FILE); >+ >+ if ($previousContents ne $currentContents) { >+ print "Contents for file $filename have changed.\n"; >+ print "Previous contents were: $previousContents\n\n"; >+ print "New contents are: $currentContents\n"; >+ return 1; >+ } >+ >+ return 0; >+} >+ >+sub wrapperPrefixIfNeeded() >+{ >+ if (isAnyWindows() || isJSCOnly()) { >+ return (); >+ } >+ if (isAppleCocoaWebKit()) { >+ return ("xcrun"); >+ } >+ if (-e getJhbuildPath()) { >+ my @prefix = (File::Spec->catfile(sourceDir(), "Tools", "jhbuild", "jhbuild-wrapper")); >+ if (isGtk()) { >+ push(@prefix, "--gtk"); >+ } elsif (isWPE()) { >+ push(@prefix, "--wpe"); >+ } >+ push(@prefix, "run"); >+ >+ return @prefix; >+ } >+ >+ return (); >+} >+ >+sub shouldUseJhbuild() >+{ >+ return ((isGtk() or isWPE()) and -e getJhbuildPath()); >+} >+ >+sub cmakeCachePath() >+{ >+ return File::Spec->catdir(baseProductDir(), configuration(), "CMakeCache.txt"); >+} >+ >+sub cmakeFilesPath() >+{ >+ return File::Spec->catdir(baseProductDir(), configuration(), "CMakeFiles"); >+} >+ >+sub shouldRemoveCMakeCache(@) >+{ >+ my ($cacheFilePath, @buildArgs) = @_; >+ >+ # We check this first, because we always want to create this file for a fresh build. >+ my $productDir = File::Spec->catdir(baseProductDir(), configuration()); >+ my $optionsCache = File::Spec->catdir($productDir, "build-webkit-options.txt"); >+ my $joinedBuildArgs = join(" ", @buildArgs); >+ if (isCachedArgumentfileOutOfDate($optionsCache, $joinedBuildArgs)) { >+ File::Path::mkpath($productDir) unless -d $productDir; >+ open(CACHED_ARGUMENTS, ">", $optionsCache); >+ print CACHED_ARGUMENTS $joinedBuildArgs; >+ close(CACHED_ARGUMENTS); >+ >+ return 1; >+ } >+ >+ my $cmakeCache = cmakeCachePath(); >+ unless (-e $cmakeCache) { >+ return 0; >+ } >+ >+ my $cacheFileModifiedTime = stat($cmakeCache)->mtime; >+ my $platformConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "Options" . cmakeBasedPortName() . ".cmake"); >+ if ($cacheFileModifiedTime < stat($platformConfiguration)->mtime) { >+ return 1; >+ } >+ >+ my $globalConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsCommon.cmake"); >+ if ($cacheFileModifiedTime < stat($globalConfiguration)->mtime) { >+ return 1; >+ } >+ >+ # FIXME: This probably does not work as expected, or the next block to >+ # delete the images subdirectory would not be here. Directory mtime does not >+ # percolate upwards when files are added or removed from subdirectories. >+ my $inspectorUserInterfaceDirectory = File::Spec->catdir(sourceDir(), "Source", "WebInspectorUI", "UserInterface"); >+ if ($cacheFileModifiedTime < stat($inspectorUserInterfaceDirectory)->mtime) { >+ return 1; >+ } >+ >+ my $inspectorImageDirectory = File::Spec->catdir(sourceDir(), "Source", "WebInspectorUI", "UserInterface", "Images"); >+ if ($cacheFileModifiedTime < stat($inspectorImageDirectory)->mtime) { >+ return 1; >+ } >+ >+ if(isAnyWindows()) { >+ my $winConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsWin.cmake"); >+ if ($cacheFileModifiedTime < stat($winConfiguration)->mtime) { >+ return 1; >+ } >+ } >+ >+ return 0; >+} >+ >+sub removeCMakeCache(@) >+{ >+ my (@buildArgs) = @_; >+ if (shouldRemoveCMakeCache(@buildArgs)) { >+ my $cmakeCache = cmakeCachePath(); >+ my $cmakeFiles = cmakeFilesPath(); >+ unlink($cmakeCache) if -e $cmakeCache; >+ rmtree($cmakeFiles) if -d $cmakeFiles; >+ } >+} >+ >+sub canUseNinja(@) >+{ >+ if (!defined($shouldNotUseNinja)) { >+ $shouldNotUseNinja = checkForArgumentAndRemoveFromARGV("--no-ninja"); >+ } >+ >+ if ($shouldNotUseNinja) { >+ return 0; >+ } >+ >+ if (isAppleCocoaWebKit()) { >+ my $devnull = File::Spec->devnull(); >+ if (exitStatus(system("xcrun -find ninja >$devnull 2>&1")) == 0) { >+ return 1; >+ } >+ } >+ >+ # Test both ninja and ninja-build. Fedora uses ninja-build and has patched CMake to also call ninja-build. >+ return commandExists("ninja") || commandExists("ninja-build"); >+} >+ >+sub canUseEclipseNinjaGenerator(@) >+{ >+ # Check that eclipse and eclipse Ninja generator is installed >+ my $devnull = File::Spec->devnull(); >+ return commandExists("eclipse") && exitStatus(system("cmake -N -G 'Eclipse CDT4 - Ninja' >$devnull 2>&1")) == 0; >+} >+ >+sub cmakeGeneratedBuildfile(@) >+{ >+ my ($willUseNinja) = @_; >+ if ($willUseNinja) { >+ return File::Spec->catfile(baseProductDir(), configuration(), "build.ninja") >+ } elsif (isAnyWindows()) { >+ return File::Spec->catfile(baseProductDir(), configuration(), "WebKit.sln") >+ } else { >+ return File::Spec->catfile(baseProductDir(), configuration(), "Makefile") >+ } >+} >+ >+sub generateBuildSystemFromCMakeProject >+{ >+ my ($prefixPath, @cmakeArgs) = @_; >+ my $config = configuration(); >+ my $port = cmakeBasedPortName(); >+ my $buildPath = File::Spec->catdir(baseProductDir(), $config); >+ File::Path::mkpath($buildPath) unless -d $buildPath; >+ my $originalWorkingDirectory = getcwd(); >+ chdir($buildPath) or die; >+ >+ # We try to be smart about when to rerun cmake, so that we can have faster incremental builds. >+ my $willUseNinja = canUseNinja(); >+ if (-e cmakeCachePath() && -e cmakeGeneratedBuildfile($willUseNinja)) { >+ return 0; >+ } >+ >+ my @args; >+ push @args, "-DPORT=\"$port\""; >+ push @args, "-DCMAKE_INSTALL_PREFIX=\"$prefixPath\"" if $prefixPath; >+ push @args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"; >+ if ($config =~ /release/i) { >+ push @args, "-DCMAKE_BUILD_TYPE=Release"; >+ } elsif ($config =~ /debug/i) { >+ push @args, "-DCMAKE_BUILD_TYPE=Debug"; >+ } >+ >+ if ($willUseNinja) { >+ push @args, "-G"; >+ if (canUseEclipseNinjaGenerator()) { >+ push @args, "'Eclipse CDT4 - Ninja'"; >+ } else { >+ push @args, "Ninja"; >+ } >+ } elsif (isAnyWindows() && isWin64()) { >+ push @args, '-G "Visual Studio 15 2017 Win64"'; >+ push @args, '-DCMAKE_GENERATOR_TOOLSET="host=x64"'; >+ } >+ # Do not show progress of generating bindings in interactive Ninja build not to leave noisy lines on tty >+ push @args, '-DSHOW_BINDINGS_GENERATION_PROGRESS=1' unless ($willUseNinja && -t STDOUT); >+ >+ # Some ports have production mode, but build-webkit should always use developer mode. >+ push @args, "-DDEVELOPER_MODE=ON" if isGtk() || isJSCOnly() || isWPE(); >+ >+ push @args, @cmakeArgs if @cmakeArgs; >+ >+ my $cmakeSourceDir = isCygwin() ? windowsSourceDir() : sourceDir(); >+ push @args, '"' . $cmakeSourceDir . '"'; >+ >+ # Compiler options to keep floating point values consistent >+ # between 32-bit and 64-bit architectures. >+ determineArchitecture(); >+ if ($architecture eq "i686" && !isCrossCompilation() && !isAnyWindows()) { >+ $ENV{'CXXFLAGS'} = "-march=pentium4 -msse2 -mfpmath=sse " . ($ENV{'CXXFLAGS'} || ""); >+ } >+ >+ # We call system("cmake @args") instead of system("cmake", @args) so that @args is >+ # parsed for shell metacharacters. >+ my $wrapper = join(" ", wrapperPrefixIfNeeded()) . " "; >+ my $returnCode = systemVerbose($wrapper . "cmake @args"); >+ >+ chdir($originalWorkingDirectory); >+ return $returnCode; >+} >+ >+sub buildCMakeGeneratedProject($) >+{ >+ my (@makeArgs) = @_; >+ my $config = configuration(); >+ my $buildPath = File::Spec->catdir(baseProductDir(), $config); >+ if (! -d $buildPath) { >+ die "Must call generateBuildSystemFromCMakeProject() before building CMake project."; >+ } >+ >+ if ($ENV{VERBOSE} && canUseNinja()) { >+ push @makeArgs, "-v"; >+ push @makeArgs, "-d keeprsp" if (version->parse(determineNinjaVersion()) >= version->parse("1.4.0")); >+ } >+ >+ my $command = "cmake"; >+ my @args = ("--build", $buildPath, "--config", $config); >+ push @args, ("--", @makeArgs) if @makeArgs; >+ >+ # GTK and JSCOnly can use a build script to preserve colors and pretty-printing. >+ if ((isGtk() || isJSCOnly()) && -e "$buildPath/build.sh") { >+ chdir "$buildPath" or die; >+ $command = "$buildPath/build.sh"; >+ @args = (@makeArgs); >+ } >+ >+ # We call system("cmake @args") instead of system("cmake", @args) so that @args is >+ # parsed for shell metacharacters. In particular, @makeArgs may contain such metacharacters. >+ my $wrapper = join(" ", wrapperPrefixIfNeeded()) . " "; >+ return systemVerbose($wrapper . "$command @args"); >+} >+ >+sub cleanCMakeGeneratedProject() >+{ >+ my $config = configuration(); >+ my $buildPath = File::Spec->catdir(baseProductDir(), $config); >+ if (-d $buildPath) { >+ return systemVerbose("cmake", "--build", $buildPath, "--config", $config, "--target", "clean"); >+ } >+ return 0; >+} >+ >+sub buildCMakeProjectOrExit($$$@) >+{ >+ my ($clean, $prefixPath, $makeArgs, @cmakeArgs) = @_; >+ my $returnCode; >+ >+ exit(exitStatus(cleanCMakeGeneratedProject())) if $clean; >+ >+ if (isGtk() && checkForArgumentAndRemoveFromARGV("--update-gtk")) { >+ system("perl", "$sourceDir/Tools/Scripts/update-webkitgtk-libs") == 0 or die $!; >+ } >+ >+ if (isWPE() && checkForArgumentAndRemoveFromARGV("--update-wpe")) { >+ system("perl", "$sourceDir/Tools/Scripts/update-webkitwpe-libs") == 0 or die $!; >+ } >+ >+ $returnCode = exitStatus(generateBuildSystemFromCMakeProject($prefixPath, @cmakeArgs)); >+ exit($returnCode) if $returnCode; >+ exit 0 if isGenerateProjectOnly(); >+ >+ $returnCode = exitStatus(buildCMakeGeneratedProject($makeArgs)); >+ exit($returnCode) if $returnCode; >+ return 0; >+} >+ >+sub cmakeBasedPortArguments() >+{ >+ return (); >+} >+ >+sub cmakeBasedPortName() >+{ >+ return ucfirst portName(); >+} >+ >+sub determineIsCMakeBuild() >+{ >+ return if defined($isCMakeBuild); >+ $isCMakeBuild = checkForArgumentAndRemoveFromARGV("--cmake"); >+} >+ >+sub isCMakeBuild() >+{ >+ return 1 unless isAppleCocoaWebKit(); >+ determineIsCMakeBuild(); >+ return $isCMakeBuild; >+} >+ >+sub determineIsGenerateProjectOnly() >+{ >+ return if defined($isGenerateProjectOnly); >+ $isGenerateProjectOnly = checkForArgumentAndRemoveFromARGV("--generate-project-only"); >+} >+ >+sub isGenerateProjectOnly() >+{ >+ determineIsGenerateProjectOnly(); >+ return $isGenerateProjectOnly; >+} >+ >+sub promptUser >+{ >+ my ($prompt, $default) = @_; >+ my $defaultValue = $default ? "[$default]" : ""; >+ print "$prompt $defaultValue: "; >+ chomp(my $input = <STDIN>); >+ return $input ? $input : $default; >+} >+ >+sub appleApplicationSupportPath >+{ >+ open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir"; >+ my $path = <INSTALL_DIR>; >+ $path =~ s/[\r\n\x00].*//; >+ close INSTALL_DIR; >+ >+ my $unixPath = `cygpath -u '$path'`; >+ chomp $unixPath; >+ return $unixPath; >+} >+ >+sub setPathForRunningWebKitApp >+{ >+ my ($env) = @_; >+ >+ if (isAnyWindows()) { >+ my $productBinaryDir = executableProductDir(); >+ if (isAppleWinWebKit()) { >+ $env->{PATH} = join(':', $productBinaryDir, appleApplicationSupportPath(), $env->{PATH} || ""); >+ } elsif (isWinCairo()) { >+ my $winCairoBin = sourceDir() . "/WebKitLibraries/win/" . (isWin64() ? "bin64/" : "bin32/"); >+ my $gstreamerBin = isWin64() ? $ENV{"GSTREAMER_1_0_ROOT_X86_64"} . "bin" : $ENV{"GSTREAMER_1_0_ROOT_X86"} . "bin"; >+ $env->{PATH} = join(':', $productBinaryDir, $winCairoBin, $gstreamerBin, $env->{PATH} || ""); >+ } >+ } >+} >+ >+sub printHelpAndExitForRunAndDebugWebKitAppIfNeeded >+{ >+ return unless checkForArgumentAndRemoveFromARGV("--help"); >+ >+ print STDERR <<EOF; >+Usage: @{[basename($0)]} [options] [args ...] >+ --help Show this help message >+ --no-saved-state Launch the application without state restoration >+ >+Options specific to macOS: >+ -g|--guard-malloc Enable Guard Malloc >+ --lang=LANGUAGE Use a specific language instead of system language. >+ This accepts a language name (German) or a language code (de, ar, pt_BR, etc). >+ --locale=LOCALE Use a specific locale instead of the system region. >+EOF >+ >+ exit(1); >+} >+ >+sub argumentsForRunAndDebugMacWebKitApp() >+{ >+ my @args = (); >+ if (checkForArgumentAndRemoveFromARGV("--no-saved-state")) { >+ push @args, ("-ApplePersistenceIgnoreStateQuietly", "YES"); >+ # FIXME: Don't set ApplePersistenceIgnoreState once all supported OS versions respect ApplePersistenceIgnoreStateQuietly (rdar://15032886). >+ push @args, ("-ApplePersistenceIgnoreState", "YES"); >+ } >+ >+ my $lang; >+ if (checkForArgumentAndRemoveFromARGVGettingValue("--lang", \$lang)) { >+ push @args, ("-AppleLanguages", "(" . $lang . ")"); >+ } >+ >+ my $locale; >+ if (checkForArgumentAndRemoveFromARGVGettingValue("--locale", \$locale)) { >+ push @args, ("-AppleLocale", $locale); >+ } >+ >+ unshift @args, @ARGV; >+ >+ return @args; >+} >+ >+sub setupMacWebKitEnvironment($) >+{ >+ my ($dyldFrameworkPath) = @_; >+ >+ $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath); >+ >+ prependToEnvironmentVariableList("DYLD_FRAMEWORK_PATH", $dyldFrameworkPath); >+ prependToEnvironmentVariableList("__XPC_DYLD_FRAMEWORK_PATH", $dyldFrameworkPath); >+ prependToEnvironmentVariableList("DYLD_LIBRARY_PATH", $dyldFrameworkPath); >+ prependToEnvironmentVariableList("__XPC_DYLD_LIBRARY_PATH", $dyldFrameworkPath); >+ $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES"; >+ >+ setUpGuardMallocIfNeeded(); >+} >+ >+sub setupIOSWebKitEnvironment($) >+{ >+ my ($dyldFrameworkPath) = @_; >+ $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath); >+ >+ prependToEnvironmentVariableList("DYLD_FRAMEWORK_PATH", $dyldFrameworkPath); >+ prependToEnvironmentVariableList("DYLD_LIBRARY_PATH", $dyldFrameworkPath); >+ >+ setUpGuardMallocIfNeeded(); >+} >+ >+sub iosSimulatorApplicationsPath() >+{ >+ my $iphoneOSPlatformPath = sdkPlatformDirectory("iphoneos"); >+ return File::Spec->catdir($iphoneOSPlatformPath, "Developer", "Library", "CoreSimulator", "Profiles", "Runtimes", "iOS.simruntime", "Contents", "Resources", "RuntimeRoot", "Applications"); >+} >+ >+sub installedMobileSafariBundle() >+{ >+ return File::Spec->catfile(iosSimulatorApplicationsPath(), "MobileSafari.app"); >+} >+ >+sub mobileSafariBundle() >+{ >+ determineConfigurationProductDir(); >+ >+ # Use MobileSafari.app in product directory if present. >+ if (isAppleCocoaWebKit() && -d "$configurationProductDir/MobileSafari.app") { >+ return "$configurationProductDir/MobileSafari.app"; >+ } >+ return installedMobileSafariBundle(); >+} >+ >+sub plistPathFromBundle($) >+{ >+ my ($appBundle) = @_; >+ return "$appBundle/Info.plist" if -f "$appBundle/Info.plist"; # iOS app bundle >+ return "$appBundle/Contents/Info.plist" if -f "$appBundle/Contents/Info.plist"; # Mac app bundle >+ return ""; >+} >+ >+sub appIdentifierFromBundle($) >+{ >+ my ($appBundle) = @_; >+ my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute. >+ chomp(my $bundleIdentifier = `defaults read '$plistPath' CFBundleIdentifier 2> /dev/null`); >+ return $bundleIdentifier; >+} >+ >+sub appDisplayNameFromBundle($) >+{ >+ my ($appBundle) = @_; >+ my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute. >+ chomp(my $bundleDisplayName = `defaults read '$plistPath' CFBundleDisplayName 2> /dev/null`); >+ return $bundleDisplayName; >+} >+ >+sub waitUntilIOSSimulatorDeviceIsInState($$) >+{ >+ my ($deviceUDID, $waitUntilState) = @_; >+ my $device = iosSimulatorDeviceByUDID($deviceUDID); >+ # FIXME: We should add a maximum time limit to wait here. >+ while ($device->{state} ne $waitUntilState) { >+ usleep(500 * 1000); # Waiting 500ms between file system polls does not make script run-safari feel sluggish. >+ $device = iosSimulatorDeviceByUDID($deviceUDID); >+ } >+} >+ >+sub waitUntilProcessNotRunning($) >+{ >+ my ($process) = @_; >+ while (system("/bin/ps -eo pid,comm | /usr/bin/grep '$process\$'") == 0) { >+ usleep(500 * 1000); >+ } >+} >+ >+sub shutDownIOSSimulatorDevice($) >+{ >+ my ($simulatorDevice) = @_; >+ system("xcrun --sdk iphonesimulator simctl shutdown $simulatorDevice->{UDID} > /dev/null 2>&1"); >+} >+ >+sub restartIOSSimulatorDevice($) >+{ >+ my ($simulatorDevice) = @_; >+ shutDownIOSSimulatorDevice($simulatorDevice); >+ >+ exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "boot", $simulatorDevice->{UDID})) == 0 or die "Failed to boot simulator device $simulatorDevice->{UDID}"; >+} >+ >+sub relaunchIOSSimulator($) >+{ >+ my ($simulatedDevice) = @_; >+ quitIOSSimulator($simulatedDevice->{UDID}); >+ >+ # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator >+ chomp(my $developerDirectory = $ENV{DEVELOPER_DIR} || `xcode-select --print-path`); >+ my $iosSimulatorPath = File::Spec->catfile($developerDirectory, "Applications", "Simulator.app"); >+ system("open", "-a", $iosSimulatorPath, "--args", "-CurrentDeviceUDID", $simulatedDevice->{UDID}) == 0 or die "Failed to open $iosSimulatorPath: $!"; >+ >+ waitUntilIOSSimulatorDeviceIsInState($simulatedDevice->{UDID}, SIMULATOR_DEVICE_STATE_BOOTED); >+ waitUntilProcessNotRunning("com.apple.datamigrator"); >+} >+ >+sub quitIOSSimulator(;$) >+{ >+ my ($waitForShutdownOfSimulatedDeviceUDID) = @_; >+ # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator >+ if (exitStatus(system {"osascript"} "osascript", "-e", 'tell application id "com.apple.iphonesimulator" to quit')) { >+ # osascript returns a non-zero exit status if Simulator.app is not registered in LaunchServices. >+ return; >+ } >+ >+ if (!defined($waitForShutdownOfSimulatedDeviceUDID)) { >+ return; >+ } >+ # FIXME: We assume that $waitForShutdownOfSimulatedDeviceUDID was not booted using the simctl command line tool. >+ # Otherwise we will spin indefinitely since quiting the iOS Simulator will not shutdown this device. We >+ # should add a maximum time limit to wait for a device to shutdown and either return an error or die() >+ # on expiration of the time limit. >+ waitUntilIOSSimulatorDeviceIsInState($waitForShutdownOfSimulatedDeviceUDID, SIMULATOR_DEVICE_STATE_SHUTDOWN); >+} >+ >+sub iosSimulatorDeviceByName($) >+{ >+ my ($simulatorName) = @_; >+ my $simulatorRuntime = iosSimulatorRuntime(); >+ my @devices = iOSSimulatorDevices(); >+ for my $device (@devices) { >+ if ($device->{name} eq $simulatorName && $device->{runtime} eq $simulatorRuntime) { >+ return $device; >+ } >+ } >+ return undef; >+} >+ >+sub iosSimulatorDeviceByUDID($) >+{ >+ my ($simulatedDeviceUDID) = @_; >+ my $devicePlistPath = File::Spec->catfile(iOSSimulatorDevicesPath(), $simulatedDeviceUDID, "device.plist"); >+ if (!-f $devicePlistPath) { >+ return; >+ } >+ # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known >+ # dictionary keys so as to decouple our representation of the plist from the actual structure >+ # of the plist, which may change. >+ eval "require Foundation"; >+ return Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_($devicePlistPath)); >+} >+ >+sub iosSimulatorRuntime() >+{ >+ my $xcodeSDKVersion = xcodeSDKVersion(); >+ $xcodeSDKVersion =~ s/\./-/; >+ return "com.apple.CoreSimulator.SimRuntime.iOS-$xcodeSDKVersion"; >+} >+ >+sub findOrCreateSimulatorForIOSDevice($) >+{ >+ my ($simulatorNameSuffix) = @_; >+ my $simulatorName; >+ my $simulatorDeviceType; >+ if (architecture() eq "x86_64") { >+ $simulatorName = "iPhone 5s " . $simulatorNameSuffix; >+ $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5s"; >+ } else { >+ $simulatorName = "iPhone 5 " . $simulatorNameSuffix; >+ $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5"; >+ } >+ my $simulatedDevice = iosSimulatorDeviceByName($simulatorName); >+ return $simulatedDevice if $simulatedDevice; >+ return createiOSSimulatorDevice($simulatorName, $simulatorDeviceType, iosSimulatorRuntime()); >+} >+ >+sub isIOSSimulatorSystemInstalledApp($) >+{ >+ my ($appBundle) = @_; >+ my $simulatorApplicationsPath = realpath(iosSimulatorApplicationsPath()); >+ return substr(realpath($appBundle), 0, length($simulatorApplicationsPath)) eq $simulatorApplicationsPath; >+} >+ >+sub hasUserInstalledAppInSimulatorDevice($$) >+{ >+ my ($appIdentifier, $simulatedDeviceUDID) = @_; >+ my $userInstalledAppPath = File::Spec->catfile($ENV{HOME}, "Library", "Developer", "CoreSimulator", "Devices", $simulatedDeviceUDID, "data", "Containers", "Bundle", "Application"); >+ if (!-d $userInstalledAppPath) { >+ return 0; # No user installed apps. >+ } >+ local @::userInstalledAppBundles; >+ my $wantedFunction = sub { >+ my $file = $_; >+ >+ # Ignore hidden files and directories. >+ if ($file =~ /^\../) { >+ $File::Find::prune = 1; >+ return; >+ } >+ >+ return if !-d $file || $file !~ /\.app$/; >+ push @::userInstalledAppBundles, $File::Find::name; >+ $File::Find::prune = 1; # Do not traverse contents of app bundle. >+ }; >+ find($wantedFunction, $userInstalledAppPath); >+ for my $userInstalledAppBundle (@::userInstalledAppBundles) { >+ if (appIdentifierFromBundle($userInstalledAppBundle) eq $appIdentifier) { >+ return 1; # Has user installed app. >+ } >+ } >+ return 0; # Does not have user installed app. >+} >+ >+sub isSimulatorDeviceBooted($) >+{ >+ my ($simulatedDeviceUDID) = @_; >+ my $device = iosSimulatorDeviceByUDID($simulatedDeviceUDID); >+ return $device && $device->{state} eq SIMULATOR_DEVICE_STATE_BOOTED; >+} >+ >+sub runIOSWebKitAppInSimulator($;$) >+{ >+ my ($appBundle, $simulatorOptions) = @_; >+ my $productDir = productDir(); >+ my $appDisplayName = appDisplayNameFromBundle($appBundle); >+ my $appIdentifier = appIdentifierFromBundle($appBundle); >+ my $simulatedDevice = findOrCreateSimulatorForIOSDevice(SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT); >+ my $simulatedDeviceUDID = $simulatedDevice->{UDID}; >+ >+ my $willUseSystemInstalledApp = isIOSSimulatorSystemInstalledApp($appBundle); >+ if ($willUseSystemInstalledApp) { >+ if (hasUserInstalledAppInSimulatorDevice($appIdentifier, $simulatedDeviceUDID)) { >+ # Restore the system-installed app in the simulator device corresponding to $appBundle as it >+ # was previously overwritten with a custom built version of the app. >+ # FIXME: Only restore the system-installed version of the app instead of erasing all contents and settings. >+ print "Quitting iOS Simulator...\n"; >+ quitIOSSimulator($simulatedDeviceUDID); >+ print "Erasing contents and settings for simulator device \"$simulatedDevice->{name}\".\n"; >+ exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "erase", $simulatedDeviceUDID)) == 0 or die; >+ } >+ # FIXME: We assume that if $simulatedDeviceUDID is not booted then iOS Simulator is not open. However >+ # $simulatedDeviceUDID may have been booted using the simctl command line tool. If $simulatedDeviceUDID >+ # was booted using simctl then we should shutdown the device and launch iOS Simulator to boot it again. >+ if (!isSimulatorDeviceBooted($simulatedDeviceUDID)) { >+ print "Launching iOS Simulator...\n"; >+ relaunchIOSSimulator($simulatedDevice); >+ } >+ } else { >+ # FIXME: We should killall(1) any running instances of $appBundle before installing it to ensure >+ # that simctl launch opens the latest installed version of the app. For now we quit and >+ # launch the iOS Simulator again to ensure there are no running instances of $appBundle. >+ print "Quitting and launching iOS Simulator...\n"; >+ relaunchIOSSimulator($simulatedDevice); >+ >+ print "Installing $appBundle.\n"; >+ # Install custom built app, overwriting an app with the same app identifier if one exists. >+ exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "install", $simulatedDeviceUDID, $appBundle)) == 0 or die; >+ >+ } >+ >+ $simulatorOptions = {} unless $simulatorOptions; >+ >+ my %simulatorENV; >+ %simulatorENV = %{$simulatorOptions->{applicationEnvironment}} if $simulatorOptions->{applicationEnvironment}; >+ { >+ local %ENV; # Shadow global-scope %ENV so that changes to it will not be seen outside of this scope. >+ setupIOSWebKitEnvironment($productDir); >+ %simulatorENV = %ENV; >+ } >+ my $applicationArguments = \@ARGV; >+ $applicationArguments = $simulatorOptions->{applicationArguments} if $simulatorOptions && $simulatorOptions->{applicationArguments}; >+ >+ # Prefix the environment variables with SIMCTL_CHILD_ per `xcrun simctl help launch`. >+ foreach my $key (keys %simulatorENV) { >+ $ENV{"SIMCTL_CHILD_$key"} = $simulatorENV{$key}; >+ } >+ >+ print "Starting $appDisplayName with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; >+ return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "launch", $simulatedDeviceUDID, $appIdentifier, @$applicationArguments)); >+} >+ >+sub runIOSWebKitApp($) >+{ >+ my ($appBundle) = @_; >+ if (willUseIOSDeviceSDK()) { >+ die "Only running Safari in iOS Simulator is supported now."; >+ } >+ if (willUseIOSSimulatorSDK()) { >+ return runIOSWebKitAppInSimulator($appBundle); >+ } >+ die "Not using an iOS SDK." >+} >+ >+sub archCommandLineArgumentsForRestrictedEnvironmentVariables() >+{ >+ my @arguments = (); >+ foreach my $key (keys(%ENV)) { >+ if ($key =~ /^DYLD_/) { >+ push @arguments, "-e", "$key=$ENV{$key}"; >+ } >+ } >+ return @arguments; >+} >+ >+sub runMacWebKitApp($;$) >+{ >+ my ($appPath, $useOpenCommand) = @_; >+ my $productDir = productDir(); >+ print "Starting @{[basename($appPath)]} with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; >+ >+ local %ENV = %ENV; >+ setupMacWebKitEnvironment($productDir); >+ >+ if (defined($useOpenCommand) && $useOpenCommand == USE_OPEN_COMMAND) { >+ return system("open", "-W", "-a", $appPath, "--args", argumentsForRunAndDebugMacWebKitApp()); >+ } >+ if (architecture()) { >+ return system "arch", "-" . architecture(), archCommandLineArgumentsForRestrictedEnvironmentVariables(), $appPath, argumentsForRunAndDebugMacWebKitApp(); >+ } >+ return system { $appPath } $appPath, argumentsForRunAndDebugMacWebKitApp(); >+} >+ >+sub execMacWebKitAppForDebugging($) >+{ >+ my ($appPath) = @_; >+ my $architectureSwitch = "--arch"; >+ my $argumentsSeparator = "--"; >+ >+ my $debuggerPath = `xcrun -find lldb`; >+ chomp $debuggerPath; >+ die "Can't find the lldb executable.\n" unless -x $debuggerPath; >+ >+ my $productDir = productDir(); >+ setupMacWebKitEnvironment($productDir); >+ >+ my @architectureFlags = ($architectureSwitch, architecture()); >+ print "Starting @{[basename($appPath)]} under lldb with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n"; >+ exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $appPath, argumentsForRunAndDebugMacWebKitApp() or die; >+} >+ >+sub debugSafari >+{ >+ if (isAppleCocoaWebKit()) { >+ checkFrameworks(); >+ execMacWebKitAppForDebugging(safariPath()); >+ } >+ >+ return 1; # Unsupported platform; can't debug Safari on this platform. >+} >+ >+sub runSafari >+{ >+ if (isIOSWebKit()) { >+ return runIOSWebKitApp(mobileSafariBundle()); >+ } >+ >+ if (isAppleCocoaWebKit()) { >+ return runMacWebKitApp(safariPath()); >+ } >+ >+ if (isAppleWinWebKit()) { >+ my $result; >+ my $webKitLauncherPath = File::Spec->catfile(executableProductDir(), "MiniBrowser.exe"); >+ return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV; >+ } >+ >+ return 1; # Unsupported platform; can't run Safari on this platform. >+} >+ >+sub runMiniBrowser >+{ >+ if (isAppleCocoaWebKit()) { >+ return runMacWebKitApp(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser")); >+ } elsif (isAppleWinWebKit()) { >+ my $result; >+ my $webKitLauncherPath = File::Spec->catfile(executableProductDir(), "MiniBrowser.exe"); >+ return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV; >+ } >+ >+ return 1; >+} >+ >+sub debugMiniBrowser >+{ >+ if (isAppleCocoaWebKit()) { >+ execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser")); >+ } >+ >+ return 1; >+} >+ >+sub runWebKitTestRunner >+{ >+ if (isAppleCocoaWebKit()) { >+ return runMacWebKitApp(File::Spec->catfile(productDir(), "WebKitTestRunner")); >+ } >+ >+ return 1; >+} >+ >+sub debugWebKitTestRunner >+{ >+ if (isAppleCocoaWebKit()) { >+ execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "WebKitTestRunner")); >+ } >+ >+ return 1; >+} >+ >+sub readRegistryString >+{ >+ my ($valueName) = @_; >+ chomp(my $string = `regtool --wow32 get "$valueName"`); >+ return $string; >+} >+ >+sub writeRegistryString >+{ >+ my ($valueName, $string) = @_; >+ >+ my $error = system "regtool", "--wow32", "set", "-s", $valueName, $string; >+ >+ # On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still >+ # return a successful exit code. So we double-check here that the value we tried to write to the >+ # registry was really written. >+ return !$error && readRegistryString($valueName) eq $string; >+} >+ >+sub formatBuildTime($) >+{ >+ my ($buildTime) = @_; >+ >+ my $buildHours = int($buildTime / 3600); >+ my $buildMins = int(($buildTime - $buildHours * 3600) / 60); >+ my $buildSecs = $buildTime - $buildHours * 3600 - $buildMins * 60; >+ >+ if ($buildHours) { >+ return sprintf("%dh:%02dm:%02ds", $buildHours, $buildMins, $buildSecs); >+ } >+ return sprintf("%02dm:%02ds", $buildMins, $buildSecs); >+} >+ >+sub runSvnUpdateAndResolveChangeLogs(@) >+{ >+ my @svnOptions = @_; >+ my $openCommand = "svn update " . join(" ", @svnOptions); >+ open my $update, "$openCommand |" or die "cannot execute command $openCommand"; >+ my @conflictedChangeLogs; >+ while (my $line = <$update>) { >+ print $line; >+ $line =~ m/^C\s+(.+?)[\r\n]*$/; >+ if ($1) { >+ my $filename = normalizePath($1); >+ push @conflictedChangeLogs, $filename if basename($filename) eq "ChangeLog"; >+ } >+ } >+ close $update or die; >+ >+ if (@conflictedChangeLogs) { >+ print "Attempting to merge conflicted ChangeLogs.\n"; >+ my $resolveChangeLogsPath = File::Spec->catfile(sourceDir(), "Tools", "Scripts", "resolve-ChangeLogs"); >+ (system($resolveChangeLogsPath, "--no-warnings", @conflictedChangeLogs) == 0) >+ or die "Could not open resolve-ChangeLogs script: $!.\n"; >+ } >+} >+ >+sub runGitUpdate() >+{ >+ # Doing a git fetch first allows setups with svn-remote.svn.fetch = trunk:refs/remotes/origin/master >+ # to perform the rebase much much faster. >+ system("git", "fetch"); >+ if (isGitSVNDirectory(".")) { >+ system("git", "svn", "rebase") == 0 or die; >+ } else { >+ # This will die if branch.$BRANCHNAME.merge isn't set, which is >+ # almost certainly what we want. >+ system("git", "pull") == 0 or die; >+ } >+} >+ >+1;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
ews-watchlist
:
commit-queue-
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 185514
: 340316 |
340334