[CMAKE] Use *bin* and *lib* directories for executable and libraries.
[WebKit-https.git] / Tools / Scripts / webkitdirs.pm
1 # Copyright (C) 2005, 2006, 2007, 2010, 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        &passedConfiguration
60        &printHelpAndExitForRunAndDebugWebKitAppIfNeeded
61        &productDir
62        &runMacWebKitApp
63        &safariPath
64        &setConfiguration
65        USE_OPEN_COMMAND
66    );
67    %EXPORT_TAGS = ( );
68    @EXPORT_OK   = ();
69 }
70
71 use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp().
72
73 our @EXPORT_OK;
74
75 my $architecture;
76 my $numberOfCPUs;
77 my $baseProductDir;
78 my @baseProductDirOption;
79 my $configuration;
80 my $configurationForVisualStudio;
81 my $configurationProductDir;
82 my $sourceDir;
83 my $currentSVNRevision;
84 my $osXVersion;
85 my $generateDsym;
86 my $isQt;
87 my $qmakebin = "qmake"; # Allow override of the qmake binary from $PATH
88 my $isGtk;
89 my $isWinCE;
90 my $isWinCairo;
91 my $isWx;
92 my $isEfl;
93 my @wxArgs;
94 my $isBlackBerry;
95 my $isChromium;
96 my $isChromiumAndroid;
97 my $isChromiumMacMake;
98 my $isChromiumNinja;
99 my $forceChromiumUpdate;
100 my $isInspectorFrontend;
101 my $isWK2;
102 my $shouldTargetWebProcess;
103 my $xcodeVersion;
104
105 # Variables for Win32 support
106 my $vcBuildPath;
107 my $windowsSourceDir;
108 my $winVersion;
109 my $willUseVCExpressWhenBuilding = 0;
110
111 # Defined in VCSUtils.
112 sub exitStatus($);
113
114 sub determineSourceDir
115 {
116     return if $sourceDir;
117     $sourceDir = $FindBin::Bin;
118     $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
119
120     # walks up path checking each directory to see if it is the main WebKit project dir, 
121     # defined by containing Sources, WebCore, and WebKit
122     until ((-d "$sourceDir/Source" && -d "$sourceDir/Source/WebCore" && -d "$sourceDir/Source/WebKit") || (-d "$sourceDir/Internal" && -d "$sourceDir/OpenSource"))
123     {
124         if ($sourceDir !~ s|/[^/]+$||) {
125             die "Could not find top level webkit directory above source directory using FindBin.\n";
126         }
127     }
128
129     $sourceDir = "$sourceDir/OpenSource" if -d "$sourceDir/OpenSource";
130 }
131
132 sub currentPerlPath()
133 {
134     my $thisPerl = $^X;
135     if ($^O ne 'VMS') {
136         $thisPerl .= $Config{_exe} unless $thisPerl =~ m/$Config{_exe}$/i;
137     }
138     return $thisPerl;
139 }
140
141 sub setQmakeBinaryPath($)
142 {
143     ($qmakebin) = @_;
144 }
145
146 # used for scripts which are stored in a non-standard location
147 sub setSourceDir($)
148 {
149     ($sourceDir) = @_;
150 }
151
152 sub determineXcodeVersion
153 {
154     return if defined $xcodeVersion;
155     my $xcodebuildVersionOutput = `xcodebuild -version`;
156     $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : "3.0";
157 }
158
159 sub readXcodeUserDefault($)
160 {
161     my ($unprefixedKey) = @_;
162
163     determineXcodeVersion();
164
165     my $xcodeDefaultsDomain = (eval "v$xcodeVersion" lt v4) ? "com.apple.Xcode" : "com.apple.dt.Xcode";
166     my $xcodeDefaultsPrefix = (eval "v$xcodeVersion" lt v4) ? "PBX" : "IDE";
167     my $devnull = File::Spec->devnull();
168
169     my $value = `defaults read $xcodeDefaultsDomain ${xcodeDefaultsPrefix}${unprefixedKey} 2> ${devnull}`;
170     return if $?;
171
172     chomp $value;
173     return $value;
174 }
175
176 sub determineBaseProductDir
177 {
178     return if defined $baseProductDir;
179     determineSourceDir();
180
181     my $setSharedPrecompsDir;
182     $baseProductDir = $ENV{"WEBKITOUTPUTDIR"};
183
184     if (!defined($baseProductDir) and isAppleMacWebKit()) {
185         # Silently remove ~/Library/Preferences/xcodebuild.plist which can
186         # cause build failure. The presence of
187         # ~/Library/Preferences/xcodebuild.plist can prevent xcodebuild from
188         # respecting global settings such as a custom build products directory
189         # (<rdar://problem/5585899>).
190         my $personalPlistFile = $ENV{HOME} . "/Library/Preferences/xcodebuild.plist";
191         if (-e $personalPlistFile) {
192             unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!";
193         }
194
195         determineXcodeVersion();
196
197         if (eval "v$xcodeVersion" ge v4) {
198             my $buildLocationStyle = join '', readXcodeUserDefault("BuildLocationStyle");
199             if ($buildLocationStyle eq "Custom") {
200                 my $buildLocationType = join '', readXcodeUserDefault("CustomBuildLocationType");
201                 # FIXME: Read CustomBuildIntermediatesPath and set OBJROOT accordingly.
202                 $baseProductDir = readXcodeUserDefault("CustomBuildProductsPath") if $buildLocationType eq "Absolute";
203                 $setSharedPrecompsDir = 1;
204             }
205         }
206
207         if (!defined($baseProductDir)) {
208             $baseProductDir = join '', readXcodeUserDefault("ApplicationwideBuildSettings");
209             $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s;
210         }
211
212         undef $baseProductDir unless $baseProductDir =~ /^\//;
213     } elsif (isChromium()) {
214         if (isLinux() || isChromiumAndroid() || isChromiumMacMake()) {
215             $baseProductDir = "$sourceDir/out";
216         } elsif (isDarwin()) {
217             $baseProductDir = "$sourceDir/Source/WebKit/chromium/xcodebuild";
218         } elsif (isWindows() || isCygwin()) {
219             $baseProductDir = "$sourceDir/Source/WebKit/chromium/build";
220         }
221     }
222
223     if (!defined($baseProductDir)) { # Port-spesific checks failed, use default
224         $baseProductDir = "$sourceDir/WebKitBuild";
225         undef $setSharedPrecompsDir;
226     }
227
228     if (isBlackBerry()) {
229         my %archInfo = blackberryTargetArchitecture();
230         $baseProductDir = "$baseProductDir/" . $archInfo{"cpuDir"};
231     }
232
233     if (isGit() && isGitBranchBuild() && !isChromium()) {
234         my $branch = gitBranch();
235         $baseProductDir = "$baseProductDir/$branch";
236     }
237
238     if (isAppleMacWebKit()) {
239         $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|;
240         $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|;
241         $baseProductDir =~ s|^~/|$ENV{HOME}/|;
242         die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/;
243         die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/;
244         @baseProductDirOption = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir");
245         push(@baseProductDirOption, "SHARED_PRECOMPS_DIR=${baseProductDir}/PrecompiledHeaders") if $setSharedPrecompsDir;
246     }
247
248     if (isCygwin()) {
249         my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`;
250         chomp $dosBuildPath;
251         $ENV{"WEBKITOUTPUTDIR"} = $dosBuildPath;
252         my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`;
253         chomp $unixBuildPath;
254         $baseProductDir = $unixBuildPath;
255     }
256 }
257
258 sub setBaseProductDir($)
259 {
260     ($baseProductDir) = @_;
261 }
262
263 sub determineConfiguration
264 {
265     return if defined $configuration;
266     determineBaseProductDir();
267     if (open CONFIGURATION, "$baseProductDir/Configuration") {
268         $configuration = <CONFIGURATION>;
269         close CONFIGURATION;
270     }
271     if ($configuration) {
272         chomp $configuration;
273         # compatibility for people who have old Configuration files
274         $configuration = "Release" if $configuration eq "Deployment";
275         $configuration = "Debug" if $configuration eq "Development";
276     } else {
277         $configuration = "Release";
278     }
279
280     if ($configuration && isWinCairo()) {
281         unless ($configuration =~ /_Cairo_CFLite$/) {
282             $configuration .= "_Cairo_CFLite";
283         }
284     }
285 }
286
287 sub determineArchitecture
288 {
289     return if defined $architecture;
290     # make sure $architecture is defined in all cases
291     $architecture = "";
292
293     determineBaseProductDir();
294
295     if (isGtk()) {
296         determineConfigurationProductDir();
297         my $host_triple = `grep -E '^host = ' $configurationProductDir/GNUmakefile`;
298         if ($host_triple =~ m/^host = ([^-]+)-/) {
299             # We have a configured build tree; use it.
300             $architecture = $1;
301         } else {
302             # Fall back to output of `arch', if it is present.
303             $architecture = `arch`;
304             chomp $architecture;
305         }
306     } elsif (isAppleMacWebKit()) {
307         if (open ARCHITECTURE, "$baseProductDir/Architecture") {
308             $architecture = <ARCHITECTURE>;
309             close ARCHITECTURE;
310         }
311         if ($architecture) {
312             chomp $architecture;
313         } else {
314             if (isLeopard()) {
315                 $architecture = `arch`;
316             } else {
317                 my $supports64Bit = `sysctl -n hw.optional.x86_64`;
318                 chomp $supports64Bit;
319                 $architecture = $supports64Bit ? 'x86_64' : `arch`;
320             }
321             chomp $architecture;
322         }
323     }
324 }
325
326 sub determineNumberOfCPUs
327 {
328     return if defined $numberOfCPUs;
329     if (isLinux()) {
330         # First try the nproc utility, if it exists. If we get no
331         # results fall back to just interpretting /proc directly.
332         chomp($numberOfCPUs = `nproc 2> /dev/null`);
333         if ($numberOfCPUs eq "") {
334             $numberOfCPUs = (grep /processor/, `cat /proc/cpuinfo`);
335         }
336     } elsif (isWindows() || isCygwin()) {
337         if (defined($ENV{NUMBER_OF_PROCESSORS})) {
338             $numberOfCPUs = $ENV{NUMBER_OF_PROCESSORS};
339         } else {
340             # Assumes cygwin
341             $numberOfCPUs = `ls /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor | wc -w`;
342         }
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/Source/WebKit/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, "-DPUBLIC_BUILD=0";
985     push @cmakeArgs, '-DCMAKE_SYSTEM_NAME="QNX"';
986     push @cmakeArgs, "-DCMAKE_SYSTEM_PROCESSOR=\"$cpuDir\"";
987     push @cmakeArgs, '-DCMAKE_SYSTEM_VERSION="1"';
988     push @cmakeArgs, "-DCMAKE_C_COMPILER=\"$ccCommand\"";
989     push @cmakeArgs, "-DCMAKE_CXX_COMPILER=\"$cxxCommand\"";
990     push @cmakeArgs, "-DCMAKE_C_FLAGS=\"-Vgcc_nto${cpu} -g @cxxFlags\"";
991     push @cmakeArgs, "-DCMAKE_CXX_FLAGS=\"-Vgcc_nto${cpu}_cpp-ne -g -lang-c++ @cxxFlags\"";
992
993     # We cannot use CMAKE_INCLUDE_PATH since this describes the search path for header files in user directories.
994     # And the QNX system headers are in user directories on the host OS (i.e. they aren't installed in the host OS's
995     # system header search path). So, we need to inform g++ that these user directories (@includeSystemDirectories)
996     # are to be taken as the host OS's system header directories when building our port.
997     #
998     # Also, we cannot use CMAKE_SYSTEM_INCLUDE_PATH since that will override the entire system header path.
999     # So, we define the additional system include paths in ADDITIONAL_SYSTEM_INCLUDE_PATH. This list will
1000     # be processed in OptionsBlackBerry.cmake.
1001     push @cmakeArgs, '-DADDITIONAL_SYSTEM_INCLUDE_PATH="' . join(';', @includeSystemDirectories) . '"';
1002
1003     # FIXME: Make this more general purpose such that we can pass a list of directories and files.
1004     push @cmakeArgs, '-DTHIRD_PARTY_ICU_DIR="' . File::Spec->catdir($stageInc, "unicode") . '"';
1005     push @cmakeArgs, '-DTHIRD_PARTY_UNICODE_FILE="' . File::Spec->catfile($stageInc, "unicode.h") . '"';
1006
1007     push @cmakeArgs, "-DCMAKE_LIBRARY_PATH=\"$stageLib;$stageUsrLib\"";
1008     push @cmakeArgs, '-DCMAKE_AR="' . File::Spec->catfile($qnxHost, "usr", "bin", "nto${buSuffix}-ar") . '"';
1009     push @cmakeArgs, '-DCMAKE_RANLIB="' . File::Spec->catfile($qnxHost, "usr", "bin", "nto${buSuffix}-ranlib") . '"';
1010     push @cmakeArgs, '-DCMAKE_LD="'. File::Spec->catfile($qnxHost, "usr", "bin", "nto${buSuffix}-ld") . '"';
1011     push @cmakeArgs, '-DCMAKE_LINKER="' . File::Spec->catfile($qnxHost, "usr", "bin", "nto${buSuffix}-ld") . '"';
1012     push @cmakeArgs, "-DECLIPSE_CDT4_GENERATE_SOURCE_PROJECT=TRUE";
1013     push @cmakeArgs, '-G"Eclipse CDT4 - Unix Makefiles"';
1014     push @cmakeArgs, @cmakeExtraOptions;
1015     return @cmakeArgs;
1016 }
1017
1018 sub determineIsEfl()
1019 {
1020     return if defined($isEfl);
1021     $isEfl = checkForArgumentAndRemoveFromARGV("--efl");
1022 }
1023
1024 sub isEfl()
1025 {
1026     determineIsEfl();
1027     return $isEfl;
1028 }
1029
1030 sub isGtk()
1031 {
1032     determineIsGtk();
1033     return $isGtk;
1034 }
1035
1036 sub determineIsGtk()
1037 {
1038     return if defined($isGtk);
1039     $isGtk = checkForArgumentAndRemoveFromARGV("--gtk");
1040 }
1041
1042 sub isWinCE()
1043 {
1044     determineIsWinCE();
1045     return $isWinCE;
1046 }
1047
1048 sub determineIsWinCE()
1049 {
1050     return if defined($isWinCE);
1051     $isWinCE = checkForArgumentAndRemoveFromARGV("--wince");
1052 }
1053
1054 sub isWx()
1055 {
1056     determineIsWx();
1057     return $isWx;
1058 }
1059
1060 sub determineIsWx()
1061 {
1062     return if defined($isWx);
1063     $isWx = checkForArgumentAndRemoveFromARGV("--wx");
1064 }
1065
1066 sub getWxArgs()
1067 {
1068     if (!@wxArgs) {
1069         @wxArgs = ("");
1070         my $rawWxArgs = "";
1071         foreach my $opt (@ARGV) {
1072             if ($opt =~ /^--wx-args/i ) {
1073                 @ARGV = grep(!/^--wx-args/i, @ARGV);
1074                 $rawWxArgs = $opt;
1075                 $rawWxArgs =~ s/--wx-args=//i;
1076             }
1077         }
1078         @wxArgs = split(/,/, $rawWxArgs);
1079     }
1080     return @wxArgs;
1081 }
1082
1083 # Determine if this is debian, ubuntu, linspire, or something similar.
1084 sub isDebianBased()
1085 {
1086     return -e "/etc/debian_version";
1087 }
1088
1089 sub isFedoraBased()
1090 {
1091     return -e "/etc/fedora-release";
1092 }
1093
1094 sub isChromium()
1095 {
1096     determineIsChromium();
1097     determineIsChromiumAndroid();
1098     return $isChromium || $isChromiumAndroid;
1099 }
1100
1101 sub determineIsChromium()
1102 {
1103     return if defined($isChromium);
1104     $isChromium = checkForArgumentAndRemoveFromARGV("--chromium");
1105     if ($isChromium) {
1106         $forceChromiumUpdate = checkForArgumentAndRemoveFromARGV("--force-update");
1107     }
1108 }
1109
1110 sub isChromiumAndroid()
1111 {
1112     determineIsChromiumAndroid();
1113     return $isChromiumAndroid;
1114 }
1115
1116 sub determineIsChromiumAndroid()
1117 {
1118     return if defined($isChromiumAndroid);
1119     $isChromiumAndroid = checkForArgumentAndRemoveFromARGV("--chromium-android");
1120 }
1121
1122 sub isChromiumMacMake()
1123 {
1124     determineIsChromiumMacMake();
1125     return $isChromiumMacMake;
1126 }
1127
1128 sub determineIsChromiumMacMake()
1129 {
1130     return if defined($isChromiumMacMake);
1131
1132     my $hasUpToDateMakefile = 0;
1133     if (-e 'Makefile.chromium') {
1134         unless (-e 'Source/WebKit/chromium/WebKit.xcodeproj') {
1135             $hasUpToDateMakefile = 1;
1136         } else {
1137             $hasUpToDateMakefile = stat('Makefile.chromium')->mtime > stat('Source/WebKit/chromium/WebKit.xcodeproj')->mtime;
1138         }
1139     }
1140     $isChromiumMacMake = isDarwin() && $hasUpToDateMakefile;
1141 }
1142
1143 sub isChromiumNinja()
1144 {
1145     determineIsChromiumNinja();
1146     return $isChromiumNinja;
1147 }
1148
1149 sub determineIsChromiumNinja()
1150 {
1151     return if defined($isChromiumNinja);
1152
1153     my $config = configuration();
1154
1155     my $hasUpToDateNinjabuild = 0;
1156     if (-e "out/$config/build.ninja") {
1157         my $statNinja = stat("out/$config/build.ninja");
1158
1159         my $statXcode = 0;
1160         if (-e 'Source/WebKit/chromium/WebKit.xcodeproj') {
1161           $statXcode = stat('Source/WebKit/chromium/WebKit.xcodeproj')->mtime;
1162         }
1163
1164         my $statMake = 0;
1165         if (-e 'Makefile.chromium') {
1166           $statXcode = stat('Makefile.chromium')->mtime;
1167         }
1168
1169         $hasUpToDateNinjabuild = $statNinja > $statXcode && $statNinja > $statMake;
1170     }
1171     $isChromiumNinja = $hasUpToDateNinjabuild;
1172 }
1173
1174 sub forceChromiumUpdate()
1175 {
1176     determineIsChromium();
1177     return $forceChromiumUpdate;
1178 }
1179
1180 sub isWinCairo()
1181 {
1182     determineIsWinCairo();
1183     return $isWinCairo;
1184 }
1185
1186 sub determineIsWinCairo()
1187 {
1188     return if defined($isWinCairo);
1189     $isWinCairo = checkForArgumentAndRemoveFromARGV("--wincairo");
1190 }
1191
1192 sub isCygwin()
1193 {
1194     return ($^O eq "cygwin") || 0;
1195 }
1196
1197 sub isAnyWindows()
1198 {
1199     return isWindows() || isCygwin() || isMsys();
1200 }
1201
1202 sub determineWinVersion()
1203 {
1204     return if $winVersion;
1205
1206     if (!isAnyWindows()) {
1207         $winVersion = -1;
1208         return;
1209     }
1210
1211     my $versionString = `cmd /c ver`;
1212     $versionString =~ /(\d)\.(\d)\.(\d+)/;
1213
1214     $winVersion = {
1215         major => $1,
1216         minor => $2,
1217         build => $3,
1218     };
1219 }
1220
1221 sub winVersion()
1222 {
1223     determineWinVersion();
1224     return $winVersion;
1225 }
1226
1227 sub isWindows7SP0()
1228 {
1229     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 1 && winVersion()->{build} == 7600;
1230 }
1231
1232 sub isWindowsVista()
1233 {
1234     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 0;
1235 }
1236
1237 sub isWindowsXP()
1238 {
1239     return isAnyWindows() && winVersion()->{major} == 5 && winVersion()->{minor} == 1;
1240 }
1241
1242 sub isDarwin()
1243 {
1244     return ($^O eq "darwin") || 0;
1245 }
1246
1247 sub isWindows()
1248 {
1249     return ($^O eq "MSWin32") || 0;
1250 }
1251
1252 sub isMsys()
1253 {
1254     return ($^O eq "msys") || 0;
1255 }
1256
1257 sub isLinux()
1258 {
1259     return ($^O eq "linux") || 0;
1260 }
1261
1262 sub isARM()
1263 {
1264     return $Config{archname} =~ /^arm-/;
1265 }
1266
1267 sub isCrossCompilation()
1268 {
1269   my $compiler = "" unless $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 determineOSXVersion()
1313 {
1314     return if $osXVersion;
1315
1316     if (!isDarwin()) {
1317         $osXVersion = -1;
1318         return;
1319     }
1320
1321     my $version = `sw_vers -productVersion`;
1322     my @splitVersion = split(/\./, $version);
1323     @splitVersion >= 2 or die "Invalid version $version";
1324     $osXVersion = {
1325             "major" => $splitVersion[0],
1326             "minor" => $splitVersion[1],
1327             "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0),
1328     };
1329 }
1330
1331 sub osXVersion()
1332 {
1333     determineOSXVersion();
1334     return $osXVersion;
1335 }
1336
1337 sub isLeopard()
1338 {
1339     return isDarwin() && osXVersion()->{"minor"} == 5;
1340 }
1341
1342 sub isSnowLeopard()
1343 {
1344     return isDarwin() && osXVersion()->{"minor"} == 6;
1345 }
1346
1347 sub isLion()
1348 {
1349     return isDarwin() && osXVersion()->{"minor"} == 7;
1350 }
1351
1352 sub isWindowsNT()
1353 {
1354     return $ENV{'OS'} eq 'Windows_NT';
1355 }
1356
1357 sub shouldTargetWebProcess
1358 {
1359     determineShouldTargetWebProcess();
1360     return $shouldTargetWebProcess;
1361 }
1362
1363 sub determineShouldTargetWebProcess
1364 {
1365     return if defined($shouldTargetWebProcess);
1366     $shouldTargetWebProcess = checkForArgumentAndRemoveFromARGV("--target-web-process");
1367 }
1368
1369 sub relativeScriptsDir()
1370 {
1371     my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel($FindBin::Bin, getcwd()), "");
1372     if ($scriptDir eq "") {
1373         $scriptDir = ".";
1374     }
1375     return $scriptDir;
1376 }
1377
1378 sub launcherPath()
1379 {
1380     my $relativeScriptsPath = relativeScriptsDir();
1381     if (isGtk() || isQt() || isWx() || isEfl() || isWinCE()) {
1382         return "$relativeScriptsPath/run-launcher";
1383     } elsif (isAppleWebKit()) {
1384         return "$relativeScriptsPath/run-safari";
1385     }
1386 }
1387
1388 sub launcherName()
1389 {
1390     if (isGtk()) {
1391         return "GtkLauncher";
1392     } elsif (isQt()) {
1393         return "QtTestBrowser";
1394     } elsif (isWx()) {
1395         return "wxBrowser";
1396     } elsif (isAppleWebKit()) {
1397         return "Safari";
1398     } elsif (isEfl()) {
1399         return "EWebLauncher";
1400     } elsif (isWinCE()) {
1401         return "WinCELauncher";
1402     }
1403 }
1404
1405 sub checkRequiredSystemConfig
1406 {
1407     if (isDarwin()) {
1408         chomp(my $productVersion = `sw_vers -productVersion`);
1409         if (eval "v$productVersion" lt v10.4) {
1410             print "*************************************************************\n";
1411             print "Mac OS X Version 10.4.0 or later is required to build WebKit.\n";
1412             print "You have " . $productVersion . ", thus the build will most likely fail.\n";
1413             print "*************************************************************\n";
1414         }
1415         my $xcodebuildVersionOutput = `xcodebuild -version`;
1416         my $devToolsCoreVersion = ($xcodebuildVersionOutput =~ /DevToolsCore-(\d+)/) ? $1 : undef;
1417         my $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : undef;
1418         if (!$devToolsCoreVersion && !$xcodeVersion
1419             || $devToolsCoreVersion && $devToolsCoreVersion < 747
1420             || $xcodeVersion && eval "v$xcodeVersion" lt v2.3) {
1421             print "*************************************************************\n";
1422             print "Xcode Version 2.3 or later is required to build WebKit.\n";
1423             print "You have an earlier version of Xcode, thus the build will\n";
1424             print "most likely fail.  The latest Xcode is available from the web:\n";
1425             print "http://developer.apple.com/tools/xcode\n";
1426             print "*************************************************************\n";
1427         }
1428     } elsif (isGtk() or isQt() or isWx() or isEfl()) {
1429         my @cmds = qw(flex bison gperf);
1430         my @missing = ();
1431         foreach my $cmd (@cmds) {
1432             push @missing, $cmd if not commandExists($cmd);
1433         }
1434
1435         if (@missing) {
1436             my $list = join ", ", @missing;
1437             die "ERROR: $list missing but required to build WebKit.\n";
1438         }
1439     }
1440     # Win32 and other platforms may want to check for minimum config
1441 }
1442
1443 sub determineWindowsSourceDir()
1444 {
1445     return if $windowsSourceDir;
1446     $windowsSourceDir = sourceDir();
1447     chomp($windowsSourceDir = `cygpath -w '$windowsSourceDir'`) if isCygwin();
1448 }
1449
1450 sub windowsSourceDir()
1451 {
1452     determineWindowsSourceDir();
1453     return $windowsSourceDir;
1454 }
1455
1456 sub windowsLibrariesDir()
1457 {
1458     return windowsSourceDir() . "\\WebKitLibraries\\win";
1459 }
1460
1461 sub windowsOutputDir()
1462 {
1463     return windowsSourceDir() . "\\WebKitBuild";
1464 }
1465
1466 sub setupAppleWinEnv()
1467 {
1468     return unless isAppleWinWebKit();
1469
1470     if (isWindowsNT()) {
1471         my $restartNeeded = 0;
1472         my %variablesToSet = ();
1473
1474         # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios)
1475         # for UNIX-like ttys in the Windows console
1476         $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN};
1477         
1478         # Those environment variables must be set to be able to build inside Visual Studio.
1479         $variablesToSet{WEBKITLIBRARIESDIR} = windowsLibrariesDir() unless $ENV{WEBKITLIBRARIESDIR};
1480         $variablesToSet{WEBKITOUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKITOUTPUTDIR};
1481
1482         foreach my $variable (keys %variablesToSet) {
1483             print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n";
1484             system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable};
1485             $restartNeeded ||= $variable eq "WEBKITLIBRARIESDIR" || $variable eq "WEBKITOUTPUTDIR";
1486         }
1487
1488         if ($restartNeeded) {
1489             print "Please restart your computer before attempting to build inside Visual Studio.\n\n";
1490         }
1491     } else {
1492         if (!$ENV{'WEBKITLIBRARIESDIR'}) {
1493             print "Warning: You must set the 'WebKitLibrariesDir' environment variable\n";
1494             print "         to be able build WebKit from within Visual Studio.\n";
1495             print "         Make sure that 'WebKitLibrariesDir' points to the\n";
1496             print "         'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n";
1497         }
1498         if (!$ENV{'WEBKITOUTPUTDIR'}) {
1499             print "Warning: You must set the 'WebKitOutputDir' environment variable\n";
1500             print "         to be able build WebKit from within Visual Studio.\n\n";
1501         }
1502     }
1503 }
1504
1505 sub setupCygwinEnv()
1506 {
1507     return if !isCygwin() && !isWindows();
1508     return if $vcBuildPath;
1509
1510     my $vsInstallDir;
1511     my $programFilesPath = $ENV{'PROGRAMFILES(X86)'} || $ENV{'PROGRAMFILES'} || "C:\\Program Files";
1512     if ($ENV{'VSINSTALLDIR'}) {
1513         $vsInstallDir = $ENV{'VSINSTALLDIR'};
1514     } else {
1515         $vsInstallDir = File::Spec->catdir($programFilesPath, "Microsoft Visual Studio 8");
1516     }
1517     chomp($vsInstallDir = `cygpath "$vsInstallDir"`) if isCygwin();
1518     $vcBuildPath = File::Spec->catfile($vsInstallDir, qw(Common7 IDE devenv.com));
1519     if (-e $vcBuildPath) {
1520         # Visual Studio is installed; we can use pdevenv to build.
1521         # FIXME: Make pdevenv work with non-Cygwin Perl.
1522         $vcBuildPath = File::Spec->catfile(sourceDir(), qw(Tools Scripts pdevenv)) if isCygwin();
1523     } else {
1524         # Visual Studio not found, try VC++ Express
1525         $vcBuildPath = File::Spec->catfile($vsInstallDir, qw(Common7 IDE VCExpress.exe));
1526         if (! -e $vcBuildPath) {
1527             print "*************************************************************\n";
1528             print "Cannot find '$vcBuildPath'\n";
1529             print "Please execute the file 'vcvars32.bat' from\n";
1530             print "'$programFilesPath\\Microsoft Visual Studio 8\\VC\\bin\\'\n";
1531             print "to setup the necessary environment variables.\n";
1532             print "*************************************************************\n";
1533             die;
1534         }
1535         $willUseVCExpressWhenBuilding = 1;
1536     }
1537
1538     my $qtSDKPath = File::Spec->catdir($programFilesPath, "QuickTime SDK");
1539     if (0 && ! -e $qtSDKPath) {
1540         print "*************************************************************\n";
1541         print "Cannot find '$qtSDKPath'\n";
1542         print "Please download the QuickTime SDK for Windows from\n";
1543         print "http://developer.apple.com/quicktime/download/\n";
1544         print "*************************************************************\n";
1545         die;
1546     }
1547     
1548     unless ($ENV{WEBKITLIBRARIESDIR}) {
1549         $ENV{'WEBKITLIBRARIESDIR'} = File::Spec->catdir($sourceDir, "WebKitLibraries", "win");
1550         chomp($ENV{WEBKITLIBRARIESDIR} = `cygpath -wa '$ENV{WEBKITLIBRARIESDIR}'`) if isCygwin();
1551     }
1552
1553     print "Building results into: ", baseProductDir(), "\n";
1554     print "WEBKITOUTPUTDIR is set to: ", $ENV{"WEBKITOUTPUTDIR"}, "\n";
1555     print "WEBKITLIBRARIESDIR is set to: ", $ENV{"WEBKITLIBRARIESDIR"}, "\n";
1556 }
1557
1558 sub dieIfWindowsPlatformSDKNotInstalled
1559 {
1560     my $registry32Path = "/proc/registry/";
1561     my $registry64Path = "/proc/registry64/";
1562     my $windowsPlatformSDKRegistryEntry = "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1";
1563
1564     # FIXME: It would be better to detect whether we are using 32- or 64-bit Windows
1565     # and only check the appropriate entry. But for now we just blindly check both.
1566     return if (-e $registry32Path . $windowsPlatformSDKRegistryEntry) || (-e $registry64Path . $windowsPlatformSDKRegistryEntry);
1567
1568     print "*************************************************************\n";
1569     print "Cannot find registry entry '$windowsPlatformSDKRegistryEntry'.\n";
1570     print "Please download and install the Microsoft Windows Server 2003 R2\n";
1571     print "Platform SDK from <http://www.microsoft.com/downloads/details.aspx?\n";
1572     print "familyid=0baf2b35-c656-4969-ace8-e4c0c0716adb&displaylang=en>.\n\n";
1573     print "Then follow step 2 in the Windows section of the \"Installing Developer\n";
1574     print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n";
1575     print "*************************************************************\n";
1576     die;
1577 }
1578
1579 sub copyInspectorFrontendFiles
1580 {
1581     my $productDir = productDir();
1582     my $sourceInspectorPath = sourceDir() . "/Source/WebCore/inspector/front-end/";
1583     my $inspectorResourcesDirPath = $ENV{"WEBKITINSPECTORRESOURCESDIR"};
1584
1585     if (!defined($inspectorResourcesDirPath)) {
1586         $inspectorResourcesDirPath = "";
1587     }
1588
1589     if (isAppleMacWebKit()) {
1590         $inspectorResourcesDirPath = $productDir . "/WebCore.framework/Resources/inspector";
1591     } elsif (isAppleWinWebKit()) {
1592         $inspectorResourcesDirPath = $productDir . "/WebKit.resources/inspector";
1593     } elsif (isQt() || isGtk()) {
1594         my $prefix = $ENV{"WebKitInstallationPrefix"};
1595         $inspectorResourcesDirPath = (defined($prefix) ? $prefix : "/usr/share") . "/webkit-1.0/webinspector";
1596     } elsif (isEfl()) {
1597         my $prefix = $ENV{"WebKitInstallationPrefix"};
1598         $inspectorResourcesDirPath = (defined($prefix) ? $prefix : "/usr/share") . "/ewebkit/webinspector";
1599     }
1600
1601     if (! -d $inspectorResourcesDirPath) {
1602         print "*************************************************************\n";
1603         print "Cannot find '$inspectorResourcesDirPath'.\n" if (defined($inspectorResourcesDirPath));
1604         print "Make sure that you have built WebKit first.\n" if (! -d $productDir || defined($inspectorResourcesDirPath));
1605         print "Optionally, set the environment variable 'WebKitInspectorResourcesDir'\n";
1606         print "to point to the directory that contains the WebKit Inspector front-end\n";
1607         print "files for the built WebCore framework.\n";
1608         print "*************************************************************\n";
1609         die;
1610     }
1611
1612     if (isAppleMacWebKit()) {
1613         my $sourceLocalizedStrings = sourceDir() . "/Source/WebCore/English.lproj/localizedStrings.js";
1614         my $destinationLocalizedStrings = $productDir . "/WebCore.framework/Resources/English.lproj/localizedStrings.js";
1615         system "ditto", $sourceLocalizedStrings, $destinationLocalizedStrings;
1616     }
1617
1618     return system "rsync", "-aut", "--exclude=/.DS_Store", "--exclude=*.re2js", "--exclude=.svn/", !isQt() ? "--exclude=/WebKit.qrc" : "", $sourceInspectorPath, $inspectorResourcesDirPath;
1619 }
1620
1621 sub buildXCodeProject($$@)
1622 {
1623     my ($project, $clean, @extraOptions) = @_;
1624
1625     if ($clean) {
1626         push(@extraOptions, "-alltargets");
1627         push(@extraOptions, "clean");
1628     }
1629
1630     return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions;
1631 }
1632
1633 sub usingVisualStudioExpress()
1634 {
1635     setupCygwinEnv();
1636     return $willUseVCExpressWhenBuilding;
1637 }
1638
1639 sub buildVisualStudioProject
1640 {
1641     my ($project, $clean) = @_;
1642     setupCygwinEnv();
1643
1644     my $config = configurationForVisualStudio();
1645
1646     dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding;
1647
1648     chomp($project = `cygpath -w "$project"`) if isCygwin();
1649     
1650     my $action = "/build";
1651     if ($clean) {
1652         $action = "/clean";
1653     }
1654
1655     my @command = ($vcBuildPath, $project, $action, $config);
1656
1657     print join(" ", @command), "\n";
1658     return system @command;
1659 }
1660
1661 sub downloadWafIfNeeded
1662 {
1663     # get / update waf if needed
1664     my $waf = "$sourceDir/Tools/waf/waf";
1665     my $wafURL = 'http://wxwebkit.kosoftworks.com/downloads/deps/waf';
1666     if (!-f $waf) {
1667         my $result = system "curl -o $waf $wafURL";
1668         chmod 0755, $waf;
1669     }
1670 }
1671
1672 sub buildWafProject
1673 {
1674     my ($project, $shouldClean, @options) = @_;
1675     
1676     # set the PYTHONPATH for waf
1677     my $pythonPath = $ENV{'PYTHONPATH'};
1678     if (!defined($pythonPath)) {
1679         $pythonPath = '';
1680     }
1681     my $sourceDir = sourceDir();
1682     my $newPythonPath = "$sourceDir/Tools/waf/build:$pythonPath";
1683     if (isCygwin()) {
1684         $newPythonPath = `cygpath --mixed --path $newPythonPath`;
1685     }
1686     $ENV{'PYTHONPATH'} = $newPythonPath;
1687     
1688     print "Building $project\n";
1689
1690     my $wafCommand = "$sourceDir/Tools/waf/waf";
1691     if ($ENV{'WXWEBKIT_WAF'}) {
1692         $wafCommand = $ENV{'WXWEBKIT_WAF'};
1693     }
1694     if (isCygwin()) {
1695         $wafCommand = `cygpath --windows "$wafCommand"`;
1696         chomp($wafCommand);
1697     }
1698     if ($shouldClean) {
1699         return system $wafCommand, "uninstall", "clean", "distclean";
1700     }
1701     
1702     return system $wafCommand, 'configure', 'build', 'install', @options;
1703 }
1704
1705 sub retrieveQMakespecVar
1706 {
1707     my $mkspec = $_[0];
1708     my $varname = $_[1];
1709
1710     my $varvalue = undef;
1711     #print "retrieveMakespecVar " . $mkspec . ", " . $varname . "\n";
1712
1713     local *SPEC;
1714     open SPEC, "<$mkspec" or return $varvalue;
1715     while (<SPEC>) {
1716         if ($_ =~ /\s*include\((.+)\)/) {
1717             # open the included mkspec
1718             my $oldcwd = getcwd();
1719             (my $volume, my $directories, my $file) = File::Spec->splitpath($mkspec);
1720             my $newcwd = "$volume$directories";
1721             chdir $newcwd if $newcwd;
1722             $varvalue = retrieveQMakespecVar($1, $varname);
1723             chdir $oldcwd;
1724         } elsif ($_ =~ /$varname\s*=\s*([^\s]+)/) {
1725             $varvalue = $1;
1726             last;
1727         }
1728     }
1729     close SPEC;
1730     return $varvalue;
1731 }
1732
1733 sub qtMakeCommand($)
1734 {
1735     my ($qmakebin) = @_;
1736     chomp(my $mkspec = `$qmakebin -query QMAKE_MKSPECS`);
1737     $mkspec .= "/default";
1738     my $compiler = retrieveQMakespecVar("$mkspec/qmake.conf", "QMAKE_CC");
1739
1740     #print "default spec: " . $mkspec . "\n";
1741     #print "compiler found: " . $compiler . "\n";
1742
1743     if ($compiler && $compiler eq "cl") {
1744         return "nmake";
1745     }
1746
1747     return "make";
1748 }
1749
1750 sub autotoolsFlag($$)
1751 {
1752     my ($flag, $feature) = @_;
1753     my $prefix = $flag ? "--enable" : "--disable";
1754
1755     return $prefix . '-' . $feature;
1756 }
1757
1758 sub getMD5HashForFile($)
1759 {
1760     my $file = shift;
1761
1762     open(FILE_CONTENTS, $file);
1763
1764     # Read the whole file.
1765     my $contents = "";
1766     while (<FILE_CONTENTS>) {
1767         $contents .= $_;
1768     }
1769
1770     close(FILE_CONTENTS);
1771
1772     return md5_hex($contents);
1773 }
1774
1775 sub runAutogenForAutotoolsProjectIfNecessary($@)
1776 {
1777     my ($dir, $prefix, $sourceDir, $project, @buildArgs) = @_;
1778
1779     my $argumentsFile = "previous-autogen-arguments.txt";
1780     if (-e "GNUmakefile") {
1781         # Just assume that build-jsc will never be used to reconfigure JSC. Later
1782         # we can go back and make this more complicated if the demand is there.
1783         if ($project ne "WebKit") {
1784             return;
1785         }
1786
1787         # We only run autogen.sh again if the arguments passed have changed.
1788         if (!mustReRunAutogen($sourceDir, $argumentsFile, @buildArgs)) {
1789             return;
1790         }
1791     }
1792
1793     print "Calling autogen.sh in " . $dir . "\n\n";
1794     print "Installation prefix directory: $prefix\n" if(defined($prefix));
1795
1796     # Save md5sum for jhbuild-related files.
1797     foreach my $file (qw(jhbuildrc jhbuild.modules)) {
1798         my $path = join('/', $sourceDir, 'Tools', 'gtk', $file);
1799         open(SUM, ">$file.md5sum");
1800         print SUM getMD5HashForFile($path);
1801         close(SUM);
1802     }
1803
1804     # Only for WebKit, write the autogen.sh arguments to a file so that we can detect
1805     # when they change and automatically re-run it.
1806     if ($project eq 'WebKit') {
1807         open(AUTOTOOLS_ARGUMENTS, ">$argumentsFile");
1808         print AUTOTOOLS_ARGUMENTS join(" ", @buildArgs);
1809         close(AUTOTOOLS_ARGUMENTS);
1810     }
1811
1812     # Make the path relative since it will appear in all -I compiler flags.
1813     # Long argument lists cause bizarre slowdowns in libtool.
1814     my $relSourceDir = File::Spec->abs2rel($sourceDir) || ".";
1815
1816     # Compiler options to keep floating point values consistent
1817     # between 32-bit and 64-bit architectures. The options are also
1818     # used on Chromium build.
1819     determineArchitecture();
1820     if ($architecture ne "x86_64" && !isARM()) {
1821         $ENV{'CXXFLAGS'} = "-march=pentium4 -msse2 -mfpmath=sse";
1822     }
1823
1824     # Prefix the command with jhbuild run.
1825     unshift(@buildArgs, "$relSourceDir/autogen.sh");
1826     unshift(@buildArgs, "$sourceDir/Tools/gtk/run-with-jhbuild");
1827     if (system(@buildArgs) ne 0) {
1828         die "Calling autogen.sh failed!\n";
1829     }
1830 }
1831
1832 sub mustReRunAutogen($@)
1833 {
1834     my ($sourceDir, $filename, @currentArguments) = @_;
1835
1836     if (! -e $filename) {
1837         return 1;
1838     }
1839
1840     open(AUTOTOOLS_ARGUMENTS, $filename);
1841     chomp(my $previousArguments = <AUTOTOOLS_ARGUMENTS>);
1842     close(AUTOTOOLS_ARGUMENTS);
1843
1844     # We only care about the WebKit2 argument when we are building WebKit itself.
1845     # build-jsc never passes --enable-webkit2, so if we didn't do this, autogen.sh
1846     # would run for every single build on the bots, since it runs both build-webkit
1847     # and build-jsc.
1848     my $joinedCurrentArguments = join(" ", @currentArguments);
1849     if ($previousArguments ne $joinedCurrentArguments) {
1850         print "Previous autogen arguments were: $previousArguments\n\n";
1851         print "New autogen arguments are: $joinedCurrentArguments\n";
1852         return 1;
1853     }
1854
1855     # Now check jhbuild configuration for changes.
1856     foreach my $file (qw(jhbuildrc.md5sum jhbuild.modules.md5sum)) {
1857         if (! -e $file) {
1858             return 1;
1859         }
1860
1861         # Get the md5 sum of the file we're testing.
1862         $file =~ m/(.+)\.md5sum/;
1863         my $actualFile = join('/', $sourceDir, 'Tools', 'gtk', $1);
1864         my $currentSum = getMD5HashForFile($actualFile);
1865
1866         # Get our previous record.
1867         open(PREVIOUS_MD5, $file);
1868         chomp(my $previousSum = <PREVIOUS_MD5>);
1869         close(PREVIOUS_MD5);
1870
1871         if ($previousSum ne $currentSum) {
1872             return 1;
1873         }
1874     }
1875
1876     return 0;
1877 }
1878
1879 sub buildAutotoolsProject($@)
1880 {
1881     my ($project, $clean, @buildParams) = @_;
1882
1883     my $make = 'make';
1884     my $dir = productDir();
1885     my $config = passedConfiguration() || configuration();
1886     my $prefix;
1887
1888     # Use rm to clean the build directory since distclean may miss files
1889     if ($clean && -d $dir) {
1890         system "rm", "-rf", "$dir";
1891     }
1892
1893     if (! -d $dir) {
1894         File::Path::mkpath($dir) or die "Failed to create build directory " . $dir
1895     }
1896     chdir $dir or die "Failed to cd into " . $dir . "\n";
1897
1898     if ($clean) {
1899         return 0;
1900     }
1901
1902     # We might need to update jhbuild dependencies.
1903     if (checkForArgumentAndRemoveFromArrayRef("--update-gtk", \@buildParams)) {
1904         system("perl", "$sourceDir/Tools/Scripts/update-webkitgtk-libs") == 0 or die $!;
1905     }
1906
1907     my @buildArgs = ();
1908     my $makeArgs = $ENV{"WebKitMakeArguments"} || "";
1909     for my $i (0 .. $#buildParams) {
1910         my $opt = $buildParams[$i];
1911         if ($opt =~ /^--makeargs=(.*)/i ) {
1912             $makeArgs = $makeArgs . " " . $1;
1913         } elsif ($opt =~ /^--prefix=(.*)/i ) {
1914             $prefix = $1;
1915         } else {
1916             push @buildArgs, $opt;
1917         }
1918     }
1919
1920     # Automatically determine the number of CPUs for make only
1921     # if make arguments haven't already been specified.
1922     if ($makeArgs eq "") {
1923         $makeArgs = "-j" . numberOfCPUs();
1924     }
1925
1926     # WebKit is the default target, so we don't need to specify anything.
1927     if ($project eq "JavaScriptCore") {
1928         $makeArgs .= " jsc";
1929     }
1930
1931     $prefix = $ENV{"WebKitInstallationPrefix"} if !defined($prefix);
1932     push @buildArgs, "--prefix=" . $prefix if defined($prefix);
1933
1934     # Check if configuration is Debug.
1935     my $debug = $config =~ m/debug/i;
1936     if ($debug) {
1937         push @buildArgs, "--enable-debug";
1938     } else {
1939         push @buildArgs, "--disable-debug";
1940     }
1941
1942     # If GNUmakefile exists, don't run autogen.sh unless its arguments
1943     # have changed. The makefile should be smart enough to track autotools
1944     # dependencies and re-run autogen.sh when build files change.
1945     runAutogenForAutotoolsProjectIfNecessary($dir, $prefix, $sourceDir, $project, @buildArgs);
1946
1947     my $gtkScriptsPath = "$sourceDir/Tools/gtk";
1948     my $runWithJhbuild = "$gtkScriptsPath/run-with-jhbuild";
1949     if (system("$runWithJhbuild $make $makeArgs") ne 0) {
1950         die "\nFailed to build WebKit using '$make'!\n";
1951     }
1952
1953     chdir ".." or die;
1954
1955     if ($project eq 'WebKit' && !isCrossCompilation()) {
1956         my @docGenerationOptions = ($runWithJhbuild, "$gtkScriptsPath/generate-gtkdoc", "--skip-html");
1957         if ($debug) {
1958             push(@docGenerationOptions, "--debug");
1959         }
1960
1961         if (system(@docGenerationOptions)) {
1962             die "\n gtkdoc did not build without warnings\n";
1963         }
1964     }
1965
1966     return 0;
1967 }
1968
1969 sub generateBuildSystemFromCMakeProject
1970 {
1971     my ($port, $prefixPath, @cmakeArgs, $additionalCMakeArgs) = @_;
1972     my $config = configuration();
1973     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1974     File::Path::mkpath($buildPath) unless -d $buildPath;
1975     my $originalWorkingDirectory = getcwd();
1976     chdir($buildPath) or die;
1977
1978     my @args;
1979     push @args, "-DPORT=\"$port\"";
1980     push @args, "-DCMAKE_INSTALL_PREFIX=\"$prefixPath\"" if $prefixPath;
1981     if ($config =~ /release/i) {
1982         push @args, "-DCMAKE_BUILD_TYPE=Release";
1983     } elsif ($config =~ /debug/i) {
1984         push @args, "-DCMAKE_BUILD_TYPE=Debug";
1985     }
1986     push @args, @cmakeArgs if @cmakeArgs;
1987     push @args, $additionalCMakeArgs if $additionalCMakeArgs;
1988
1989     push @args, '"' . sourceDir() . '"';
1990
1991     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
1992     # parsed for shell metacharacters.
1993     my $returnCode = system("cmake @args");
1994
1995     chdir($originalWorkingDirectory);
1996     return $returnCode;
1997 }
1998
1999 sub buildCMakeGeneratedProject($)
2000 {
2001     my ($makeArgs) = @_;
2002     my $config = configuration();
2003     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
2004     if (! -d $buildPath) {
2005         die "Must call generateBuildSystemFromCMakeProject() before building CMake project.";
2006     }
2007     my @args = ("--build", $buildPath, "--config", $config);
2008     push @args, ("--", $makeArgs) if $makeArgs;
2009
2010     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
2011     # parsed for shell metacharacters. In particular, $makeArgs may contain such metacharacters.
2012     return system("cmake @args");
2013 }
2014
2015 sub cleanCMakeGeneratedProject()
2016 {
2017     my $config = configuration();
2018     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
2019     if (-d $buildPath) {
2020         return system("cmake", "--build", $buildPath, "--config", $config, "--target", "clean");
2021     }
2022     return 0;
2023 }
2024
2025 sub buildCMakeProjectOrExit($$$$@)
2026 {
2027     my ($clean, $port, $prefixPath, $makeArgs, @cmakeArgs) = @_;
2028     my $returnCode;
2029
2030     exit(exitStatus(cleanCMakeGeneratedProject())) if $clean;
2031
2032     $returnCode = exitStatus(generateBuildSystemFromCMakeProject($port, $prefixPath, @cmakeArgs));
2033     exit($returnCode) if $returnCode;
2034     $returnCode = exitStatus(buildCMakeGeneratedProject($makeArgs));
2035     exit($returnCode) if $returnCode;
2036 }
2037
2038 sub cmakeBasedPortArguments()
2039 {
2040     return blackberryCMakeArguments() if isBlackBerry();
2041     return ('-DCMAKE_WINCE_SDK="STANDARDSDK_500 (ARMV4I)"') if isWinCE();
2042     return ();
2043 }
2044
2045 sub cmakeBasedPortName()
2046 {
2047     return "BlackBerry" if isBlackBerry();
2048     return "Efl" if isEfl();
2049     return "WinCE" if isWinCE();
2050     return "";
2051 }
2052
2053 sub promptUser
2054 {
2055     my ($prompt, $default) = @_;
2056     my $defaultValue = $default ? "[$default]" : "";
2057     print "$prompt $defaultValue: ";
2058     chomp(my $input = <STDIN>);
2059     return $input ? $input : $default;
2060 }
2061
2062 sub buildQMakeProjects
2063 {
2064     my ($projects, $clean, @buildParams) = @_;
2065
2066     my @buildArgs = ();
2067
2068     my $makeargs = "";
2069     my $installHeaders;
2070     my $installLibs;
2071     for my $i (0 .. $#buildParams) {
2072         my $opt = $buildParams[$i];
2073         if ($opt =~ /^--qmake=(.*)/i ) {
2074             $qmakebin = $1;
2075         } elsif ($opt =~ /^--qmakearg=(.*)/i ) {
2076             push @buildArgs, $1;
2077         } elsif ($opt =~ /^--makeargs=(.*)/i ) {
2078             $makeargs = $1;
2079         } elsif ($opt =~ /^--install-headers=(.*)/i ) {
2080             $installHeaders = $1;
2081         } elsif ($opt =~ /^--install-libs=(.*)/i ) {
2082             $installLibs = $1;
2083         } else {
2084             push @buildArgs, $opt;
2085         }
2086     }
2087
2088     my $qmakepath = File::Spec->catfile(sourceDir(), "Tools", "qmake");
2089     my $qmakecommand;
2090     if (isWindows()) {
2091         $qmakecommand = "(set QMAKEPATH=$qmakepath) && $qmakebin";
2092     } else {
2093         $qmakecommand = "QMAKEPATH=$qmakepath $qmakebin";
2094     }
2095
2096     my $make = qtMakeCommand($qmakebin);
2097     my $config = configuration();
2098     push @buildArgs, "INSTALL_HEADERS=" . $installHeaders if defined($installHeaders);
2099     push @buildArgs, "INSTALL_LIBS=" . $installLibs if defined($installLibs);
2100
2101     my $passedConfig = passedConfiguration() || "";
2102     if ($passedConfig =~ m/debug/i) {
2103         push @buildArgs, "CONFIG-=release";
2104         push @buildArgs, "CONFIG+=debug";
2105     } elsif ($passedConfig =~ m/release/i) {
2106         push @buildArgs, "CONFIG+=release";
2107         push @buildArgs, "CONFIG-=debug";
2108     }
2109     push @buildArgs, "CONFIG-=debug_and_release" if ($passedConfig && isDarwin());
2110
2111     my $originalCwd = getcwd();
2112     my $dir = File::Spec->canonpath(productDir());
2113     File::Path::mkpath($dir);
2114     chdir $dir or die "Failed to cd into " . $dir . "\n";
2115
2116     my %defines = qtFeatureDefaults(\@buildArgs);
2117
2118     my $svnRevision = currentSVNRevision();
2119
2120     my $buildHint = "";
2121
2122     my $pathToDefinesCache = File::Spec->catfile($dir, ".webkit.config");
2123     my $pathToOldDefinesFile = File::Spec->catfile($dir, "defaults.txt");
2124
2125     # Ease transition to new build layout
2126     if (-e $pathToOldDefinesFile) {
2127         print "Old build layout detected";
2128         $buildHint = "clean";
2129     } elsif (-e $pathToDefinesCache && open(DEFAULTS, $pathToDefinesCache)) {
2130         my %previousDefines;
2131         while (<DEFAULTS>) {
2132             if ($_ =~ m/(\S+)=(\S+)/gi) {
2133                 $previousDefines{$1} = $2;
2134             }
2135         }
2136         close (DEFAULTS);
2137
2138         $previousDefines{"SVN_REVISION"} = "unknown" if not exists $previousDefines{"SVN_REVISION"};
2139
2140         if ($svnRevision ne $previousDefines{"SVN_REVISION"}) {
2141             print "Last built revision was " . $previousDefines{"SVN_REVISION"} .
2142                 ", now at revision $svnRevision. Full incremental build needed.\n";
2143
2144             $buildHint = "incremental";
2145         }
2146
2147         # Don't confuse the should-we-clean heuristics below
2148         delete($previousDefines{"SVN_REVISION"});
2149
2150         my @uniqueDefineNames = keys %{ +{ map { $_, 1 } (keys %defines, keys %previousDefines) } };
2151         foreach my $define (@uniqueDefineNames) {
2152             if (! exists $previousDefines{$define}) {
2153                 print "Feature $define added";
2154                 $buildHint = "clean";
2155                 last;
2156             }
2157
2158             if (! exists $defines{$define}) {
2159                 print "Feature $define removed";
2160                 $buildHint = "clean";
2161                 last;
2162             }
2163
2164             if ($defines{$define} != $previousDefines{$define}) {
2165                 print "Feature $define changed ($previousDefines{$define} -> $defines{$define})";
2166                 $buildHint = "clean";
2167                 last;
2168             }
2169         }
2170     } else {
2171         # Missing build cache suggests we had a broken build after a clean,
2172         # so we assume we have to do an incremental build just in case.
2173         $buildHint = "incremental";
2174     }
2175
2176     if ($buildHint eq "clean") {
2177         print ", clean build needed!\n";
2178         # FIXME: This STDIN/STDOUT check does not work on the bots. Disable until it does.
2179         # if (! -t STDIN || ( &promptUser("Would you like to clean the build directory?", "yes") eq "yes")) {
2180             chdir $originalCwd;
2181             File::Path::rmtree($dir);
2182             File::Path::mkpath($dir);
2183             chdir $dir or die "Failed to cd into " . $dir . "\n";
2184
2185             # After removing WebKitBuild directory, we have to call qtFeatureDefaults()
2186             # to run config tests and generate the removed Tools/qmake/.qmake.cache again.
2187             qtFeatureDefaults(\@buildArgs);
2188         #}
2189     }
2190
2191     # Save config up-front so we can detect changes to the build config even
2192     # when the user re-configures after aborting the build.
2193     open(DEFAULTS, ">$pathToDefinesCache");
2194     print DEFAULTS "# These defines were set when building WebKit last time\n";
2195     foreach my $key (sort keys %defines) {
2196         print DEFAULTS "$key=$defines{$key}\n";
2197     }
2198     close(DEFAULTS);
2199
2200     my $result = 0;
2201
2202     my $makefile = File::Spec->catfile($dir, "Makefile");
2203     if (! -e $makefile) {
2204         push @buildArgs, "-after OVERRIDE_SUBDIRS=\"@{$projects}\"" if @{$projects};
2205
2206         push @buildArgs, File::Spec->catfile(sourceDir(), "WebKit.pro");
2207         my $command = "$qmakecommand @buildArgs";
2208         print "Calling '$command' in " . $dir . "\n\n";
2209         print "Installation headers directory: $installHeaders\n" if(defined($installHeaders));
2210         print "Installation libraries directory: $installLibs\n" if(defined($installLibs));
2211
2212         $result = system "$command";
2213         if ($result ne 0) {
2214            die "Failed to setup build environment using $qmakebin!\n";
2215         }
2216     }
2217
2218     my $command = "$make $makeargs";
2219     $command =~ s/\s+$//;
2220
2221     if ($clean) {
2222         $command = "$command distclean";
2223     } elsif ($buildHint eq "incremental") {
2224         $command = "$command incremental";
2225     }
2226
2227     print "Calling '$command' in " . $dir . "\n\n";
2228     $result = system $command;
2229
2230     chdir ".." or die;
2231
2232     if ($result eq 0) {
2233         # Now that the build completed successfully we can save the SVN revision
2234         open(DEFAULTS, ">>$pathToDefinesCache");
2235         print DEFAULTS "SVN_REVISION=$svnRevision\n";
2236         close(DEFAULTS);
2237     } elsif ($buildHint eq "" && exitStatus($result)) {
2238         my $exitCode = exitStatus($result);
2239         my $failMessage = <<EOF;
2240
2241 ===== BUILD FAILED ======
2242
2243 The build failed with exit code $exitCode. This may have been because you
2244
2245   - added an #include to a source/header
2246   - added a Q_OBJECT macro to a class
2247   - added a new resource to a qrc file
2248
2249 as dependencies are not automatically re-computed for local developer builds.
2250 You may try computing dependencies manually by running 'make qmake' in:
2251
2252   $dir
2253
2254 or passing --makeargs="qmake" to build-webkit.
2255
2256 =========================
2257
2258 EOF
2259         print "$failMessage";
2260     }
2261
2262     return $result;
2263 }
2264
2265 sub buildGtkProject
2266 {
2267     my ($project, $clean, @buildArgs) = @_;
2268
2269     if ($project ne "WebKit" and $project ne "JavaScriptCore") {
2270         die "Unsupported project: $project. Supported projects: WebKit, JavaScriptCore\n";
2271     }
2272
2273     return buildAutotoolsProject($project, $clean, @buildArgs);
2274 }
2275
2276 sub buildChromiumMakefile($$@)
2277 {
2278     my ($target, $clean, @options) = @_;
2279     if ($clean) {
2280         return system qw(rm -rf out);
2281     }
2282     my $config = configuration();
2283     my $numCpus = numberOfCPUs();
2284     my $makeArgs;
2285     for (@options) {
2286         $makeArgs = $1 if /^--makeargs=(.*)/i;
2287     }
2288     $makeArgs = "-j$numCpus" if not $makeArgs;
2289     my $command = "";
2290
2291     # Building the WebKit Chromium port for Android requires us to cross-
2292     # compile, which will be set up by Chromium's envsetup.sh. The script itself
2293     # will verify that the installed NDK is indeed available.
2294     if (isChromiumAndroid()) {
2295         $command .= "bash -c \"source " . sourceDir() . "/Source/WebKit/chromium/build/android/envsetup.sh && ";
2296         $ENV{ANDROID_NDK_ROOT} = sourceDir() . "/Source/WebKit/chromium/android-ndk-r7";
2297         $ENV{WEBKIT_ANDROID_BUILD} = 1;
2298     }
2299
2300     $command .= "make -fMakefile.chromium $makeArgs BUILDTYPE=$config $target";
2301     $command .= "\"" if isChromiumAndroid();
2302
2303     print "$command\n";
2304     return system $command;
2305 }
2306
2307 sub buildChromiumNinja($$@)
2308 {
2309     # rm -rf out requires rerunning gyp, so don't support --clean for now.
2310     my ($target, @options) = @_;
2311     my $config = configuration();
2312     my $makeArgs;
2313     for (@options) {
2314         $makeArgs = $1 if /^--makeargs=(.*)/i;
2315     }
2316     my $command = "";
2317
2318     $command .= "ninja -C out/$config $target $makeArgs";
2319
2320     print "$command\n";
2321     return system $command;
2322 }
2323
2324 sub buildChromiumVisualStudioProject($$)
2325 {
2326     my ($projectPath, $clean) = @_;
2327
2328     my $config = configuration();
2329     my $action = "/build";
2330     $action = "/clean" if $clean;
2331
2332     # Find Visual Studio installation.
2333     my $vsInstallDir;
2334     my $programFilesPath = $ENV{'PROGRAMFILES'} || "C:\\Program Files";
2335     if ($ENV{'VSINSTALLDIR'}) {
2336         $vsInstallDir = $ENV{'VSINSTALLDIR'};
2337     } else {
2338         $vsInstallDir = "$programFilesPath/Microsoft Visual Studio 8";
2339     }
2340     $vsInstallDir = `cygpath "$vsInstallDir"` if isCygwin();
2341     chomp $vsInstallDir;
2342     $vcBuildPath = "$vsInstallDir/Common7/IDE/devenv.com";
2343     if (! -e $vcBuildPath) {
2344         # Visual Studio not found, try VC++ Express
2345         $vcBuildPath = "$vsInstallDir/Common7/IDE/VCExpress.exe";
2346         if (! -e $vcBuildPath) {
2347             print "*************************************************************\n";
2348             print "Cannot find '$vcBuildPath'\n";
2349             print "Please execute the file 'vcvars32.bat' from\n";
2350             print "'$programFilesPath\\Microsoft Visual Studio 8\\VC\\bin\\'\n";
2351             print "to setup the necessary environment variables.\n";
2352             print "*************************************************************\n";
2353             die;
2354         }
2355     }
2356
2357     # Create command line and execute it.
2358     my @command = ($vcBuildPath, $projectPath, $action, $config);
2359     print "Building results into: ", baseProductDir(), "\n";
2360     print join(" ", @command), "\n";
2361     return system @command;
2362 }
2363
2364 sub buildChromium($@)
2365 {
2366     my ($clean, @options) = @_;
2367
2368     # We might need to update DEPS or re-run GYP if things have changed.
2369     if (checkForArgumentAndRemoveFromArrayRef("--update-chromium", \@options)) {
2370         system("perl", "Tools/Scripts/update-webkit-chromium", "--force") == 0 or die $!;
2371     }
2372
2373     my $result = 1;
2374     if (isDarwin() && !isChromiumAndroid() && !isChromiumMacMake() && !isChromiumNinja()) {
2375         # Mac build - builds the root xcode project.
2376         $result = buildXCodeProject("Source/WebKit/chromium/All", $clean, "-configuration", configuration(), @options);
2377     } elsif (isCygwin() || isWindows()) {
2378         # Windows build - builds the root visual studio solution.
2379         $result = buildChromiumVisualStudioProject("Source/WebKit/chromium/All.sln", $clean);
2380     } elsif (isChromiumNinja()) {
2381         $result = buildChromiumNinja("all", $clean, @options);
2382     } elsif (isLinux() || isChromiumAndroid() || isChromiumMacMake()) {
2383         # Linux build - build using make.
2384         $result = buildChromiumMakefile("all", $clean, @options);
2385     } else {
2386         print STDERR "This platform is not supported by chromium.\n";
2387     }
2388     return $result;
2389 }
2390
2391 sub appleApplicationSupportPath
2392 {
2393     open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir";
2394     my $path = <INSTALL_DIR>;
2395     $path =~ s/[\r\n\x00].*//;
2396     close INSTALL_DIR;
2397
2398     my $unixPath = `cygpath -u '$path'`;
2399     chomp $unixPath;
2400     return $unixPath;
2401 }
2402
2403 sub setPathForRunningWebKitApp
2404 {
2405     my ($env) = @_;
2406
2407     if (isAppleWinWebKit()) {
2408         $env->{PATH} = join(':', productDir(), dirname(installedSafariPath()), appleApplicationSupportPath(), $env->{PATH} || "");
2409     } elsif (isQt()) {
2410         my $qtLibs = `$qmakebin -query QT_INSTALL_LIBS`;
2411         $qtLibs =~ s/[\n|\r]$//g;
2412         $env->{PATH} = join(';', $qtLibs, productDir() . "/lib", $env->{PATH} || "");
2413     }
2414 }
2415
2416 sub printHelpAndExitForRunAndDebugWebKitAppIfNeeded()
2417 {
2418     return unless checkForArgumentAndRemoveFromARGV("--help");
2419     print STDERR <<EOF;
2420 Usage: @{[basename($0)]} [options] [args ...]
2421   --help                Show this help message
2422   --no-saved-state      Disable application resume for the session on Mac OS 10.7
2423 EOF
2424     exit(1);
2425 }
2426
2427 sub argumentsForRunAndDebugMacWebKitApp()
2428 {
2429     my @args = @ARGV;
2430     push @args, ("-ApplePersistenceIgnoreState", "YES") if isLion() && checkForArgumentAndRemoveFromArrayRef("--no-saved-state", \@args);
2431     return @args;
2432 }
2433
2434 sub runMacWebKitApp($;$)
2435 {
2436     my ($appPath, $useOpenCommand) = @_;
2437     my $productDir = productDir();
2438     print "Starting @{[basename($appPath)]} with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2439     $ENV{DYLD_FRAMEWORK_PATH} = $productDir;
2440     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
2441     if (defined($useOpenCommand) && $useOpenCommand == USE_OPEN_COMMAND) {
2442         return system("open", "-W", "-a", $appPath, "--args", argumentsForRunAndDebugMacWebKitApp());
2443     }
2444     if (architecture()) {
2445         return system "arch", "-" . architecture(), $appPath, argumentsForRunAndDebugMacWebKitApp();
2446     }
2447     return system { $appPath } $appPath, argumentsForRunAndDebugMacWebKitApp();
2448 }
2449
2450 sub execMacWebKitAppForDebugging($)
2451 {
2452     my ($appPath) = @_;
2453
2454     my $gdbPath = "/usr/bin/gdb";
2455     die "Can't find gdb executable. Is gdb installed?\n" unless -x $gdbPath;
2456
2457     my $productDir = productDir();
2458     $ENV{DYLD_FRAMEWORK_PATH} = $productDir;
2459     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
2460     my @architectureFlags = ("-arch", architecture());
2461     if (!shouldTargetWebProcess()) {
2462         print "Starting @{[basename($appPath)]} under gdb with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2463         exec { $gdbPath } $gdbPath, @architectureFlags, "--args", $appPath, argumentsForRunAndDebugMacWebKitApp() or die;
2464     } else {
2465         my $webProcessShimPath = File::Spec->catfile($productDir, "WebProcessShim.dylib");
2466         my $webProcessPath = File::Spec->catdir($productDir, "WebProcess.app");
2467         my $webKit2ExecutablePath = File::Spec->catfile($productDir, "WebKit2.framework", "WebKit2");
2468         $ENV{DYLD_INSERT_LIBRARIES} = $webProcessShimPath;
2469
2470         print "Starting WebProcess under gdb with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2471         exec { $gdbPath } $gdbPath, @architectureFlags, "--args", $webProcessPath, $webKit2ExecutablePath, "-type", "webprocess", "-client-executable", $appPath or die;
2472     }
2473 }
2474
2475 sub debugSafari
2476 {
2477     if (isAppleMacWebKit()) {
2478         checkFrameworks();
2479         execMacWebKitAppForDebugging(safariPath());
2480     }
2481
2482     if (isAppleWinWebKit()) {
2483         setupCygwinEnv();
2484         my $productDir = productDir();
2485         chomp($ENV{WEBKITNIGHTLY} = `cygpath -wa "$productDir"`);
2486         my $safariPath = safariPath();
2487         chomp($safariPath = `cygpath -wa "$safariPath"`);
2488         return system { $vcBuildPath } $vcBuildPath, "/debugexe", "\"$safariPath\"", @ARGV;
2489     }
2490
2491     return 1; # Unsupported platform; can't debug Safari on this platform.
2492 }
2493
2494 sub runSafari
2495 {
2496
2497     if (isAppleMacWebKit()) {
2498         return runMacWebKitApp(safariPath());
2499     }
2500
2501     if (isAppleWinWebKit()) {
2502         my $result;
2503         my $productDir = productDir();
2504         my $webKitLauncherPath = File::Spec->catfile(productDir(), "WebKit.exe");
2505         return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
2506     }
2507
2508     return 1; # Unsupported platform; can't run Safari on this platform.
2509 }
2510
2511 sub runMiniBrowser
2512 {
2513     if (isAppleMacWebKit()) {
2514         return runMacWebKitApp(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2515     }
2516
2517     return 1;
2518 }
2519
2520 sub debugMiniBrowser
2521 {
2522     if (isAppleMacWebKit()) {
2523         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2524     }
2525     
2526     return 1;
2527 }
2528
2529 sub runWebKitTestRunner
2530 {
2531     if (isAppleMacWebKit()) {
2532         return runMacWebKitApp(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2533     } elsif (isGtk()) {
2534         my $productDir = productDir();
2535         my $injectedBundlePath = "$productDir/Libraries/.libs/libTestRunnerInjectedBundle";
2536         print "Starting WebKitTestRunner with TEST_RUNNER_INJECTED_BUNDLE_FILENAME set to point to $injectedBundlePath.\n";
2537         $ENV{TEST_RUNNER_INJECTED_BUNDLE_FILENAME} = $injectedBundlePath;
2538         my @args = ("$productDir/Programs/WebKitTestRunner", @ARGV);
2539         return system {$args[0] } @args;
2540     }
2541
2542     return 1;
2543 }
2544
2545 sub debugWebKitTestRunner
2546 {
2547     if (isAppleMacWebKit()) {
2548         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2549     }
2550
2551     return 1;
2552 }
2553
2554 sub runTestWebKitAPI
2555 {
2556     if (isAppleMacWebKit()) {
2557         return runMacWebKitApp(File::Spec->catfile(productDir(), "TestWebKitAPI"));
2558     }
2559
2560     return 1;
2561 }
2562
2563 sub readRegistryString
2564 {
2565     my ($valueName) = @_;
2566     chomp(my $string = `regtool --wow32 get "$valueName"`);
2567     return $string;
2568 }
2569
2570 sub writeRegistryString
2571 {
2572     my ($valueName, $string) = @_;
2573
2574     my $error = system "regtool", "--wow32", "set", "-s", $valueName, $string;
2575
2576     # On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still
2577     # return a successful exit code. So we double-check here that the value we tried to write to the
2578     # registry was really written.
2579     return !$error && readRegistryString($valueName) eq $string;
2580 }
2581
2582 1;