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