eec28b9b3266e66214d972bd5b28e5e81188eff9
[WebKit-https.git] / Tools / Scripts / webkitdirs.pm
1 # Copyright (C) 2005-2007, 2010-2016 Apple Inc. All rights reserved.
2 # Copyright (C) 2009 Google Inc. All rights reserved.
3 # Copyright (C) 2011 Research In Motion Limited. All rights reserved.
4 # Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # 1.  Redistributions of source code must retain the above copyright
11 #     notice, this list of conditions and the following disclaimer. 
12 # 2.  Redistributions in binary form must reproduce the above copyright
13 #     notice, this list of conditions and the following disclaimer in the
14 #     documentation and/or other materials provided with the distribution. 
15 # 3.  Neither the name of Apple Inc. ("Apple") nor the names of
16 #     its contributors may be used to endorse or promote products derived
17 #     from this software without specific prior written permission. 
18 #
19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 # Module to share code to get to WebKit directories.
31
32 use strict;
33 use version;
34 use warnings;
35 use Config;
36 use Cwd qw(realpath);
37 use Digest::MD5 qw(md5_hex);
38 use FindBin;
39 use File::Basename;
40 use File::Find;
41 use File::Path qw(make_path mkpath rmtree);
42 use File::Spec;
43 use File::stat;
44 use List::Util;
45 use POSIX;
46 use Time::HiRes qw(usleep);
47 use VCSUtils;
48
49 BEGIN {
50    use Exporter   ();
51    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
52    $VERSION     = 1.00;
53    @ISA         = qw(Exporter);
54    @EXPORT      = qw(
55        &XcodeCoverageSupportOptions
56        &XcodeOptionString
57        &XcodeOptionStringNoConfig
58        &XcodeOptions
59        &XcodeStaticAnalyzerOption
60        &appDisplayNameFromBundle
61        &appendToEnvironmentVariableList
62        &archCommandLineArgumentsForRestrictedEnvironmentVariables
63        &baseProductDir
64        &chdirWebKit
65        &checkFrameworks
66        &cmakeBasedPortArguments
67        &currentSVNRevision
68        &debugSafari
69        &executableProductDir
70        &findOrCreateSimulatorForIOSDevice
71        &iosSimulatorDeviceByName
72        &nmPath
73        &passedConfiguration
74        &prependToEnvironmentVariableList
75        &printHelpAndExitForRunAndDebugWebKitAppIfNeeded
76        &productDir
77        &quitIOSSimulator
78        &relaunchIOSSimulator
79        &restartIOSSimulatorDevice
80        &runIOSWebKitApp
81        &runMacWebKitApp
82        &safariPath
83        &iosVersion
84        &setConfiguration
85        &setupMacWebKitEnvironment
86        &sharedCommandLineOptions
87        &sharedCommandLineOptionsUsage
88        &shutDownIOSSimulatorDevice
89        &willUseIOSDeviceSDK
90        &willUseIOSSimulatorSDK
91        SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT
92        USE_OPEN_COMMAND
93    );
94    %EXPORT_TAGS = ( );
95    @EXPORT_OK   = ();
96 }
97
98 # Ports
99 use constant {
100     AppleWin => "AppleWin",
101     GTK      => "GTK",
102     Efl      => "Efl",
103     iOS      => "iOS",
104     Mac      => "Mac",
105     JSCOnly  => "JSCOnly",
106     WinCairo => "WinCairo",
107     Unknown  => "Unknown"
108 };
109
110 use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp().
111 use constant SIMULATOR_DEVICE_STATE_SHUTDOWN => "1";
112 use constant SIMULATOR_DEVICE_STATE_BOOTED => "3";
113 use constant SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT  => "For WebKit Development";
114
115 # 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>.
116 use constant IOS_DEVELOPMENT_CERTIFICATE_NAME_PREFIX => "iPhone Developer: ";
117
118 our @EXPORT_OK;
119
120 my $architecture;
121 my $asanIsEnabled;
122 my $numberOfCPUs;
123 my $maxCPULoad;
124 my $baseProductDir;
125 my @baseProductDirOption;
126 my $configuration;
127 my $xcodeSDK;
128 my $configurationForVisualStudio;
129 my $configurationProductDir;
130 my $sourceDir;
131 my $currentSVNRevision;
132 my $didLoadIPhoneSimulatorNotification;
133 my $nmPath;
134 my $osXVersion;
135 my $iosVersion;
136 my $generateDsym;
137 my $isCMakeBuild;
138 my $isWin64;
139 my $isInspectorFrontend;
140 my $portName;
141 my $shouldUseGuardMalloc;
142 my $shouldNotUseNinja;
143 my $xcodeVersion;
144
145 my $unknownPortProhibited = 0;
146
147 # Variables for Win32 support
148 my $programFilesPath;
149 my $vcBuildPath;
150 my $vsInstallDir;
151 my $msBuildInstallDir;
152 my $vsVersion;
153 my $windowsSourceDir;
154 my $winVersion;
155 my $willUseVCExpressWhenBuilding = 0;
156
157 # Defined in VCSUtils.
158 sub exitStatus($);
159
160 sub findMatchingArguments($$);
161 sub hasArgument($$);
162
163 sub determineSourceDir
164 {
165     return if $sourceDir;
166     $sourceDir = $FindBin::Bin;
167     $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
168
169     # walks up path checking each directory to see if it is the main WebKit project dir, 
170     # defined by containing Sources, WebCore, and JavaScriptCore.
171     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")))
172     {
173         if ($sourceDir !~ s|/[^/]+$||) {
174             die "Could not find top level webkit directory above source directory using FindBin.\n";
175         }
176     }
177
178     $sourceDir = File::Spec->catdir($sourceDir, "OpenSource") if -d File::Spec->catdir($sourceDir, "OpenSource");
179 }
180
181 sub currentPerlPath()
182 {
183     my $thisPerl = $^X;
184     if ($^O ne 'VMS') {
185         $thisPerl .= $Config{_exe} unless $thisPerl =~ m/$Config{_exe}$/i;
186     }
187     return $thisPerl;
188 }
189
190 # used for scripts which are stored in a non-standard location
191 sub setSourceDir($)
192 {
193     ($sourceDir) = @_;
194 }
195
196 sub determineNinjaVersion
197 {
198     chomp(my $ninjaVersion = `ninja --version`);
199     return $ninjaVersion;
200 }
201
202 sub determineXcodeVersion
203 {
204     return if defined $xcodeVersion;
205     my $xcodebuildVersionOutput = `xcodebuild -version`;
206     $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : "3.0";
207 }
208
209 sub readXcodeUserDefault($)
210 {
211     my ($key) = @_;
212
213     my $devnull = File::Spec->devnull();
214
215     my $value = `defaults read com.apple.dt.Xcode ${key} 2> ${devnull}`;
216     return if $?;
217
218     chomp $value;
219     return $value;
220 }
221
222 sub determineBaseProductDir
223 {
224     return if defined $baseProductDir;
225     determineSourceDir();
226
227     my $setSharedPrecompsDir;
228     $baseProductDir = $ENV{"WEBKIT_OUTPUTDIR"};
229
230     if (!defined($baseProductDir) and isAppleCocoaWebKit()) {
231         # Silently remove ~/Library/Preferences/xcodebuild.plist which can
232         # cause build failure. The presence of
233         # ~/Library/Preferences/xcodebuild.plist can prevent xcodebuild from
234         # respecting global settings such as a custom build products directory
235         # (<rdar://problem/5585899>).
236         my $personalPlistFile = $ENV{HOME} . "/Library/Preferences/xcodebuild.plist";
237         if (-e $personalPlistFile) {
238             unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!";
239         }
240
241         my $buildLocationStyle = join '', readXcodeUserDefault("IDEBuildLocationStyle");
242         if ($buildLocationStyle eq "Custom") {
243             my $buildLocationType = join '', readXcodeUserDefault("IDECustomBuildLocationType");
244             # FIXME: Read CustomBuildIntermediatesPath and set OBJROOT accordingly.
245             $baseProductDir = readXcodeUserDefault("IDECustomBuildProductsPath") if $buildLocationType eq "Absolute";
246         }
247
248         # DeterminedByTargets corresponds to a setting of "Legacy" in Xcode.
249         # It is the only build location style for which SHARED_PRECOMPS_DIR is not
250         # overridden when building from within Xcode.
251         $setSharedPrecompsDir = 1 if $buildLocationStyle ne "DeterminedByTargets";
252
253         if (!defined($baseProductDir)) {
254             $baseProductDir = join '', readXcodeUserDefault("IDEApplicationwideBuildSettings");
255             $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s;
256         }
257
258         undef $baseProductDir unless $baseProductDir =~ /^\//;
259     }
260
261     if (!defined($baseProductDir)) { # Port-specific checks failed, use default
262         $baseProductDir = File::Spec->catdir($sourceDir, "WebKitBuild");
263     }
264
265     if (isGit() && isGitBranchBuild()) {
266         my $branch = gitBranch();
267         $baseProductDir = "$baseProductDir/$branch";
268     }
269
270     if (isAppleCocoaWebKit()) {
271         $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|;
272         $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|;
273         $baseProductDir =~ s|^~/|$ENV{HOME}/|;
274         die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/;
275         die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/;
276         @baseProductDirOption = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir");
277         push(@baseProductDirOption, "SHARED_PRECOMPS_DIR=${baseProductDir}/PrecompiledHeaders") if $setSharedPrecompsDir;
278     }
279
280     if (isCygwin()) {
281         my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`;
282         chomp $dosBuildPath;
283         $ENV{"WEBKIT_OUTPUTDIR"} = $dosBuildPath;
284         my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`;
285         chomp $unixBuildPath;
286         $baseProductDir = $dosBuildPath;
287     }
288 }
289
290 sub systemVerbose {
291     print "+ @_\n";
292     return system(@_);
293 }
294
295 sub setBaseProductDir($)
296 {
297     ($baseProductDir) = @_;
298 }
299
300 sub determineConfiguration
301 {
302     return if defined $configuration;
303     determineBaseProductDir();
304     if (open CONFIGURATION, "$baseProductDir/Configuration") {
305         $configuration = <CONFIGURATION>;
306         close CONFIGURATION;
307     }
308     if ($configuration) {
309         chomp $configuration;
310         # compatibility for people who have old Configuration files
311         $configuration = "Release" if $configuration eq "Deployment";
312         $configuration = "Debug" if $configuration eq "Development";
313     } else {
314         $configuration = "Release";
315     }
316 }
317
318 sub determineArchitecture
319 {
320     return if defined $architecture;
321     # make sure $architecture is defined in all cases
322     $architecture = "";
323
324     determineBaseProductDir();
325     determineXcodeSDK();
326
327     if (isAppleCocoaWebKit()) {
328         if (open ARCHITECTURE, "$baseProductDir/Architecture") {
329             $architecture = <ARCHITECTURE>;
330             close ARCHITECTURE;
331         }
332         if ($architecture) {
333             chomp $architecture;
334         } else {
335             if (not defined $xcodeSDK or $xcodeSDK =~ /^(\/$|macosx)/) {
336                 my $supports64Bit = `sysctl -n hw.optional.x86_64`;
337                 chomp $supports64Bit;
338                 $architecture = 'x86_64' if $supports64Bit;
339             } elsif ($xcodeSDK =~ /^iphonesimulator/) {
340                 $architecture = 'x86_64';
341             } elsif ($xcodeSDK =~ /^iphoneos/) {
342                 $architecture = 'arm64';
343             }
344         }
345     } elsif (isCMakeBuild()) {
346         if (open my $cmake_sysinfo, "cmake --system-information |") {
347             while (<$cmake_sysinfo>) {
348                 next unless index($_, 'CMAKE_SYSTEM_PROCESSOR') == 0;
349                 if (/^CMAKE_SYSTEM_PROCESSOR \"([^"]+)\"/) {
350                     $architecture = $1;
351                     $architecture = 'x86_64' if $architecture eq 'amd64';
352                     last;
353                 }
354             }
355             close $cmake_sysinfo;
356         }
357     }
358
359     if (!isAnyWindows()) {
360         if (!$architecture) {
361             # Fall back to output of `arch', if it is present.
362             $architecture = `arch`;
363             chomp $architecture;
364         }
365
366         if (!$architecture) {
367             # Fall back to output of `uname -m', if it is present.
368             $architecture = `uname -m`;
369             chomp $architecture;
370         }
371     }
372
373     $architecture = 'x86_64' if ($architecture =~ /amd64/ && isBSD());
374 }
375
376 sub determineASanIsEnabled
377 {
378     return if defined $asanIsEnabled;
379     determineBaseProductDir();
380
381     $asanIsEnabled = 0;
382     my $asanConfigurationValue;
383
384     if (open ASAN, "$baseProductDir/ASan") {
385         $asanConfigurationValue = <ASAN>;
386         close ASAN;
387         chomp $asanConfigurationValue;
388         $asanIsEnabled = 1 if $asanConfigurationValue eq "YES";
389     }
390 }
391
392 sub determineNumberOfCPUs
393 {
394     return if defined $numberOfCPUs;
395     if (defined($ENV{NUMBER_OF_PROCESSORS})) {
396         $numberOfCPUs = $ENV{NUMBER_OF_PROCESSORS};
397     } elsif (isLinux()) {
398         # First try the nproc utility, if it exists. If we get no
399         # results fall back to just interpretting /proc directly.
400         chomp($numberOfCPUs = `nproc --all 2> /dev/null`);
401         if ($numberOfCPUs eq "") {
402             $numberOfCPUs = (grep /processor/, `cat /proc/cpuinfo`);
403         }
404     } elsif (isAnyWindows()) {
405         # Assumes cygwin
406         $numberOfCPUs = `ls /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor | wc -w`;
407     } elsif (isDarwin() || isBSD()) {
408         chomp($numberOfCPUs = `sysctl -n hw.ncpu`);
409     }
410 }
411
412 sub determineMaxCPULoad
413 {
414     return if defined $maxCPULoad;
415     if (defined($ENV{MAX_CPU_LOAD})) {
416         $maxCPULoad = $ENV{MAX_CPU_LOAD};
417     }
418 }
419
420 sub jscPath($)
421 {
422     my ($productDir) = @_;
423     my $jscName = "jsc";
424     $jscName .= "_debug"  if configuration() eq "Debug_All";
425     $jscName .= ".exe" if (isAnyWindows());
426     return "$productDir/$jscName" if -e "$productDir/$jscName";
427     return "$productDir/JavaScriptCore.framework/Resources/$jscName";
428 }
429
430 sub argumentsForConfiguration()
431 {
432     determineConfiguration();
433     determineArchitecture();
434     determineXcodeSDK();
435
436     my @args = ();
437     # FIXME: Is it necessary to pass --debug, --release, --32-bit or --64-bit?
438     # These are determined automatically from stored configuration.
439     push(@args, '--debug') if ($configuration =~ "^Debug");
440     push(@args, '--release') if ($configuration =~ "^Release");
441     push(@args, '--ios-device') if (defined $xcodeSDK && $xcodeSDK =~ /^iphoneos/);
442     push(@args, '--ios-simulator') if (defined $xcodeSDK && $xcodeSDK =~ /^iphonesimulator/);
443     push(@args, '--32-bit') if ($architecture ne "x86_64" and !isWin64());
444     push(@args, '--64-bit') if (isWin64());
445     push(@args, '--gtk') if isGtk();
446     push(@args, '--efl') if isEfl();
447     push(@args, '--jsc-only') if isJSCOnly();
448     push(@args, '--wincairo') if isWinCairo();
449     push(@args, '--inspector-frontend') if isInspectorFrontend();
450     return @args;
451 }
452
453 sub determineXcodeSDK
454 {
455     return if defined $xcodeSDK;
456     my $sdk;
457     if (checkForArgumentAndRemoveFromARGVGettingValue("--sdk", \$sdk)) {
458         $xcodeSDK = $sdk;
459     }
460     if (checkForArgumentAndRemoveFromARGV("--device") || checkForArgumentAndRemoveFromARGV("--ios-device")) {
461         my $hasInternalSDK = exitStatus(system("xcrun --sdk iphoneos.internal --show-sdk-version > /dev/null 2>&1")) == 0;
462         $xcodeSDK ||= $hasInternalSDK ? "iphoneos.internal" : "iphoneos";
463     }
464     if (checkForArgumentAndRemoveFromARGV("--simulator") || checkForArgumentAndRemoveFromARGV("--ios-simulator")) {
465         $xcodeSDK ||= 'iphonesimulator';
466     }
467 }
468
469 sub xcodeSDK
470 {
471     determineXcodeSDK();
472     return $xcodeSDK;
473 }
474
475 sub setXcodeSDK($)
476 {
477     ($xcodeSDK) = @_;
478 }
479
480
481 sub xcodeSDKPlatformName()
482 {
483     determineXcodeSDK();
484     return "" if !defined $xcodeSDK;
485     return "iphoneos" if $xcodeSDK =~ /iphoneos/i;
486     return "iphonesimulator" if $xcodeSDK =~ /iphonesimulator/i;
487     return "macosx" if $xcodeSDK =~ /macosx/i;
488     die "Couldn't determine platform name from Xcode SDK";
489 }
490
491 sub XcodeSDKPath
492 {
493     determineXcodeSDK();
494
495     die "Can't find the SDK path because no Xcode SDK was specified" if not $xcodeSDK;
496
497     my $sdkPath = `xcrun --sdk $xcodeSDK --show-sdk-path` if $xcodeSDK;
498     die 'Failed to get SDK path from xcrun' if $?;
499     chomp $sdkPath;
500
501     return $sdkPath;
502 }
503
504 sub xcodeSDKVersion
505 {
506     determineXcodeSDK();
507
508     die "Can't find the SDK version because no Xcode SDK was specified" if !$xcodeSDK;
509
510     chomp(my $sdkVersion = `xcrun --sdk $xcodeSDK --show-sdk-version`);
511     die "Failed to get SDK version from xcrun" if exitStatus($?);
512
513     return $sdkVersion;
514 }
515
516 sub programFilesPath
517 {
518     return $programFilesPath if defined $programFilesPath;
519
520     $programFilesPath = $ENV{'PROGRAMFILES(X86)'} || $ENV{'PROGRAMFILES'} || "C:\\Program Files";
521
522     return $programFilesPath;
523 }
524
525 sub visualStudioInstallDir
526 {
527     return $vsInstallDir if defined $vsInstallDir;
528
529     if ($ENV{'VSINSTALLDIR'}) {
530         $vsInstallDir = $ENV{'VSINSTALLDIR'};
531         $vsInstallDir =~ s|[\\/]$||;
532     } else {
533         $vsInstallDir = File::Spec->catdir(programFilesPath(), "Microsoft Visual Studio 14.0");
534     }
535     chomp($vsInstallDir = `cygpath "$vsInstallDir"`) if isCygwin();
536
537     print "Using Visual Studio: $vsInstallDir\n";
538     return $vsInstallDir;
539 }
540
541 sub msBuildInstallDir
542 {
543     return $msBuildInstallDir if defined $msBuildInstallDir;
544
545     $msBuildInstallDir = File::Spec->catdir(programFilesPath(), "MSBuild", "14.0", "Bin");
546    
547     chomp($msBuildInstallDir = `cygpath "$msBuildInstallDir"`) if isCygwin();
548
549     print "Using MSBuild: $msBuildInstallDir\n";
550     return $msBuildInstallDir;
551 }
552
553 sub visualStudioVersion
554 {
555     return $vsVersion if defined $vsVersion;
556
557     my $installDir = visualStudioInstallDir();
558
559     $vsVersion = ($installDir =~ /Microsoft Visual Studio ([0-9]+\.[0-9]*)/) ? $1 : "14";
560
561     print "Using Visual Studio $vsVersion\n";
562     return $vsVersion;
563 }
564
565 sub determineConfigurationForVisualStudio
566 {
567     return if defined $configurationForVisualStudio;
568     determineConfiguration();
569     # FIXME: We should detect when Debug_All or Production has been chosen.
570     $configurationForVisualStudio = "/p:Configuration=" . $configuration;
571 }
572
573 sub usesPerConfigurationBuildDirectory
574 {
575     # [Gtk] We don't have Release/Debug configurations in straight
576     # autotool builds (non build-webkit). In this case and if
577     # WEBKIT_OUTPUTDIR exist, use that as our configuration dir. This will
578     # allows us to run run-webkit-tests without using build-webkit.
579     return ($ENV{"WEBKIT_OUTPUTDIR"} && isGtk()) || isAppleWinWebKit();
580 }
581
582 sub determineConfigurationProductDir
583 {
584     return if defined $configurationProductDir;
585     determineBaseProductDir();
586     determineConfiguration();
587     if (isAppleWinWebKit() || isWinCairo()) {
588         $configurationProductDir = File::Spec->catdir($baseProductDir, $configuration);
589     } else {
590         if (usesPerConfigurationBuildDirectory()) {
591             $configurationProductDir = "$baseProductDir";
592         } else {
593             $configurationProductDir = "$baseProductDir/$configuration";
594             $configurationProductDir .= "-" . xcodeSDKPlatformName() if isIOSWebKit();
595         }
596     }
597 }
598
599 sub setConfigurationProductDir($)
600 {
601     ($configurationProductDir) = @_;
602 }
603
604 sub determineCurrentSVNRevision
605 {
606     # We always update the current SVN revision here, and leave the caching
607     # to currentSVNRevision(), so that changes to the SVN revision while the
608     # script is running can be picked up by calling this function again.
609     determineSourceDir();
610     $currentSVNRevision = svnRevisionForDirectory($sourceDir);
611     return $currentSVNRevision;
612 }
613
614
615 sub chdirWebKit
616 {
617     determineSourceDir();
618     chdir $sourceDir or die;
619 }
620
621 sub baseProductDir
622 {
623     determineBaseProductDir();
624     return $baseProductDir;
625 }
626
627 sub sourceDir
628 {
629     determineSourceDir();
630     return $sourceDir;
631 }
632
633 sub productDir
634 {
635     determineConfigurationProductDir();
636     return $configurationProductDir;
637 }
638
639 sub executableProductDir
640 {
641     my $productDirectory = productDir();
642
643     my $binaryDirectory;
644     if (isEfl() || isGtk() || isJSCOnly()) {
645         $binaryDirectory = "bin";
646     } elsif (isAnyWindows()) {
647         $binaryDirectory = isWin64() ? "bin64" : "bin32";
648     } else {
649         return $productDirectory;
650     }
651
652     return File::Spec->catdir($productDirectory, $binaryDirectory);
653 }
654
655 sub jscProductDir
656 {
657     return executableProductDir();
658 }
659
660 sub configuration()
661 {
662     determineConfiguration();
663     return $configuration;
664 }
665
666 sub asanIsEnabled()
667 {
668     determineASanIsEnabled();
669     return $asanIsEnabled;
670 }
671
672 sub configurationForVisualStudio()
673 {
674     determineConfigurationForVisualStudio();
675     return $configurationForVisualStudio;
676 }
677
678 sub currentSVNRevision
679 {
680     determineCurrentSVNRevision() if not defined $currentSVNRevision;
681     return $currentSVNRevision;
682 }
683
684 sub generateDsym()
685 {
686     determineGenerateDsym();
687     return $generateDsym;
688 }
689
690 sub determineGenerateDsym()
691 {
692     return if defined($generateDsym);
693     $generateDsym = checkForArgumentAndRemoveFromARGV("--dsym");
694 }
695
696 sub hasIOSDevelopmentCertificate()
697 {
698     return !exitStatus(system("security find-identity -p codesigning | grep '" . IOS_DEVELOPMENT_CERTIFICATE_NAME_PREFIX . "' > /dev/null 2>&1"));
699 }
700
701 sub argumentsForXcode()
702 {
703     my @args = ();
704     push @args, "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym" if generateDsym();
705     return @args;
706 }
707
708 sub XcodeOptions
709 {
710     determineBaseProductDir();
711     determineConfiguration();
712     determineArchitecture();
713     determineASanIsEnabled();
714     determineXcodeSDK();
715
716     my @options;
717     push @options, "-UseSanitizedBuildSystemEnvironment=YES";
718     push @options, ("-configuration", $configuration);
719     push @options, ("-xcconfig", sourceDir() . "/Tools/asan/asan.xcconfig", "ASAN_IGNORE=" . sourceDir() . "/Tools/asan/webkit-asan-ignore.txt") if $asanIsEnabled;
720     push @options, @baseProductDirOption;
721     push @options, "ARCHS=$architecture" if $architecture;
722     push @options, "SDKROOT=$xcodeSDK" if $xcodeSDK;
723     if (willUseIOSDeviceSDK()) {
724         push @options, "ENABLE_BITCODE=NO";
725         if (hasIOSDevelopmentCertificate()) {
726             # FIXME: May match more than one installed development certificate.
727             push @options, "CODE_SIGN_IDENTITY=" . IOS_DEVELOPMENT_CERTIFICATE_NAME_PREFIX;
728         } else {
729             push @options, "CODE_SIGN_IDENTITY="; # No identity
730             push @options, "CODE_SIGNING_REQUIRED=NO";
731         }
732     }
733     push @options, argumentsForXcode();
734     return @options;
735 }
736
737 sub XcodeOptionString
738 {
739     return join " ", XcodeOptions();
740 }
741
742 sub XcodeOptionStringNoConfig
743 {
744     return join " ", @baseProductDirOption;
745 }
746
747 sub XcodeCoverageSupportOptions()
748 {
749     my @coverageSupportOptions = ();
750     push @coverageSupportOptions, "GCC_GENERATE_TEST_COVERAGE_FILES=YES";
751     push @coverageSupportOptions, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES";
752     return @coverageSupportOptions;
753 }
754
755 sub XcodeStaticAnalyzerOption()
756 {
757     return "RUN_CLANG_STATIC_ANALYZER=YES";
758 }
759
760 my $passedConfiguration;
761 my $searchedForPassedConfiguration;
762 sub determinePassedConfiguration
763 {
764     return if $searchedForPassedConfiguration;
765     $searchedForPassedConfiguration = 1;
766     $passedConfiguration = undef;
767
768     if (checkForArgumentAndRemoveFromARGV("--debug")) {
769         $passedConfiguration = "Debug";
770     } elsif(checkForArgumentAndRemoveFromARGV("--release")) {
771         $passedConfiguration = "Release";
772     } elsif (checkForArgumentAndRemoveFromARGV("--profile") || checkForArgumentAndRemoveFromARGV("--profiling")) {
773         $passedConfiguration = "Profiling";
774     }
775 }
776
777 sub passedConfiguration
778 {
779     determinePassedConfiguration();
780     return $passedConfiguration;
781 }
782
783 sub setConfiguration
784 {
785     setArchitecture();
786
787     if (my $config = shift @_) {
788         $configuration = $config;
789         return;
790     }
791
792     determinePassedConfiguration();
793     $configuration = $passedConfiguration if $passedConfiguration;
794 }
795
796
797 my $passedArchitecture;
798 my $searchedForPassedArchitecture;
799 sub determinePassedArchitecture
800 {
801     return if $searchedForPassedArchitecture;
802     $searchedForPassedArchitecture = 1;
803
804     $passedArchitecture = undef;
805     if (checkForArgumentAndRemoveFromARGV("--32-bit")) {
806         if (isAppleCocoaWebKit()) {
807             # PLATFORM_IOS: Don't run `arch` command inside Simulator environment
808             local %ENV = %ENV;
809             delete $ENV{DYLD_ROOT_PATH};
810             delete $ENV{DYLD_FRAMEWORK_PATH};
811
812             $passedArchitecture = `arch`;
813             chomp $passedArchitecture;
814         }
815     }
816 }
817
818 sub passedArchitecture
819 {
820     determinePassedArchitecture();
821     return $passedArchitecture;
822 }
823
824 sub architecture()
825 {
826     determineArchitecture();
827     return $architecture;
828 }
829
830 sub numberOfCPUs()
831 {
832     determineNumberOfCPUs();
833     return $numberOfCPUs;
834 }
835
836 sub maxCPULoad()
837 {
838     determineMaxCPULoad();
839     return $maxCPULoad;
840 }
841
842 sub setArchitecture
843 {
844     if (my $arch = shift @_) {
845         $architecture = $arch;
846         return;
847     }
848
849     determinePassedArchitecture();
850     $architecture = $passedArchitecture if $passedArchitecture;
851 }
852
853 # Locate Safari.
854 sub safariPath
855 {
856     die "Safari path is only relevant on Apple Mac platform\n" unless isAppleCocoaWebKit();
857
858     my $safariPath;
859
860     # Use WEBKIT_SAFARI environment variable if present.
861     my $safariBundle = $ENV{WEBKIT_SAFARI};
862     if (!$safariBundle) {
863         determineConfigurationProductDir();
864         # Use Safari.app in product directory if present (good for Safari development team).
865         if (-d "$configurationProductDir/Safari.app") {
866             $safariBundle = "$configurationProductDir/Safari.app";
867         }
868     }
869
870     if ($safariBundle) {
871         $safariPath = "$safariBundle/Contents/MacOS/Safari";
872     } else {
873         $safariPath = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment";
874     }
875
876     die "Can't find executable at $safariPath.\n" if !-x $safariPath;
877     return $safariPath;
878 }
879
880 sub builtDylibPathForName
881 {
882     my $libraryName = shift;
883     determineConfigurationProductDir();
884
885     if (isGtk()) {
886         my $extension = isDarwin() ? ".dylib" : ".so";
887         return "$configurationProductDir/lib/libwebkit2gtk-4.0" . $extension;
888     }
889     if (isEfl()) {
890         return "$configurationProductDir/lib/libewebkit2.so";
891     }
892     if (isIOSWebKit()) {
893         return "$configurationProductDir/$libraryName.framework/$libraryName";
894     }
895     if (isAppleCocoaWebKit()) {
896         return "$configurationProductDir/$libraryName.framework/Versions/A/$libraryName";
897     }
898     if (isAppleWinWebKit()) {
899         if ($libraryName eq "JavaScriptCore") {
900             return "$baseProductDir/lib/$libraryName.lib";
901         } else {
902             return "$baseProductDir/$libraryName.intermediate/$configuration/$libraryName.intermediate/$libraryName.lib";
903         }
904     }
905
906     die "Unsupported platform, can't determine built library locations.\nTry `build-webkit --help` for more information.\n";
907 }
908
909 # Check to see that all the frameworks are built.
910 sub checkFrameworks # FIXME: This is a poor name since only the Mac calls built WebCore a Framework.
911 {
912     return if isAnyWindows();
913     my @frameworks = ("JavaScriptCore", "WebCore");
914     push(@frameworks, "WebKit") if isAppleCocoaWebKit(); # FIXME: This seems wrong, all ports should have a WebKit these days.
915     for my $framework (@frameworks) {
916         my $path = builtDylibPathForName($framework);
917         die "Can't find built framework at \"$path\".\n" unless -e $path;
918     }
919 }
920
921 sub isInspectorFrontend()
922 {
923     determineIsInspectorFrontend();
924     return $isInspectorFrontend;
925 }
926
927 sub determineIsInspectorFrontend()
928 {
929     return if defined($isInspectorFrontend);
930     $isInspectorFrontend = checkForArgumentAndRemoveFromARGV("--inspector-frontend");
931 }
932
933 sub commandExists($)
934 {
935     my $command = shift;
936     my $devnull = File::Spec->devnull();
937
938     if (isAnyWindows()) {
939         return exitStatus(system("where /q $command >$devnull 2>&1")) == 0;
940     }
941     return exitStatus(system("which $command >$devnull 2>&1")) == 0;
942 }
943
944 sub checkForArgumentAndRemoveFromARGV($)
945 {
946     my $argToCheck = shift;
947     return checkForArgumentAndRemoveFromArrayRef($argToCheck, \@ARGV);
948 }
949
950 sub checkForArgumentAndRemoveFromArrayRefGettingValue($$$)
951 {
952     my ($argToCheck, $valueRef, $arrayRef) = @_;
953     my $argumentStartRegEx = qr#^$argToCheck(?:=\S|$)#;
954     my $i = 0;
955     for (; $i < @$arrayRef; ++$i) {
956         last if $arrayRef->[$i] =~ $argumentStartRegEx;
957     }
958     if ($i >= @$arrayRef) {
959         return $$valueRef = undef;
960     }
961     my ($key, $value) = split("=", $arrayRef->[$i]);
962     splice(@$arrayRef, $i, 1);
963     if (defined($value)) {
964         # e.g. --sdk=iphonesimulator
965         return $$valueRef = $value;
966     }
967     return $$valueRef = splice(@$arrayRef, $i, 1); # e.g. --sdk iphonesimulator
968 }
969
970 sub checkForArgumentAndRemoveFromARGVGettingValue($$)
971 {
972     my ($argToCheck, $valueRef) = @_;
973     return checkForArgumentAndRemoveFromArrayRefGettingValue($argToCheck, $valueRef, \@ARGV);
974 }
975
976 sub findMatchingArguments($$)
977 {
978     my ($argToCheck, $arrayRef) = @_;
979     my @matchingIndices;
980     foreach my $index (0 .. $#$arrayRef) {
981         my $opt = $$arrayRef[$index];
982         if ($opt =~ /^$argToCheck$/i ) {
983             push(@matchingIndices, $index);
984         }
985     }
986     return @matchingIndices; 
987 }
988
989 sub hasArgument($$)
990 {
991     my ($argToCheck, $arrayRef) = @_;
992     my @matchingIndices = findMatchingArguments($argToCheck, $arrayRef);
993     return scalar @matchingIndices > 0;
994 }
995
996 sub checkForArgumentAndRemoveFromArrayRef
997 {
998     my ($argToCheck, $arrayRef) = @_;
999     my @indicesToRemove = findMatchingArguments($argToCheck, $arrayRef);
1000     my $removeOffset = 0;
1001     foreach my $index (@indicesToRemove) {
1002         splice(@$arrayRef, $index - $removeOffset++, 1);
1003     }
1004     return scalar @indicesToRemove > 0;
1005 }
1006
1007 sub prohibitUnknownPort()
1008 {
1009     $unknownPortProhibited = 1;
1010 }
1011
1012 sub determinePortName()
1013 {
1014     return if defined $portName;
1015
1016     my %argToPortName = (
1017         efl => Efl,
1018         gtk => GTK,
1019         'jsc-only' => JSCOnly,
1020         wincairo => WinCairo
1021     );
1022
1023     for my $arg (sort keys %argToPortName) {
1024         if (checkForArgumentAndRemoveFromARGV("--$arg")) {
1025             die "Argument '--$arg' conflicts with selected port '$portName'\n"
1026                 if defined $portName;
1027
1028             $portName = $argToPortName{$arg};
1029         }
1030     }
1031
1032     return if defined $portName;
1033
1034     # Port was not selected via command line, use appropriate default value
1035
1036     if (isAnyWindows()) {
1037         $portName = AppleWin;
1038     } elsif (isDarwin()) {
1039         determineXcodeSDK();
1040         if (willUseIOSDeviceSDK() || willUseIOSSimulatorSDK()) {
1041             $portName = iOS;
1042         } else {
1043             $portName = Mac;
1044         }
1045     } else {
1046         if ($unknownPortProhibited) {
1047             my $portsChoice = join "\n\t", qw(
1048                 --efl
1049                 --gtk
1050                 --jsc-only
1051             );
1052             die "Please specify which WebKit port to build using one of the following options:"
1053                 . "\n\t$portsChoice\n";
1054         }
1055
1056         # If script is run without arguments we cannot determine port
1057         # TODO: This state should be outlawed
1058         $portName = Unknown;
1059     }
1060 }
1061
1062 sub portName()
1063 {
1064     determinePortName();
1065     return $portName;
1066 }
1067
1068 sub isEfl()
1069 {
1070     return portName() eq Efl;
1071 }
1072
1073 sub isGtk()
1074 {
1075     return portName() eq GTK;
1076 }
1077
1078 sub isJSCOnly()
1079 {
1080     return portName() eq JSCOnly;
1081 }
1082
1083 # Determine if this is debian, ubuntu, linspire, or something similar.
1084 sub isDebianBased()
1085 {
1086     return -e "/etc/debian_version";
1087 }
1088
1089 sub isFedoraBased()
1090 {
1091     return -e "/etc/fedora-release";
1092 }
1093
1094 sub isWinCairo()
1095 {
1096     return portName() eq WinCairo;
1097 }
1098
1099 sub isWin64()
1100 {
1101     determineIsWin64();
1102     return $isWin64;
1103 }
1104
1105 sub determineIsWin64()
1106 {
1107     return if defined($isWin64);
1108     $isWin64 = checkForArgumentAndRemoveFromARGV("--64-bit");
1109 }
1110
1111 sub determineIsWin64FromArchitecture($)
1112 {
1113     my $arch = shift;
1114     $isWin64 = ($arch eq "x86_64");
1115     return $isWin64;
1116 }
1117
1118 sub isCygwin()
1119 {
1120     return ($^O eq "cygwin") || 0;
1121 }
1122
1123 sub isAnyWindows()
1124 {
1125     return isWindows() || isCygwin();
1126 }
1127
1128 sub determineWinVersion()
1129 {
1130     return if $winVersion;
1131
1132     if (!isAnyWindows()) {
1133         $winVersion = -1;
1134         return;
1135     }
1136
1137     my $versionString = `cmd /c ver`;
1138     $versionString =~ /(\d)\.(\d)\.(\d+)/;
1139
1140     $winVersion = {
1141         major => $1,
1142         minor => $2,
1143         build => $3,
1144     };
1145 }
1146
1147 sub winVersion()
1148 {
1149     determineWinVersion();
1150     return $winVersion;
1151 }
1152
1153 sub isWindows7SP0()
1154 {
1155     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 1 && winVersion()->{build} == 7600;
1156 }
1157
1158 sub isWindowsVista()
1159 {
1160     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 0;
1161 }
1162
1163 sub isWindowsXP()
1164 {
1165     return isAnyWindows() && winVersion()->{major} == 5 && winVersion()->{minor} == 1;
1166 }
1167
1168 sub isDarwin()
1169 {
1170     return ($^O eq "darwin") || 0;
1171 }
1172
1173 sub isWindows()
1174 {
1175     return ($^O eq "MSWin32") || 0;
1176 }
1177
1178 sub isLinux()
1179 {
1180     return ($^O eq "linux") || 0;
1181 }
1182
1183 sub isBSD()
1184 {
1185     return ($^O eq "freebsd") || ($^O eq "openbsd") || ($^O eq "netbsd") || 0;
1186 }
1187
1188 sub isARM()
1189 {
1190     return ($Config{archname} =~ /^arm[v\-]/) || ($Config{archname} =~ /^aarch64[v\-]/);
1191 }
1192
1193 sub isX86_64()
1194 {
1195     return (architecture() eq "x86_64") || 0;
1196 }
1197
1198 sub isCrossCompilation()
1199 {
1200   my $compiler = "";
1201   $compiler = $ENV{'CC'} if (defined($ENV{'CC'}));
1202   if ($compiler =~ /gcc/) {
1203       my $compiler_options = `$compiler -v 2>&1`;
1204       my @host = $compiler_options =~ m/--host=(.*?)\s/;
1205       my @target = $compiler_options =~ m/--target=(.*?)\s/;
1206
1207       return ($host[0] ne "" && $target[0] ne "" && $host[0] ne $target[0]);
1208   }
1209   return 0;
1210 }
1211
1212 sub isAppleWebKit()
1213 {
1214     return isAppleCocoaWebKit() || isAppleWinWebKit();
1215 }
1216
1217 sub isAppleCocoaWebKit()
1218 {
1219     return (portName() eq Mac) || isIOSWebKit();
1220 }
1221
1222 sub isAppleWinWebKit()
1223 {
1224     return portName() eq AppleWin;
1225 }
1226
1227 sub iOSSimulatorDevicesPath
1228 {
1229     return "$ENV{HOME}/Library/Developer/CoreSimulator/Devices";
1230 }
1231
1232 sub iOSSimulatorDevices
1233 {
1234     eval "require Foundation";
1235     my $devicesPath = iOSSimulatorDevicesPath();
1236     opendir(DEVICES, $devicesPath);
1237     my @udids = grep {
1238         $_ =~ m/^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$/;
1239     } readdir(DEVICES);
1240     close(DEVICES);
1241
1242     # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
1243     #        dictionary keys so as to decouple our representation of the plist from the actual structure
1244     #        of the plist, which may change.
1245     my @devices = map {
1246         Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_("$devicesPath/$_/device.plist"));
1247     } @udids;
1248
1249     return @devices;
1250 }
1251
1252 sub createiOSSimulatorDevice
1253 {
1254     my $name = shift;
1255     my $deviceTypeId = shift;
1256     my $runtimeId = shift;
1257
1258     my $created = system("xcrun", "--sdk", "iphonesimulator", "simctl", "create", $name, $deviceTypeId, $runtimeId) == 0;
1259     die "Couldn't create simulator device: $name $deviceTypeId $runtimeId" if not $created;
1260
1261     system("xcrun", "--sdk", "iphonesimulator", "simctl", "list");
1262
1263     print "Waiting for device to be created ...\n";
1264     sleep 5;
1265     for (my $tries = 0; $tries < 5; $tries++){
1266         my @devices = iOSSimulatorDevices();
1267         foreach my $device (@devices) {
1268             return $device if $device->{name} eq $name and $device->{deviceType} eq $deviceTypeId and $device->{runtime} eq $runtimeId;
1269         }
1270         sleep 5;
1271     }
1272     die "Device $name $deviceTypeId $runtimeId wasn't found in " . iOSSimulatorDevicesPath();
1273 }
1274
1275 sub willUseIOSDeviceSDK()
1276 {
1277     return xcodeSDKPlatformName() eq "iphoneos";
1278 }
1279
1280 sub willUseIOSSimulatorSDK()
1281 {
1282     return xcodeSDKPlatformName() eq "iphonesimulator";
1283 }
1284
1285 sub isIOSWebKit()
1286 {
1287     return portName() eq iOS;
1288 }
1289
1290 sub determineNmPath()
1291 {
1292     return if $nmPath;
1293
1294     if (isAppleCocoaWebKit()) {
1295         $nmPath = `xcrun -find nm`;
1296         chomp $nmPath;
1297     }
1298     $nmPath = "nm" if !$nmPath;
1299 }
1300
1301 sub nmPath()
1302 {
1303     determineNmPath();
1304     return $nmPath;
1305 }
1306
1307 sub splitVersionString
1308 {
1309     my $versionString = shift;
1310     my @splitVersion = split(/\./, $versionString);
1311     @splitVersion >= 2 or die "Invalid version $versionString";
1312     $osXVersion = {
1313             "major" => $splitVersion[0],
1314             "minor" => $splitVersion[1],
1315             "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0),
1316     };
1317 }
1318
1319 sub determineOSXVersion()
1320 {
1321     return if $osXVersion;
1322
1323     if (!isDarwin()) {
1324         $osXVersion = -1;
1325         return;
1326     }
1327
1328     my $versionString = `sw_vers -productVersion`;
1329     $osXVersion = splitVersionString($versionString);
1330 }
1331
1332 sub osXVersion()
1333 {
1334     determineOSXVersion();
1335     return $osXVersion;
1336 }
1337
1338 sub determineIOSVersion()
1339 {
1340     return if $iosVersion;
1341
1342     if (!isIOSWebKit()) {
1343         $iosVersion = -1;
1344         return;
1345     }
1346
1347     my $versionString = xcodeSDKVersion();
1348     $iosVersion = splitVersionString($versionString);
1349 }
1350
1351 sub iosVersion()
1352 {
1353     determineIOSVersion();
1354     return $iosVersion;
1355 }
1356
1357 sub isWindowsNT()
1358 {
1359     return $ENV{'OS'} eq 'Windows_NT';
1360 }
1361
1362 sub appendToEnvironmentVariableList($$)
1363 {
1364     my ($name, $value) = @_;
1365
1366     if (defined($ENV{$name})) {
1367         $ENV{$name} .= $Config{path_sep} . $value;
1368     } else {
1369         $ENV{$name} = $value;
1370     }
1371 }
1372
1373 sub prependToEnvironmentVariableList($$)
1374 {
1375     my ($name, $value) = @_;
1376
1377     if (defined($ENV{$name})) {
1378         $ENV{$name} = $value . $Config{path_sep} . $ENV{$name};
1379     } else {
1380         $ENV{$name} = $value;
1381     }
1382 }
1383
1384 sub sharedCommandLineOptions()
1385 {
1386     return (
1387         "g|guard-malloc" => \$shouldUseGuardMalloc,
1388     );
1389 }
1390
1391 sub sharedCommandLineOptionsUsage
1392 {
1393     my %opts = @_;
1394
1395     my %switches = (
1396         '-g|--guard-malloc' => 'Use guardmalloc when running executable',
1397     );
1398
1399     my $indent = " " x ($opts{indent} || 2);
1400     my $switchWidth = List::Util::max(int($opts{switchWidth}), List::Util::max(map { length($_) } keys %switches) + ($opts{brackets} ? 2 : 0));
1401
1402     my $result = "Common switches:\n";
1403
1404     for my $switch (keys %switches) {
1405         my $switchName = $opts{brackets} ? "[" . $switch . "]" : $switch;
1406         $result .= sprintf("%s%-" . $switchWidth . "s %s\n", $indent, $switchName, $switches{$switch});
1407     }
1408
1409     return $result;
1410 }
1411
1412 sub setUpGuardMallocIfNeeded
1413 {
1414     if (!isDarwin()) {
1415         return;
1416     }
1417
1418     if (!defined($shouldUseGuardMalloc)) {
1419         $shouldUseGuardMalloc = checkForArgumentAndRemoveFromARGV("-g") || checkForArgumentAndRemoveFromARGV("--guard-malloc");
1420     }
1421
1422     if ($shouldUseGuardMalloc) {
1423         appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib");
1424         appendToEnvironmentVariableList("__XPC_DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib");
1425     }
1426 }
1427
1428 sub relativeScriptsDir()
1429 {
1430     my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel($FindBin::Bin, getcwd()), "");
1431     if ($scriptDir eq "") {
1432         $scriptDir = ".";
1433     }
1434     return $scriptDir;
1435 }
1436
1437 sub launcherPath()
1438 {
1439     my $relativeScriptsPath = relativeScriptsDir();
1440     if (isGtk() || isEfl()) {
1441         return "$relativeScriptsPath/run-minibrowser";
1442     } elsif (isAppleWebKit()) {
1443         return "$relativeScriptsPath/run-safari";
1444     }
1445 }
1446
1447 sub launcherName()
1448 {
1449     if (isGtk() || isEfl()) {
1450         return "MiniBrowser";
1451     } elsif (isAppleCocoaWebKit()) {
1452         return "Safari";
1453     } elsif (isAppleWinWebKit()) {
1454         return "MiniBrowser";
1455     }
1456 }
1457
1458 sub checkRequiredSystemConfig
1459 {
1460     if (isDarwin()) {
1461         chomp(my $productVersion = `sw_vers -productVersion`);
1462         if (eval "v$productVersion" lt v10.10.5) {
1463             print "*************************************************************\n";
1464             print "OS X Yosemite v10.10.5 or later is required to build WebKit.\n";
1465             print "You have " . $productVersion . ", thus the build will most likely fail.\n";
1466             print "*************************************************************\n";
1467         }
1468         determineXcodeVersion();
1469         if (eval "v$xcodeVersion" lt v7.0) {
1470             print "*************************************************************\n";
1471             print "Xcode 7.0 or later is required to build WebKit.\n";
1472             print "You have an earlier version of Xcode, thus the build will\n";
1473             print "most likely fail. The latest Xcode is available from the App Store.\n";
1474             print "*************************************************************\n";
1475         }
1476     }
1477 }
1478
1479 sub determineWindowsSourceDir()
1480 {
1481     return if $windowsSourceDir;
1482     $windowsSourceDir = sourceDir();
1483     chomp($windowsSourceDir = `cygpath -w '$windowsSourceDir'`) if isCygwin();
1484 }
1485
1486 sub windowsSourceDir()
1487 {
1488     determineWindowsSourceDir();
1489     return $windowsSourceDir;
1490 }
1491
1492 sub windowsSourceSourceDir()
1493 {
1494     return File::Spec->catdir(windowsSourceDir(), "Source");
1495 }
1496
1497 sub windowsLibrariesDir()
1498 {
1499     return File::Spec->catdir(windowsSourceDir(), "WebKitLibraries", "win");
1500 }
1501
1502 sub windowsOutputDir()
1503 {
1504     return File::Spec->catdir(windowsSourceDir(), "WebKitBuild");
1505 }
1506
1507 sub fontExists($)
1508 {
1509     my $font = shift;
1510     my $cmd = "reg query \"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\" /v \"$font\" 2>&1";
1511     my $val = `$cmd`;
1512     return $? == 0;
1513 }
1514
1515 sub checkInstalledTools()
1516 {
1517     # environment variables. Avoid until this is corrected.
1518     my $pythonVer = `python --version 2>&1`;
1519     die "You must have Python installed to build WebKit.\n" if ($?);
1520
1521     # cURL 7.34.0 has a bug that prevents authentication with opensource.apple.com (and other things using SSL3).
1522     my $curlVer = `curl --version 2> NUL`;
1523     if (!$? and $curlVer =~ "(.*curl.*)") {
1524         $curlVer = $1;
1525         if ($curlVer =~ /libcurl\/7\.34\.0/) {
1526             print "cURL version 7.34.0 has a bug that prevents authentication with SSL v2 or v3.\n";
1527             print "cURL 7.33.0 is known to work. The cURL projects is preparing an update to\n";
1528             print "correct this problem.\n\n";
1529             die "Please install a working cURL and try again.\n";
1530         }
1531     }
1532
1533     # MathML requires fonts that may not ship with Windows.
1534     # Warn the user if they are missing.
1535     my @fonts = ('Cambria & Cambria Math (TrueType)', 'LatinModernMath-Regular (TrueType)', 'STIXMath-Regular (TrueType)');
1536     my @missing = ();
1537     foreach my $font (@fonts) {
1538         push @missing, $font if not fontExists($font);
1539     }
1540
1541     if (scalar @missing > 0) {
1542         print "*************************************************************\n";
1543         print "Mathematical fonts, such as Latin Modern Math are needed to\n";
1544         print "use the MathML feature.  You do not appear to have these fonts\n";
1545         print "on your system.\n\n";
1546         print "You can download a suitable set of fonts from the following URL:\n";
1547         print "https://trac.webkit.org/wiki/MathML/Fonts\n";
1548         print "*************************************************************\n";
1549     }
1550
1551     print "Installed tools are correct for the WebKit build.\n";
1552 }
1553
1554 sub setupAppleWinEnv()
1555 {
1556     return unless isAppleWinWebKit();
1557
1558     checkInstalledTools();
1559
1560     if (isWindowsNT()) {
1561         my $restartNeeded = 0;
1562         my %variablesToSet = ();
1563
1564         # FIXME: We should remove this explicit version check for cygwin once we stop supporting Cygwin 1.7.9 or older versions. 
1565         # https://bugs.webkit.org/show_bug.cgi?id=85791
1566         my $uname_version = (POSIX::uname())[2];
1567         $uname_version =~ s/\(.*\)//;  # Remove the trailing cygwin version, if any.
1568         $uname_version =~ s/\-.*$//; # Remove trailing dash-version content, if any
1569         if (version->parse($uname_version) < version->parse("1.7.10")) {
1570             # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios)
1571             # for UNIX-like ttys in the Windows console
1572             $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN};
1573         }
1574         
1575         # Those environment variables must be set to be able to build inside Visual Studio.
1576         $variablesToSet{WEBKIT_LIBRARIES} = windowsLibrariesDir() unless $ENV{WEBKIT_LIBRARIES};
1577         $variablesToSet{WEBKIT_OUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKIT_OUTPUTDIR};
1578         $variablesToSet{MSBUILDDISABLENODEREUSE} = "1" unless $ENV{MSBUILDDISABLENODEREUSE};
1579         $variablesToSet{_IsNativeEnvironment} = "true" unless $ENV{_IsNativeEnvironment};
1580         $variablesToSet{PreferredToolArchitecture} = "x64" unless $ENV{PreferredToolArchitecture};
1581
1582         foreach my $variable (keys %variablesToSet) {
1583             print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n";
1584             my $ret = system "setx", $variable, $variablesToSet{$variable};
1585             if ($ret != 0) {
1586                 system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable};
1587             }
1588             $restartNeeded ||=  $variable eq "WEBKIT_LIBRARIES" || $variable eq "WEBKIT_OUTPUTDIR";
1589         }
1590
1591         if ($restartNeeded) {
1592             print "Please restart your computer before attempting to build inside Visual Studio.\n\n";
1593         }
1594     } else {
1595         if (!defined $ENV{'WEBKIT_LIBRARIES'} || !$ENV{'WEBKIT_LIBRARIES'}) {
1596             print "Warning: You must set the 'WebKit_Libraries' environment variable\n";
1597             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n";
1598             print "         Make sure that 'WebKit_Libraries' points to the\n";
1599             print "         'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n";
1600         }
1601         if (!defined $ENV{'WEBKIT_OUTPUTDIR'} || !$ENV{'WEBKIT_OUTPUTDIR'}) {
1602             print "Warning: You must set the 'WebKit_OutputDir' environment variable\n";
1603             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n\n";
1604         }
1605         if (!defined $ENV{'MSBUILDDISABLENODEREUSE'} || !$ENV{'MSBUILDDISABLENODEREUSE'}) {
1606             print "Warning: You should set the 'MSBUILDDISABLENODEREUSE' environment variable to '1'\n";
1607             print "         to avoid periodic locked log files when building.\n\n";
1608         }
1609     }
1610     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1611     if (isWin64() and !$ENV{'WEBKIT_64_SUPPORT'}) {
1612         print "Warning: You must set the 'WEBKIT_64_SUPPORT' environment variable\n";
1613         print "         to be able run WebKit or JavaScriptCore tests.\n\n";
1614     }
1615 }
1616
1617 sub setupCygwinEnv()
1618 {
1619     return if !isAnyWindows();
1620     return if $vcBuildPath;
1621
1622     my $programFilesPath = programFilesPath();
1623     my $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.com));
1624     if (-e $visualStudioPath) {
1625         # Visual Studio is installed;
1626         if (visualStudioVersion() eq "12") {
1627             $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.exe));
1628         }
1629     } else {
1630         # Visual Studio not found, try VC++ Express
1631         $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE WDExpress.exe));
1632         if (! -e $visualStudioPath) {
1633             print "*************************************************************\n";
1634             print "Cannot find '$visualStudioPath'\n";
1635             print "Please execute the file 'vcvars32.bat' from\n";
1636             print "'$programFilesPath\\Microsoft Visual Studio 14.0\\VC\\bin\\'\n";
1637             print "to setup the necessary environment variables.\n";
1638             print "*************************************************************\n";
1639             die;
1640         }
1641         $willUseVCExpressWhenBuilding = 1;
1642     }
1643
1644     print "Building results into: ", baseProductDir(), "\n";
1645     print "WEBKIT_OUTPUTDIR is set to: ", $ENV{"WEBKIT_OUTPUTDIR"}, "\n";
1646     print "WEBKIT_LIBRARIES is set to: ", $ENV{"WEBKIT_LIBRARIES"}, "\n";
1647     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1648     print "WEBKIT_64_SUPPORT is set to: ", $ENV{"WEBKIT_64_SUPPORT"}, "\n" if isWin64();
1649
1650     # We will actually use MSBuild to build WebKit, but we need to find the Visual Studio install (above) to make
1651     # sure we use the right options.
1652     $vcBuildPath = File::Spec->catfile(msBuildInstallDir(), qw(MSBuild.exe));
1653     if (! -e $vcBuildPath) {
1654         print "*************************************************************\n";
1655         print "Cannot find '$vcBuildPath'\n";
1656         print "Please make sure execute that the Microsoft .NET Framework SDK\n";
1657         print "is installed on this machine.\n";
1658         print "*************************************************************\n";
1659         die;
1660     }
1661 }
1662
1663 sub dieIfWindowsPlatformSDKNotInstalled
1664 {
1665     my $registry32Path = "/proc/registry/";
1666     my $registry64Path = "/proc/registry64/";
1667     my @windowsPlatformSDKRegistryEntries = (
1668         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0A",
1669         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0",
1670         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.1A",
1671         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.0A",
1672         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1",
1673     );
1674
1675     # FIXME: It would be better to detect whether we are using 32- or 64-bit Windows
1676     # and only check the appropriate entry. But for now we just blindly check both.
1677     my $recommendedPlatformSDK = $windowsPlatformSDKRegistryEntries[0];
1678
1679     while (@windowsPlatformSDKRegistryEntries) {
1680         my $windowsPlatformSDKRegistryEntry = shift @windowsPlatformSDKRegistryEntries;
1681         return if (-e $registry32Path . $windowsPlatformSDKRegistryEntry) || (-e $registry64Path . $windowsPlatformSDKRegistryEntry);
1682     }
1683
1684     print "*************************************************************\n";
1685     print "Cannot find registry entry '$recommendedPlatformSDK'.\n";
1686     print "Please download and install the Microsoft Windows SDK\n";
1687     print "from <http://www.microsoft.com/en-us/download/details.aspx?id=8279>.\n\n";
1688     print "Then follow step 2 in the Windows section of the \"Installing Developer\n";
1689     print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n";
1690     print "*************************************************************\n";
1691     die;
1692 }
1693
1694 sub buildXCodeProject($$@)
1695 {
1696     my ($project, $clean, @extraOptions) = @_;
1697
1698     if ($clean) {
1699         push(@extraOptions, "-alltargets");
1700         push(@extraOptions, "clean");
1701     }
1702
1703     chomp($ENV{DSYMUTIL_NUM_THREADS} = `sysctl -n hw.activecpu`);
1704     return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions;
1705 }
1706
1707 sub usingVisualStudioExpress()
1708 {
1709     setupCygwinEnv();
1710     return $willUseVCExpressWhenBuilding;
1711 }
1712
1713 sub buildVisualStudioProject
1714 {
1715     my ($project, $clean) = @_;
1716     setupCygwinEnv();
1717
1718     my $config = configurationForVisualStudio();
1719
1720     dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding;
1721
1722     chomp($project = `cygpath -w "$project"`) if isCygwin();
1723
1724     my $action = "/t:build";
1725     if ($clean) {
1726         $action = "/t:clean";
1727     }
1728
1729     my $platform = "/p:Platform=" . (isWin64() ? "x64" : "Win32");
1730     my $logPath = File::Spec->catdir($baseProductDir, $configuration);
1731     make_path($logPath) unless -d $logPath or $logPath eq ".";
1732
1733     my $errorLogFile = File::Spec->catfile($logPath, "webkit_errors.log");
1734     chomp($errorLogFile = `cygpath -w "$errorLogFile"`) if isCygwin();
1735     my $errorLogging = "/flp:LogFile=" . $errorLogFile . ";ErrorsOnly";
1736
1737     my $warningLogFile = File::Spec->catfile($logPath, "webkit_warnings.log");
1738     chomp($warningLogFile = `cygpath -w "$warningLogFile"`) if isCygwin();
1739     my $warningLogging = "/flp1:LogFile=" . $warningLogFile . ";WarningsOnly";
1740
1741     my @command = ($vcBuildPath, "/verbosity:minimal", $project, $action, $config, $platform, "/fl", $errorLogging, "/fl1", $warningLogging);
1742     print join(" ", @command), "\n";
1743     return system @command;
1744 }
1745
1746 sub getJhbuildPath()
1747 {
1748     my @jhbuildPath = File::Spec->splitdir(baseProductDir());
1749     if (isGit() && isGitBranchBuild() && gitBranch()) {
1750         pop(@jhbuildPath);
1751     }
1752     if (isEfl()) {
1753         push(@jhbuildPath, "DependenciesEFL");
1754     } elsif (isGtk()) {
1755         push(@jhbuildPath, "DependenciesGTK");
1756     } else {
1757         die "Cannot get JHBuild path for platform that isn't GTK+ or EFL.\n";
1758     }
1759     return File::Spec->catdir(@jhbuildPath);
1760 }
1761
1762 sub isCachedArgumentfileOutOfDate($@)
1763 {
1764     my ($filename, $currentContents) = @_;
1765
1766     if (! -e $filename) {
1767         return 1;
1768     }
1769
1770     open(CONTENTS_FILE, $filename);
1771     chomp(my $previousContents = <CONTENTS_FILE>);
1772     close(CONTENTS_FILE);
1773
1774     if ($previousContents ne $currentContents) {
1775         print "Contents for file $filename have changed.\n";
1776         print "Previous contents were: $previousContents\n\n";
1777         print "New contents are: $currentContents\n";
1778         return 1;
1779     }
1780
1781     return 0;
1782 }
1783
1784 sub wrapperPrefixIfNeeded()
1785 {
1786     if (isAnyWindows() || isJSCOnly()) {
1787         return ();
1788     }
1789     if (isAppleCocoaWebKit()) {
1790         return ("xcrun");
1791     }
1792     if (-e getJhbuildPath()) {
1793         my @prefix = (File::Spec->catfile(sourceDir(), "Tools", "jhbuild", "jhbuild-wrapper"));
1794         if (isEfl()) {
1795             push(@prefix, "--efl");
1796         } elsif (isGtk()) {
1797             push(@prefix, "--gtk");
1798         }
1799         push(@prefix, "run");
1800
1801         return @prefix;
1802     }
1803
1804     return ();
1805 }
1806
1807 sub cmakeCachePath()
1808 {
1809     return File::Spec->catdir(baseProductDir(), configuration(), "CMakeCache.txt");
1810 }
1811
1812 sub cmakeFilesPath()
1813 {
1814     return File::Spec->catdir(baseProductDir(), configuration(), "CMakeFiles");
1815 }
1816
1817 sub shouldRemoveCMakeCache(@)
1818 {
1819     my ($cacheFilePath, @buildArgs) = @_;
1820
1821     # We check this first, because we always want to create this file for a fresh build.
1822     my $productDir = File::Spec->catdir(baseProductDir(), configuration());
1823     my $optionsCache = File::Spec->catdir($productDir, "build-webkit-options.txt");
1824     my $joinedBuildArgs = join(" ", @buildArgs);
1825     if (isCachedArgumentfileOutOfDate($optionsCache, $joinedBuildArgs)) {
1826         File::Path::mkpath($productDir) unless -d $productDir;
1827         open(CACHED_ARGUMENTS, ">", $optionsCache);
1828         print CACHED_ARGUMENTS $joinedBuildArgs;
1829         close(CACHED_ARGUMENTS);
1830
1831         return 1;
1832     }
1833
1834     my $cmakeCache = cmakeCachePath();
1835     unless (-e $cmakeCache) {
1836         return 0;
1837     }
1838
1839     my $cacheFileModifiedTime = stat($cmakeCache)->mtime;
1840     my $platformConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "Options" . cmakeBasedPortName() . ".cmake");
1841     if ($cacheFileModifiedTime < stat($platformConfiguration)->mtime) {
1842         return 1;
1843     }
1844
1845     my $globalConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsCommon.cmake");
1846     if ($cacheFileModifiedTime < stat($globalConfiguration)->mtime) {
1847         return 1;
1848     }
1849
1850     my $inspectorUserInterfaceDircetory = File::Spec->catdir(sourceDir(), "Source", "WebInspectorUI", "UserInterface");
1851     if ($cacheFileModifiedTime < stat($inspectorUserInterfaceDircetory)->mtime) {
1852         return 1;
1853     }
1854
1855     if(isAnyWindows()) {
1856         my $winConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsWin.cmake");
1857         if ($cacheFileModifiedTime < stat($winConfiguration)->mtime) {
1858             return 1;
1859         }
1860     }
1861
1862     return 0;
1863 }
1864
1865 sub removeCMakeCache(@)
1866 {
1867     my (@buildArgs) = @_;
1868     if (shouldRemoveCMakeCache(@buildArgs)) {
1869         my $cmakeCache = cmakeCachePath();
1870         my $cmakeFiles = cmakeFilesPath();
1871         unlink($cmakeCache) if -e $cmakeCache;
1872         rmtree($cmakeFiles) if -d $cmakeFiles;
1873     }
1874 }
1875
1876 sub canUseNinja(@)
1877 {
1878     if (!defined($shouldNotUseNinja)) {
1879         $shouldNotUseNinja = checkForArgumentAndRemoveFromARGV("--no-ninja");
1880     }
1881
1882     if ($shouldNotUseNinja) {
1883         return 0;
1884     }
1885
1886     # Test both ninja and ninja-build. Fedora uses ninja-build and has patched CMake to also call ninja-build.
1887     return commandExists("ninja") || commandExists("ninja-build");
1888 }
1889
1890 sub canUseNinjaGenerator(@)
1891 {
1892     # Check that a Ninja generator is installed
1893     my $devnull = File::Spec->devnull();
1894     return exitStatus(system("cmake -N -G Ninja >$devnull 2>&1")) == 0;
1895 }
1896
1897 sub canUseEclipseNinjaGenerator(@)
1898 {
1899     # Check that eclipse and eclipse Ninja generator is installed
1900     my $devnull = File::Spec->devnull();
1901     return commandExists("eclipse") && exitStatus(system("cmake -N -G 'Eclipse CDT4 - Ninja' >$devnull 2>&1")) == 0;
1902 }
1903
1904 sub cmakeGeneratedBuildfile(@)
1905 {
1906     my ($willUseNinja) = @_;
1907     if ($willUseNinja) {
1908         return File::Spec->catfile(baseProductDir(), configuration(), "build.ninja")
1909     } elsif (isAnyWindows()) {
1910         return File::Spec->catfile(baseProductDir(), configuration(), "WebKit.sln")
1911     } else {
1912         return File::Spec->catfile(baseProductDir(), configuration(), "Makefile")
1913     }
1914 }
1915
1916 sub generateBuildSystemFromCMakeProject
1917 {
1918     my ($prefixPath, @cmakeArgs) = @_;
1919     my $config = configuration();
1920     my $port = cmakeBasedPortName();
1921     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1922     File::Path::mkpath($buildPath) unless -d $buildPath;
1923     my $originalWorkingDirectory = getcwd();
1924     chdir($buildPath) or die;
1925
1926     # We try to be smart about when to rerun cmake, so that we can have faster incremental builds.
1927     my $willUseNinja = canUseNinja() && canUseNinjaGenerator();
1928     if (-e cmakeCachePath() && -e cmakeGeneratedBuildfile($willUseNinja)) {
1929         return 0;
1930     }
1931
1932     my @args;
1933     push @args, "-DPORT=\"$port\"";
1934     push @args, "-DCMAKE_INSTALL_PREFIX=\"$prefixPath\"" if $prefixPath;
1935     push @args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON";
1936     if ($config =~ /release/i) {
1937         push @args, "-DCMAKE_BUILD_TYPE=Release";
1938     } elsif ($config =~ /debug/i) {
1939         push @args, "-DCMAKE_BUILD_TYPE=Debug";
1940     }
1941
1942     if ($willUseNinja) {
1943         push @args, "-G";
1944         if (canUseEclipseNinjaGenerator()) {
1945             push @args, "'Eclipse CDT4 - Ninja'";
1946         } else {
1947             push @args, "Ninja";
1948         }
1949     } elsif (isAnyWindows() && isWin64()) {
1950         push @args, '-G "Visual Studio 14 2015 Win64"';
1951     }
1952     # Do not show progress of generating bindings in interactive Ninja build not to leave noisy lines on tty
1953     push @args, '-DSHOW_BINDINGS_GENERATION_PROGRESS=1' unless ($willUseNinja && -t STDOUT);
1954
1955     # Some ports have production mode, but build-webkit should always use developer mode.
1956     push @args, "-DDEVELOPER_MODE=ON" if isEfl() || isGtk() || isJSCOnly();
1957
1958     # Don't warn variables which aren't used by cmake ports.
1959     push @args, "--no-warn-unused-cli";
1960     push @args, @cmakeArgs if @cmakeArgs;
1961
1962     my $cmakeSourceDir = isCygwin() ? windowsSourceDir() : sourceDir();
1963     push @args, '"' . $cmakeSourceDir . '"';
1964
1965     # Compiler options to keep floating point values consistent
1966     # between 32-bit and 64-bit architectures.
1967     determineArchitecture();
1968     if ($architecture eq "i686" && !isCrossCompilation() && !isAnyWindows()) {
1969         $ENV{'CXXFLAGS'} = "-march=pentium4 -msse2 -mfpmath=sse " . ($ENV{'CXXFLAGS'} || "");
1970     }
1971
1972     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
1973     # parsed for shell metacharacters.
1974     my $wrapper = join(" ", wrapperPrefixIfNeeded()) . " ";
1975     my $returnCode = systemVerbose($wrapper . "cmake @args");
1976
1977     chdir($originalWorkingDirectory);
1978     return $returnCode;
1979 }
1980
1981 sub buildCMakeGeneratedProject($)
1982 {
1983     my ($makeArgs) = @_;
1984     my $config = configuration();
1985     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1986     if (! -d $buildPath) {
1987         die "Must call generateBuildSystemFromCMakeProject() before building CMake project.";
1988     }
1989
1990     my $command = "cmake";
1991     my @args = ("--build", $buildPath, "--config", $config);
1992     push @args, ("--", $makeArgs) if $makeArgs;
1993
1994     # GTK and JSCOnly can use a build script to preserve colors and pretty-printing.
1995     if ((isGtk() || isJSCOnly()) && -e "$buildPath/build.sh") {
1996         chdir "$buildPath" or die;
1997         $command = "$buildPath/build.sh";
1998         @args = ($makeArgs);
1999     }
2000
2001     if ($ENV{VERBOSE} && canUseNinja()) {
2002         push @args, "-v";
2003         push @args, "-d keeprsp" if (version->parse(determineNinjaVersion()) >= version->parse("1.4.0"));
2004     }
2005
2006     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
2007     # parsed for shell metacharacters. In particular, $makeArgs may contain such metacharacters.
2008     my $wrapper = join(" ", wrapperPrefixIfNeeded()) . " ";
2009     return systemVerbose($wrapper . "$command @args");
2010 }
2011
2012 sub cleanCMakeGeneratedProject()
2013 {
2014     my $config = configuration();
2015     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
2016     if (-d $buildPath) {
2017         return systemVerbose("cmake", "--build", $buildPath, "--config", $config, "--target", "clean");
2018     }
2019     return 0;
2020 }
2021
2022 sub buildCMakeProjectOrExit($$$@)
2023 {
2024     my ($clean, $prefixPath, $makeArgs, @cmakeArgs) = @_;
2025     my $returnCode;
2026
2027     exit(exitStatus(cleanCMakeGeneratedProject())) if $clean;
2028
2029     if (isEfl() && checkForArgumentAndRemoveFromARGV("--update-efl")) {
2030         system("perl", "$sourceDir/Tools/Scripts/update-webkitefl-libs") == 0 or die $!;
2031     }
2032
2033     if (isGtk() && checkForArgumentAndRemoveFromARGV("--update-gtk")) {
2034         system("perl", "$sourceDir/Tools/Scripts/update-webkitgtk-libs") == 0 or die $!;
2035     }
2036
2037     $returnCode = exitStatus(generateBuildSystemFromCMakeProject($prefixPath, @cmakeArgs));
2038     exit($returnCode) if $returnCode;
2039
2040     $returnCode = exitStatus(buildCMakeGeneratedProject($makeArgs));
2041     exit($returnCode) if $returnCode;
2042     return 0;
2043 }
2044
2045 sub cmakeBasedPortArguments()
2046 {
2047     return ();
2048 }
2049
2050 sub cmakeBasedPortName()
2051 {
2052     return ucfirst portName();
2053 }
2054
2055 sub determineIsCMakeBuild()
2056 {
2057     return if defined($isCMakeBuild);
2058     $isCMakeBuild = checkForArgumentAndRemoveFromARGV("--cmake");
2059 }
2060
2061 sub isCMakeBuild()
2062 {
2063     return 1 unless isAppleCocoaWebKit();
2064     determineIsCMakeBuild();
2065     return $isCMakeBuild;
2066 }
2067
2068 sub promptUser
2069 {
2070     my ($prompt, $default) = @_;
2071     my $defaultValue = $default ? "[$default]" : "";
2072     print "$prompt $defaultValue: ";
2073     chomp(my $input = <STDIN>);
2074     return $input ? $input : $default;
2075 }
2076
2077 sub appleApplicationSupportPath
2078 {
2079     open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir";
2080     my $path = <INSTALL_DIR>;
2081     $path =~ s/[\r\n\x00].*//;
2082     close INSTALL_DIR;
2083
2084     my $unixPath = `cygpath -u '$path'`;
2085     chomp $unixPath;
2086     return $unixPath;
2087 }
2088
2089 sub setPathForRunningWebKitApp
2090 {
2091     my ($env) = @_;
2092
2093     if (isAnyWindows()) {
2094         my $productBinaryDir = executableProductDir();
2095         if (isAppleWinWebKit()) {
2096             $env->{PATH} = join(':', $productBinaryDir, appleApplicationSupportPath(), $env->{PATH} || "");
2097         } elsif (isWinCairo()) {
2098             my $winCairoBin = sourceDir() . "/WebKitLibraries/win/" . (isWin64() ? "bin64/" : "bin32/");
2099             my $gstreamerBin = isWin64() ? $ENV{"GSTREAMER_1_0_ROOT_X86_64"} . "bin" : $ENV{"GSTREAMER_1_0_ROOT_X86"} . "bin";
2100             $env->{PATH} = join(':', $productBinaryDir, $winCairoBin, $gstreamerBin, $env->{PATH} || "");
2101         }
2102     }
2103 }
2104
2105 sub printHelpAndExitForRunAndDebugWebKitAppIfNeeded
2106 {
2107     return unless checkForArgumentAndRemoveFromARGV("--help");
2108
2109     print STDERR <<EOF;
2110 Usage: @{[basename($0)]} [options] [args ...]
2111   --help                            Show this help message
2112   --no-saved-state                  Launch the application without state restoration
2113   -g|--guard-malloc                 Enable Guard Malloc (OS X only)
2114 EOF
2115
2116     exit(1);
2117 }
2118
2119 sub argumentsForRunAndDebugMacWebKitApp()
2120 {
2121     my @args = ();
2122     if (checkForArgumentAndRemoveFromARGV("--no-saved-state")) {
2123         push @args, ("-ApplePersistenceIgnoreStateQuietly", "YES");
2124         # FIXME: Don't set ApplePersistenceIgnoreState once all supported OS versions respect ApplePersistenceIgnoreStateQuietly (rdar://15032886).
2125         push @args, ("-ApplePersistenceIgnoreState", "YES");
2126     }
2127     unshift @args, @ARGV;
2128
2129     return @args;
2130 }
2131
2132 sub setupMacWebKitEnvironment($)
2133 {
2134     my ($dyldFrameworkPath) = @_;
2135
2136     $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
2137
2138     prependToEnvironmentVariableList("DYLD_FRAMEWORK_PATH", $dyldFrameworkPath);
2139     prependToEnvironmentVariableList("__XPC_DYLD_FRAMEWORK_PATH", $dyldFrameworkPath);
2140     prependToEnvironmentVariableList("DYLD_LIBRARY_PATH", $dyldFrameworkPath);
2141     prependToEnvironmentVariableList("__XPC_DYLD_LIBRARY_PATH", $dyldFrameworkPath);
2142     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
2143
2144     setUpGuardMallocIfNeeded();
2145 }
2146
2147 sub setupIOSWebKitEnvironment($)
2148 {
2149     my ($dyldFrameworkPath) = @_;
2150     $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
2151
2152     prependToEnvironmentVariableList("DYLD_FRAMEWORK_PATH", $dyldFrameworkPath);
2153     prependToEnvironmentVariableList("DYLD_LIBRARY_PATH", $dyldFrameworkPath);
2154
2155     setUpGuardMallocIfNeeded();
2156 }
2157
2158 sub iosSimulatorApplicationsPath()
2159 {
2160     return File::Spec->catdir(XcodeSDKPath(), "Applications");
2161 }
2162
2163 sub installedMobileSafariBundle()
2164 {
2165     return File::Spec->catfile(iosSimulatorApplicationsPath(), "MobileSafari.app");
2166 }
2167
2168 sub mobileSafariBundle()
2169 {
2170     determineConfigurationProductDir();
2171
2172     # Use MobileSafari.app in product directory if present.
2173     if (isAppleCocoaWebKit() && -d "$configurationProductDir/MobileSafari.app") {
2174         return "$configurationProductDir/MobileSafari.app";
2175     }
2176     return installedMobileSafariBundle();
2177 }
2178
2179 sub plistPathFromBundle($)
2180 {
2181     my ($appBundle) = @_;
2182     return "$appBundle/Info.plist" if -f "$appBundle/Info.plist"; # iOS app bundle
2183     return "$appBundle/Contents/Info.plist" if -f "$appBundle/Contents/Info.plist"; # Mac app bundle
2184     return "";
2185 }
2186
2187 sub appIdentifierFromBundle($)
2188 {
2189     my ($appBundle) = @_;
2190     my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
2191     chomp(my $bundleIdentifier = `defaults read '$plistPath' CFBundleIdentifier 2> /dev/null`);
2192     return $bundleIdentifier;
2193 }
2194
2195 sub appDisplayNameFromBundle($)
2196 {
2197     my ($appBundle) = @_;
2198     my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
2199     chomp(my $bundleDisplayName = `defaults read '$plistPath' CFBundleDisplayName 2> /dev/null`);
2200     return $bundleDisplayName;
2201 }
2202
2203 sub waitUntilIOSSimulatorDeviceIsInState($$)
2204 {
2205     my ($deviceUDID, $waitUntilState) = @_;
2206     my $device = iosSimulatorDeviceByUDID($deviceUDID);
2207     # FIXME: We should add a maximum time limit to wait here.
2208     while ($device->{state} ne $waitUntilState) {
2209         usleep(500 * 1000); # Waiting 500ms between file system polls does not make script run-safari feel sluggish.
2210         $device = iosSimulatorDeviceByUDID($deviceUDID);
2211     }
2212 }
2213
2214 sub shutDownIOSSimulatorDevice($)
2215 {
2216     my ($simulatorDevice) = @_;
2217     system("xcrun --sdk iphonesimulator simctl shutdown $simulatorDevice->{UDID} > /dev/null 2>&1");
2218 }
2219
2220 sub restartIOSSimulatorDevice($)
2221 {
2222     my ($simulatorDevice) = @_;
2223     shutDownIOSSimulatorDevice($simulatorDevice);
2224
2225     exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "boot", $simulatorDevice->{UDID})) == 0 or die "Failed to boot simulator device $simulatorDevice->{UDID}";
2226 }
2227
2228 sub relaunchIOSSimulator($)
2229 {
2230     my ($simulatedDevice) = @_;
2231     quitIOSSimulator($simulatedDevice->{UDID});
2232
2233     # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
2234     chomp(my $developerDirectory = $ENV{DEVELOPER_DIR} || `xcode-select --print-path`); 
2235     my $iosSimulatorPath = File::Spec->catfile($developerDirectory, "Applications", "Simulator.app"); 
2236     system("open", "-a", $iosSimulatorPath, "--args", "-CurrentDeviceUDID", $simulatedDevice->{UDID}) == 0 or die "Failed to open $iosSimulatorPath: $!"; 
2237
2238     waitUntilIOSSimulatorDeviceIsInState($simulatedDevice->{UDID}, SIMULATOR_DEVICE_STATE_BOOTED);
2239 }
2240
2241 sub quitIOSSimulator(;$)
2242 {
2243     my ($waitForShutdownOfSimulatedDeviceUDID) = @_;
2244     # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
2245     if (exitStatus(system {"osascript"} "osascript", "-e", 'tell application id "com.apple.iphonesimulator" to quit')) {
2246         # osascript returns a non-zero exit status if Simulator.app is not registered in LaunchServices.
2247         return;
2248     }
2249
2250     if (!defined($waitForShutdownOfSimulatedDeviceUDID)) {
2251         return;
2252     }
2253     # FIXME: We assume that $waitForShutdownOfSimulatedDeviceUDID was not booted using the simctl command line tool.
2254     #        Otherwise we will spin indefinitely since quiting the iOS Simulator will not shutdown this device. We
2255     #        should add a maximum time limit to wait for a device to shutdown and either return an error or die()
2256     #        on expiration of the time limit.
2257     waitUntilIOSSimulatorDeviceIsInState($waitForShutdownOfSimulatedDeviceUDID, SIMULATOR_DEVICE_STATE_SHUTDOWN);
2258 }
2259
2260 sub iosSimulatorDeviceByName($)
2261 {
2262     my ($simulatorName) = @_;
2263     my $simulatorRuntime = iosSimulatorRuntime();
2264     my @devices = iOSSimulatorDevices();
2265     for my $device (@devices) {
2266         if ($device->{name} eq $simulatorName && $device->{runtime} eq $simulatorRuntime) {
2267             return $device;
2268         }
2269     }
2270     return undef;
2271 }
2272
2273 sub iosSimulatorDeviceByUDID($)
2274 {
2275     my ($simulatedDeviceUDID) = @_;
2276     my $devicePlistPath = File::Spec->catfile(iOSSimulatorDevicesPath(), $simulatedDeviceUDID, "device.plist");
2277     if (!-f $devicePlistPath) {
2278         return;
2279     }
2280     # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
2281     #        dictionary keys so as to decouple our representation of the plist from the actual structure
2282     #        of the plist, which may change.
2283     eval "require Foundation";
2284     return Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_($devicePlistPath));
2285 }
2286
2287 sub iosSimulatorRuntime()
2288 {
2289     my $xcodeSDKVersion = xcodeSDKVersion();
2290     $xcodeSDKVersion =~ s/\./-/;
2291     return "com.apple.CoreSimulator.SimRuntime.iOS-$xcodeSDKVersion";
2292 }
2293
2294 sub findOrCreateSimulatorForIOSDevice($)
2295 {
2296     my ($simulatorNameSuffix) = @_;
2297     my $simulatorName;
2298     my $simulatorDeviceType;
2299     if (architecture() eq "x86_64") {
2300         $simulatorName = "iPhone 5s " . $simulatorNameSuffix;
2301         $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5s";
2302     } else {
2303         $simulatorName = "iPhone 5 " . $simulatorNameSuffix;
2304         $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5";
2305     }
2306     my $simulatedDevice = iosSimulatorDeviceByName($simulatorName);
2307     return $simulatedDevice if $simulatedDevice;
2308     return createiOSSimulatorDevice($simulatorName, $simulatorDeviceType, iosSimulatorRuntime());
2309 }
2310
2311 sub isIOSSimulatorSystemInstalledApp($)
2312 {
2313     my ($appBundle) = @_;
2314     my $simulatorApplicationsPath = realpath(iosSimulatorApplicationsPath());
2315     return substr(realpath($appBundle), 0, length($simulatorApplicationsPath)) eq $simulatorApplicationsPath;
2316 }
2317
2318 sub hasUserInstalledAppInSimulatorDevice($$)
2319 {
2320     my ($appIdentifier, $simulatedDeviceUDID) = @_;
2321     my $userInstalledAppPath = File::Spec->catfile($ENV{HOME}, "Library", "Developer", "CoreSimulator", "Devices", $simulatedDeviceUDID, "data", "Containers", "Bundle", "Application");
2322     if (!-d $userInstalledAppPath) {
2323         return 0; # No user installed apps.
2324     }
2325     local @::userInstalledAppBundles;
2326     my $wantedFunction = sub {
2327         my $file = $_;
2328
2329         # Ignore hidden files and directories.
2330         if ($file =~ /^\../) {
2331             $File::Find::prune = 1;
2332             return;
2333         }
2334
2335         return if !-d $file || $file !~ /\.app$/;
2336         push @::userInstalledAppBundles, $File::Find::name;
2337         $File::Find::prune = 1; # Do not traverse contents of app bundle.
2338     };
2339     find($wantedFunction, $userInstalledAppPath);
2340     for my $userInstalledAppBundle (@::userInstalledAppBundles) {
2341         if (appIdentifierFromBundle($userInstalledAppBundle) eq $appIdentifier) {
2342             return 1; # Has user installed app.
2343         }
2344     }
2345     return 0; # Does not have user installed app.
2346 }
2347
2348 sub isSimulatorDeviceBooted($)
2349 {
2350     my ($simulatedDeviceUDID) = @_;
2351     my $device = iosSimulatorDeviceByUDID($simulatedDeviceUDID);
2352     return $device && $device->{state} eq SIMULATOR_DEVICE_STATE_BOOTED;
2353 }
2354
2355 sub runIOSWebKitAppInSimulator($;$)
2356 {
2357     my ($appBundle, $simulatorOptions) = @_;
2358     my $productDir = productDir();
2359     my $appDisplayName = appDisplayNameFromBundle($appBundle);
2360     my $appIdentifier = appIdentifierFromBundle($appBundle);
2361     my $simulatedDevice = findOrCreateSimulatorForIOSDevice(SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT);
2362     my $simulatedDeviceUDID = $simulatedDevice->{UDID};
2363
2364     my $willUseSystemInstalledApp = isIOSSimulatorSystemInstalledApp($appBundle);
2365     if ($willUseSystemInstalledApp) {
2366         if (hasUserInstalledAppInSimulatorDevice($appIdentifier, $simulatedDeviceUDID)) {
2367             # Restore the system-installed app in the simulator device corresponding to $appBundle as it
2368             # was previously overwritten with a custom built version of the app.
2369             # FIXME: Only restore the system-installed version of the app instead of erasing all contents and settings.
2370             print "Quitting iOS Simulator...\n";
2371             quitIOSSimulator($simulatedDeviceUDID);
2372             print "Erasing contents and settings for simulator device \"$simulatedDevice->{name}\".\n";
2373             exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "erase", $simulatedDeviceUDID)) == 0 or die;
2374         }
2375         # FIXME: We assume that if $simulatedDeviceUDID is not booted then iOS Simulator is not open. However
2376         #        $simulatedDeviceUDID may have been booted using the simctl command line tool. If $simulatedDeviceUDID
2377         #        was booted using simctl then we should shutdown the device and launch iOS Simulator to boot it again.
2378         if (!isSimulatorDeviceBooted($simulatedDeviceUDID)) {
2379             print "Launching iOS Simulator...\n";
2380             relaunchIOSSimulator($simulatedDevice);
2381         }
2382     } else {
2383         # FIXME: We should killall(1) any running instances of $appBundle before installing it to ensure
2384         #        that simctl launch opens the latest installed version of the app. For now we quit and
2385         #        launch the iOS Simulator again to ensure there are no running instances of $appBundle.
2386         print "Quitting and launching iOS Simulator...\n";
2387         relaunchIOSSimulator($simulatedDevice);
2388
2389         print "Installing $appBundle.\n";
2390         # Install custom built app, overwriting an app with the same app identifier if one exists.
2391         exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "install", $simulatedDeviceUDID, $appBundle)) == 0 or die;
2392
2393     }
2394
2395     $simulatorOptions = {} unless $simulatorOptions;
2396
2397     my %simulatorENV;
2398     %simulatorENV = %{$simulatorOptions->{applicationEnvironment}} if $simulatorOptions->{applicationEnvironment};
2399     {
2400         local %ENV; # Shadow global-scope %ENV so that changes to it will not be seen outside of this scope.
2401         setupIOSWebKitEnvironment($productDir);
2402         %simulatorENV = %ENV;
2403     }
2404     my $applicationArguments = \@ARGV;
2405     $applicationArguments = $simulatorOptions->{applicationArguments} if $simulatorOptions && $simulatorOptions->{applicationArguments};
2406
2407     # Prefix the environment variables with SIMCTL_CHILD_ per `xcrun simctl help launch`.
2408     foreach my $key (keys %simulatorENV) {
2409         $ENV{"SIMCTL_CHILD_$key"} = $simulatorENV{$key};
2410     }
2411
2412     print "Starting $appDisplayName with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2413     return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "launch", $simulatedDeviceUDID, $appIdentifier, @$applicationArguments));
2414 }
2415
2416 sub runIOSWebKitApp($)
2417 {
2418     my ($appBundle) = @_;
2419     if (willUseIOSDeviceSDK()) {
2420         die "Only running Safari in iOS Simulator is supported now.";
2421     }
2422     if (willUseIOSSimulatorSDK()) {
2423         return runIOSWebKitAppInSimulator($appBundle);
2424     }
2425     die "Not using an iOS SDK."
2426 }
2427
2428 sub archCommandLineArgumentsForRestrictedEnvironmentVariables()
2429 {
2430     my @arguments = ();
2431     foreach my $key (keys(%ENV)) {
2432         if ($key =~ /^DYLD_/) {
2433             push @arguments, "-e", "$key=$ENV{$key}";
2434         }
2435     }
2436     return @arguments;
2437 }
2438
2439 sub runMacWebKitApp($;$)
2440 {
2441     my ($appPath, $useOpenCommand) = @_;
2442     my $productDir = productDir();
2443     print "Starting @{[basename($appPath)]} with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2444
2445     local %ENV = %ENV;
2446     setupMacWebKitEnvironment($productDir);
2447
2448     if (defined($useOpenCommand) && $useOpenCommand == USE_OPEN_COMMAND) {
2449         return system("open", "-W", "-a", $appPath, "--args", argumentsForRunAndDebugMacWebKitApp());
2450     }
2451     if (architecture()) {
2452         return system "arch", "-" . architecture(), archCommandLineArgumentsForRestrictedEnvironmentVariables(), $appPath, argumentsForRunAndDebugMacWebKitApp();
2453     }
2454     return system { $appPath } $appPath, argumentsForRunAndDebugMacWebKitApp();
2455 }
2456
2457 sub execMacWebKitAppForDebugging($)
2458 {
2459     my ($appPath) = @_;
2460     my $architectureSwitch = "--arch";
2461     my $argumentsSeparator = "--";
2462
2463     my $debuggerPath = `xcrun -find lldb`;
2464     chomp $debuggerPath;
2465     die "Can't find the lldb executable.\n" unless -x $debuggerPath;
2466
2467     my $productDir = productDir();
2468     setupMacWebKitEnvironment($productDir);
2469
2470     my @architectureFlags = ($architectureSwitch, architecture());
2471     print "Starting @{[basename($appPath)]} under lldb with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2472     exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $appPath, argumentsForRunAndDebugMacWebKitApp() or die;
2473 }
2474
2475 sub debugSafari
2476 {
2477     if (isAppleCocoaWebKit()) {
2478         checkFrameworks();
2479         execMacWebKitAppForDebugging(safariPath());
2480     }
2481
2482     return 1; # Unsupported platform; can't debug Safari on this platform.
2483 }
2484
2485 sub runSafari
2486 {
2487     if (isIOSWebKit()) {
2488         return runIOSWebKitApp(mobileSafariBundle());
2489     }
2490
2491     if (isAppleCocoaWebKit()) {
2492         return runMacWebKitApp(safariPath());
2493     }
2494
2495     if (isAppleWinWebKit()) {
2496         my $result;
2497         my $webKitLauncherPath = File::Spec->catfile(executableProductDir(), "MiniBrowser.exe");
2498         return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
2499     }
2500
2501     return 1; # Unsupported platform; can't run Safari on this platform.
2502 }
2503
2504 sub runMiniBrowser
2505 {
2506     if (isAppleCocoaWebKit()) {
2507         return runMacWebKitApp(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2508     } elsif (isAppleWinWebKit()) {
2509         my $result;
2510         my $webKitLauncherPath = File::Spec->catfile(executableProductDir(), "MiniBrowser.exe");
2511         return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
2512     }
2513
2514     return 1;
2515 }
2516
2517 sub debugMiniBrowser
2518 {
2519     if (isAppleCocoaWebKit()) {
2520         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2521     }
2522     
2523     return 1;
2524 }
2525
2526 sub runWebKitTestRunner
2527 {
2528     if (isAppleCocoaWebKit()) {
2529         return runMacWebKitApp(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2530     }
2531
2532     return 1;
2533 }
2534
2535 sub debugWebKitTestRunner
2536 {
2537     if (isAppleCocoaWebKit()) {
2538         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2539     }
2540
2541     return 1;
2542 }
2543
2544 sub readRegistryString
2545 {
2546     my ($valueName) = @_;
2547     chomp(my $string = `regtool --wow32 get "$valueName"`);
2548     return $string;
2549 }
2550
2551 sub writeRegistryString
2552 {
2553     my ($valueName, $string) = @_;
2554
2555     my $error = system "regtool", "--wow32", "set", "-s", $valueName, $string;
2556
2557     # On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still
2558     # return a successful exit code. So we double-check here that the value we tried to write to the
2559     # registry was really written.
2560     return !$error && readRegistryString($valueName) eq $string;
2561 }
2562
2563 sub formatBuildTime($)
2564 {
2565     my ($buildTime) = @_;
2566
2567     my $buildHours = int($buildTime / 3600);
2568     my $buildMins = int(($buildTime - $buildHours * 3600) / 60);
2569     my $buildSecs = $buildTime - $buildHours * 3600 - $buildMins * 60;
2570
2571     if ($buildHours) {
2572         return sprintf("%dh:%02dm:%02ds", $buildHours, $buildMins, $buildSecs);
2573     }
2574     return sprintf("%02dm:%02ds", $buildMins, $buildSecs);
2575 }
2576
2577 sub runSvnUpdateAndResolveChangeLogs(@)
2578 {
2579     my @svnOptions = @_;
2580     my $openCommand = "svn update " . join(" ", @svnOptions);
2581     open my $update, "$openCommand |" or die "cannot execute command $openCommand";
2582     my @conflictedChangeLogs;
2583     while (my $line = <$update>) {
2584         print $line;
2585         $line =~ m/^C\s+(.+?)[\r\n]*$/;
2586         if ($1) {
2587           my $filename = normalizePath($1);
2588           push @conflictedChangeLogs, $filename if basename($filename) eq "ChangeLog";
2589         }
2590     }
2591     close $update or die;
2592
2593     if (@conflictedChangeLogs) {
2594         print "Attempting to merge conflicted ChangeLogs.\n";
2595         my $resolveChangeLogsPath = File::Spec->catfile(sourceDir(), "Tools", "Scripts", "resolve-ChangeLogs");
2596         (system($resolveChangeLogsPath, "--no-warnings", @conflictedChangeLogs) == 0)
2597             or die "Could not open resolve-ChangeLogs script: $!.\n";
2598     }
2599 }
2600
2601 sub runGitUpdate()
2602 {
2603     # Doing a git fetch first allows setups with svn-remote.svn.fetch = trunk:refs/remotes/origin/master
2604     # to perform the rebase much much faster.
2605     system("git", "fetch");
2606     if (isGitSVNDirectory(".")) {
2607         system("git", "svn", "rebase") == 0 or die;
2608     } else {
2609         # This will die if branch.$BRANCHNAME.merge isn't set, which is
2610         # almost certainly what we want.
2611         system("git", "pull") == 0 or die;
2612     }
2613 }
2614
2615 1;