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