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