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