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