[iOS] REGRESSION (r168910): webkitdirs.pm: Be more forgiving when parsing --sdk,...
[WebKit-https.git] / Tools / Scripts / webkitdirs.pm
1 # Copyright (C) 2005-2007, 2010-2014 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 Digest::MD5 qw(md5_hex);
37 use FindBin;
38 use File::Basename;
39 use File::Path qw(mkpath rmtree);
40 use File::Spec;
41 use File::stat;
42 use List::Util;
43 use POSIX;
44 use VCSUtils;
45
46 BEGIN {
47    use Exporter   ();
48    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
49    $VERSION     = 1.00;
50    @ISA         = qw(Exporter);
51    @EXPORT      = qw(
52        &XcodeOptionString
53        &XcodeOptionStringNoConfig
54        &XcodeOptions
55        &baseProductDir
56        &chdirWebKit
57        &checkFrameworks
58        &cmakeBasedPortArguments
59        &cmakeBasedPortName
60        &currentSVNRevision
61        &debugSafari
62        &nmPath
63        &passedConfiguration
64        &printHelpAndExitForRunAndDebugWebKitAppIfNeeded
65        &productDir
66        &runMacWebKitApp
67        &safariPath
68        &setConfiguration
69        &setupMacWebKitEnvironment
70        &sharedCommandLineOptions
71        &sharedCommandLineOptionsUsage
72        USE_OPEN_COMMAND
73    );
74    %EXPORT_TAGS = ( );
75    @EXPORT_OK   = ();
76 }
77
78 use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp().
79 use constant INCLUDE_OPTIONS_FOR_DEBUGGING => 1;
80
81 our @EXPORT_OK;
82
83 my $architecture;
84 my $numberOfCPUs;
85 my $maxCPULoad;
86 my $baseProductDir;
87 my @baseProductDirOption;
88 my $configuration;
89 my $xcodeSDK;
90 my $configurationForVisualStudio;
91 my $configurationProductDir;
92 my $sourceDir;
93 my $currentSVNRevision;
94 my $debugger;
95 my $iPhoneSimulatorVersion;
96 my $nmPath;
97 my $osXVersion;
98 my $generateDsym;
99 my $isGtk;
100 my $isWinCE;
101 my $isWinCairo;
102 my $isWin64;
103 my $isEfl;
104 my $isInspectorFrontend;
105 my $isWK2;
106 my $shouldTargetWebProcess;
107 my $shouldUseXPCServiceForWebProcess;
108 my $shouldUseGuardMalloc;
109 my $xcodeVersion;
110
111 # Variables for Win32 support
112 my $programFilesPath;
113 my $vcBuildPath;
114 my $vsInstallDir;
115 my $vsVersion;
116 my $windowsSourceDir;
117 my $winVersion;
118 my $willUseVCExpressWhenBuilding = 0;
119
120 # Defined in VCSUtils.
121 sub exitStatus($);
122
123 sub findMatchingArguments($$);
124 sub hasArgument($$);
125
126 sub determineSourceDir
127 {
128     return if $sourceDir;
129     $sourceDir = $FindBin::Bin;
130     $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
131
132     # walks up path checking each directory to see if it is the main WebKit project dir, 
133     # defined by containing Sources, WebCore, and WebKit
134     until ((-d "$sourceDir/Source" && -d "$sourceDir/Source/WebCore" && -d "$sourceDir/Source/WebKit") || (-d "$sourceDir/Internal" && -d "$sourceDir/OpenSource"))
135     {
136         if ($sourceDir !~ s|/[^/]+$||) {
137             die "Could not find top level webkit directory above source directory using FindBin.\n";
138         }
139     }
140
141     $sourceDir = "$sourceDir/OpenSource" if -d "$sourceDir/OpenSource";
142 }
143
144 sub currentPerlPath()
145 {
146     my $thisPerl = $^X;
147     if ($^O ne 'VMS') {
148         $thisPerl .= $Config{_exe} unless $thisPerl =~ m/$Config{_exe}$/i;
149     }
150     return $thisPerl;
151 }
152
153 # used for scripts which are stored in a non-standard location
154 sub setSourceDir($)
155 {
156     ($sourceDir) = @_;
157 }
158
159 sub determineXcodeVersion
160 {
161     return if defined $xcodeVersion;
162     my $xcodebuildVersionOutput = `xcodebuild -version`;
163     $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : "3.0";
164 }
165
166 sub readXcodeUserDefault($)
167 {
168     my ($unprefixedKey) = @_;
169
170     determineXcodeVersion();
171
172     my $xcodeDefaultsDomain = (eval "v$xcodeVersion" lt v4) ? "com.apple.Xcode" : "com.apple.dt.Xcode";
173     my $xcodeDefaultsPrefix = (eval "v$xcodeVersion" lt v4) ? "PBX" : "IDE";
174     my $devnull = File::Spec->devnull();
175
176     my $value = `defaults read $xcodeDefaultsDomain ${xcodeDefaultsPrefix}${unprefixedKey} 2> ${devnull}`;
177     return if $?;
178
179     chomp $value;
180     return $value;
181 }
182
183 sub determineBaseProductDir
184 {
185     return if defined $baseProductDir;
186     determineSourceDir();
187
188     my $setSharedPrecompsDir;
189     $baseProductDir = $ENV{"WEBKIT_OUTPUTDIR"};
190
191     if (!defined($baseProductDir) and isAppleMacWebKit()) {
192         # Silently remove ~/Library/Preferences/xcodebuild.plist which can
193         # cause build failure. The presence of
194         # ~/Library/Preferences/xcodebuild.plist can prevent xcodebuild from
195         # respecting global settings such as a custom build products directory
196         # (<rdar://problem/5585899>).
197         my $personalPlistFile = $ENV{HOME} . "/Library/Preferences/xcodebuild.plist";
198         if (-e $personalPlistFile) {
199             unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!";
200         }
201
202         determineXcodeVersion();
203
204         if (eval "v$xcodeVersion" ge v4) {
205             my $buildLocationStyle = join '', readXcodeUserDefault("BuildLocationStyle");
206             if ($buildLocationStyle eq "Custom") {
207                 my $buildLocationType = join '', readXcodeUserDefault("CustomBuildLocationType");
208                 # FIXME: Read CustomBuildIntermediatesPath and set OBJROOT accordingly.
209                 $baseProductDir = readXcodeUserDefault("CustomBuildProductsPath") if $buildLocationType eq "Absolute";
210             }
211
212             # DeterminedByTargets corresponds to a setting of "Legacy" in Xcode.
213             # It is the only build location style for which SHARED_PRECOMPS_DIR is not
214             # overridden when building from within Xcode.
215             $setSharedPrecompsDir = 1 if $buildLocationStyle ne "DeterminedByTargets";
216         }
217
218         if (!defined($baseProductDir)) {
219             $baseProductDir = join '', readXcodeUserDefault("ApplicationwideBuildSettings");
220             $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s;
221         }
222
223         undef $baseProductDir unless $baseProductDir =~ /^\//;
224     }
225
226     if (!defined($baseProductDir)) { # Port-specific checks failed, use default
227         $baseProductDir = "$sourceDir/WebKitBuild";
228     }
229
230     if (isGit() && isGitBranchBuild()) {
231         my $branch = gitBranch();
232         $baseProductDir = "$baseProductDir/$branch";
233     }
234
235     if (isAppleMacWebKit()) {
236         $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|;
237         $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|;
238         $baseProductDir =~ s|^~/|$ENV{HOME}/|;
239         die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/;
240         die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/;
241         @baseProductDirOption = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir");
242         push(@baseProductDirOption, "SHARED_PRECOMPS_DIR=${baseProductDir}/PrecompiledHeaders") if $setSharedPrecompsDir;
243     }
244
245     if (isCygwin()) {
246         my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`;
247         chomp $dosBuildPath;
248         $ENV{"WEBKIT_OUTPUTDIR"} = $dosBuildPath;
249         my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`;
250         chomp $unixBuildPath;
251         $baseProductDir = $unixBuildPath;
252     }
253 }
254
255 sub setBaseProductDir($)
256 {
257     ($baseProductDir) = @_;
258 }
259
260 sub determineConfiguration
261 {
262     return if defined $configuration;
263     determineBaseProductDir();
264     if (open CONFIGURATION, "$baseProductDir/Configuration") {
265         $configuration = <CONFIGURATION>;
266         close CONFIGURATION;
267     }
268     if ($configuration) {
269         chomp $configuration;
270         # compatibility for people who have old Configuration files
271         $configuration = "Release" if $configuration eq "Deployment";
272         $configuration = "Debug" if $configuration eq "Development";
273     } else {
274         $configuration = "Release";
275     }
276
277     if ($configuration && isWinCairo()) {
278         unless ($configuration =~ /_WinCairo$/) {
279             $configuration .= "_WinCairo";
280         }
281     }
282 }
283
284 sub determineArchitecture
285 {
286     return if defined $architecture;
287     # make sure $architecture is defined in all cases
288     $architecture = "";
289
290     determineBaseProductDir();
291     determineXcodeSDK();
292
293     if (isAppleMacWebKit()) {
294         if (open ARCHITECTURE, "$baseProductDir/Architecture") {
295             $architecture = <ARCHITECTURE>;
296             close ARCHITECTURE;
297         }
298         if ($architecture) {
299             chomp $architecture;
300         } else {
301             if (not defined $xcodeSDK or $xcodeSDK =~ /^(\/$|macosx)/) {
302                 my $supports64Bit = `sysctl -n hw.optional.x86_64`;
303                 chomp $supports64Bit;
304                 $architecture = 'x86_64' if $supports64Bit;
305             } elsif ($xcodeSDK =~ /^iphonesimulator/) {
306                 $architecture = 'x86_64';
307             } elsif ($xcodeSDK =~ /^iphoneos/) {
308                 $architecture = 'armv7';
309             }
310         }
311     } elsif (isEfl() || isGtk()) {
312         my $host_processor = "";
313         $host_processor = `cmake --system-information | grep CMAKE_SYSTEM_PROCESSOR`;
314         if ($host_processor =~ m/^CMAKE_SYSTEM_PROCESSOR \"([^"]+)\"/) {
315             # We have a configured build tree; use it.
316             $architecture = $1;
317             $architecture = 'x86_64' if $architecture eq 'amd64';
318         }
319     }
320
321     if (!$architecture && (isGtk() || isAppleMacWebKit() || isEfl())) {
322         # Fall back to output of `arch', if it is present.
323         $architecture = `arch`;
324         chomp $architecture;
325     }
326
327     if (!$architecture && (isGtk() || isAppleMacWebKit() || isEfl())) {
328         # Fall back to output of `uname -m', if it is present.
329         $architecture = `uname -m`;
330         chomp $architecture;
331     }
332
333     $architecture = 'x86_64' if ($architecture =~ /amd64/ && isBSD());
334 }
335
336 sub determineNumberOfCPUs
337 {
338     return if defined $numberOfCPUs;
339     if (defined($ENV{NUMBER_OF_PROCESSORS})) {
340         $numberOfCPUs = $ENV{NUMBER_OF_PROCESSORS};
341     } elsif (isLinux()) {
342         # First try the nproc utility, if it exists. If we get no
343         # results fall back to just interpretting /proc directly.
344         chomp($numberOfCPUs = `nproc --all 2> /dev/null`);
345         if ($numberOfCPUs eq "") {
346             $numberOfCPUs = (grep /processor/, `cat /proc/cpuinfo`);
347         }
348     } elsif (isWindows() || isCygwin()) {
349         # Assumes cygwin
350         $numberOfCPUs = `ls /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor | wc -w`;
351     } elsif (isDarwin() || isBSD()) {
352         chomp($numberOfCPUs = `sysctl -n hw.ncpu`);
353     }
354 }
355
356 sub determineMaxCPULoad
357 {
358     return if defined $maxCPULoad;
359     if (defined($ENV{MAX_CPU_LOAD})) {
360         $maxCPULoad = $ENV{MAX_CPU_LOAD};
361     }
362 }
363
364 sub jscPath($)
365 {
366     my ($productDir) = @_;
367     my $jscName = "jsc";
368     $jscName .= "_debug"  if configuration() eq "Debug_All";
369     $jscName .= ".exe" if (isWindows() || isCygwin());
370     return "$productDir/$jscName" if -e "$productDir/$jscName";
371     return "$productDir/JavaScriptCore.framework/Resources/$jscName";
372 }
373
374 sub argumentsForConfiguration()
375 {
376     determineConfiguration();
377     determineArchitecture();
378     determineXcodeSDK();
379
380     my @args = ();
381     push(@args, '--debug') if ($configuration =~ "^Debug");
382     push(@args, '--release') if ($configuration =~ "^Release");
383     push(@args, '--device') if ($xcodeSDK =~ /^iphoneos/);
384     push(@args, '--sim') if ($xcodeSDK =~ /^iphonesimulator/);
385     push(@args, '--32-bit') if ($architecture ne "x86_64" and !isWin64());
386     push(@args, '--64-bit') if (isWin64());
387     push(@args, '--gtk') if isGtk();
388     push(@args, '--efl') if isEfl();
389     push(@args, '--wincairo') if isWinCairo();
390     push(@args, '--wince') if isWinCE();
391     push(@args, '--inspector-frontend') if isInspectorFrontend();
392     return @args;
393 }
394
395 sub determineXcodeSDK
396 {
397     return if defined $xcodeSDK;
398     my $sdk;
399     if (checkForArgumentAndRemoveFromARGVGettingValue("--sdk", \$sdk)) {
400         $xcodeSDK = $sdk;
401     }
402     if (checkForArgumentAndRemoveFromARGV("--device")) {
403         $xcodeSDK ||= 'iphoneos.internal';
404     }
405     if (checkForArgumentAndRemoveFromARGV("--sim") ||
406         checkForArgumentAndRemoveFromARGV("--simulator")) {
407         $xcodeSDK ||= 'iphonesimulator';
408     }
409 }
410
411 sub xcodeSDK
412 {
413     determineXcodeSDK();
414     return $xcodeSDK;
415 }
416
417 sub xcodeSDKPlatformName()
418 {
419     determineXcodeSDK();
420     return "" if !defined $xcodeSDK;
421     return "iphoneos" if $xcodeSDK =~ /iphoneos/i;
422     return "iphonesimulator" if $xcodeSDK =~ /iphonesimulator/i;
423     return "macosx" if $xcodeSDK =~ /macosx/i;
424     die "Couldn't determine platform name from Xcode SDK";
425 }
426
427 sub XcodeSDKPath
428 {
429     determineXcodeSDK();
430
431     die "Can't find the SDK path because no Xcode SDK was specified" if not $xcodeSDK;
432
433     my $sdkPath = `xcrun --sdk $xcodeSDK --show-sdk-path` if $xcodeSDK;
434     die 'Failed to get SDK path from xcrun' if $?;
435     chomp $sdkPath;
436
437     return $sdkPath;
438 }
439
440
441 sub programFilesPath
442 {
443     return $programFilesPath if defined $programFilesPath;
444
445     $programFilesPath = $ENV{'PROGRAMFILES(X86)'} || $ENV{'PROGRAMFILES'} || "C:\\Program Files";
446
447     return $programFilesPath;
448 }
449
450 sub visualStudioInstallDir
451 {
452     return $vsInstallDir if defined $vsInstallDir;
453
454     if ($ENV{'VSINSTALLDIR'}) {
455         $vsInstallDir = $ENV{'VSINSTALLDIR'};
456         $vsInstallDir =~ s|[\\/]$||;
457     } else {
458         $vsInstallDir = File::Spec->catdir(programFilesPath(), "Microsoft Visual Studio 12.0");
459     }
460     chomp($vsInstallDir = `cygpath "$vsInstallDir"`) if isCygwin();
461
462     return $vsInstallDir;
463 }
464
465 sub visualStudioVersion
466 {
467     return $vsVersion if defined $vsVersion;
468
469     my $installDir = visualStudioInstallDir();
470
471     $vsVersion = ($installDir =~ /Microsoft Visual Studio ([0-9]+\.[0-9]*)/) ? $1 : "12";
472
473     return $vsVersion;
474 }
475
476 sub determineConfigurationForVisualStudio
477 {
478     return if defined $configurationForVisualStudio;
479     determineConfiguration();
480     # FIXME: We should detect when Debug_All or Production has been chosen.
481     $configurationForVisualStudio = $configuration . (isWin64() ? "|x64" : "|Win32");
482 }
483
484 sub usesPerConfigurationBuildDirectory
485 {
486     # [Gtk] We don't have Release/Debug configurations in straight
487     # autotool builds (non build-webkit). In this case and if
488     # WEBKIT_OUTPUTDIR exist, use that as our configuration dir. This will
489     # allows us to run run-webkit-tests without using build-webkit.
490     return ($ENV{"WEBKIT_OUTPUTDIR"} && isGtk()) || isAppleWinWebKit();
491 }
492
493 sub determineConfigurationProductDir
494 {
495     return if defined $configurationProductDir;
496     determineBaseProductDir();
497     determineConfiguration();
498     if (isAppleWinWebKit() || isWinCairo()) {
499         my $binDir = isWin64() ? "bin64" : "bin32";
500         $configurationProductDir = File::Spec->catdir($baseProductDir, $configuration, $binDir);
501     } else {
502         if (usesPerConfigurationBuildDirectory()) {
503             $configurationProductDir = "$baseProductDir";
504         } else {
505             $configurationProductDir = "$baseProductDir/$configuration";
506             $configurationProductDir .= "-" . xcodeSDKPlatformName() if isIOSWebKit();
507         }
508     }
509 }
510
511 sub setConfigurationProductDir($)
512 {
513     ($configurationProductDir) = @_;
514 }
515
516 sub determineCurrentSVNRevision
517 {
518     # We always update the current SVN revision here, and leave the caching
519     # to currentSVNRevision(), so that changes to the SVN revision while the
520     # script is running can be picked up by calling this function again.
521     determineSourceDir();
522     $currentSVNRevision = svnRevisionForDirectory($sourceDir);
523     return $currentSVNRevision;
524 }
525
526
527 sub chdirWebKit
528 {
529     determineSourceDir();
530     chdir $sourceDir or die;
531 }
532
533 sub baseProductDir
534 {
535     determineBaseProductDir();
536     return $baseProductDir;
537 }
538
539 sub sourceDir
540 {
541     determineSourceDir();
542     return $sourceDir;
543 }
544
545 sub productDir
546 {
547     determineConfigurationProductDir();
548     return $configurationProductDir;
549 }
550
551 sub jscProductDir
552 {
553     my $productDir = productDir();
554     $productDir .= "/bin" if (isEfl() || isGtk());
555
556     return $productDir;
557 }
558
559 sub configuration()
560 {
561     determineConfiguration();
562     return $configuration;
563 }
564
565 sub configurationForVisualStudio()
566 {
567     determineConfigurationForVisualStudio();
568     return $configurationForVisualStudio;
569 }
570
571 sub currentSVNRevision
572 {
573     determineCurrentSVNRevision() if not defined $currentSVNRevision;
574     return $currentSVNRevision;
575 }
576
577 sub generateDsym()
578 {
579     determineGenerateDsym();
580     return $generateDsym;
581 }
582
583 sub determineGenerateDsym()
584 {
585     return if defined($generateDsym);
586     $generateDsym = checkForArgumentAndRemoveFromARGV("--dsym");
587 }
588
589 sub argumentsForXcode()
590 {
591     my @args = ();
592     push @args, "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym" if generateDsym();
593     return @args;
594 }
595
596 sub XcodeOptions
597 {
598     determineBaseProductDir();
599     determineConfiguration();
600     determineArchitecture();
601     determineXcodeSDK();
602
603     my @sdkOption = ($xcodeSDK ? "SDKROOT=$xcodeSDK" : ());
604     my @architectureOption = ($architecture ? "ARCHS=$architecture" : ());
605
606     return (@baseProductDirOption, "-configuration", $configuration, @architectureOption, @sdkOption, argumentsForXcode());
607 }
608
609 sub XcodeOptionString
610 {
611     return join " ", XcodeOptions();
612 }
613
614 sub XcodeOptionStringNoConfig
615 {
616     return join " ", @baseProductDirOption;
617 }
618
619 sub XcodeCoverageSupportOptions()
620 {
621     my @coverageSupportOptions = ();
622     push @coverageSupportOptions, "GCC_GENERATE_TEST_COVERAGE_FILES=YES";
623     push @coverageSupportOptions, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES";
624     return @coverageSupportOptions;
625 }
626
627 my $passedConfiguration;
628 my $searchedForPassedConfiguration;
629 sub determinePassedConfiguration
630 {
631     return if $searchedForPassedConfiguration;
632     $searchedForPassedConfiguration = 1;
633     $passedConfiguration = undef;
634
635     if (checkForArgumentAndRemoveFromARGV("--debug")) {
636         $passedConfiguration = "Debug";
637     } elsif(checkForArgumentAndRemoveFromARGV("--release")) {
638         $passedConfiguration = "Release";
639     } elsif (checkForArgumentAndRemoveFromARGV("--profile") || checkForArgumentAndRemoveFromARGV("--profiling")) {
640         $passedConfiguration = "Profiling";
641     }
642
643     $passedConfiguration .= "_WinCairo" if (defined($passedConfiguration) && isWinCairo() && isCygwin());
644 }
645
646 sub passedConfiguration
647 {
648     determinePassedConfiguration();
649     return $passedConfiguration;
650 }
651
652 sub setConfiguration
653 {
654     setArchitecture();
655
656     if (my $config = shift @_) {
657         $configuration = $config;
658         return;
659     }
660
661     determinePassedConfiguration();
662     $configuration = $passedConfiguration if $passedConfiguration;
663 }
664
665
666 my $passedArchitecture;
667 my $searchedForPassedArchitecture;
668 sub determinePassedArchitecture
669 {
670     return if $searchedForPassedArchitecture;
671     $searchedForPassedArchitecture = 1;
672
673     $passedArchitecture = undef;
674     if (checkForArgumentAndRemoveFromARGV("--32-bit")) {
675         if (isAppleMacWebKit()) {
676             # PLATFORM_IOS: Don't run `arch` command inside Simulator environment
677             local %ENV = %ENV;
678             delete $ENV{DYLD_ROOT_PATH};
679             delete $ENV{DYLD_FRAMEWORK_PATH};
680
681             $passedArchitecture = `arch`;
682             chomp $passedArchitecture;
683         }
684     }
685 }
686
687 sub passedArchitecture
688 {
689     determinePassedArchitecture();
690     return $passedArchitecture;
691 }
692
693 sub architecture()
694 {
695     determineArchitecture();
696     return $architecture;
697 }
698
699 sub numberOfCPUs()
700 {
701     determineNumberOfCPUs();
702     return $numberOfCPUs;
703 }
704
705 sub maxCPULoad()
706 {
707     determineMaxCPULoad();
708     return $maxCPULoad;
709 }
710
711 sub setArchitecture
712 {
713     if (my $arch = shift @_) {
714         $architecture = $arch;
715         return;
716     }
717
718     determinePassedArchitecture();
719     $architecture = $passedArchitecture if $passedArchitecture;
720 }
721
722 sub skipSafariExecutableEntitlementChecks
723 {
724     return `defaults read /Library/Preferences/org.webkit.BuildConfiguration SkipSafariExecutableEntitlementChecks 2>/dev/null` eq "1\n";
725 }
726
727 sub executableHasEntitlements
728 {
729     my $executablePath = shift;
730     return (`codesign -d --entitlements - $executablePath 2>&1` =~ /<key>/);
731 }
732
733 sub safariPathFromSafariBundle
734 {
735     my ($safariBundle) = @_;
736
737     if (isAppleMacWebKit()) {
738         my $safariPath = "$safariBundle/Contents/MacOS/Safari";
739         return $safariPath if skipSafariExecutableEntitlementChecks();
740
741         my $safariForWebKitDevelopmentPath = "$safariBundle/Contents/MacOS/SafariForWebKitDevelopment";
742         return $safariForWebKitDevelopmentPath if -f $safariForWebKitDevelopmentPath && executableHasEntitlements($safariPath);
743
744         return $safariPath;
745     }
746     return $safariBundle if isAppleWinWebKit();
747 }
748
749 sub installedSafariPath
750 {
751     my $safariBundle;
752
753     if (isAppleMacWebKit()) {
754         $safariBundle = "/Applications/Safari.app";
755     } elsif (isAppleWinWebKit()) {
756         $safariBundle = readRegistryString("/HKLM/SOFTWARE/Apple Inc./Safari/InstallDir");
757         $safariBundle =~ s/[\r\n]+$//;
758         $safariBundle = `cygpath -u '$safariBundle'` if isCygwin();
759         $safariBundle =~ s/[\r\n]+$//;
760         $safariBundle .= "Safari.exe";
761     }
762
763     return safariPathFromSafariBundle($safariBundle);
764 }
765
766 # Locate Safari.
767 sub safariPath
768 {
769     # Use WEBKIT_SAFARI environment variable if present.
770     my $safariBundle = $ENV{WEBKIT_SAFARI};
771     if (!$safariBundle) {
772         determineConfigurationProductDir();
773         # Use Safari.app in product directory if present (good for Safari development team).
774         if (isAppleMacWebKit() && -d "$configurationProductDir/Safari.app") {
775             $safariBundle = "$configurationProductDir/Safari.app";
776         } elsif (isAppleWinWebKit()) {
777             my $path = "$configurationProductDir/Safari.exe";
778             my $debugPath = "$configurationProductDir/Safari_debug.exe";
779
780             if (configuration() eq "Debug_All" && -x $debugPath) {
781                 $safariBundle = $debugPath;
782             } elsif (-x $path) {
783                 $safariBundle = $path;
784             }
785         }
786         if (!$safariBundle) {
787             return installedSafariPath();
788         }
789     }
790     my $safariPath = safariPathFromSafariBundle($safariBundle);
791     die "Can't find executable at $safariPath.\n" if isAppleMacWebKit() && !-x $safariPath;
792     return $safariPath;
793 }
794
795 sub builtDylibPathForName
796 {
797     my $libraryName = shift;
798     determineConfigurationProductDir();
799
800     if (isGtk()) {
801         # WebKitGTK+ for GTK2, WebKitGTK+ for GTK3, and WebKit2 respectively.
802         my @libraries = ("libwebkitgtk-1.0", "libwebkitgtk-3.0", "libwebkit2gtk-3.0");
803         my $extension = isDarwin() ? ".dylib" : ".so";
804         my $builtLibraryPath = "$configurationProductDir/lib/";
805
806         foreach $libraryName (@libraries) {
807             my $libraryPath = "$builtLibraryPath" . $libraryName . $extension;
808             return $libraryPath if -e $libraryPath;
809         }
810         return "NotFound";
811     }
812     if (isEfl()) {
813         if (isWK2()) {
814             return "$configurationProductDir/lib/libewebkit2.so";
815         }
816         return "$configurationProductDir/lib/libewebkit.so";
817     }
818     if (isWinCE()) {
819         return "$configurationProductDir/$libraryName";
820     }
821     if (isIOSWebKit()) {
822         return "$configurationProductDir/$libraryName.framework/$libraryName";
823     }
824     if (isAppleMacWebKit()) {
825         return "$configurationProductDir/$libraryName.framework/Versions/A/$libraryName";
826     }
827     if (isAppleWinWebKit()) {
828         if ($libraryName eq "JavaScriptCore") {
829             return "$baseProductDir/lib/$libraryName.lib";
830         } else {
831             return "$baseProductDir/$libraryName.intermediate/$configuration/$libraryName.intermediate/$libraryName.lib";
832         }
833     }
834
835     die "Unsupported platform, can't determine built library locations.\nTry `build-webkit --help` for more information.\n";
836 }
837
838 # Check to see that all the frameworks are built.
839 sub checkFrameworks # FIXME: This is a poor name since only the Mac calls built WebCore a Framework.
840 {
841     return if isCygwin() || isWindows();
842     my @frameworks = ("JavaScriptCore", "WebCore");
843     push(@frameworks, "WebKit") if isAppleMacWebKit(); # FIXME: This seems wrong, all ports should have a WebKit these days.
844     for my $framework (@frameworks) {
845         my $path = builtDylibPathForName($framework);
846         die "Can't find built framework at \"$path\".\n" unless -e $path;
847     }
848 }
849
850 sub isInspectorFrontend()
851 {
852     determineIsInspectorFrontend();
853     return $isInspectorFrontend;
854 }
855
856 sub determineIsInspectorFrontend()
857 {
858     return if defined($isInspectorFrontend);
859     $isInspectorFrontend = checkForArgumentAndRemoveFromARGV("--inspector-frontend");
860 }
861
862 sub commandExists($)
863 {
864     my $command = shift;
865     my $devnull = File::Spec->devnull();
866     return `$command --version 2> $devnull`;
867 }
868
869 sub checkForArgumentAndRemoveFromARGV($)
870 {
871     my $argToCheck = shift;
872     return checkForArgumentAndRemoveFromArrayRef($argToCheck, \@ARGV);
873 }
874
875 sub checkForArgumentAndRemoveFromArrayRefGettingValue($$$)
876 {
877     my ($argToCheck, $valueRef, $arrayRef) = @_;
878     my $argumentStartRegEx = qr#^$argToCheck(?:=\S|$)#;
879     my $i = 0;
880     for (; $i < @$arrayRef; ++$i) {
881         last if $arrayRef->[$i] =~ $argumentStartRegEx;
882     }
883     if ($i >= @$arrayRef) {
884         return $$valueRef = undef;
885     }
886     my ($key, $value) = split("=", $arrayRef->[$i]);
887     splice(@$arrayRef, $i, 1);
888     if (defined($value)) {
889         # e.g. --sdk=iphonesimulator
890         return $$valueRef = $value;
891     }
892     return $$valueRef = splice(@$arrayRef, $i, 1); # e.g. --sdk iphonesimulator
893 }
894
895 sub checkForArgumentAndRemoveFromARGVGettingValue($$)
896 {
897     my ($argToCheck, $valueRef) = @_;
898     return checkForArgumentAndRemoveFromArrayRefGettingValue($argToCheck, $valueRef, \@ARGV);
899 }
900
901 sub findMatchingArguments($$)
902 {
903     my ($argToCheck, $arrayRef) = @_;
904     my @matchingIndices;
905     foreach my $index (0 .. $#$arrayRef) {
906         my $opt = $$arrayRef[$index];
907         if ($opt =~ /^$argToCheck$/i ) {
908             push(@matchingIndices, $index);
909         }
910     }
911     return @matchingIndices; 
912 }
913
914 sub hasArgument($$)
915 {
916     my ($argToCheck, $arrayRef) = @_;
917     my @matchingIndices = findMatchingArguments($argToCheck, $arrayRef);
918     return scalar @matchingIndices > 0;
919 }
920
921 sub checkForArgumentAndRemoveFromArrayRef
922 {
923     my ($argToCheck, $arrayRef) = @_;
924     my @indicesToRemove = findMatchingArguments($argToCheck, $arrayRef);
925     my $removeOffset = 0;
926     foreach my $index (@indicesToRemove) {
927         splice(@$arrayRef, $index - $removeOffset++, 1);
928     }
929     return scalar @indicesToRemove > 0;
930 }
931
932 sub isWK2()
933 {
934     if (defined($isWK2)) {
935         return $isWK2;
936     }
937     if (checkForArgumentAndRemoveFromARGV("-2")) {
938         $isWK2 = 1;
939     } else {
940         $isWK2 = 0;
941     }
942     return $isWK2;
943 }
944
945 sub determineIsEfl()
946 {
947     return if defined($isEfl);
948     $isEfl = checkForArgumentAndRemoveFromARGV("--efl");
949 }
950
951 sub isEfl()
952 {
953     determineIsEfl();
954     return $isEfl;
955 }
956
957 sub determineIsGtk()
958 {
959     return if defined($isGtk);
960     $isGtk = checkForArgumentAndRemoveFromARGV("--gtk");
961 }
962
963 sub isGtk()
964 {
965     determineIsGtk();
966     return $isGtk;
967 }
968
969 sub isWinCE()
970 {
971     determineIsWinCE();
972     return $isWinCE;
973 }
974
975 sub determineIsWinCE()
976 {
977     return if defined($isWinCE);
978     $isWinCE = checkForArgumentAndRemoveFromARGV("--wince");
979 }
980
981 # Determine if this is debian, ubuntu, linspire, or something similar.
982 sub isDebianBased()
983 {
984     return -e "/etc/debian_version";
985 }
986
987 sub isFedoraBased()
988 {
989     return -e "/etc/fedora-release";
990 }
991
992 sub isWinCairo()
993 {
994     determineIsWinCairo();
995     return $isWinCairo;
996 }
997
998 sub determineIsWinCairo()
999 {
1000     return if defined($isWinCairo);
1001     $isWinCairo = checkForArgumentAndRemoveFromARGV("--wincairo");
1002 }
1003
1004 sub isWin64()
1005 {
1006     determineIsWin64();
1007     return $isWin64;
1008 }
1009
1010 sub determineIsWin64()
1011 {
1012     return if defined($isWin64);
1013     $isWin64 = checkForArgumentAndRemoveFromARGV("--64-bit");
1014 }
1015
1016 sub isCygwin()
1017 {
1018     return ($^O eq "cygwin") || 0;
1019 }
1020
1021 sub isAnyWindows()
1022 {
1023     return isWindows() || isCygwin();
1024 }
1025
1026 sub determineWinVersion()
1027 {
1028     return if $winVersion;
1029
1030     if (!isAnyWindows()) {
1031         $winVersion = -1;
1032         return;
1033     }
1034
1035     my $versionString = `cmd /c ver`;
1036     $versionString =~ /(\d)\.(\d)\.(\d+)/;
1037
1038     $winVersion = {
1039         major => $1,
1040         minor => $2,
1041         build => $3,
1042     };
1043 }
1044
1045 sub winVersion()
1046 {
1047     determineWinVersion();
1048     return $winVersion;
1049 }
1050
1051 sub isWindows7SP0()
1052 {
1053     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 1 && winVersion()->{build} == 7600;
1054 }
1055
1056 sub isWindowsVista()
1057 {
1058     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 0;
1059 }
1060
1061 sub isWindowsXP()
1062 {
1063     return isAnyWindows() && winVersion()->{major} == 5 && winVersion()->{minor} == 1;
1064 }
1065
1066 sub isDarwin()
1067 {
1068     return ($^O eq "darwin") || 0;
1069 }
1070
1071 sub isWindows()
1072 {
1073     return ($^O eq "MSWin32") || 0;
1074 }
1075
1076 sub isLinux()
1077 {
1078     return ($^O eq "linux") || 0;
1079 }
1080
1081 sub isBSD()
1082 {
1083     return ($^O eq "freebsd") || ($^O eq "openbsd") || ($^O eq "netbsd") || 0;
1084 }
1085
1086 sub isARM()
1087 {
1088     return ($Config{archname} =~ /^arm[v\-]/) || ($Config{archname} =~ /^aarch64[v\-]/);
1089 }
1090
1091 sub isCrossCompilation()
1092 {
1093   my $compiler = "";
1094   $compiler = $ENV{'CC'} if (defined($ENV{'CC'}));
1095   if ($compiler =~ /gcc/) {
1096       my $compiler_options = `$compiler -v 2>&1`;
1097       my @host = $compiler_options =~ m/--host=(.*?)\s/;
1098       my @target = $compiler_options =~ m/--target=(.*?)\s/;
1099
1100       return ($host[0] ne "" && $target[0] ne "" && $host[0] ne $target[0]);
1101   }
1102   return 0;
1103 }
1104
1105 sub isAppleWebKit()
1106 {
1107     return isAppleMacWebKit() || isAppleWinWebKit();
1108 }
1109
1110 sub isAppleMacWebKit()
1111 {
1112     return isDarwin() && !isGtk();
1113 }
1114
1115 sub isAppleWinWebKit()
1116 {
1117     return (isCygwin() || isWindows()) && !isWinCairo() && !isGtk() && !isWinCE();
1118 }
1119
1120 sub willUseIOSDeviceSDKWhenBuilding()
1121 {
1122     return xcodeSDKPlatformName() eq "iphoneos";
1123 }
1124
1125 sub willUseIOSSimulatorSDKWhenBuilding()
1126 {
1127     return xcodeSDKPlatformName() eq "iphonesimulator";
1128 }
1129
1130 sub isIOSWebKit()
1131 {
1132     determineXcodeSDK();
1133     return isAppleMacWebKit() && (willUseIOSDeviceSDKWhenBuilding() || willUseIOSSimulatorSDKWhenBuilding());
1134 }
1135
1136 sub isPerianInstalled()
1137 {
1138     if (!isAppleWebKit()) {
1139         return 0;
1140     }
1141
1142     if (-d "/Library/QuickTime/Perian.component") {
1143         return 1;
1144     }
1145
1146     if (-d "$ENV{HOME}/Library/QuickTime/Perian.component") {
1147         return 1;
1148     }
1149
1150     return 0;
1151 }
1152
1153 sub determineIPhoneSimulatorVersion()
1154 {
1155     return if $iPhoneSimulatorVersion;
1156
1157     if (!isIOSWebKit()) {
1158         $iPhoneSimulatorVersion = -1;
1159         return;
1160     }
1161
1162     my $version = `/usr/local/bin/psw_vers -productVersion`;
1163     my @splitVersion = split(/\./, $version);
1164     @splitVersion >= 2 or die "Invalid version $version";
1165     $iPhoneSimulatorVersion = {
1166             "major" => $splitVersion[0],
1167             "minor" => $splitVersion[1],
1168             "subminor" => defined($splitVersion[2] ? $splitVersion[2] : 0),
1169     };
1170 }
1171
1172 sub iPhoneSimulatorVersion()
1173 {
1174     determineIPhoneSimulatorVersion();
1175     return $iPhoneSimulatorVersion;
1176 }
1177
1178 sub determineNmPath()
1179 {
1180     return if $nmPath;
1181
1182     if (isAppleMacWebKit()) {
1183         $nmPath = `xcrun -find nm`;
1184         chomp $nmPath;
1185     }
1186     $nmPath = "nm" if !$nmPath;
1187 }
1188
1189 sub nmPath()
1190 {
1191     determineNmPath();
1192     return $nmPath;
1193 }
1194
1195 sub determineOSXVersion()
1196 {
1197     return if $osXVersion;
1198
1199     if (!isDarwin()) {
1200         $osXVersion = -1;
1201         return;
1202     }
1203
1204     my $version = `sw_vers -productVersion`;
1205     my @splitVersion = split(/\./, $version);
1206     @splitVersion >= 2 or die "Invalid version $version";
1207     $osXVersion = {
1208             "major" => $splitVersion[0],
1209             "minor" => $splitVersion[1],
1210             "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0),
1211     };
1212 }
1213
1214 sub osXVersion()
1215 {
1216     determineOSXVersion();
1217     return $osXVersion;
1218 }
1219
1220 sub isSnowLeopard()
1221 {
1222     return isDarwin() && osXVersion()->{"minor"} == 6;
1223 }
1224
1225 sub isLion()
1226 {
1227     return isDarwin() && osXVersion()->{"minor"} == 7;
1228 }
1229
1230 sub isWindowsNT()
1231 {
1232     return $ENV{'OS'} eq 'Windows_NT';
1233 }
1234
1235 sub shouldTargetWebProcess
1236 {
1237     determineShouldTargetWebProcess();
1238     return $shouldTargetWebProcess;
1239 }
1240
1241 sub determineShouldTargetWebProcess
1242 {
1243     return if defined($shouldTargetWebProcess);
1244     $shouldTargetWebProcess = checkForArgumentAndRemoveFromARGV("--target-web-process");
1245 }
1246
1247 sub shouldUseXPCServiceForWebProcess
1248 {
1249     determineShouldUseXPCServiceForWebProcess();
1250     return $shouldUseXPCServiceForWebProcess;
1251 }
1252
1253 sub determineShouldUseXPCServiceForWebProcess
1254 {
1255     return if defined($shouldUseXPCServiceForWebProcess);
1256     $shouldUseXPCServiceForWebProcess = checkForArgumentAndRemoveFromARGV("--use-web-process-xpc-service");
1257 }
1258
1259 sub debugger
1260 {
1261     determineDebugger();
1262     return $debugger;
1263 }
1264
1265 sub determineDebugger
1266 {
1267     return if defined($debugger);
1268
1269     determineXcodeVersion();
1270     if (eval "v$xcodeVersion" ge v4.5) {
1271         $debugger = "lldb";
1272     } else {
1273         $debugger = "gdb";
1274     }
1275
1276     if (checkForArgumentAndRemoveFromARGV("--use-lldb")) {
1277         $debugger = "lldb";
1278     }
1279
1280     if (checkForArgumentAndRemoveFromARGV("--use-gdb")) {
1281         $debugger = "gdb";
1282     }
1283 }
1284
1285 sub appendToEnvironmentVariableList
1286 {
1287     my ($environmentVariableName, $value) = @_;
1288
1289     if (defined($ENV{$environmentVariableName})) {
1290         $ENV{$environmentVariableName} .= ":" . $value;
1291     } else {
1292         $ENV{$environmentVariableName} = $value;
1293     }
1294 }
1295
1296 sub sharedCommandLineOptions()
1297 {
1298     return (
1299         "g|guard-malloc" => \$shouldUseGuardMalloc,
1300     );
1301 }
1302
1303 sub sharedCommandLineOptionsUsage
1304 {
1305     my %opts = @_;
1306
1307     my %switches = (
1308         '-g|--guard-malloc' => 'Use guardmalloc when running executable',
1309     );
1310
1311     my $indent = " " x ($opts{indent} || 2);
1312     my $switchWidth = List::Util::max(int($opts{switchWidth}), List::Util::max(map { length($_) } keys %switches) + ($opts{brackets} ? 2 : 0));
1313
1314     my $result = "Common switches:\n";
1315
1316     for my $switch (keys %switches) {
1317         my $switchName = $opts{brackets} ? "[" . $switch . "]" : $switch;
1318         $result .= sprintf("%s%-" . $switchWidth . "s %s\n", $indent, $switchName, $switches{$switch});
1319     }
1320
1321     return $result;
1322 }
1323
1324 sub setUpGuardMallocIfNeeded
1325 {
1326     if (!isDarwin()) {
1327         return;
1328     }
1329
1330     if (!defined($shouldUseGuardMalloc)) {
1331         $shouldUseGuardMalloc = checkForArgumentAndRemoveFromARGV("-g") || checkForArgumentAndRemoveFromARGV("--guard-malloc");
1332     }
1333
1334     if ($shouldUseGuardMalloc) {
1335         appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib");
1336     }
1337 }
1338
1339 sub relativeScriptsDir()
1340 {
1341     my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel($FindBin::Bin, getcwd()), "");
1342     if ($scriptDir eq "") {
1343         $scriptDir = ".";
1344     }
1345     return $scriptDir;
1346 }
1347
1348 sub launcherPath()
1349 {
1350     my $relativeScriptsPath = relativeScriptsDir();
1351     if (isGtk() || isEfl() || isWinCE()) {
1352         return "$relativeScriptsPath/run-launcher";
1353     } elsif (isAppleWebKit()) {
1354         return "$relativeScriptsPath/run-safari";
1355     }
1356 }
1357
1358 sub launcherName()
1359 {
1360     if (isGtk()) {
1361         return "MiniBrowser";
1362     } elsif (isAppleMacWebKit()) {
1363         return "Safari";
1364     } elsif (isAppleWinWebKit()) {
1365         return "WinLauncher";
1366     } elsif (isEfl()) {
1367         return "EWebLauncher/MiniBrowser";
1368     } elsif (isWinCE()) {
1369         return "WinCELauncher";
1370     }
1371 }
1372
1373 sub checkRequiredSystemConfig
1374 {
1375     if (isDarwin()) {
1376         chomp(my $productVersion = `sw_vers -productVersion`);
1377         if (eval "v$productVersion" lt v10.7.5) {
1378             print "*************************************************************\n";
1379             print "Mac OS X Version 10.7.5 or later is required to build WebKit.\n";
1380             print "You have " . $productVersion . ", thus the build will most likely fail.\n";
1381             print "*************************************************************\n";
1382         }
1383         my $xcodebuildVersionOutput = `xcodebuild -version`;
1384         my $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : undef;
1385         if (!$xcodeVersion || $xcodeVersion && eval "v$xcodeVersion" lt v4.6) {
1386             print "*************************************************************\n";
1387             print "Xcode Version 4.6 or later is required to build WebKit.\n";
1388             print "You have an earlier version of Xcode, thus the build will\n";
1389             print "most likely fail. The latest Xcode is available from the App Store.\n";
1390             print "*************************************************************\n";
1391         }
1392     } elsif (isGtk() or isEfl() or isWindows()) {
1393         my @cmds = qw(bison gperf flex);
1394         my @missing = ();
1395         my $oldPath = $ENV{PATH};
1396         foreach my $cmd (@cmds) {
1397             push @missing, $cmd if not commandExists($cmd);
1398         }
1399
1400         if (@missing) {
1401             my $list = join ", ", @missing;
1402             die "ERROR: $list missing but required to build WebKit.\n";
1403         }
1404     }
1405     # Win32 and other platforms may want to check for minimum config
1406 }
1407
1408 sub determineWindowsSourceDir()
1409 {
1410     return if $windowsSourceDir;
1411     $windowsSourceDir = sourceDir();
1412     chomp($windowsSourceDir = `cygpath -w '$windowsSourceDir'`) if isCygwin();
1413 }
1414
1415 sub windowsSourceDir()
1416 {
1417     determineWindowsSourceDir();
1418     return $windowsSourceDir;
1419 }
1420
1421 sub windowsSourceSourceDir()
1422 {
1423     return windowsSourceDir() . "\\Source";
1424 }
1425
1426 sub windowsLibrariesDir()
1427 {
1428     return windowsSourceDir() . "\\WebKitLibraries\\win";
1429 }
1430
1431 sub windowsOutputDir()
1432 {
1433     return windowsSourceDir() . "\\WebKitBuild";
1434 }
1435
1436 sub fontExists($)
1437 {
1438     my $font = shift;
1439     my $val = system qw(regtool get), '\\HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\' . $font . ' (TrueType)';
1440     return 0 == $val;
1441 }
1442
1443 sub checkInstalledTools()
1444 {
1445     # SVN 1.7.10 is known to be compatible with current servers. SVN 1.8.x seems to be missing some authentication
1446     # protocols we use for svn.webkit.org:
1447     my $svnVersion = `svn --version | grep "\\sversion"`;
1448     chomp($svnVersion);
1449     if (!$? and $svnVersion =~ /1\.8\./) {
1450         print "svn 1.7.10 is known to be compatible with our servers. You are running $svnVersion,\nwhich may not work properly.\n"
1451     }
1452
1453     # environment variables. Avoid until this is corrected.
1454     my $pythonVer = `python --version 2>&1`;
1455     die "You must have Python installed to build WebKit.\n" if ($?);
1456
1457     # cURL 7.34.0 has a bug that prevents authentication with opensource.apple.com (and other things using SSL3).
1458     my $curlVer = `curl --version | grep "curl"`;
1459     chomp($curlVer);
1460     if (!$? and $curlVer =~ /libcurl\/7\.34\.0/) {
1461         print "cURL version 7.34.0 has a bug that prevents authentication with SSL v2 or v3.\n";
1462         print "cURL 7.33.0 is known to work. The cURL projects is preparing an update to\n";
1463         print "correct this problem.\n\n";
1464         die "Please install a working cURL and try again.\n";
1465     }
1466
1467     # MathML requires fonts that do not ship with Windows (at least through Windows 8). Warn the user if they are missing
1468     my @fonts = qw(STIXGeneral-Regular MathJax_Main-Regular);
1469     my @missing = ();
1470     foreach my $font (@fonts) {
1471         push @missing, $font if not fontExists($font);
1472     }
1473
1474     if (scalar @missing > 0) {
1475         print "*************************************************************\n";
1476         print "Mathematical fonts, such as STIX and MathJax, are needed to\n";
1477         print "use the MathML feature.  You do not appear to have these fonts\n";
1478         print "on your system.\n\n";
1479         print "You can download a suitable set of fonts from the following URL:\n";
1480         print "https://developer.mozilla.org/Mozilla/MathML_Projects/Fonts\n";
1481         print "*************************************************************\n";
1482     }
1483
1484     print "Installed tools are correct for the WebKit build.\n";
1485 }
1486
1487 sub setupAppleWinEnv()
1488 {
1489     return unless isAppleWinWebKit();
1490
1491     checkInstalledTools();
1492
1493     if (isWindowsNT()) {
1494         my $restartNeeded = 0;
1495         my %variablesToSet = ();
1496
1497         # FIXME: We should remove this explicit version check for cygwin once we stop supporting Cygwin 1.7.9 or older versions. 
1498         # https://bugs.webkit.org/show_bug.cgi?id=85791
1499         my $uname_version = (POSIX::uname())[2];
1500         $uname_version =~ s/\(.*\)//;  # Remove the trailing cygwin version, if any.
1501         if (version->parse($uname_version) < version->parse("1.7.10")) {
1502             # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios)
1503             # for UNIX-like ttys in the Windows console
1504             $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN};
1505         }
1506         
1507         # Those environment variables must be set to be able to build inside Visual Studio.
1508         $variablesToSet{WEBKIT_LIBRARIES} = windowsLibrariesDir() unless $ENV{WEBKIT_LIBRARIES};
1509         $variablesToSet{WEBKIT_OUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKIT_OUTPUTDIR};
1510         $variablesToSet{MSBUILDDISABLENODEREUSE} = "1" unless $ENV{MSBUILDDISABLENODEREUSE};
1511
1512         foreach my $variable (keys %variablesToSet) {
1513             print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n";
1514             system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable};
1515             $restartNeeded ||=  $variable eq "WEBKIT_LIBRARIES" || $variable eq "WEBKIT_OUTPUTDIR";
1516         }
1517
1518         if ($restartNeeded) {
1519             print "Please restart your computer before attempting to build inside Visual Studio.\n\n";
1520         }
1521     } else {
1522         if (!defined $ENV{'WEBKIT_LIBRARIES'} || !$ENV{'WEBKIT_LIBRARIES'}) {
1523             print "Warning: You must set the 'WebKit_Libraries' environment variable\n";
1524             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n";
1525             print "         Make sure that 'WebKit_Libraries' points to the\n";
1526             print "         'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n";
1527         }
1528         if (!defined $ENV{'WEBKIT_OUTPUTDIR'} || !$ENV{'WEBKIT_OUTPUTDIR'}) {
1529             print "Warning: You must set the 'WebKit_OutputDir' environment variable\n";
1530             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n\n";
1531         }
1532         if (!defined $ENV{'MSBUILDDISABLENODEREUSE'} || !$ENV{'MSBUILDDISABLENODEREUSE'}) {
1533             print "Warning: You should set the 'MSBUILDDISABLENODEREUSE' environment variable to '1'\n";
1534             print "         to avoid periodic locked log files when building.\n\n";
1535         }
1536     }
1537     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1538     if (isWin64() and !$ENV{'WEBKIT_64_SUPPORT'}) {
1539         print "Warning: You must set the 'WEBKIT_64_SUPPORT' environment variable\n";
1540         print "         to be able run WebKit or JavaScriptCore tests.\n\n";
1541     }
1542 }
1543
1544 sub setupCygwinEnv()
1545 {
1546     return if !isCygwin() && !isWindows();
1547     return if $vcBuildPath;
1548
1549     my $programFilesPath = programFilesPath();
1550     $vcBuildPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.com));
1551     if (-e $vcBuildPath) {
1552         # Visual Studio is installed;
1553         if (visualStudioVersion() eq "12") {
1554             $vcBuildPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.exe));
1555         }
1556     } else {
1557         # Visual Studio not found, try VC++ Express
1558         $vcBuildPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE WDExpress.exe));
1559         if (! -e $vcBuildPath) {
1560             print "*************************************************************\n";
1561             print "Cannot find '$vcBuildPath'\n";
1562             print "Please execute the file 'vcvars32.bat' from\n";
1563             print "'$programFilesPath\\Microsoft Visual Studio 12.0\\VC\\bin\\'\n";
1564             print "to setup the necessary environment variables.\n";
1565             print "*************************************************************\n";
1566             die;
1567         }
1568         $willUseVCExpressWhenBuilding = 1;
1569     }
1570
1571     print "Building results into: ", baseProductDir(), "\n";
1572     print "WEBKIT_OUTPUTDIR is set to: ", $ENV{"WEBKIT_OUTPUTDIR"}, "\n";
1573     print "WEBKIT_LIBRARIES is set to: ", $ENV{"WEBKIT_LIBRARIES"}, "\n";
1574     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1575     print "WEBKIT_64_SUPPORT is set to: ", $ENV{"WEBKIT_64_SUPPORT"}, "\n" if isWin64();
1576 }
1577
1578 sub dieIfWindowsPlatformSDKNotInstalled
1579 {
1580     my $registry32Path = "/proc/registry/";
1581     my $registry64Path = "/proc/registry64/";
1582     my @windowsPlatformSDKRegistryEntries = (
1583         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0A",
1584         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0",
1585         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.1A",
1586         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.0A",
1587         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1",
1588     );
1589
1590     # FIXME: It would be better to detect whether we are using 32- or 64-bit Windows
1591     # and only check the appropriate entry. But for now we just blindly check both.
1592     my $recommendedPlatformSDK = $windowsPlatformSDKRegistryEntries[0];
1593
1594     while (@windowsPlatformSDKRegistryEntries) {
1595         my $windowsPlatformSDKRegistryEntry = shift @windowsPlatformSDKRegistryEntries;
1596         return if (-e $registry32Path . $windowsPlatformSDKRegistryEntry) || (-e $registry64Path . $windowsPlatformSDKRegistryEntry);
1597     }
1598
1599     print "*************************************************************\n";
1600     print "Cannot find registry entry '$recommendedPlatformSDK'.\n";
1601     print "Please download and install the Microsoft Windows SDK\n";
1602     print "from <http://www.microsoft.com/en-us/download/details.aspx?id=8279>.\n\n";
1603     print "Then follow step 2 in the Windows section of the \"Installing Developer\n";
1604     print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n";
1605     print "*************************************************************\n";
1606     die;
1607 }
1608
1609 sub copyInspectorFrontendFiles
1610 {
1611     my $productDir = productDir();
1612     my $sourceInspectorPath = sourceDir() . "/Source/WebCore/inspector/front-end/";
1613     my $inspectorResourcesDirPath = $ENV{"WEBKITINSPECTORRESOURCESDIR"};
1614
1615     if (!defined($inspectorResourcesDirPath)) {
1616         $inspectorResourcesDirPath = "";
1617     }
1618
1619     if (isAppleMacWebKit()) {
1620         if (isIOSWebKit()) {
1621             $inspectorResourcesDirPath = $productDir . "/WebCore.framework/inspector";
1622         } else {
1623             $inspectorResourcesDirPath = $productDir . "/WebCore.framework/Resources/inspector";
1624         }
1625     } elsif (isAppleWinWebKit() || isWinCairo()) {
1626         $inspectorResourcesDirPath = $productDir . "/WebKit.resources/inspector";
1627     } elsif (isGtk()) {
1628         my $prefix = $ENV{"WebKitInstallationPrefix"};
1629         $inspectorResourcesDirPath = (defined($prefix) ? $prefix : "/usr/share") . "/webkit-1.0/webinspector";
1630     } elsif (isEfl()) {
1631         my $prefix = $ENV{"WebKitInstallationPrefix"};
1632         $inspectorResourcesDirPath = (defined($prefix) ? $prefix : "/usr/share") . "/ewebkit/webinspector";
1633     }
1634
1635     if (! -d $inspectorResourcesDirPath) {
1636         print "*************************************************************\n";
1637         print "Cannot find '$inspectorResourcesDirPath'.\n" if (defined($inspectorResourcesDirPath));
1638         print "Make sure that you have built WebKit first.\n" if (! -d $productDir || defined($inspectorResourcesDirPath));
1639         print "Optionally, set the environment variable 'WebKitInspectorResourcesDir'\n";
1640         print "to point to the directory that contains the WebKit Inspector front-end\n";
1641         print "files for the built WebCore framework.\n";
1642         print "*************************************************************\n";
1643         die;
1644     }
1645
1646     if (isAppleMacWebKit()) {
1647         my $sourceLocalizedStrings = sourceDir() . "/Source/WebCore/English.lproj/localizedStrings.js";
1648         my $destinationLocalizedStrings;
1649         if (isIOSWebKit()) {
1650             $destinationLocalizedStrings = $productDir . "/WebCore.framework/English.lproj/localizedStrings.js";
1651         } else {
1652             $destinationLocalizedStrings = $productDir . "/WebCore.framework/Resources/English.lproj/localizedStrings.js";
1653         }
1654         system "ditto", $sourceLocalizedStrings, $destinationLocalizedStrings;
1655     }
1656
1657     my $exitStatus = system "rsync", "-aut", "--exclude=/.DS_Store", "--exclude=*.re2js", "--exclude=.svn/", $sourceInspectorPath, $inspectorResourcesDirPath;
1658     return $exitStatus if $exitStatus;
1659
1660     if (isIOSWebKit()) {
1661         chdir($productDir . "/WebCore.framework");
1662         return system "zip", "--quiet", "--exclude=*.qrc", "-r", "inspector-remote.zip", "inspector";
1663     }
1664
1665     return 0; # Success; did copy files.
1666 }
1667
1668 sub buildXCodeProject($$@)
1669 {
1670     my ($project, $clean, @extraOptions) = @_;
1671
1672     if ($clean) {
1673         push(@extraOptions, "-alltargets");
1674         push(@extraOptions, "clean");
1675     }
1676
1677     return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions;
1678 }
1679
1680 sub usingVisualStudioExpress()
1681 {
1682     setupCygwinEnv();
1683     return $willUseVCExpressWhenBuilding;
1684 }
1685
1686 sub buildVisualStudioProject
1687 {
1688     my ($project, $clean) = @_;
1689     setupCygwinEnv();
1690
1691     my $config = configurationForVisualStudio();
1692
1693     dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding;
1694
1695     chomp($project = `cygpath -w "$project"`) if isCygwin();
1696     
1697     my $action = "/build";
1698     if ($clean) {
1699         $action = "/clean";
1700     }
1701
1702     my @command = ($vcBuildPath, $project, $action, $config);
1703
1704     print join(" ", @command), "\n";
1705     return system @command;
1706 }
1707
1708 sub getJhbuildPath()
1709 {
1710     my @jhbuildPath = File::Spec->splitdir(baseProductDir());
1711     if (isGit() && isGitBranchBuild() && gitBranch()) {
1712         pop(@jhbuildPath);
1713     }
1714     push(@jhbuildPath, "Dependencies");
1715     return File::Spec->catdir(@jhbuildPath);
1716 }
1717
1718 sub isCachedArgumentfileOutOfDate($@)
1719 {
1720     my ($filename, $currentContents) = @_;
1721
1722     if (! -e $filename) {
1723         return 1;
1724     }
1725
1726     open(CONTENTS_FILE, $filename);
1727     chomp(my $previousContents = <CONTENTS_FILE>);
1728     close(CONTENTS_FILE);
1729
1730     if ($previousContents ne $currentContents) {
1731         print "Contents for file $filename have changed.\n";
1732         print "Previous contents were: $previousContents\n\n";
1733         print "New contents are: $currentContents\n";
1734         return 1;
1735     }
1736
1737     return 0;
1738 }
1739
1740 sub jhbuildWrapperPrefixIfNeeded()
1741 {
1742     if (-e getJhbuildPath()) {
1743         my @prefix = (File::Spec->catfile(sourceDir(), "Tools", "jhbuild", "jhbuild-wrapper"));
1744         if (isEfl()) {
1745             push(@prefix, "--efl");
1746         } elsif (isGtk()) {
1747             push(@prefix, "--gtk");
1748         }
1749         push(@prefix, "run");
1750
1751         return @prefix;
1752     }
1753
1754     return ();
1755 }
1756
1757 sub cmakeCachePath()
1758 {
1759     return File::Spec->catdir(baseProductDir(), configuration(), "CMakeCache.txt");
1760 }
1761
1762 sub shouldRemoveCMakeCache(@)
1763 {
1764     my ($cacheFilePath, @buildArgs) = @_;
1765     if (isWinCE()) {
1766         return 0;
1767     }
1768
1769     if (!isGtk()) {
1770         return 1;
1771     }
1772
1773     # We check this first, because we always want to create this file for a fresh build.
1774     my $productDir = File::Spec->catdir(baseProductDir(), configuration());
1775     my $optionsCache = File::Spec->catdir($productDir, "build-webkit-options.txt");
1776     my $joinedBuildArgs = join(" ", @buildArgs);
1777     if (isCachedArgumentfileOutOfDate($optionsCache, $joinedBuildArgs)) {
1778         File::Path::mkpath($productDir) unless -d $productDir;
1779         open(CACHED_ARGUMENTS, ">", $optionsCache);
1780         print CACHED_ARGUMENTS $joinedBuildArgs;
1781         close(CACHED_ARGUMENTS);
1782
1783         return 1;
1784     }
1785
1786     my $cmakeCache = cmakeCachePath();
1787     unless (-e $cmakeCache) {
1788         return 0;
1789     }
1790
1791     my $cacheFileModifiedTime = stat($cmakeCache)->mtime;
1792     my $platformConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "Options" . cmakeBasedPortName() . ".cmake");
1793     if ($cacheFileModifiedTime < stat($platformConfiguration)->mtime) {
1794         return 1;
1795     }
1796
1797     my $globalConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsCommon.cmake");
1798     if ($cacheFileModifiedTime < stat($globalConfiguration)->mtime) {
1799         return 1;
1800     }
1801
1802     return 0;
1803 }
1804
1805 sub removeCMakeCache(@)
1806 {
1807     my (@buildArgs) = @_;
1808     if (shouldRemoveCMakeCache(@buildArgs)) {
1809         my $cmakeCache = cmakeCachePath();
1810         unlink($cmakeCache) if -e $cmakeCache;
1811     }
1812 }
1813
1814 sub canUseNinja(@)
1815 {
1816     # Test both ninja and ninja-build. Fedora uses ninja-build and has patched CMake to also call ninja-build.
1817     system('which ninja > /dev/null || which ninja-build > /dev/null');
1818     return $? == 0;
1819 }
1820
1821 sub canUseEclipse(@)
1822 {
1823     system('which eclipse > /dev/null');
1824     return $? == 0;
1825 }
1826
1827 sub cmakeGeneratedBuildfile(@)
1828 {
1829     my ($willUseNinja) = @_;
1830     if ($willUseNinja) {
1831         return File::Spec->catfile(baseProductDir(), configuration(), "build.ninja")
1832     } else {
1833         return File::Spec->catfile(baseProductDir(), configuration(), "Makefile")
1834     }
1835 }
1836
1837 sub generateBuildSystemFromCMakeProject
1838 {
1839     my ($port, $prefixPath, @cmakeArgs, $additionalCMakeArgs) = @_;
1840     my $config = configuration();
1841     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1842     File::Path::mkpath($buildPath) unless -d $buildPath;
1843     my $originalWorkingDirectory = getcwd();
1844     chdir($buildPath) or die;
1845
1846     # For GTK+ we try to be smart about when to rerun cmake, so that we can have faster incremental builds.
1847     my $willUseNinja = isGtk() && canUseNinja();
1848     if (isGtk() && -e cmakeCachePath() && -e cmakeGeneratedBuildfile($willUseNinja)) {
1849         return 0;
1850     }
1851
1852     my @args;
1853     push @args, "-DPORT=\"$port\"";
1854     push @args, "-DCMAKE_INSTALL_PREFIX=\"$prefixPath\"" if $prefixPath;
1855     push @args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" if isGtk();
1856     push @args, "-DSHARED_CORE=ON" if isEfl() && $ENV{"ENABLE_DRT"};
1857     if ($config =~ /release/i) {
1858         push @args, "-DCMAKE_BUILD_TYPE=Release";
1859     } elsif ($config =~ /debug/i) {
1860         push @args, "-DCMAKE_BUILD_TYPE=Debug";
1861     }
1862
1863     if ($willUseNinja) {
1864         push @args, "-G";
1865         if (canUseEclipse()) {
1866             push @args, "'Eclipse CDT4 - Ninja'";
1867         } else {
1868             push @args, "Ninja";
1869         }
1870     }
1871
1872     # GTK+ has a production mode, but build-webkit should always use developer mode.
1873     push @args, "-DDEVELOPER_MODE=ON" if isGtk();
1874
1875     # Don't warn variables which aren't used by cmake ports.
1876     push @args, "--no-warn-unused-cli";
1877     push @args, @cmakeArgs if @cmakeArgs;
1878     push @args, $additionalCMakeArgs if $additionalCMakeArgs;
1879
1880     push @args, '"' . sourceDir() . '"';
1881
1882     # Compiler options to keep floating point values consistent
1883     # between 32-bit and 64-bit architectures.
1884     determineArchitecture();
1885     if ($architecture ne "x86_64" && !isARM() && !isCrossCompilation()) {
1886         $ENV{'CXXFLAGS'} = "-march=pentium4 -msse2 -mfpmath=sse " . ($ENV{'CXXFLAGS'} || "");
1887     }
1888
1889     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
1890     # parsed for shell metacharacters.
1891     my $wrapper = join(" ", jhbuildWrapperPrefixIfNeeded()) . " ";
1892     my $returnCode = system($wrapper . "cmake @args");
1893
1894     chdir($originalWorkingDirectory);
1895     return $returnCode;
1896 }
1897
1898 sub buildCMakeGeneratedProject($)
1899 {
1900     my ($makeArgs) = @_;
1901     my $config = configuration();
1902     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1903     if (! -d $buildPath) {
1904         die "Must call generateBuildSystemFromCMakeProject() before building CMake project.";
1905     }
1906
1907     my $command = "cmake";
1908     my @args = ("--build", $buildPath, "--config", $config);
1909     push @args, ("--", $makeArgs) if $makeArgs;
1910
1911     # GTK uses a build script to preserve colors and pretty-printing.
1912     if (isGtk()) {
1913         chdir "$buildPath" or die;
1914         $command = "$buildPath/build.sh";
1915         @args = ($makeArgs);
1916     }
1917
1918     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
1919     # parsed for shell metacharacters. In particular, $makeArgs may contain such metacharacters.
1920     my $wrapper = join(" ", jhbuildWrapperPrefixIfNeeded()) . " ";
1921     return system($wrapper . "$command @args");
1922
1923 }
1924
1925 sub cleanCMakeGeneratedProject()
1926 {
1927     my $config = configuration();
1928     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1929     if (-d $buildPath) {
1930         return system("cmake", "--build", $buildPath, "--config", $config, "--target", "clean");
1931     }
1932     return 0;
1933 }
1934
1935 sub buildCMakeProjectOrExit($$$$@)
1936 {
1937     my ($clean, $port, $prefixPath, $makeArgs, @cmakeArgs) = @_;
1938     my $returnCode;
1939
1940     exit(exitStatus(cleanCMakeGeneratedProject())) if $clean;
1941
1942     if (isEfl() && checkForArgumentAndRemoveFromARGV("--update-efl")) {
1943         system("perl", "$sourceDir/Tools/Scripts/update-webkitefl-libs") == 0 or die $!;
1944     }
1945
1946     if (isGtk() && checkForArgumentAndRemoveFromARGV("--update-gtk")) {
1947         system("perl", "$sourceDir/Tools/Scripts/update-webkitgtk-libs") == 0 or die $!;
1948     }
1949
1950     $returnCode = exitStatus(generateBuildSystemFromCMakeProject($port, $prefixPath, @cmakeArgs));
1951     exit($returnCode) if $returnCode;
1952
1953     $returnCode = exitStatus(buildCMakeGeneratedProject($makeArgs));
1954     exit($returnCode) if $returnCode;
1955     return 0;
1956 }
1957
1958 sub cmakeBasedPortArguments()
1959 {
1960     return ('-G "Visual Studio 8 2005 STANDARDSDK_500 (ARMV4I)"') if isWinCE();
1961     return ();
1962 }
1963
1964 sub cmakeBasedPortName()
1965 {
1966     return "Efl" if isEfl();
1967     return "WinCE" if isWinCE();
1968     return "GTK" if isGtk();
1969     return "";
1970 }
1971
1972 sub isCMakeBuild()
1973 {
1974     return isEfl() || isWinCE() || isGtk();
1975 }
1976
1977 sub promptUser
1978 {
1979     my ($prompt, $default) = @_;
1980     my $defaultValue = $default ? "[$default]" : "";
1981     print "$prompt $defaultValue: ";
1982     chomp(my $input = <STDIN>);
1983     return $input ? $input : $default;
1984 }
1985
1986 sub appleApplicationSupportPath
1987 {
1988     if (isWin64()) {
1989         # FIXME (125180): Remove the following once official 64-bit Windows support is available.
1990         return $ENV{"WEBKIT_64_SUPPORT"}, "\n" if isWin64();
1991     }
1992
1993     open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir";
1994     my $path = <INSTALL_DIR>;
1995     $path =~ s/[\r\n\x00].*//;
1996     close INSTALL_DIR;
1997
1998     my $unixPath = `cygpath -u '$path'`;
1999     chomp $unixPath;
2000     return $unixPath;
2001 }
2002
2003 sub setPathForRunningWebKitApp
2004 {
2005     my ($env) = @_;
2006
2007     if (isAppleWinWebKit()) {
2008         $env->{PATH} = join(':', productDir(), dirname(installedSafariPath()), appleApplicationSupportPath(), $env->{PATH} || "");
2009     } elsif (isWinCairo()) {
2010         my $winCairoBin = sourceDir() . "/WebKitLibraries/win/" . (isWin64() ? "bin64/" : "bin32/");
2011         my $gstreamerBin = isWin64() ? $ENV{"GSTREAMER_1_0_ROOT_X86_64"} . "bin" : $ENV{"GSTREAMER_1_0_ROOT_X86"} . "bin";
2012         $env->{PATH} = join(':', productDir(), $winCairoBin, $gstreamerBin, $env->{PATH} || "");
2013     }
2014 }
2015
2016 sub printHelpAndExitForRunAndDebugWebKitAppIfNeeded
2017 {
2018     return unless checkForArgumentAndRemoveFromARGV("--help");
2019
2020     my ($includeOptionsForDebugging) = @_;
2021
2022     print STDERR <<EOF;
2023 Usage: @{[basename($0)]} [options] [args ...]
2024   --help                            Show this help message
2025   --no-saved-state                  Launch the application without state restoration (OS X 10.7 and later)
2026   -g|--guard-malloc                 Enable Guard Malloc (OS X only)
2027   --use-web-process-xpc-service     Launch the Web Process as an XPC Service (OS X only)
2028 EOF
2029
2030     if ($includeOptionsForDebugging) {
2031         print STDERR <<EOF;
2032   --target-web-process              Debug the web process
2033   --use-gdb                         Use GDB (this is the default when using Xcode 4.4 or earlier)
2034   --use-lldb                        Use LLDB (this is the default when using Xcode 4.5 or later)
2035 EOF
2036     }
2037
2038     exit(1);
2039 }
2040
2041 sub argumentsForRunAndDebugMacWebKitApp()
2042 {
2043     my @args = ();
2044     if (checkForArgumentAndRemoveFromARGV("--no-saved-state")) {
2045         push @args, ("-ApplePersistenceIgnoreStateQuietly", "YES");
2046         # FIXME: Don't set ApplePersistenceIgnoreState once all supported OS versions respect ApplePersistenceIgnoreStateQuietly (rdar://15032886).
2047         push @args, ("-ApplePersistenceIgnoreState", "YES");
2048     }
2049     push @args, ("-WebKit2UseXPCServiceForWebProcess", "YES") if shouldUseXPCServiceForWebProcess();
2050     unshift @args, @ARGV;
2051
2052     return @args;
2053 }
2054
2055 sub setupMacWebKitEnvironment($)
2056 {
2057     my ($dyldFrameworkPath) = @_;
2058
2059     $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
2060
2061     $ENV{DYLD_FRAMEWORK_PATH} = $ENV{DYLD_FRAMEWORK_PATH} ? join(":", $dyldFrameworkPath, $ENV{DYLD_FRAMEWORK_PATH}) : $dyldFrameworkPath;
2062     $ENV{__XPC_DYLD_FRAMEWORK_PATH} = $dyldFrameworkPath;
2063     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
2064
2065     setUpGuardMallocIfNeeded();
2066 }
2067
2068 sub runMacWebKitApp($;$)
2069 {
2070     my ($appPath, $useOpenCommand) = @_;
2071     my $productDir = productDir();
2072     print "Starting @{[basename($appPath)]} with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2073
2074     local %ENV = %ENV;
2075     setupMacWebKitEnvironment($productDir);
2076
2077     if (defined($useOpenCommand) && $useOpenCommand == USE_OPEN_COMMAND) {
2078         return system("open", "-W", "-a", $appPath, "--args", argumentsForRunAndDebugMacWebKitApp());
2079     }
2080     if (architecture()) {
2081         return system "arch", "-" . architecture(), $appPath, argumentsForRunAndDebugMacWebKitApp();
2082     }
2083     return system { $appPath } $appPath, argumentsForRunAndDebugMacWebKitApp();
2084 }
2085
2086 sub execMacWebKitAppForDebugging($)
2087 {
2088     my ($appPath) = @_;
2089     my $architectureSwitch;
2090     my $argumentsSeparator;
2091
2092     if (debugger() eq "lldb") {
2093         $architectureSwitch = "--arch";
2094         $argumentsSeparator = "--";
2095     } elsif (debugger() eq "gdb") {
2096         $architectureSwitch = "-arch";
2097         $argumentsSeparator = "--args";
2098     } else {
2099         die "Unknown debugger $debugger.\n";
2100     }
2101
2102     my $debuggerPath = `xcrun -find $debugger`;
2103     chomp $debuggerPath;
2104     die "Can't find the $debugger executable.\n" unless -x $debuggerPath;
2105
2106     my $productDir = productDir();
2107     setupMacWebKitEnvironment($productDir);
2108
2109     my @architectureFlags = ($architectureSwitch, architecture());
2110     if (!shouldTargetWebProcess()) {
2111         print "Starting @{[basename($appPath)]} under $debugger with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2112         exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $appPath, argumentsForRunAndDebugMacWebKitApp() or die;
2113     } else {
2114         if (shouldUseXPCServiceForWebProcess()) {
2115             die "Targetting the Web Process is not compatible with using an XPC Service for the Web Process at this time.";
2116         }
2117         
2118         my $webProcessShimPath = File::Spec->catfile($productDir, "SecItemShim.dylib");
2119         my $webProcessPath = File::Spec->catdir($productDir, "WebProcess.app");
2120         my $webKit2ExecutablePath = File::Spec->catfile($productDir, "WebKit2.framework", "WebKit2");
2121
2122         appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", $webProcessShimPath);
2123
2124         print "Starting WebProcess under $debugger with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2125         exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $webProcessPath, $webKit2ExecutablePath, "-type", "webprocess", "-client-executable", $appPath or die;
2126     }
2127 }
2128
2129 sub debugSafari
2130 {
2131     if (isAppleMacWebKit()) {
2132         checkFrameworks();
2133         execMacWebKitAppForDebugging(safariPath());
2134     }
2135
2136     return 1; # Unsupported platform; can't debug Safari on this platform.
2137 }
2138
2139 sub runSafari
2140 {
2141
2142     if (isAppleMacWebKit()) {
2143         return runMacWebKitApp(safariPath());
2144     }
2145
2146     if (isAppleWinWebKit()) {
2147         my $result;
2148         my $productDir = productDir();
2149         my $webKitLauncherPath = File::Spec->catfile(productDir(), "WinLauncher.exe");
2150         return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
2151     }
2152
2153     return 1; # Unsupported platform; can't run Safari on this platform.
2154 }
2155
2156 sub runMiniBrowser
2157 {
2158     if (isAppleMacWebKit()) {
2159         return runMacWebKitApp(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2160     }
2161
2162     return 1;
2163 }
2164
2165 sub debugMiniBrowser
2166 {
2167     if (isAppleMacWebKit()) {
2168         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2169     }
2170     
2171     return 1;
2172 }
2173
2174 sub runWebKitTestRunner
2175 {
2176     if (isAppleMacWebKit()) {
2177         return runMacWebKitApp(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2178     }
2179
2180     return 1;
2181 }
2182
2183 sub debugWebKitTestRunner
2184 {
2185     if (isAppleMacWebKit()) {
2186         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2187     }
2188
2189     return 1;
2190 }
2191
2192 sub readRegistryString
2193 {
2194     my ($valueName) = @_;
2195     chomp(my $string = `regtool --wow32 get "$valueName"`);
2196     return $string;
2197 }
2198
2199 sub writeRegistryString
2200 {
2201     my ($valueName, $string) = @_;
2202
2203     my $error = system "regtool", "--wow32", "set", "-s", $valueName, $string;
2204
2205     # On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still
2206     # return a successful exit code. So we double-check here that the value we tried to write to the
2207     # registry was really written.
2208     return !$error && readRegistryString($valueName) eq $string;
2209 }
2210
2211 1;