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