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