55f7617dc326a544e3e6078b6e2eab3967aaea52
[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::Temp qw(tempdir);
44 use File::stat;
45 use List::Util;
46 use POSIX;
47 use Time::HiRes qw(usleep);
48 use VCSUtils;
49
50 BEGIN {
51    use Exporter   ();
52    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
53    $VERSION     = 1.00;
54    @ISA         = qw(Exporter);
55    @EXPORT      = qw(
56        &XcodeCoverageSupportOptions
57        &XcodeOptionString
58        &XcodeOptionStringNoConfig
59        &XcodeOptions
60        &XcodeStaticAnalyzerOption
61        &appDisplayNameFromBundle
62        &appendToEnvironmentVariableList
63        &archCommandLineArgumentsForRestrictedEnvironmentVariables
64        &baseProductDir
65        &chdirWebKit
66        &checkFrameworks
67        &cmakeBasedPortArguments
68        &currentSVNRevision
69        &debugSafari
70        &executableProductDir
71        &findOrCreateSimulatorForIOSDevice
72        &iosSimulatorDeviceByName
73        &nmPath
74        &passedConfiguration
75        &prependToEnvironmentVariableList
76        &printHelpAndExitForRunAndDebugWebKitAppIfNeeded
77        &productDir
78        &quitIOSSimulator
79        &relaunchIOSSimulator
80        &restartIOSSimulatorDevice
81        &runIOSWebKitApp
82        &runMacWebKitApp
83        &safariPath
84        &iosVersion
85        &setConfiguration
86        &setupMacWebKitEnvironment
87        &sharedCommandLineOptions
88        &sharedCommandLineOptionsUsage
89        &shutDownIOSSimulatorDevice
90        &willUseIOSDeviceSDK
91        &willUseIOSSimulatorSDK
92        SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT
93        USE_OPEN_COMMAND
94    );
95    %EXPORT_TAGS = ( );
96    @EXPORT_OK   = ();
97 }
98
99 # Ports
100 use constant {
101     AppleWin => "AppleWin",
102     GTK      => "GTK",
103     Efl      => "Efl",
104     iOS      => "iOS",
105     Mac      => "Mac",
106     JSCOnly  => "JSCOnly",
107     WinCairo => "WinCairo",
108     Unknown  => "Unknown"
109 };
110
111 use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp().
112 use constant SIMULATOR_DEVICE_STATE_SHUTDOWN => "1";
113 use constant SIMULATOR_DEVICE_STATE_BOOTED => "3";
114 use constant SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT  => "For WebKit Development";
115
116 # 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>.
117 use constant IOS_DEVELOPMENT_CERTIFICATE_NAME_PREFIX => "iPhone Developer: ";
118
119 our @EXPORT_OK;
120
121 my $architecture;
122 my $asanIsEnabled;
123 my $numberOfCPUs;
124 my $maxCPULoad;
125 my $baseProductDir;
126 my @baseProductDirOption;
127 my $configuration;
128 my $xcodeSDK;
129 my $configurationForVisualStudio;
130 my $configurationProductDir;
131 my $sourceDir;
132 my $currentSVNRevision;
133 my $didLoadIPhoneSimulatorNotification;
134 my $nmPath;
135 my $osXVersion;
136 my $iosVersion;
137 my $generateDsym;
138 my $isCMakeBuild;
139 my $isWin64;
140 my $isInspectorFrontend;
141 my $portName;
142 my $shouldUseGuardMalloc;
143 my $shouldNotUseNinja;
144 my $xcodeVersion;
145
146 my $unknownPortProhibited = 0;
147
148 # Variables for Win32 support
149 my $programFilesPath;
150 my $vcBuildPath;
151 my $vsInstallDir;
152 my $msBuildInstallDir;
153 my $vsVersion;
154 my $windowsSourceDir;
155 my $winVersion;
156 my $willUseVCExpressWhenBuilding = 0;
157
158 # Defined in VCSUtils.
159 sub exitStatus($);
160
161 sub findMatchingArguments($$);
162 sub hasArgument($$);
163
164 sub determineSourceDir
165 {
166     return if $sourceDir;
167     $sourceDir = $FindBin::Bin;
168     $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
169
170     # walks up path checking each directory to see if it is the main WebKit project dir, 
171     # defined by containing Sources, WebCore, and JavaScriptCore.
172     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")))
173     {
174         if ($sourceDir !~ s|/[^/]+$||) {
175             die "Could not find top level webkit directory above source directory using FindBin.\n";
176         }
177     }
178
179     $sourceDir = File::Spec->catdir($sourceDir, "OpenSource") if -d File::Spec->catdir($sourceDir, "OpenSource");
180 }
181
182 sub currentPerlPath()
183 {
184     my $thisPerl = $^X;
185     if ($^O ne 'VMS') {
186         $thisPerl .= $Config{_exe} unless $thisPerl =~ m/$Config{_exe}$/i;
187     }
188     return $thisPerl;
189 }
190
191 # used for scripts which are stored in a non-standard location
192 sub setSourceDir($)
193 {
194     ($sourceDir) = @_;
195 }
196
197 sub determineNinjaVersion
198 {
199     chomp(my $ninjaVersion = `ninja --version`);
200     return $ninjaVersion;
201 }
202
203 sub determineXcodeVersion
204 {
205     return if defined $xcodeVersion;
206     my $xcodebuildVersionOutput = `xcodebuild -version`;
207     $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : "3.0";
208 }
209
210 sub readXcodeUserDefault($)
211 {
212     my ($key) = @_;
213
214     my $devnull = File::Spec->devnull();
215
216     my $value = `defaults read com.apple.dt.Xcode ${key} 2> ${devnull}`;
217     return if $?;
218
219     chomp $value;
220     return $value;
221 }
222
223 sub determineBaseProductDir
224 {
225     return if defined $baseProductDir;
226     determineSourceDir();
227
228     my $setSharedPrecompsDir;
229     $baseProductDir = $ENV{"WEBKIT_OUTPUTDIR"};
230
231     if (!defined($baseProductDir) and isAppleCocoaWebKit()) {
232         # Silently remove ~/Library/Preferences/xcodebuild.plist which can
233         # cause build failure. The presence of
234         # ~/Library/Preferences/xcodebuild.plist can prevent xcodebuild from
235         # respecting global settings such as a custom build products directory
236         # (<rdar://problem/5585899>).
237         my $personalPlistFile = $ENV{HOME} . "/Library/Preferences/xcodebuild.plist";
238         if (-e $personalPlistFile) {
239             unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!";
240         }
241
242         my $buildLocationStyle = join '', readXcodeUserDefault("IDEBuildLocationStyle");
243         if ($buildLocationStyle eq "Custom") {
244             my $buildLocationType = join '', readXcodeUserDefault("IDECustomBuildLocationType");
245             # FIXME: Read CustomBuildIntermediatesPath and set OBJROOT accordingly.
246             $baseProductDir = readXcodeUserDefault("IDECustomBuildProductsPath") if $buildLocationType eq "Absolute";
247         }
248
249         # DeterminedByTargets corresponds to a setting of "Legacy" in Xcode.
250         # It is the only build location style for which SHARED_PRECOMPS_DIR is not
251         # overridden when building from within Xcode.
252         $setSharedPrecompsDir = 1 if $buildLocationStyle ne "DeterminedByTargets";
253
254         if (!defined($baseProductDir)) {
255             $baseProductDir = join '', readXcodeUserDefault("IDEApplicationwideBuildSettings");
256             $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s;
257         }
258
259         undef $baseProductDir unless $baseProductDir =~ /^\//;
260     }
261
262     if (!defined($baseProductDir)) { # Port-specific checks failed, use default
263         $baseProductDir = File::Spec->catdir($sourceDir, "WebKitBuild");
264     }
265
266     if (isGit() && isGitBranchBuild()) {
267         my $branch = gitBranch();
268         $baseProductDir = "$baseProductDir/$branch";
269     }
270
271     if (isAppleCocoaWebKit()) {
272         $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|;
273         $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|;
274         $baseProductDir =~ s|^~/|$ENV{HOME}/|;
275         die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/;
276         die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/;
277         @baseProductDirOption = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir");
278         push(@baseProductDirOption, "SHARED_PRECOMPS_DIR=${baseProductDir}/PrecompiledHeaders") if $setSharedPrecompsDir;
279     }
280
281     if (isCygwin()) {
282         my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`;
283         chomp $dosBuildPath;
284         $ENV{"WEBKIT_OUTPUTDIR"} = $dosBuildPath;
285         my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`;
286         chomp $unixBuildPath;
287         $baseProductDir = $dosBuildPath;
288     }
289 }
290
291 sub systemVerbose {
292     print "+ @_\n";
293     return system(@_);
294 }
295
296 sub setBaseProductDir($)
297 {
298     ($baseProductDir) = @_;
299 }
300
301 sub determineConfiguration
302 {
303     return if defined $configuration;
304     determineBaseProductDir();
305     if (open CONFIGURATION, "$baseProductDir/Configuration") {
306         $configuration = <CONFIGURATION>;
307         close CONFIGURATION;
308     }
309     if ($configuration) {
310         chomp $configuration;
311         # compatibility for people who have old Configuration files
312         $configuration = "Release" if $configuration eq "Deployment";
313         $configuration = "Debug" if $configuration eq "Development";
314     } else {
315         $configuration = "Release";
316     }
317 }
318
319 sub determineArchitecture
320 {
321     return if defined $architecture;
322     # make sure $architecture is defined in all cases
323     $architecture = "";
324
325     determineBaseProductDir();
326     determineXcodeSDK();
327
328     if (isAppleCocoaWebKit()) {
329         if (open ARCHITECTURE, "$baseProductDir/Architecture") {
330             $architecture = <ARCHITECTURE>;
331             close ARCHITECTURE;
332         }
333         if ($architecture) {
334             chomp $architecture;
335         } else {
336             if (not defined $xcodeSDK or $xcodeSDK =~ /^(\/$|macosx)/) {
337                 my $supports64Bit = `sysctl -n hw.optional.x86_64`;
338                 chomp $supports64Bit;
339                 $architecture = 'x86_64' if $supports64Bit;
340             } elsif ($xcodeSDK =~ /^iphonesimulator/) {
341                 $architecture = 'x86_64';
342             } elsif ($xcodeSDK =~ /^iphoneos/) {
343                 $architecture = 'arm64';
344             }
345         }
346     } elsif (isCMakeBuild()) {
347         if (isCrossCompilation()) {
348             my $compiler = "gcc";
349             $compiler = $ENV{'CC'} if (defined($ENV{'CC'}));
350             my @compiler_machine = split('-', `$compiler -dumpmachine`);
351             $architecture = $compiler_machine[0];
352         } elsif (open my $cmake_sysinfo, "cmake --system-information |") {
353             while (<$cmake_sysinfo>) {
354                 next unless index($_, 'CMAKE_SYSTEM_PROCESSOR') == 0;
355                 if (/^CMAKE_SYSTEM_PROCESSOR \"([^"]+)\"/) {
356                     $architecture = $1;
357                     last;
358                 }
359             }
360             close $cmake_sysinfo;
361         }
362     }
363
364     if (!isAnyWindows()) {
365         if (!$architecture) {
366             # Fall back to output of `uname -m', if it is present.
367             $architecture = `uname -m`;
368             chomp $architecture;
369         }
370     }
371
372     $architecture = 'x86_64' if $architecture =~ /amd64/i;
373     $architecture = 'arm64' if $architecture =~ /aarch64/i;
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 isX86_64()
1189 {
1190     return (architecture() eq "x86_64") || 0;
1191 }
1192
1193 sub isCrossCompilation()
1194 {
1195     my $compiler = "";
1196     $compiler = $ENV{'CC'} if (defined($ENV{'CC'}));
1197     if ($compiler =~ /gcc/) {
1198         my $compilerOptions = `$compiler -v 2>&1`;
1199         my @host = $compilerOptions =~ m/--host=(.*?)\s/;
1200         my @target = $compilerOptions =~ m/--target=(.*?)\s/;
1201         if ($target[0] ne "" && $host[0] ne "") {
1202                 return ($host[0] ne $target[0]);
1203         } else {
1204                 # $tempDir gets automatically deleted when goes out of scope
1205                 my $tempDir = File::Temp->newdir();
1206                 my $testProgramSourcePath = File::Spec->catfile($tempDir, "testcross.c");
1207                 my $testProgramBinaryPath = File::Spec->catfile($tempDir, "testcross");
1208                 open(my $testProgramSourceHandler, ">", $testProgramSourcePath);
1209                 print $testProgramSourceHandler "int main() { return 0; }\n";
1210                 system("$compiler $testProgramSourcePath -o $testProgramBinaryPath > /dev/null 2>&1") == 0 or return 0;
1211                 # Crosscompiling if the program fails to run (because it was built for other arch)
1212                 system("$testProgramBinaryPath > /dev/null 2>&1") == 0 or return 1;
1213                 return 0;
1214         }
1215     }
1216     return 0;
1217 }
1218
1219 sub isAppleWebKit()
1220 {
1221     return isAppleCocoaWebKit() || isAppleWinWebKit();
1222 }
1223
1224 sub isAppleCocoaWebKit()
1225 {
1226     return (portName() eq Mac) || isIOSWebKit();
1227 }
1228
1229 sub isAppleWinWebKit()
1230 {
1231     return portName() eq AppleWin;
1232 }
1233
1234 sub iOSSimulatorDevicesPath
1235 {
1236     return "$ENV{HOME}/Library/Developer/CoreSimulator/Devices";
1237 }
1238
1239 sub iOSSimulatorDevices
1240 {
1241     eval "require Foundation";
1242     my $devicesPath = iOSSimulatorDevicesPath();
1243     opendir(DEVICES, $devicesPath);
1244     my @udids = grep {
1245         $_ =~ m/^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$/;
1246     } readdir(DEVICES);
1247     close(DEVICES);
1248
1249     # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
1250     #        dictionary keys so as to decouple our representation of the plist from the actual structure
1251     #        of the plist, which may change.
1252     my @devices = map {
1253         Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_("$devicesPath/$_/device.plist"));
1254     } @udids;
1255
1256     return @devices;
1257 }
1258
1259 sub createiOSSimulatorDevice
1260 {
1261     my $name = shift;
1262     my $deviceTypeId = shift;
1263     my $runtimeId = shift;
1264
1265     my $created = system("xcrun", "--sdk", "iphonesimulator", "simctl", "create", $name, $deviceTypeId, $runtimeId) == 0;
1266     die "Couldn't create simulator device: $name $deviceTypeId $runtimeId" if not $created;
1267
1268     system("xcrun", "--sdk", "iphonesimulator", "simctl", "list");
1269
1270     print "Waiting for device to be created ...\n";
1271     sleep 5;
1272     for (my $tries = 0; $tries < 5; $tries++){
1273         my @devices = iOSSimulatorDevices();
1274         foreach my $device (@devices) {
1275             return $device if $device->{name} eq $name and $device->{deviceType} eq $deviceTypeId and $device->{runtime} eq $runtimeId;
1276         }
1277         sleep 5;
1278     }
1279     die "Device $name $deviceTypeId $runtimeId wasn't found in " . iOSSimulatorDevicesPath();
1280 }
1281
1282 sub willUseIOSDeviceSDK()
1283 {
1284     return xcodeSDKPlatformName() eq "iphoneos";
1285 }
1286
1287 sub willUseIOSSimulatorSDK()
1288 {
1289     return xcodeSDKPlatformName() eq "iphonesimulator";
1290 }
1291
1292 sub isIOSWebKit()
1293 {
1294     return portName() eq iOS;
1295 }
1296
1297 sub determineNmPath()
1298 {
1299     return if $nmPath;
1300
1301     if (isAppleCocoaWebKit()) {
1302         $nmPath = `xcrun -find nm`;
1303         chomp $nmPath;
1304     }
1305     $nmPath = "nm" if !$nmPath;
1306 }
1307
1308 sub nmPath()
1309 {
1310     determineNmPath();
1311     return $nmPath;
1312 }
1313
1314 sub splitVersionString
1315 {
1316     my $versionString = shift;
1317     my @splitVersion = split(/\./, $versionString);
1318     @splitVersion >= 2 or die "Invalid version $versionString";
1319     $osXVersion = {
1320             "major" => $splitVersion[0],
1321             "minor" => $splitVersion[1],
1322             "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0),
1323     };
1324 }
1325
1326 sub determineOSXVersion()
1327 {
1328     return if $osXVersion;
1329
1330     if (!isDarwin()) {
1331         $osXVersion = -1;
1332         return;
1333     }
1334
1335     my $versionString = `sw_vers -productVersion`;
1336     $osXVersion = splitVersionString($versionString);
1337 }
1338
1339 sub osXVersion()
1340 {
1341     determineOSXVersion();
1342     return $osXVersion;
1343 }
1344
1345 sub determineIOSVersion()
1346 {
1347     return if $iosVersion;
1348
1349     if (!isIOSWebKit()) {
1350         $iosVersion = -1;
1351         return;
1352     }
1353
1354     my $versionString = xcodeSDKVersion();
1355     $iosVersion = splitVersionString($versionString);
1356 }
1357
1358 sub iosVersion()
1359 {
1360     determineIOSVersion();
1361     return $iosVersion;
1362 }
1363
1364 sub isWindowsNT()
1365 {
1366     return $ENV{'OS'} eq 'Windows_NT';
1367 }
1368
1369 sub appendToEnvironmentVariableList($$)
1370 {
1371     my ($name, $value) = @_;
1372
1373     if (defined($ENV{$name})) {
1374         $ENV{$name} .= $Config{path_sep} . $value;
1375     } else {
1376         $ENV{$name} = $value;
1377     }
1378 }
1379
1380 sub prependToEnvironmentVariableList($$)
1381 {
1382     my ($name, $value) = @_;
1383
1384     if (defined($ENV{$name})) {
1385         $ENV{$name} = $value . $Config{path_sep} . $ENV{$name};
1386     } else {
1387         $ENV{$name} = $value;
1388     }
1389 }
1390
1391 sub sharedCommandLineOptions()
1392 {
1393     return (
1394         "g|guard-malloc" => \$shouldUseGuardMalloc,
1395     );
1396 }
1397
1398 sub sharedCommandLineOptionsUsage
1399 {
1400     my %opts = @_;
1401
1402     my %switches = (
1403         '-g|--guard-malloc' => 'Use guardmalloc when running executable',
1404     );
1405
1406     my $indent = " " x ($opts{indent} || 2);
1407     my $switchWidth = List::Util::max(int($opts{switchWidth}), List::Util::max(map { length($_) } keys %switches) + ($opts{brackets} ? 2 : 0));
1408
1409     my $result = "Common switches:\n";
1410
1411     for my $switch (keys %switches) {
1412         my $switchName = $opts{brackets} ? "[" . $switch . "]" : $switch;
1413         $result .= sprintf("%s%-" . $switchWidth . "s %s\n", $indent, $switchName, $switches{$switch});
1414     }
1415
1416     return $result;
1417 }
1418
1419 sub setUpGuardMallocIfNeeded
1420 {
1421     if (!isDarwin()) {
1422         return;
1423     }
1424
1425     if (!defined($shouldUseGuardMalloc)) {
1426         $shouldUseGuardMalloc = checkForArgumentAndRemoveFromARGV("-g") || checkForArgumentAndRemoveFromARGV("--guard-malloc");
1427     }
1428
1429     if ($shouldUseGuardMalloc) {
1430         appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib");
1431         appendToEnvironmentVariableList("__XPC_DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib");
1432     }
1433 }
1434
1435 sub relativeScriptsDir()
1436 {
1437     my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel($FindBin::Bin, getcwd()), "");
1438     if ($scriptDir eq "") {
1439         $scriptDir = ".";
1440     }
1441     return $scriptDir;
1442 }
1443
1444 sub launcherPath()
1445 {
1446     my $relativeScriptsPath = relativeScriptsDir();
1447     if (isGtk() || isEfl()) {
1448         return "$relativeScriptsPath/run-minibrowser";
1449     } elsif (isAppleWebKit()) {
1450         return "$relativeScriptsPath/run-safari";
1451     }
1452 }
1453
1454 sub launcherName()
1455 {
1456     if (isGtk() || isEfl()) {
1457         return "MiniBrowser";
1458     } elsif (isAppleCocoaWebKit()) {
1459         return "Safari";
1460     } elsif (isAppleWinWebKit()) {
1461         return "MiniBrowser";
1462     }
1463 }
1464
1465 sub checkRequiredSystemConfig
1466 {
1467     if (isDarwin()) {
1468         chomp(my $productVersion = `sw_vers -productVersion`);
1469         if (eval "v$productVersion" lt v10.10.5) {
1470             print "*************************************************************\n";
1471             print "OS X Yosemite v10.10.5 or later is required to build WebKit.\n";
1472             print "You have " . $productVersion . ", thus the build will most likely fail.\n";
1473             print "*************************************************************\n";
1474         }
1475         determineXcodeVersion();
1476         if (eval "v$xcodeVersion" lt v7.0) {
1477             print "*************************************************************\n";
1478             print "Xcode 7.0 or later is required to build WebKit.\n";
1479             print "You have an earlier version of Xcode, thus the build will\n";
1480             print "most likely fail. The latest Xcode is available from the App Store.\n";
1481             print "*************************************************************\n";
1482         }
1483     }
1484 }
1485
1486 sub determineWindowsSourceDir()
1487 {
1488     return if $windowsSourceDir;
1489     $windowsSourceDir = sourceDir();
1490     chomp($windowsSourceDir = `cygpath -w '$windowsSourceDir'`) if isCygwin();
1491 }
1492
1493 sub windowsSourceDir()
1494 {
1495     determineWindowsSourceDir();
1496     return $windowsSourceDir;
1497 }
1498
1499 sub windowsSourceSourceDir()
1500 {
1501     return File::Spec->catdir(windowsSourceDir(), "Source");
1502 }
1503
1504 sub windowsLibrariesDir()
1505 {
1506     return File::Spec->catdir(windowsSourceDir(), "WebKitLibraries", "win");
1507 }
1508
1509 sub windowsOutputDir()
1510 {
1511     return File::Spec->catdir(windowsSourceDir(), "WebKitBuild");
1512 }
1513
1514 sub fontExists($)
1515 {
1516     my $font = shift;
1517     my $cmd = "reg query \"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\" /v \"$font\" 2>&1";
1518     my $val = `$cmd`;
1519     return $? == 0;
1520 }
1521
1522 sub checkInstalledTools()
1523 {
1524     # environment variables. Avoid until this is corrected.
1525     my $pythonVer = `python --version 2>&1`;
1526     die "You must have Python installed to build WebKit.\n" if ($?);
1527
1528     # cURL 7.34.0 has a bug that prevents authentication with opensource.apple.com (and other things using SSL3).
1529     my $curlVer = `curl --version 2> NUL`;
1530     if (!$? and $curlVer =~ "(.*curl.*)") {
1531         $curlVer = $1;
1532         if ($curlVer =~ /libcurl\/7\.34\.0/) {
1533             print "cURL version 7.34.0 has a bug that prevents authentication with SSL v2 or v3.\n";
1534             print "cURL 7.33.0 is known to work. The cURL projects is preparing an update to\n";
1535             print "correct this problem.\n\n";
1536             die "Please install a working cURL and try again.\n";
1537         }
1538     }
1539
1540     # MathML requires fonts that may not ship with Windows.
1541     # Warn the user if they are missing.
1542     my @fonts = ('Cambria & Cambria Math (TrueType)', 'LatinModernMath-Regular (TrueType)', 'STIXMath-Regular (TrueType)');
1543     my @missing = ();
1544     foreach my $font (@fonts) {
1545         push @missing, $font if not fontExists($font);
1546     }
1547
1548     if (scalar @missing > 0) {
1549         print "*************************************************************\n";
1550         print "Mathematical fonts, such as Latin Modern Math are needed to\n";
1551         print "use the MathML feature.  You do not appear to have these fonts\n";
1552         print "on your system.\n\n";
1553         print "You can download a suitable set of fonts from the following URL:\n";
1554         print "https://trac.webkit.org/wiki/MathML/Fonts\n";
1555         print "*************************************************************\n";
1556     }
1557
1558     print "Installed tools are correct for the WebKit build.\n";
1559 }
1560
1561 sub setupAppleWinEnv()
1562 {
1563     return unless isAppleWinWebKit();
1564
1565     checkInstalledTools();
1566
1567     if (isWindowsNT()) {
1568         my $restartNeeded = 0;
1569         my %variablesToSet = ();
1570
1571         # FIXME: We should remove this explicit version check for cygwin once we stop supporting Cygwin 1.7.9 or older versions. 
1572         # https://bugs.webkit.org/show_bug.cgi?id=85791
1573         my $uname_version = (POSIX::uname())[2];
1574         $uname_version =~ s/\(.*\)//;  # Remove the trailing cygwin version, if any.
1575         $uname_version =~ s/\-.*$//; # Remove trailing dash-version content, if any
1576         if (version->parse($uname_version) < version->parse("1.7.10")) {
1577             # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios)
1578             # for UNIX-like ttys in the Windows console
1579             $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN};
1580         }
1581         
1582         # Those environment variables must be set to be able to build inside Visual Studio.
1583         $variablesToSet{WEBKIT_LIBRARIES} = windowsLibrariesDir() unless $ENV{WEBKIT_LIBRARIES};
1584         $variablesToSet{WEBKIT_OUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKIT_OUTPUTDIR};
1585         $variablesToSet{MSBUILDDISABLENODEREUSE} = "1" unless $ENV{MSBUILDDISABLENODEREUSE};
1586         $variablesToSet{_IsNativeEnvironment} = "true" unless $ENV{_IsNativeEnvironment};
1587         $variablesToSet{PreferredToolArchitecture} = "x64" unless $ENV{PreferredToolArchitecture};
1588
1589         foreach my $variable (keys %variablesToSet) {
1590             print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n";
1591             my $ret = system "setx", $variable, $variablesToSet{$variable};
1592             if ($ret != 0) {
1593                 system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable};
1594             }
1595             $restartNeeded ||=  $variable eq "WEBKIT_LIBRARIES" || $variable eq "WEBKIT_OUTPUTDIR";
1596         }
1597
1598         if ($restartNeeded) {
1599             print "Please restart your computer before attempting to build inside Visual Studio.\n\n";
1600         }
1601     } else {
1602         if (!defined $ENV{'WEBKIT_LIBRARIES'} || !$ENV{'WEBKIT_LIBRARIES'}) {
1603             print "Warning: You must set the 'WebKit_Libraries' environment variable\n";
1604             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n";
1605             print "         Make sure that 'WebKit_Libraries' points to the\n";
1606             print "         'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n";
1607         }
1608         if (!defined $ENV{'WEBKIT_OUTPUTDIR'} || !$ENV{'WEBKIT_OUTPUTDIR'}) {
1609             print "Warning: You must set the 'WebKit_OutputDir' environment variable\n";
1610             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n\n";
1611         }
1612         if (!defined $ENV{'MSBUILDDISABLENODEREUSE'} || !$ENV{'MSBUILDDISABLENODEREUSE'}) {
1613             print "Warning: You should set the 'MSBUILDDISABLENODEREUSE' environment variable to '1'\n";
1614             print "         to avoid periodic locked log files when building.\n\n";
1615         }
1616     }
1617     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1618     if (isWin64() and !$ENV{'WEBKIT_64_SUPPORT'}) {
1619         print "Warning: You must set the 'WEBKIT_64_SUPPORT' environment variable\n";
1620         print "         to be able run WebKit or JavaScriptCore tests.\n\n";
1621     }
1622 }
1623
1624 sub setupCygwinEnv()
1625 {
1626     return if !isAnyWindows();
1627     return if $vcBuildPath;
1628
1629     my $programFilesPath = programFilesPath();
1630     my $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.com));
1631     if (-e $visualStudioPath) {
1632         # Visual Studio is installed;
1633         if (visualStudioVersion() eq "12") {
1634             $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.exe));
1635         }
1636     } else {
1637         # Visual Studio not found, try VC++ Express
1638         $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE WDExpress.exe));
1639         if (! -e $visualStudioPath) {
1640             print "*************************************************************\n";
1641             print "Cannot find '$visualStudioPath'\n";
1642             print "Please execute the file 'vcvars32.bat' from\n";
1643             print "'$programFilesPath\\Microsoft Visual Studio 14.0\\VC\\bin\\'\n";
1644             print "to setup the necessary environment variables.\n";
1645             print "*************************************************************\n";
1646             die;
1647         }
1648         $willUseVCExpressWhenBuilding = 1;
1649     }
1650
1651     print "Building results into: ", baseProductDir(), "\n";
1652     print "WEBKIT_OUTPUTDIR is set to: ", $ENV{"WEBKIT_OUTPUTDIR"}, "\n";
1653     print "WEBKIT_LIBRARIES is set to: ", $ENV{"WEBKIT_LIBRARIES"}, "\n";
1654     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1655     print "WEBKIT_64_SUPPORT is set to: ", $ENV{"WEBKIT_64_SUPPORT"}, "\n" if isWin64();
1656
1657     # We will actually use MSBuild to build WebKit, but we need to find the Visual Studio install (above) to make
1658     # sure we use the right options.
1659     $vcBuildPath = File::Spec->catfile(msBuildInstallDir(), qw(MSBuild.exe));
1660     if (! -e $vcBuildPath) {
1661         print "*************************************************************\n";
1662         print "Cannot find '$vcBuildPath'\n";
1663         print "Please make sure execute that the Microsoft .NET Framework SDK\n";
1664         print "is installed on this machine.\n";
1665         print "*************************************************************\n";
1666         die;
1667     }
1668 }
1669
1670 sub dieIfWindowsPlatformSDKNotInstalled
1671 {
1672     my $registry32Path = "/proc/registry/";
1673     my $registry64Path = "/proc/registry64/";
1674     my @windowsPlatformSDKRegistryEntries = (
1675         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0A",
1676         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0",
1677         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.1A",
1678         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.0A",
1679         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1",
1680     );
1681
1682     # FIXME: It would be better to detect whether we are using 32- or 64-bit Windows
1683     # and only check the appropriate entry. But for now we just blindly check both.
1684     my $recommendedPlatformSDK = $windowsPlatformSDKRegistryEntries[0];
1685
1686     while (@windowsPlatformSDKRegistryEntries) {
1687         my $windowsPlatformSDKRegistryEntry = shift @windowsPlatformSDKRegistryEntries;
1688         return if (-e $registry32Path . $windowsPlatformSDKRegistryEntry) || (-e $registry64Path . $windowsPlatformSDKRegistryEntry);
1689     }
1690
1691     print "*************************************************************\n";
1692     print "Cannot find registry entry '$recommendedPlatformSDK'.\n";
1693     print "Please download and install the Microsoft Windows SDK\n";
1694     print "from <http://www.microsoft.com/en-us/download/details.aspx?id=8279>.\n\n";
1695     print "Then follow step 2 in the Windows section of the \"Installing Developer\n";
1696     print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n";
1697     print "*************************************************************\n";
1698     die;
1699 }
1700
1701 sub buildXCodeProject($$@)
1702 {
1703     my ($project, $clean, @extraOptions) = @_;
1704
1705     if ($clean) {
1706         push(@extraOptions, "-alltargets");
1707         push(@extraOptions, "clean");
1708     }
1709
1710     chomp($ENV{DSYMUTIL_NUM_THREADS} = `sysctl -n hw.activecpu`);
1711     return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions;
1712 }
1713
1714 sub usingVisualStudioExpress()
1715 {
1716     setupCygwinEnv();
1717     return $willUseVCExpressWhenBuilding;
1718 }
1719
1720 sub buildVisualStudioProject
1721 {
1722     my ($project, $clean) = @_;
1723     setupCygwinEnv();
1724
1725     my $config = configurationForVisualStudio();
1726
1727     dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding;
1728
1729     chomp($project = `cygpath -w "$project"`) if isCygwin();
1730
1731     my $action = "/t:build";
1732     if ($clean) {
1733         $action = "/t:clean";
1734     }
1735
1736     my $platform = "/p:Platform=" . (isWin64() ? "x64" : "Win32");
1737     my $logPath = File::Spec->catdir($baseProductDir, $configuration);
1738     make_path($logPath) unless -d $logPath or $logPath eq ".";
1739
1740     my $errorLogFile = File::Spec->catfile($logPath, "webkit_errors.log");
1741     chomp($errorLogFile = `cygpath -w "$errorLogFile"`) if isCygwin();
1742     my $errorLogging = "/flp:LogFile=" . $errorLogFile . ";ErrorsOnly";
1743
1744     my $warningLogFile = File::Spec->catfile($logPath, "webkit_warnings.log");
1745     chomp($warningLogFile = `cygpath -w "$warningLogFile"`) if isCygwin();
1746     my $warningLogging = "/flp1:LogFile=" . $warningLogFile . ";WarningsOnly";
1747
1748     my @command = ($vcBuildPath, "/verbosity:minimal", $project, $action, $config, $platform, "/fl", $errorLogging, "/fl1", $warningLogging);
1749     print join(" ", @command), "\n";
1750     return system @command;
1751 }
1752
1753 sub getJhbuildPath()
1754 {
1755     my @jhbuildPath = File::Spec->splitdir(baseProductDir());
1756     if (isGit() && isGitBranchBuild() && gitBranch()) {
1757         pop(@jhbuildPath);
1758     }
1759     if (isEfl()) {
1760         push(@jhbuildPath, "DependenciesEFL");
1761     } elsif (isGtk()) {
1762         push(@jhbuildPath, "DependenciesGTK");
1763     } else {
1764         die "Cannot get JHBuild path for platform that isn't GTK+ or EFL.\n";
1765     }
1766     return File::Spec->catdir(@jhbuildPath);
1767 }
1768
1769 sub isCachedArgumentfileOutOfDate($@)
1770 {
1771     my ($filename, $currentContents) = @_;
1772
1773     if (! -e $filename) {
1774         return 1;
1775     }
1776
1777     open(CONTENTS_FILE, $filename);
1778     chomp(my $previousContents = <CONTENTS_FILE>);
1779     close(CONTENTS_FILE);
1780
1781     if ($previousContents ne $currentContents) {
1782         print "Contents for file $filename have changed.\n";
1783         print "Previous contents were: $previousContents\n\n";
1784         print "New contents are: $currentContents\n";
1785         return 1;
1786     }
1787
1788     return 0;
1789 }
1790
1791 sub wrapperPrefixIfNeeded()
1792 {
1793     if (isAnyWindows() || isJSCOnly()) {
1794         return ();
1795     }
1796     if (isAppleCocoaWebKit()) {
1797         return ("xcrun");
1798     }
1799     if (-e getJhbuildPath()) {
1800         my @prefix = (File::Spec->catfile(sourceDir(), "Tools", "jhbuild", "jhbuild-wrapper"));
1801         if (isEfl()) {
1802             push(@prefix, "--efl");
1803         } elsif (isGtk()) {
1804             push(@prefix, "--gtk");
1805         }
1806         push(@prefix, "run");
1807
1808         return @prefix;
1809     }
1810
1811     return ();
1812 }
1813
1814 sub cmakeCachePath()
1815 {
1816     return File::Spec->catdir(baseProductDir(), configuration(), "CMakeCache.txt");
1817 }
1818
1819 sub cmakeFilesPath()
1820 {
1821     return File::Spec->catdir(baseProductDir(), configuration(), "CMakeFiles");
1822 }
1823
1824 sub shouldRemoveCMakeCache(@)
1825 {
1826     my ($cacheFilePath, @buildArgs) = @_;
1827
1828     # We check this first, because we always want to create this file for a fresh build.
1829     my $productDir = File::Spec->catdir(baseProductDir(), configuration());
1830     my $optionsCache = File::Spec->catdir($productDir, "build-webkit-options.txt");
1831     my $joinedBuildArgs = join(" ", @buildArgs);
1832     if (isCachedArgumentfileOutOfDate($optionsCache, $joinedBuildArgs)) {
1833         File::Path::mkpath($productDir) unless -d $productDir;
1834         open(CACHED_ARGUMENTS, ">", $optionsCache);
1835         print CACHED_ARGUMENTS $joinedBuildArgs;
1836         close(CACHED_ARGUMENTS);
1837
1838         return 1;
1839     }
1840
1841     my $cmakeCache = cmakeCachePath();
1842     unless (-e $cmakeCache) {
1843         return 0;
1844     }
1845
1846     my $cacheFileModifiedTime = stat($cmakeCache)->mtime;
1847     my $platformConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "Options" . cmakeBasedPortName() . ".cmake");
1848     if ($cacheFileModifiedTime < stat($platformConfiguration)->mtime) {
1849         return 1;
1850     }
1851
1852     my $globalConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsCommon.cmake");
1853     if ($cacheFileModifiedTime < stat($globalConfiguration)->mtime) {
1854         return 1;
1855     }
1856
1857     my $inspectorUserInterfaceDircetory = File::Spec->catdir(sourceDir(), "Source", "WebInspectorUI", "UserInterface");
1858     if ($cacheFileModifiedTime < stat($inspectorUserInterfaceDircetory)->mtime) {
1859         return 1;
1860     }
1861
1862     if(isAnyWindows()) {
1863         my $winConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsWin.cmake");
1864         if ($cacheFileModifiedTime < stat($winConfiguration)->mtime) {
1865             return 1;
1866         }
1867     }
1868
1869     return 0;
1870 }
1871
1872 sub removeCMakeCache(@)
1873 {
1874     my (@buildArgs) = @_;
1875     if (shouldRemoveCMakeCache(@buildArgs)) {
1876         my $cmakeCache = cmakeCachePath();
1877         my $cmakeFiles = cmakeFilesPath();
1878         unlink($cmakeCache) if -e $cmakeCache;
1879         rmtree($cmakeFiles) if -d $cmakeFiles;
1880     }
1881 }
1882
1883 sub canUseNinja(@)
1884 {
1885     if (!defined($shouldNotUseNinja)) {
1886         $shouldNotUseNinja = checkForArgumentAndRemoveFromARGV("--no-ninja");
1887     }
1888
1889     if ($shouldNotUseNinja) {
1890         return 0;
1891     }
1892
1893     # Test both ninja and ninja-build. Fedora uses ninja-build and has patched CMake to also call ninja-build.
1894     return commandExists("ninja") || commandExists("ninja-build");
1895 }
1896
1897 sub canUseNinjaGenerator(@)
1898 {
1899     # Check that a Ninja generator is installed
1900     my $devnull = File::Spec->devnull();
1901     return exitStatus(system("cmake -N -G Ninja >$devnull 2>&1")) == 0;
1902 }
1903
1904 sub canUseEclipseNinjaGenerator(@)
1905 {
1906     # Check that eclipse and eclipse Ninja generator is installed
1907     my $devnull = File::Spec->devnull();
1908     return commandExists("eclipse") && exitStatus(system("cmake -N -G 'Eclipse CDT4 - Ninja' >$devnull 2>&1")) == 0;
1909 }
1910
1911 sub cmakeGeneratedBuildfile(@)
1912 {
1913     my ($willUseNinja) = @_;
1914     if ($willUseNinja) {
1915         return File::Spec->catfile(baseProductDir(), configuration(), "build.ninja")
1916     } elsif (isAnyWindows()) {
1917         return File::Spec->catfile(baseProductDir(), configuration(), "WebKit.sln")
1918     } else {
1919         return File::Spec->catfile(baseProductDir(), configuration(), "Makefile")
1920     }
1921 }
1922
1923 sub generateBuildSystemFromCMakeProject
1924 {
1925     my ($prefixPath, @cmakeArgs) = @_;
1926     my $config = configuration();
1927     my $port = cmakeBasedPortName();
1928     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1929     File::Path::mkpath($buildPath) unless -d $buildPath;
1930     my $originalWorkingDirectory = getcwd();
1931     chdir($buildPath) or die;
1932
1933     # We try to be smart about when to rerun cmake, so that we can have faster incremental builds.
1934     my $willUseNinja = canUseNinja() && canUseNinjaGenerator();
1935     if (-e cmakeCachePath() && -e cmakeGeneratedBuildfile($willUseNinja)) {
1936         return 0;
1937     }
1938
1939     my @args;
1940     push @args, "-DPORT=\"$port\"";
1941     push @args, "-DCMAKE_INSTALL_PREFIX=\"$prefixPath\"" if $prefixPath;
1942     push @args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON";
1943     if ($config =~ /release/i) {
1944         push @args, "-DCMAKE_BUILD_TYPE=Release";
1945     } elsif ($config =~ /debug/i) {
1946         push @args, "-DCMAKE_BUILD_TYPE=Debug";
1947     }
1948
1949     if ($willUseNinja) {
1950         push @args, "-G";
1951         if (canUseEclipseNinjaGenerator()) {
1952             push @args, "'Eclipse CDT4 - Ninja'";
1953         } else {
1954             push @args, "Ninja";
1955         }
1956     } elsif (isAnyWindows() && isWin64()) {
1957         push @args, '-G "Visual Studio 14 2015 Win64"';
1958     }
1959     # Do not show progress of generating bindings in interactive Ninja build not to leave noisy lines on tty
1960     push @args, '-DSHOW_BINDINGS_GENERATION_PROGRESS=1' unless ($willUseNinja && -t STDOUT);
1961
1962     # Some ports have production mode, but build-webkit should always use developer mode.
1963     push @args, "-DDEVELOPER_MODE=ON" if isEfl() || isGtk() || isJSCOnly();
1964
1965     # Don't warn variables which aren't used by cmake ports.
1966     push @args, "--no-warn-unused-cli";
1967     push @args, @cmakeArgs if @cmakeArgs;
1968
1969     my $cmakeSourceDir = isCygwin() ? windowsSourceDir() : sourceDir();
1970     push @args, '"' . $cmakeSourceDir . '"';
1971
1972     # Compiler options to keep floating point values consistent
1973     # between 32-bit and 64-bit architectures.
1974     determineArchitecture();
1975     if ($architecture eq "i686" && !isCrossCompilation() && !isAnyWindows()) {
1976         $ENV{'CXXFLAGS'} = "-march=pentium4 -msse2 -mfpmath=sse " . ($ENV{'CXXFLAGS'} || "");
1977     }
1978
1979     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
1980     # parsed for shell metacharacters.
1981     my $wrapper = join(" ", wrapperPrefixIfNeeded()) . " ";
1982     my $returnCode = systemVerbose($wrapper . "cmake @args");
1983
1984     chdir($originalWorkingDirectory);
1985     return $returnCode;
1986 }
1987
1988 sub buildCMakeGeneratedProject($)
1989 {
1990     my ($makeArgs) = @_;
1991     my $config = configuration();
1992     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1993     if (! -d $buildPath) {
1994         die "Must call generateBuildSystemFromCMakeProject() before building CMake project.";
1995     }
1996
1997     my $command = "cmake";
1998     my @args = ("--build", $buildPath, "--config", $config);
1999     push @args, ("--", $makeArgs) if $makeArgs;
2000
2001     # GTK and JSCOnly can use a build script to preserve colors and pretty-printing.
2002     if ((isGtk() || isJSCOnly()) && -e "$buildPath/build.sh") {
2003         chdir "$buildPath" or die;
2004         $command = "$buildPath/build.sh";
2005         @args = ($makeArgs);
2006     }
2007
2008     if ($ENV{VERBOSE} && canUseNinja()) {
2009         push @args, "-v";
2010         push @args, "-d keeprsp" if (version->parse(determineNinjaVersion()) >= version->parse("1.4.0"));
2011     }
2012
2013     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
2014     # parsed for shell metacharacters. In particular, $makeArgs may contain such metacharacters.
2015     my $wrapper = join(" ", wrapperPrefixIfNeeded()) . " ";
2016     return systemVerbose($wrapper . "$command @args");
2017 }
2018
2019 sub cleanCMakeGeneratedProject()
2020 {
2021     my $config = configuration();
2022     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
2023     if (-d $buildPath) {
2024         return systemVerbose("cmake", "--build", $buildPath, "--config", $config, "--target", "clean");
2025     }
2026     return 0;
2027 }
2028
2029 sub buildCMakeProjectOrExit($$$@)
2030 {
2031     my ($clean, $prefixPath, $makeArgs, @cmakeArgs) = @_;
2032     my $returnCode;
2033
2034     exit(exitStatus(cleanCMakeGeneratedProject())) if $clean;
2035
2036     if (isEfl() && checkForArgumentAndRemoveFromARGV("--update-efl")) {
2037         system("perl", "$sourceDir/Tools/Scripts/update-webkitefl-libs") == 0 or die $!;
2038     }
2039
2040     if (isGtk() && checkForArgumentAndRemoveFromARGV("--update-gtk")) {
2041         system("perl", "$sourceDir/Tools/Scripts/update-webkitgtk-libs") == 0 or die $!;
2042     }
2043
2044     $returnCode = exitStatus(generateBuildSystemFromCMakeProject($prefixPath, @cmakeArgs));
2045     exit($returnCode) if $returnCode;
2046
2047     $returnCode = exitStatus(buildCMakeGeneratedProject($makeArgs));
2048     exit($returnCode) if $returnCode;
2049     return 0;
2050 }
2051
2052 sub cmakeBasedPortArguments()
2053 {
2054     return ();
2055 }
2056
2057 sub cmakeBasedPortName()
2058 {
2059     return ucfirst portName();
2060 }
2061
2062 sub determineIsCMakeBuild()
2063 {
2064     return if defined($isCMakeBuild);
2065     $isCMakeBuild = checkForArgumentAndRemoveFromARGV("--cmake");
2066 }
2067
2068 sub isCMakeBuild()
2069 {
2070     return 1 unless isAppleCocoaWebKit();
2071     determineIsCMakeBuild();
2072     return $isCMakeBuild;
2073 }
2074
2075 sub promptUser
2076 {
2077     my ($prompt, $default) = @_;
2078     my $defaultValue = $default ? "[$default]" : "";
2079     print "$prompt $defaultValue: ";
2080     chomp(my $input = <STDIN>);
2081     return $input ? $input : $default;
2082 }
2083
2084 sub appleApplicationSupportPath
2085 {
2086     open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir";
2087     my $path = <INSTALL_DIR>;
2088     $path =~ s/[\r\n\x00].*//;
2089     close INSTALL_DIR;
2090
2091     my $unixPath = `cygpath -u '$path'`;
2092     chomp $unixPath;
2093     return $unixPath;
2094 }
2095
2096 sub setPathForRunningWebKitApp
2097 {
2098     my ($env) = @_;
2099
2100     if (isAnyWindows()) {
2101         my $productBinaryDir = executableProductDir();
2102         if (isAppleWinWebKit()) {
2103             $env->{PATH} = join(':', $productBinaryDir, appleApplicationSupportPath(), $env->{PATH} || "");
2104         } elsif (isWinCairo()) {
2105             my $winCairoBin = sourceDir() . "/WebKitLibraries/win/" . (isWin64() ? "bin64/" : "bin32/");
2106             my $gstreamerBin = isWin64() ? $ENV{"GSTREAMER_1_0_ROOT_X86_64"} . "bin" : $ENV{"GSTREAMER_1_0_ROOT_X86"} . "bin";
2107             $env->{PATH} = join(':', $productBinaryDir, $winCairoBin, $gstreamerBin, $env->{PATH} || "");
2108         }
2109     }
2110 }
2111
2112 sub printHelpAndExitForRunAndDebugWebKitAppIfNeeded
2113 {
2114     return unless checkForArgumentAndRemoveFromARGV("--help");
2115
2116     print STDERR <<EOF;
2117 Usage: @{[basename($0)]} [options] [args ...]
2118   --help                            Show this help message
2119   --no-saved-state                  Launch the application without state restoration
2120   -g|--guard-malloc                 Enable Guard Malloc (OS X only)
2121 EOF
2122
2123     exit(1);
2124 }
2125
2126 sub argumentsForRunAndDebugMacWebKitApp()
2127 {
2128     my @args = ();
2129     if (checkForArgumentAndRemoveFromARGV("--no-saved-state")) {
2130         push @args, ("-ApplePersistenceIgnoreStateQuietly", "YES");
2131         # FIXME: Don't set ApplePersistenceIgnoreState once all supported OS versions respect ApplePersistenceIgnoreStateQuietly (rdar://15032886).
2132         push @args, ("-ApplePersistenceIgnoreState", "YES");
2133     }
2134     unshift @args, @ARGV;
2135
2136     return @args;
2137 }
2138
2139 sub setupMacWebKitEnvironment($)
2140 {
2141     my ($dyldFrameworkPath) = @_;
2142
2143     $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
2144
2145     prependToEnvironmentVariableList("DYLD_FRAMEWORK_PATH", $dyldFrameworkPath);
2146     prependToEnvironmentVariableList("__XPC_DYLD_FRAMEWORK_PATH", $dyldFrameworkPath);
2147     prependToEnvironmentVariableList("DYLD_LIBRARY_PATH", $dyldFrameworkPath);
2148     prependToEnvironmentVariableList("__XPC_DYLD_LIBRARY_PATH", $dyldFrameworkPath);
2149     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
2150
2151     setUpGuardMallocIfNeeded();
2152 }
2153
2154 sub setupIOSWebKitEnvironment($)
2155 {
2156     my ($dyldFrameworkPath) = @_;
2157     $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
2158
2159     prependToEnvironmentVariableList("DYLD_FRAMEWORK_PATH", $dyldFrameworkPath);
2160     prependToEnvironmentVariableList("DYLD_LIBRARY_PATH", $dyldFrameworkPath);
2161
2162     setUpGuardMallocIfNeeded();
2163 }
2164
2165 sub iosSimulatorApplicationsPath()
2166 {
2167     return File::Spec->catdir(XcodeSDKPath(), "Applications");
2168 }
2169
2170 sub installedMobileSafariBundle()
2171 {
2172     return File::Spec->catfile(iosSimulatorApplicationsPath(), "MobileSafari.app");
2173 }
2174
2175 sub mobileSafariBundle()
2176 {
2177     determineConfigurationProductDir();
2178
2179     # Use MobileSafari.app in product directory if present.
2180     if (isAppleCocoaWebKit() && -d "$configurationProductDir/MobileSafari.app") {
2181         return "$configurationProductDir/MobileSafari.app";
2182     }
2183     return installedMobileSafariBundle();
2184 }
2185
2186 sub plistPathFromBundle($)
2187 {
2188     my ($appBundle) = @_;
2189     return "$appBundle/Info.plist" if -f "$appBundle/Info.plist"; # iOS app bundle
2190     return "$appBundle/Contents/Info.plist" if -f "$appBundle/Contents/Info.plist"; # Mac app bundle
2191     return "";
2192 }
2193
2194 sub appIdentifierFromBundle($)
2195 {
2196     my ($appBundle) = @_;
2197     my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
2198     chomp(my $bundleIdentifier = `defaults read '$plistPath' CFBundleIdentifier 2> /dev/null`);
2199     return $bundleIdentifier;
2200 }
2201
2202 sub appDisplayNameFromBundle($)
2203 {
2204     my ($appBundle) = @_;
2205     my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
2206     chomp(my $bundleDisplayName = `defaults read '$plistPath' CFBundleDisplayName 2> /dev/null`);
2207     return $bundleDisplayName;
2208 }
2209
2210 sub waitUntilIOSSimulatorDeviceIsInState($$)
2211 {
2212     my ($deviceUDID, $waitUntilState) = @_;
2213     my $device = iosSimulatorDeviceByUDID($deviceUDID);
2214     # FIXME: We should add a maximum time limit to wait here.
2215     while ($device->{state} ne $waitUntilState) {
2216         usleep(500 * 1000); # Waiting 500ms between file system polls does not make script run-safari feel sluggish.
2217         $device = iosSimulatorDeviceByUDID($deviceUDID);
2218     }
2219 }
2220
2221 sub shutDownIOSSimulatorDevice($)
2222 {
2223     my ($simulatorDevice) = @_;
2224     system("xcrun --sdk iphonesimulator simctl shutdown $simulatorDevice->{UDID} > /dev/null 2>&1");
2225 }
2226
2227 sub restartIOSSimulatorDevice($)
2228 {
2229     my ($simulatorDevice) = @_;
2230     shutDownIOSSimulatorDevice($simulatorDevice);
2231
2232     exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "boot", $simulatorDevice->{UDID})) == 0 or die "Failed to boot simulator device $simulatorDevice->{UDID}";
2233 }
2234
2235 sub relaunchIOSSimulator($)
2236 {
2237     my ($simulatedDevice) = @_;
2238     quitIOSSimulator($simulatedDevice->{UDID});
2239
2240     # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
2241     chomp(my $developerDirectory = $ENV{DEVELOPER_DIR} || `xcode-select --print-path`); 
2242     my $iosSimulatorPath = File::Spec->catfile($developerDirectory, "Applications", "Simulator.app"); 
2243     system("open", "-a", $iosSimulatorPath, "--args", "-CurrentDeviceUDID", $simulatedDevice->{UDID}) == 0 or die "Failed to open $iosSimulatorPath: $!"; 
2244
2245     waitUntilIOSSimulatorDeviceIsInState($simulatedDevice->{UDID}, SIMULATOR_DEVICE_STATE_BOOTED);
2246 }
2247
2248 sub quitIOSSimulator(;$)
2249 {
2250     my ($waitForShutdownOfSimulatedDeviceUDID) = @_;
2251     # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
2252     if (exitStatus(system {"osascript"} "osascript", "-e", 'tell application id "com.apple.iphonesimulator" to quit')) {
2253         # osascript returns a non-zero exit status if Simulator.app is not registered in LaunchServices.
2254         return;
2255     }
2256
2257     if (!defined($waitForShutdownOfSimulatedDeviceUDID)) {
2258         return;
2259     }
2260     # FIXME: We assume that $waitForShutdownOfSimulatedDeviceUDID was not booted using the simctl command line tool.
2261     #        Otherwise we will spin indefinitely since quiting the iOS Simulator will not shutdown this device. We
2262     #        should add a maximum time limit to wait for a device to shutdown and either return an error or die()
2263     #        on expiration of the time limit.
2264     waitUntilIOSSimulatorDeviceIsInState($waitForShutdownOfSimulatedDeviceUDID, SIMULATOR_DEVICE_STATE_SHUTDOWN);
2265 }
2266
2267 sub iosSimulatorDeviceByName($)
2268 {
2269     my ($simulatorName) = @_;
2270     my $simulatorRuntime = iosSimulatorRuntime();
2271     my @devices = iOSSimulatorDevices();
2272     for my $device (@devices) {
2273         if ($device->{name} eq $simulatorName && $device->{runtime} eq $simulatorRuntime) {
2274             return $device;
2275         }
2276     }
2277     return undef;
2278 }
2279
2280 sub iosSimulatorDeviceByUDID($)
2281 {
2282     my ($simulatedDeviceUDID) = @_;
2283     my $devicePlistPath = File::Spec->catfile(iOSSimulatorDevicesPath(), $simulatedDeviceUDID, "device.plist");
2284     if (!-f $devicePlistPath) {
2285         return;
2286     }
2287     # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
2288     #        dictionary keys so as to decouple our representation of the plist from the actual structure
2289     #        of the plist, which may change.
2290     eval "require Foundation";
2291     return Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_($devicePlistPath));
2292 }
2293
2294 sub iosSimulatorRuntime()
2295 {
2296     my $xcodeSDKVersion = xcodeSDKVersion();
2297     $xcodeSDKVersion =~ s/\./-/;
2298     return "com.apple.CoreSimulator.SimRuntime.iOS-$xcodeSDKVersion";
2299 }
2300
2301 sub findOrCreateSimulatorForIOSDevice($)
2302 {
2303     my ($simulatorNameSuffix) = @_;
2304     my $simulatorName;
2305     my $simulatorDeviceType;
2306     if (architecture() eq "x86_64") {
2307         $simulatorName = "iPhone 5s " . $simulatorNameSuffix;
2308         $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5s";
2309     } else {
2310         $simulatorName = "iPhone 5 " . $simulatorNameSuffix;
2311         $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5";
2312     }
2313     my $simulatedDevice = iosSimulatorDeviceByName($simulatorName);
2314     return $simulatedDevice if $simulatedDevice;
2315     return createiOSSimulatorDevice($simulatorName, $simulatorDeviceType, iosSimulatorRuntime());
2316 }
2317
2318 sub isIOSSimulatorSystemInstalledApp($)
2319 {
2320     my ($appBundle) = @_;
2321     my $simulatorApplicationsPath = realpath(iosSimulatorApplicationsPath());
2322     return substr(realpath($appBundle), 0, length($simulatorApplicationsPath)) eq $simulatorApplicationsPath;
2323 }
2324
2325 sub hasUserInstalledAppInSimulatorDevice($$)
2326 {
2327     my ($appIdentifier, $simulatedDeviceUDID) = @_;
2328     my $userInstalledAppPath = File::Spec->catfile($ENV{HOME}, "Library", "Developer", "CoreSimulator", "Devices", $simulatedDeviceUDID, "data", "Containers", "Bundle", "Application");
2329     if (!-d $userInstalledAppPath) {
2330         return 0; # No user installed apps.
2331     }
2332     local @::userInstalledAppBundles;
2333     my $wantedFunction = sub {
2334         my $file = $_;
2335
2336         # Ignore hidden files and directories.
2337         if ($file =~ /^\../) {
2338             $File::Find::prune = 1;
2339             return;
2340         }
2341
2342         return if !-d $file || $file !~ /\.app$/;
2343         push @::userInstalledAppBundles, $File::Find::name;
2344         $File::Find::prune = 1; # Do not traverse contents of app bundle.
2345     };
2346     find($wantedFunction, $userInstalledAppPath);
2347     for my $userInstalledAppBundle (@::userInstalledAppBundles) {
2348         if (appIdentifierFromBundle($userInstalledAppBundle) eq $appIdentifier) {
2349             return 1; # Has user installed app.
2350         }
2351     }
2352     return 0; # Does not have user installed app.
2353 }
2354
2355 sub isSimulatorDeviceBooted($)
2356 {
2357     my ($simulatedDeviceUDID) = @_;
2358     my $device = iosSimulatorDeviceByUDID($simulatedDeviceUDID);
2359     return $device && $device->{state} eq SIMULATOR_DEVICE_STATE_BOOTED;
2360 }
2361
2362 sub runIOSWebKitAppInSimulator($;$)
2363 {
2364     my ($appBundle, $simulatorOptions) = @_;
2365     my $productDir = productDir();
2366     my $appDisplayName = appDisplayNameFromBundle($appBundle);
2367     my $appIdentifier = appIdentifierFromBundle($appBundle);
2368     my $simulatedDevice = findOrCreateSimulatorForIOSDevice(SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT);
2369     my $simulatedDeviceUDID = $simulatedDevice->{UDID};
2370
2371     my $willUseSystemInstalledApp = isIOSSimulatorSystemInstalledApp($appBundle);
2372     if ($willUseSystemInstalledApp) {
2373         if (hasUserInstalledAppInSimulatorDevice($appIdentifier, $simulatedDeviceUDID)) {
2374             # Restore the system-installed app in the simulator device corresponding to $appBundle as it
2375             # was previously overwritten with a custom built version of the app.
2376             # FIXME: Only restore the system-installed version of the app instead of erasing all contents and settings.
2377             print "Quitting iOS Simulator...\n";
2378             quitIOSSimulator($simulatedDeviceUDID);
2379             print "Erasing contents and settings for simulator device \"$simulatedDevice->{name}\".\n";
2380             exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "erase", $simulatedDeviceUDID)) == 0 or die;
2381         }
2382         # FIXME: We assume that if $simulatedDeviceUDID is not booted then iOS Simulator is not open. However
2383         #        $simulatedDeviceUDID may have been booted using the simctl command line tool. If $simulatedDeviceUDID
2384         #        was booted using simctl then we should shutdown the device and launch iOS Simulator to boot it again.
2385         if (!isSimulatorDeviceBooted($simulatedDeviceUDID)) {
2386             print "Launching iOS Simulator...\n";
2387             relaunchIOSSimulator($simulatedDevice);
2388         }
2389     } else {
2390         # FIXME: We should killall(1) any running instances of $appBundle before installing it to ensure
2391         #        that simctl launch opens the latest installed version of the app. For now we quit and
2392         #        launch the iOS Simulator again to ensure there are no running instances of $appBundle.
2393         print "Quitting and launching iOS Simulator...\n";
2394         relaunchIOSSimulator($simulatedDevice);
2395
2396         print "Installing $appBundle.\n";
2397         # Install custom built app, overwriting an app with the same app identifier if one exists.
2398         exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "install", $simulatedDeviceUDID, $appBundle)) == 0 or die;
2399
2400     }
2401
2402     $simulatorOptions = {} unless $simulatorOptions;
2403
2404     my %simulatorENV;
2405     %simulatorENV = %{$simulatorOptions->{applicationEnvironment}} if $simulatorOptions->{applicationEnvironment};
2406     {
2407         local %ENV; # Shadow global-scope %ENV so that changes to it will not be seen outside of this scope.
2408         setupIOSWebKitEnvironment($productDir);
2409         %simulatorENV = %ENV;
2410     }
2411     my $applicationArguments = \@ARGV;
2412     $applicationArguments = $simulatorOptions->{applicationArguments} if $simulatorOptions && $simulatorOptions->{applicationArguments};
2413
2414     # Prefix the environment variables with SIMCTL_CHILD_ per `xcrun simctl help launch`.
2415     foreach my $key (keys %simulatorENV) {
2416         $ENV{"SIMCTL_CHILD_$key"} = $simulatorENV{$key};
2417     }
2418
2419     print "Starting $appDisplayName with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2420     return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "launch", $simulatedDeviceUDID, $appIdentifier, @$applicationArguments));
2421 }
2422
2423 sub runIOSWebKitApp($)
2424 {
2425     my ($appBundle) = @_;
2426     if (willUseIOSDeviceSDK()) {
2427         die "Only running Safari in iOS Simulator is supported now.";
2428     }
2429     if (willUseIOSSimulatorSDK()) {
2430         return runIOSWebKitAppInSimulator($appBundle);
2431     }
2432     die "Not using an iOS SDK."
2433 }
2434
2435 sub archCommandLineArgumentsForRestrictedEnvironmentVariables()
2436 {
2437     my @arguments = ();
2438     foreach my $key (keys(%ENV)) {
2439         if ($key =~ /^DYLD_/) {
2440             push @arguments, "-e", "$key=$ENV{$key}";
2441         }
2442     }
2443     return @arguments;
2444 }
2445
2446 sub runMacWebKitApp($;$)
2447 {
2448     my ($appPath, $useOpenCommand) = @_;
2449     my $productDir = productDir();
2450     print "Starting @{[basename($appPath)]} with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2451
2452     local %ENV = %ENV;
2453     setupMacWebKitEnvironment($productDir);
2454
2455     if (defined($useOpenCommand) && $useOpenCommand == USE_OPEN_COMMAND) {
2456         return system("open", "-W", "-a", $appPath, "--args", argumentsForRunAndDebugMacWebKitApp());
2457     }
2458     if (architecture()) {
2459         return system "arch", "-" . architecture(), archCommandLineArgumentsForRestrictedEnvironmentVariables(), $appPath, argumentsForRunAndDebugMacWebKitApp();
2460     }
2461     return system { $appPath } $appPath, argumentsForRunAndDebugMacWebKitApp();
2462 }
2463
2464 sub execMacWebKitAppForDebugging($)
2465 {
2466     my ($appPath) = @_;
2467     my $architectureSwitch = "--arch";
2468     my $argumentsSeparator = "--";
2469
2470     my $debuggerPath = `xcrun -find lldb`;
2471     chomp $debuggerPath;
2472     die "Can't find the lldb executable.\n" unless -x $debuggerPath;
2473
2474     my $productDir = productDir();
2475     setupMacWebKitEnvironment($productDir);
2476
2477     my @architectureFlags = ($architectureSwitch, architecture());
2478     print "Starting @{[basename($appPath)]} under lldb with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2479     exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $appPath, argumentsForRunAndDebugMacWebKitApp() or die;
2480 }
2481
2482 sub debugSafari
2483 {
2484     if (isAppleCocoaWebKit()) {
2485         checkFrameworks();
2486         execMacWebKitAppForDebugging(safariPath());
2487     }
2488
2489     return 1; # Unsupported platform; can't debug Safari on this platform.
2490 }
2491
2492 sub runSafari
2493 {
2494     if (isIOSWebKit()) {
2495         return runIOSWebKitApp(mobileSafariBundle());
2496     }
2497
2498     if (isAppleCocoaWebKit()) {
2499         return runMacWebKitApp(safariPath());
2500     }
2501
2502     if (isAppleWinWebKit()) {
2503         my $result;
2504         my $webKitLauncherPath = File::Spec->catfile(executableProductDir(), "MiniBrowser.exe");
2505         return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
2506     }
2507
2508     return 1; # Unsupported platform; can't run Safari on this platform.
2509 }
2510
2511 sub runMiniBrowser
2512 {
2513     if (isAppleCocoaWebKit()) {
2514         return runMacWebKitApp(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2515     } elsif (isAppleWinWebKit()) {
2516         my $result;
2517         my $webKitLauncherPath = File::Spec->catfile(executableProductDir(), "MiniBrowser.exe");
2518         return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
2519     }
2520
2521     return 1;
2522 }
2523
2524 sub debugMiniBrowser
2525 {
2526     if (isAppleCocoaWebKit()) {
2527         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2528     }
2529     
2530     return 1;
2531 }
2532
2533 sub runWebKitTestRunner
2534 {
2535     if (isAppleCocoaWebKit()) {
2536         return runMacWebKitApp(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2537     }
2538
2539     return 1;
2540 }
2541
2542 sub debugWebKitTestRunner
2543 {
2544     if (isAppleCocoaWebKit()) {
2545         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2546     }
2547
2548     return 1;
2549 }
2550
2551 sub readRegistryString
2552 {
2553     my ($valueName) = @_;
2554     chomp(my $string = `regtool --wow32 get "$valueName"`);
2555     return $string;
2556 }
2557
2558 sub writeRegistryString
2559 {
2560     my ($valueName, $string) = @_;
2561
2562     my $error = system "regtool", "--wow32", "set", "-s", $valueName, $string;
2563
2564     # On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still
2565     # return a successful exit code. So we double-check here that the value we tried to write to the
2566     # registry was really written.
2567     return !$error && readRegistryString($valueName) eq $string;
2568 }
2569
2570 sub formatBuildTime($)
2571 {
2572     my ($buildTime) = @_;
2573
2574     my $buildHours = int($buildTime / 3600);
2575     my $buildMins = int(($buildTime - $buildHours * 3600) / 60);
2576     my $buildSecs = $buildTime - $buildHours * 3600 - $buildMins * 60;
2577
2578     if ($buildHours) {
2579         return sprintf("%dh:%02dm:%02ds", $buildHours, $buildMins, $buildSecs);
2580     }
2581     return sprintf("%02dm:%02ds", $buildMins, $buildSecs);
2582 }
2583
2584 sub runSvnUpdateAndResolveChangeLogs(@)
2585 {
2586     my @svnOptions = @_;
2587     my $openCommand = "svn update " . join(" ", @svnOptions);
2588     open my $update, "$openCommand |" or die "cannot execute command $openCommand";
2589     my @conflictedChangeLogs;
2590     while (my $line = <$update>) {
2591         print $line;
2592         $line =~ m/^C\s+(.+?)[\r\n]*$/;
2593         if ($1) {
2594           my $filename = normalizePath($1);
2595           push @conflictedChangeLogs, $filename if basename($filename) eq "ChangeLog";
2596         }
2597     }
2598     close $update or die;
2599
2600     if (@conflictedChangeLogs) {
2601         print "Attempting to merge conflicted ChangeLogs.\n";
2602         my $resolveChangeLogsPath = File::Spec->catfile(sourceDir(), "Tools", "Scripts", "resolve-ChangeLogs");
2603         (system($resolveChangeLogsPath, "--no-warnings", @conflictedChangeLogs) == 0)
2604             or die "Could not open resolve-ChangeLogs script: $!.\n";
2605     }
2606 }
2607
2608 sub runGitUpdate()
2609 {
2610     # Doing a git fetch first allows setups with svn-remote.svn.fetch = trunk:refs/remotes/origin/master
2611     # to perform the rebase much much faster.
2612     system("git", "fetch");
2613     if (isGitSVNDirectory(".")) {
2614         system("git", "svn", "rebase") == 0 or die;
2615     } else {
2616         # This will die if branch.$BRANCHNAME.merge isn't set, which is
2617         # almost certainly what we want.
2618         system("git", "pull") == 0 or die;
2619     }
2620 }
2621
2622 1;