06811695c95cb1283d666d03acffbca5d330eeee
[WebKit-https.git] / Tools / Scripts / webkitdirs.pm
1 # Copyright (C) 2005-2007, 2010-2014 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 # Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # 1.  Redistributions of source code must retain the above copyright
11 #     notice, this list of conditions and the following disclaimer. 
12 # 2.  Redistributions in binary form must reproduce the above copyright
13 #     notice, this list of conditions and the following disclaimer in the
14 #     documentation and/or other materials provided with the distribution. 
15 # 3.  Neither the name of Apple Inc. ("Apple") nor the names of
16 #     its contributors may be used to endorse or promote products derived
17 #     from this software without specific prior written permission. 
18 #
19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 # Module to share code to get to WebKit directories.
31
32 use strict;
33 use version;
34 use warnings;
35 use Config;
36 use Cwd qw(realpath);
37 use Digest::MD5 qw(md5_hex);
38 use FindBin;
39 use File::Basename;
40 use File::Find;
41 use File::Path qw(make_path mkpath rmtree);
42 use File::Spec;
43 use File::stat;
44 use List::Util;
45 use POSIX;
46 use Time::HiRes qw(usleep);
47 use VCSUtils;
48
49 BEGIN {
50    use Exporter   ();
51    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
52    $VERSION     = 1.00;
53    @ISA         = qw(Exporter);
54    @EXPORT      = qw(
55        &XcodeCoverageSupportOptions
56        &XcodeOptionString
57        &XcodeOptionStringNoConfig
58        &XcodeOptions
59        &XcodeStaticAnalyzerOption
60        &appDisplayNameFromBundle
61        &baseProductDir
62        &chdirWebKit
63        &checkFrameworks
64        &cmakeBasedPortArguments
65        &cmakeBasedPortName
66        &currentSVNRevision
67        &debugSafari
68        &findOrCreateSimulatorForIOSDevice
69        &iosSimulatorDeviceByName
70        &nmPath
71        &passedConfiguration
72        &printHelpAndExitForRunAndDebugWebKitAppIfNeeded
73        &productDir
74        &quitIOSSimulator
75        &relaunchIOSSimulator
76        &runIOSWebKitApp
77        &runMacWebKitApp
78        &safariPath
79        &setConfiguration
80        &setupMacWebKitEnvironment
81        &sharedCommandLineOptions
82        &sharedCommandLineOptionsUsage
83        USE_OPEN_COMMAND
84    );
85    %EXPORT_TAGS = ( );
86    @EXPORT_OK   = ();
87 }
88
89 use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp().
90 use constant INCLUDE_OPTIONS_FOR_DEBUGGING => 1;
91 use constant SIMULATOR_DEVICE_STATE_SHUTDOWN => "1";
92 use constant SIMULATOR_DEVICE_STATE_BOOTED => "3";
93
94 our @EXPORT_OK;
95
96 my $architecture;
97 my $asanIsEnabled;
98 my $numberOfCPUs;
99 my $maxCPULoad;
100 my $baseProductDir;
101 my @baseProductDirOption;
102 my $configuration;
103 my $xcodeSDK;
104 my $configurationForVisualStudio;
105 my $configurationProductDir;
106 my $sourceDir;
107 my $currentSVNRevision;
108 my $debugger;
109 my $didLoadIPhoneSimulatorNotification;
110 my $nmPath;
111 my $osXVersion;
112 my $generateDsym;
113 my $isGtk;
114 my $isWinCairo;
115 my $isWin64;
116 my $isEfl;
117 my $isInspectorFrontend;
118 my $isWK2;
119 my $shouldTargetWebProcess;
120 my $shouldUseXPCServiceForWebProcess;
121 my $shouldUseGuardMalloc;
122 my $xcodeVersion;
123
124 # Variables for Win32 support
125 my $programFilesPath;
126 my $vcBuildPath;
127 my $vsInstallDir;
128 my $msBuildInstallDir;
129 my $vsVersion;
130 my $windowsSourceDir;
131 my $winVersion;
132 my $willUseVCExpressWhenBuilding = 0;
133
134 # Defined in VCSUtils.
135 sub exitStatus($);
136
137 sub findMatchingArguments($$);
138 sub hasArgument($$);
139
140 sub determineSourceDir
141 {
142     return if $sourceDir;
143     $sourceDir = $FindBin::Bin;
144     $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
145
146     # walks up path checking each directory to see if it is the main WebKit project dir, 
147     # defined by containing Sources, WebCore, and WebKit
148     until ((-d "$sourceDir/Source" && -d "$sourceDir/Source/WebCore" && -d "$sourceDir/Source/WebKit") || (-d "$sourceDir/Internal" && -d "$sourceDir/OpenSource"))
149     {
150         if ($sourceDir !~ s|/[^/]+$||) {
151             die "Could not find top level webkit directory above source directory using FindBin.\n";
152         }
153     }
154
155     $sourceDir = "$sourceDir/OpenSource" if -d "$sourceDir/OpenSource";
156 }
157
158 sub currentPerlPath()
159 {
160     my $thisPerl = $^X;
161     if ($^O ne 'VMS') {
162         $thisPerl .= $Config{_exe} unless $thisPerl =~ m/$Config{_exe}$/i;
163     }
164     return $thisPerl;
165 }
166
167 # used for scripts which are stored in a non-standard location
168 sub setSourceDir($)
169 {
170     ($sourceDir) = @_;
171 }
172
173 sub determineNinjaVersion
174 {
175     chomp(my $ninjaVersion = `ninja --version`);
176     return $ninjaVersion;
177 }
178
179 sub determineXcodeVersion
180 {
181     return if defined $xcodeVersion;
182     my $xcodebuildVersionOutput = `xcodebuild -version`;
183     $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : "3.0";
184 }
185
186 sub readXcodeUserDefault($)
187 {
188     my ($unprefixedKey) = @_;
189
190     determineXcodeVersion();
191
192     my $xcodeDefaultsDomain = (eval "v$xcodeVersion" lt v4) ? "com.apple.Xcode" : "com.apple.dt.Xcode";
193     my $xcodeDefaultsPrefix = (eval "v$xcodeVersion" lt v4) ? "PBX" : "IDE";
194     my $devnull = File::Spec->devnull();
195
196     my $value = `defaults read $xcodeDefaultsDomain ${xcodeDefaultsPrefix}${unprefixedKey} 2> ${devnull}`;
197     return if $?;
198
199     chomp $value;
200     return $value;
201 }
202
203 sub determineBaseProductDir
204 {
205     return if defined $baseProductDir;
206     determineSourceDir();
207
208     my $setSharedPrecompsDir;
209     $baseProductDir = $ENV{"WEBKIT_OUTPUTDIR"};
210
211     if (!defined($baseProductDir) and isAppleMacWebKit()) {
212         # Silently remove ~/Library/Preferences/xcodebuild.plist which can
213         # cause build failure. The presence of
214         # ~/Library/Preferences/xcodebuild.plist can prevent xcodebuild from
215         # respecting global settings such as a custom build products directory
216         # (<rdar://problem/5585899>).
217         my $personalPlistFile = $ENV{HOME} . "/Library/Preferences/xcodebuild.plist";
218         if (-e $personalPlistFile) {
219             unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!";
220         }
221
222         determineXcodeVersion();
223
224         if (eval "v$xcodeVersion" ge v4) {
225             my $buildLocationStyle = join '', readXcodeUserDefault("BuildLocationStyle");
226             if ($buildLocationStyle eq "Custom") {
227                 my $buildLocationType = join '', readXcodeUserDefault("CustomBuildLocationType");
228                 # FIXME: Read CustomBuildIntermediatesPath and set OBJROOT accordingly.
229                 $baseProductDir = readXcodeUserDefault("CustomBuildProductsPath") if $buildLocationType eq "Absolute";
230             }
231
232             # DeterminedByTargets corresponds to a setting of "Legacy" in Xcode.
233             # It is the only build location style for which SHARED_PRECOMPS_DIR is not
234             # overridden when building from within Xcode.
235             $setSharedPrecompsDir = 1 if $buildLocationStyle ne "DeterminedByTargets";
236         }
237
238         if (!defined($baseProductDir)) {
239             $baseProductDir = join '', readXcodeUserDefault("ApplicationwideBuildSettings");
240             $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s;
241         }
242
243         undef $baseProductDir unless $baseProductDir =~ /^\//;
244     }
245
246     if (!defined($baseProductDir)) { # Port-specific checks failed, use default
247         $baseProductDir = "$sourceDir/WebKitBuild";
248     }
249
250     if (isGit() && isGitBranchBuild()) {
251         my $branch = gitBranch();
252         $baseProductDir = "$baseProductDir/$branch";
253     }
254
255     if (isAppleMacWebKit()) {
256         $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|;
257         $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|;
258         $baseProductDir =~ s|^~/|$ENV{HOME}/|;
259         die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/;
260         die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/;
261         @baseProductDirOption = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir");
262         push(@baseProductDirOption, "SHARED_PRECOMPS_DIR=${baseProductDir}/PrecompiledHeaders") if $setSharedPrecompsDir;
263     }
264
265     if (isCygwin()) {
266         my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`;
267         chomp $dosBuildPath;
268         $ENV{"WEBKIT_OUTPUTDIR"} = $dosBuildPath;
269         my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`;
270         chomp $unixBuildPath;
271         $baseProductDir = $unixBuildPath;
272     }
273 }
274
275 sub setBaseProductDir($)
276 {
277     ($baseProductDir) = @_;
278 }
279
280 sub determineConfiguration
281 {
282     return if defined $configuration;
283     determineBaseProductDir();
284     if (open CONFIGURATION, "$baseProductDir/Configuration") {
285         $configuration = <CONFIGURATION>;
286         close CONFIGURATION;
287     }
288     if ($configuration) {
289         chomp $configuration;
290         # compatibility for people who have old Configuration files
291         $configuration = "Release" if $configuration eq "Deployment";
292         $configuration = "Debug" if $configuration eq "Development";
293     } else {
294         $configuration = "Release";
295     }
296
297     if ($configuration && isWinCairo()) {
298         unless ($configuration =~ /_WinCairo$/) {
299             $configuration .= "_WinCairo";
300         }
301     }
302 }
303
304 sub determineArchitecture
305 {
306     return if defined $architecture;
307     # make sure $architecture is defined in all cases
308     $architecture = "";
309
310     determineBaseProductDir();
311     determineXcodeSDK();
312
313     if (isAppleMacWebKit()) {
314         if (open ARCHITECTURE, "$baseProductDir/Architecture") {
315             $architecture = <ARCHITECTURE>;
316             close ARCHITECTURE;
317         }
318         if ($architecture) {
319             chomp $architecture;
320         } else {
321             if (not defined $xcodeSDK or $xcodeSDK =~ /^(\/$|macosx)/) {
322                 my $supports64Bit = `sysctl -n hw.optional.x86_64`;
323                 chomp $supports64Bit;
324                 $architecture = 'x86_64' if $supports64Bit;
325             } elsif ($xcodeSDK =~ /^iphonesimulator/) {
326                 $architecture = 'x86_64';
327             } elsif ($xcodeSDK =~ /^iphoneos/) {
328                 $architecture = 'armv7';
329             }
330         }
331     } elsif (isEfl() || isGtk()) {
332         my $host_processor = "";
333         $host_processor = `cmake --system-information | grep CMAKE_SYSTEM_PROCESSOR`;
334         if ($host_processor =~ m/^CMAKE_SYSTEM_PROCESSOR \"([^"]+)\"/) {
335             # We have a configured build tree; use it.
336             $architecture = $1;
337             $architecture = 'x86_64' if $architecture eq 'amd64';
338         }
339     }
340
341     if (!$architecture && (isGtk() || isAppleMacWebKit() || isEfl())) {
342         # Fall back to output of `arch', if it is present.
343         $architecture = `arch`;
344         chomp $architecture;
345     }
346
347     if (!$architecture && (isGtk() || isAppleMacWebKit() || isEfl())) {
348         # Fall back to output of `uname -m', if it is present.
349         $architecture = `uname -m`;
350         chomp $architecture;
351     }
352
353     $architecture = 'x86_64' if ($architecture =~ /amd64/ && isBSD());
354 }
355
356 sub determineASanIsEnabled
357 {
358     return if defined $asanIsEnabled;
359     determineBaseProductDir();
360
361     $asanIsEnabled = 0;
362     my $asanConfigurationValue;
363
364     if (open ASAN, "$baseProductDir/ASan") {
365         $asanConfigurationValue = <ASAN>;
366         close ASAN;
367         chomp $asanConfigurationValue;
368         $asanIsEnabled = 1 if $asanConfigurationValue eq "YES";
369     }
370 }
371
372 sub determineNumberOfCPUs
373 {
374     return if defined $numberOfCPUs;
375     if (defined($ENV{NUMBER_OF_PROCESSORS})) {
376         $numberOfCPUs = $ENV{NUMBER_OF_PROCESSORS};
377     } elsif (isLinux()) {
378         # First try the nproc utility, if it exists. If we get no
379         # results fall back to just interpretting /proc directly.
380         chomp($numberOfCPUs = `nproc --all 2> /dev/null`);
381         if ($numberOfCPUs eq "") {
382             $numberOfCPUs = (grep /processor/, `cat /proc/cpuinfo`);
383         }
384     } elsif (isWindows() || isCygwin()) {
385         # Assumes cygwin
386         $numberOfCPUs = `ls /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor | wc -w`;
387     } elsif (isDarwin() || isBSD()) {
388         chomp($numberOfCPUs = `sysctl -n hw.ncpu`);
389     }
390 }
391
392 sub determineMaxCPULoad
393 {
394     return if defined $maxCPULoad;
395     if (defined($ENV{MAX_CPU_LOAD})) {
396         $maxCPULoad = $ENV{MAX_CPU_LOAD};
397     }
398 }
399
400 sub jscPath($)
401 {
402     my ($productDir) = @_;
403     my $jscName = "jsc";
404     $jscName .= "_debug"  if configuration() eq "Debug_All";
405     $jscName .= ".exe" if (isWindows() || isCygwin());
406     return "$productDir/$jscName" if -e "$productDir/$jscName";
407     return "$productDir/JavaScriptCore.framework/Resources/$jscName";
408 }
409
410 sub argumentsForConfiguration()
411 {
412     determineConfiguration();
413     determineArchitecture();
414     determineXcodeSDK();
415
416     my @args = ();
417     # FIXME: Is it necessary to pass --debug, --release, --32-bit or --64-bit?
418     # These are determined automatically from stored configuration.
419     push(@args, '--debug') if ($configuration =~ "^Debug");
420     push(@args, '--release') if ($configuration =~ "^Release");
421     push(@args, '--device') if (defined $xcodeSDK && $xcodeSDK =~ /^iphoneos/);
422     push(@args, '--ios-simulator') if (defined $xcodeSDK && $xcodeSDK =~ /^iphonesimulator/);
423     push(@args, '--32-bit') if ($architecture ne "x86_64" and !isWin64());
424     push(@args, '--64-bit') if (isWin64());
425     push(@args, '--gtk') if isGtk();
426     push(@args, '--efl') if isEfl();
427     push(@args, '--wincairo') if isWinCairo();
428     push(@args, '--inspector-frontend') if isInspectorFrontend();
429     return @args;
430 }
431
432 sub determineXcodeSDK
433 {
434     return if defined $xcodeSDK;
435     my $sdk;
436     if (checkForArgumentAndRemoveFromARGVGettingValue("--sdk", \$sdk)) {
437         $xcodeSDK = $sdk;
438     }
439     if (checkForArgumentAndRemoveFromARGV("--device")) {
440         my $hasInternalSDK = exitStatus(system("xcrun --sdk iphoneos.internal --show-sdk-version > /dev/null 2>&1")) == 0;
441         $xcodeSDK ||= $hasInternalSDK ? "iphoneos.internal" : "iphoneos";
442     }
443     if (checkForArgumentAndRemoveFromARGV("--ios-simulator")) {
444         $xcodeSDK ||= 'iphonesimulator';
445     }
446 }
447
448 sub xcodeSDK
449 {
450     determineXcodeSDK();
451     return $xcodeSDK;
452 }
453
454 sub setXcodeSDK($)
455 {
456     ($xcodeSDK) = @_;
457 }
458
459
460 sub xcodeSDKPlatformName()
461 {
462     determineXcodeSDK();
463     return "" if !defined $xcodeSDK;
464     return "iphoneos" if $xcodeSDK =~ /iphoneos/i;
465     return "iphonesimulator" if $xcodeSDK =~ /iphonesimulator/i;
466     return "macosx" if $xcodeSDK =~ /macosx/i;
467     die "Couldn't determine platform name from Xcode SDK";
468 }
469
470 sub XcodeSDKPath
471 {
472     determineXcodeSDK();
473
474     die "Can't find the SDK path because no Xcode SDK was specified" if not $xcodeSDK;
475
476     my $sdkPath = `xcrun --sdk $xcodeSDK --show-sdk-path` if $xcodeSDK;
477     die 'Failed to get SDK path from xcrun' if $?;
478     chomp $sdkPath;
479
480     return $sdkPath;
481 }
482
483 sub xcodeSDKVersion
484 {
485     determineXcodeSDK();
486
487     die "Can't find the SDK version because no Xcode SDK was specified" if !$xcodeSDK;
488
489     chomp(my $sdkVersion = `xcrun --sdk $xcodeSDK --show-sdk-version`);
490     die "Failed to get SDK version from xcrun" if exitStatus($?);
491
492     return $sdkVersion;
493 }
494
495 sub programFilesPath
496 {
497     return $programFilesPath if defined $programFilesPath;
498
499     $programFilesPath = $ENV{'PROGRAMFILES(X86)'} || $ENV{'PROGRAMFILES'} || "C:\\Program Files";
500
501     return $programFilesPath;
502 }
503
504 sub visualStudioInstallDir
505 {
506     return $vsInstallDir if defined $vsInstallDir;
507
508     if ($ENV{'VSINSTALLDIR'}) {
509         $vsInstallDir = $ENV{'VSINSTALLDIR'};
510         $vsInstallDir =~ s|[\\/]$||;
511     } else {
512         $vsInstallDir = File::Spec->catdir(programFilesPath(), "Microsoft Visual Studio 12.0");
513     }
514     chomp($vsInstallDir = `cygpath "$vsInstallDir"`) if isCygwin();
515
516     return $vsInstallDir;
517 }
518
519 sub msBuildInstallDir
520 {
521     return $msBuildInstallDir if defined $msBuildInstallDir;
522
523     $msBuildInstallDir = File::Spec->catdir(programFilesPath(), "MSBuild", "12.0", "Bin");
524     chomp($msBuildInstallDir = `cygpath "$msBuildInstallDir"`) if isCygwin();
525
526     return $msBuildInstallDir;
527 }
528
529 sub visualStudioVersion
530 {
531     return $vsVersion if defined $vsVersion;
532
533     my $installDir = visualStudioInstallDir();
534
535     $vsVersion = ($installDir =~ /Microsoft Visual Studio ([0-9]+\.[0-9]*)/) ? $1 : "12";
536
537     return $vsVersion;
538 }
539
540 sub determineConfigurationForVisualStudio
541 {
542     return if defined $configurationForVisualStudio;
543     determineConfiguration();
544     # FIXME: We should detect when Debug_All or Production has been chosen.
545     $configurationForVisualStudio = "/p:Configuration=" . $configuration;
546 }
547
548 sub usesPerConfigurationBuildDirectory
549 {
550     # [Gtk] We don't have Release/Debug configurations in straight
551     # autotool builds (non build-webkit). In this case and if
552     # WEBKIT_OUTPUTDIR exist, use that as our configuration dir. This will
553     # allows us to run run-webkit-tests without using build-webkit.
554     return ($ENV{"WEBKIT_OUTPUTDIR"} && isGtk()) || isAppleWinWebKit();
555 }
556
557 sub determineConfigurationProductDir
558 {
559     return if defined $configurationProductDir;
560     determineBaseProductDir();
561     determineConfiguration();
562     if (isAppleWinWebKit() || isWinCairo()) {
563         my $binDir = isWin64() ? "bin64" : "bin32";
564         $configurationProductDir = File::Spec->catdir($baseProductDir, $configuration, $binDir);
565     } else {
566         if (usesPerConfigurationBuildDirectory()) {
567             $configurationProductDir = "$baseProductDir";
568         } else {
569             $configurationProductDir = "$baseProductDir/$configuration";
570             $configurationProductDir .= "-" . xcodeSDKPlatformName() if isIOSWebKit();
571         }
572     }
573 }
574
575 sub setConfigurationProductDir($)
576 {
577     ($configurationProductDir) = @_;
578 }
579
580 sub determineCurrentSVNRevision
581 {
582     # We always update the current SVN revision here, and leave the caching
583     # to currentSVNRevision(), so that changes to the SVN revision while the
584     # script is running can be picked up by calling this function again.
585     determineSourceDir();
586     $currentSVNRevision = svnRevisionForDirectory($sourceDir);
587     return $currentSVNRevision;
588 }
589
590
591 sub chdirWebKit
592 {
593     determineSourceDir();
594     chdir $sourceDir or die;
595 }
596
597 sub baseProductDir
598 {
599     determineBaseProductDir();
600     return $baseProductDir;
601 }
602
603 sub sourceDir
604 {
605     determineSourceDir();
606     return $sourceDir;
607 }
608
609 sub productDir
610 {
611     determineConfigurationProductDir();
612     return $configurationProductDir;
613 }
614
615 sub jscProductDir
616 {
617     my $productDir = productDir();
618     $productDir .= "/bin" if (isEfl() || isGtk());
619
620     return $productDir;
621 }
622
623 sub configuration()
624 {
625     determineConfiguration();
626     return $configuration;
627 }
628
629 sub asanIsEnabled()
630 {
631     determineASanIsEnabled();
632     return $asanIsEnabled;
633 }
634
635 sub configurationForVisualStudio()
636 {
637     determineConfigurationForVisualStudio();
638     return $configurationForVisualStudio;
639 }
640
641 sub currentSVNRevision
642 {
643     determineCurrentSVNRevision() if not defined $currentSVNRevision;
644     return $currentSVNRevision;
645 }
646
647 sub generateDsym()
648 {
649     determineGenerateDsym();
650     return $generateDsym;
651 }
652
653 sub determineGenerateDsym()
654 {
655     return if defined($generateDsym);
656     $generateDsym = checkForArgumentAndRemoveFromARGV("--dsym");
657 }
658
659 sub argumentsForXcode()
660 {
661     my @args = ();
662     push @args, "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym" if generateDsym();
663     return @args;
664 }
665
666 sub XcodeOptions
667 {
668     determineBaseProductDir();
669     determineConfiguration();
670     determineArchitecture();
671     determineASanIsEnabled();
672     determineXcodeSDK();
673
674     my @sdkOption = ($xcodeSDK ? "SDKROOT=$xcodeSDK" : ());
675     my @architectureOption = ($architecture ? "ARCHS=$architecture" : ());
676     my @asanOption = ($asanIsEnabled ? ("-xcconfig", sourceDir() . "/Tools/asan/asan.xcconfig", "ASAN_IGNORE=" . sourceDir() . "/Tools/asan/webkit-asan-ignore.txt") : ());
677
678     return (@baseProductDirOption, "-configuration", $configuration, @architectureOption, @sdkOption, @asanOption, argumentsForXcode());
679 }
680
681 sub XcodeOptionString
682 {
683     return join " ", XcodeOptions();
684 }
685
686 sub XcodeOptionStringNoConfig
687 {
688     return join " ", @baseProductDirOption;
689 }
690
691 sub XcodeCoverageSupportOptions()
692 {
693     my @coverageSupportOptions = ();
694     push @coverageSupportOptions, "GCC_GENERATE_TEST_COVERAGE_FILES=YES";
695     push @coverageSupportOptions, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES";
696     return @coverageSupportOptions;
697 }
698
699 sub XcodeStaticAnalyzerOption()
700 {
701     return "RUN_CLANG_STATIC_ANALYZER=YES";
702 }
703
704 my $passedConfiguration;
705 my $searchedForPassedConfiguration;
706 sub determinePassedConfiguration
707 {
708     return if $searchedForPassedConfiguration;
709     $searchedForPassedConfiguration = 1;
710     $passedConfiguration = undef;
711
712     if (checkForArgumentAndRemoveFromARGV("--debug")) {
713         $passedConfiguration = "Debug";
714     } elsif(checkForArgumentAndRemoveFromARGV("--release")) {
715         $passedConfiguration = "Release";
716     } elsif (checkForArgumentAndRemoveFromARGV("--profile") || checkForArgumentAndRemoveFromARGV("--profiling")) {
717         $passedConfiguration = "Profiling";
718     }
719
720     $passedConfiguration .= "_WinCairo" if (defined($passedConfiguration) && isWinCairo() && isCygwin());
721 }
722
723 sub passedConfiguration
724 {
725     determinePassedConfiguration();
726     return $passedConfiguration;
727 }
728
729 sub setConfiguration
730 {
731     setArchitecture();
732
733     if (my $config = shift @_) {
734         $configuration = $config;
735         return;
736     }
737
738     determinePassedConfiguration();
739     $configuration = $passedConfiguration if $passedConfiguration;
740 }
741
742
743 my $passedArchitecture;
744 my $searchedForPassedArchitecture;
745 sub determinePassedArchitecture
746 {
747     return if $searchedForPassedArchitecture;
748     $searchedForPassedArchitecture = 1;
749
750     $passedArchitecture = undef;
751     if (checkForArgumentAndRemoveFromARGV("--32-bit")) {
752         if (isAppleMacWebKit()) {
753             # PLATFORM_IOS: Don't run `arch` command inside Simulator environment
754             local %ENV = %ENV;
755             delete $ENV{DYLD_ROOT_PATH};
756             delete $ENV{DYLD_FRAMEWORK_PATH};
757
758             $passedArchitecture = `arch`;
759             chomp $passedArchitecture;
760         }
761     }
762 }
763
764 sub passedArchitecture
765 {
766     determinePassedArchitecture();
767     return $passedArchitecture;
768 }
769
770 sub architecture()
771 {
772     determineArchitecture();
773     return $architecture;
774 }
775
776 sub numberOfCPUs()
777 {
778     determineNumberOfCPUs();
779     return $numberOfCPUs;
780 }
781
782 sub maxCPULoad()
783 {
784     determineMaxCPULoad();
785     return $maxCPULoad;
786 }
787
788 sub setArchitecture
789 {
790     if (my $arch = shift @_) {
791         $architecture = $arch;
792         return;
793     }
794
795     determinePassedArchitecture();
796     $architecture = $passedArchitecture if $passedArchitecture;
797 }
798
799 sub skipSafariExecutableEntitlementChecks
800 {
801     return `defaults read /Library/Preferences/org.webkit.BuildConfiguration SkipSafariExecutableEntitlementChecks 2>/dev/null` eq "1\n";
802 }
803
804 sub executableHasEntitlements
805 {
806     my $executablePath = shift;
807     return (`codesign -d --entitlements - $executablePath 2>&1` =~ /<key>/);
808 }
809
810 sub safariPathFromSafariBundle
811 {
812     my ($safariBundle) = @_;
813
814     die "Safari path is only relevant on Apple Mac platform\n" unless isAppleMacWebKit();
815
816     my $safariPath = "$safariBundle/Contents/MacOS/Safari";
817     return $safariPath if skipSafariExecutableEntitlementChecks();
818
819     my $safariForWebKitDevelopmentPath = "$safariBundle/Contents/MacOS/SafariForWebKitDevelopment";
820     return $safariForWebKitDevelopmentPath if -f $safariForWebKitDevelopmentPath && executableHasEntitlements($safariPath);
821
822     return $safariPath;
823 }
824
825 sub installedSafariPath
826 {
827     return safariPathFromSafariBundle("/Applications/Safari.app");
828 }
829
830 # Locate Safari.
831 sub safariPath
832 {
833     die "Safari path is only relevant on Apple Mac platform\n" unless isAppleMacWebKit();
834
835     # Use WEBKIT_SAFARI environment variable if present.
836     my $safariBundle = $ENV{WEBKIT_SAFARI};
837     if (!$safariBundle) {
838         determineConfigurationProductDir();
839         # Use Safari.app in product directory if present (good for Safari development team).
840         if (-d "$configurationProductDir/Safari.app") {
841             $safariBundle = "$configurationProductDir/Safari.app";
842         }
843         if (!$safariBundle) {
844             return installedSafariPath();
845         }
846     }
847     my $safariPath = safariPathFromSafariBundle($safariBundle);
848     die "Can't find executable at $safariPath.\n" if !-x $safariPath;
849     return $safariPath;
850 }
851
852 sub builtDylibPathForName
853 {
854     my $libraryName = shift;
855     determineConfigurationProductDir();
856
857     if (isGtk()) {
858         my $extension = isDarwin() ? ".dylib" : ".so";
859         return "$configurationProductDir/lib/libwebkit2gtk-4.0" . $extension;
860     }
861     if (isEfl()) {
862         return "$configurationProductDir/lib/libewebkit2.so";
863     }
864     if (isIOSWebKit()) {
865         return "$configurationProductDir/$libraryName.framework/$libraryName";
866     }
867     if (isAppleMacWebKit()) {
868         return "$configurationProductDir/$libraryName.framework/Versions/A/$libraryName";
869     }
870     if (isAppleWinWebKit()) {
871         if ($libraryName eq "JavaScriptCore") {
872             return "$baseProductDir/lib/$libraryName.lib";
873         } else {
874             return "$baseProductDir/$libraryName.intermediate/$configuration/$libraryName.intermediate/$libraryName.lib";
875         }
876     }
877
878     die "Unsupported platform, can't determine built library locations.\nTry `build-webkit --help` for more information.\n";
879 }
880
881 # Check to see that all the frameworks are built.
882 sub checkFrameworks # FIXME: This is a poor name since only the Mac calls built WebCore a Framework.
883 {
884     return if isCygwin() || isWindows();
885     my @frameworks = ("JavaScriptCore", "WebCore");
886     push(@frameworks, "WebKit") if isAppleMacWebKit(); # FIXME: This seems wrong, all ports should have a WebKit these days.
887     for my $framework (@frameworks) {
888         my $path = builtDylibPathForName($framework);
889         die "Can't find built framework at \"$path\".\n" unless -e $path;
890     }
891 }
892
893 sub isInspectorFrontend()
894 {
895     determineIsInspectorFrontend();
896     return $isInspectorFrontend;
897 }
898
899 sub determineIsInspectorFrontend()
900 {
901     return if defined($isInspectorFrontend);
902     $isInspectorFrontend = checkForArgumentAndRemoveFromARGV("--inspector-frontend");
903 }
904
905 sub commandExists($)
906 {
907     my $command = shift;
908     my $devnull = File::Spec->devnull();
909
910     if (isAnyWindows()) {
911         return exitStatus(system("where /q $command >$devnull 2>&1")) == 0;
912     }
913     return exitStatus(system("which $command >$devnull 2>&1")) == 0;
914 }
915
916 sub checkForArgumentAndRemoveFromARGV($)
917 {
918     my $argToCheck = shift;
919     return checkForArgumentAndRemoveFromArrayRef($argToCheck, \@ARGV);
920 }
921
922 sub checkForArgumentAndRemoveFromArrayRefGettingValue($$$)
923 {
924     my ($argToCheck, $valueRef, $arrayRef) = @_;
925     my $argumentStartRegEx = qr#^$argToCheck(?:=\S|$)#;
926     my $i = 0;
927     for (; $i < @$arrayRef; ++$i) {
928         last if $arrayRef->[$i] =~ $argumentStartRegEx;
929     }
930     if ($i >= @$arrayRef) {
931         return $$valueRef = undef;
932     }
933     my ($key, $value) = split("=", $arrayRef->[$i]);
934     splice(@$arrayRef, $i, 1);
935     if (defined($value)) {
936         # e.g. --sdk=iphonesimulator
937         return $$valueRef = $value;
938     }
939     return $$valueRef = splice(@$arrayRef, $i, 1); # e.g. --sdk iphonesimulator
940 }
941
942 sub checkForArgumentAndRemoveFromARGVGettingValue($$)
943 {
944     my ($argToCheck, $valueRef) = @_;
945     return checkForArgumentAndRemoveFromArrayRefGettingValue($argToCheck, $valueRef, \@ARGV);
946 }
947
948 sub findMatchingArguments($$)
949 {
950     my ($argToCheck, $arrayRef) = @_;
951     my @matchingIndices;
952     foreach my $index (0 .. $#$arrayRef) {
953         my $opt = $$arrayRef[$index];
954         if ($opt =~ /^$argToCheck$/i ) {
955             push(@matchingIndices, $index);
956         }
957     }
958     return @matchingIndices; 
959 }
960
961 sub hasArgument($$)
962 {
963     my ($argToCheck, $arrayRef) = @_;
964     my @matchingIndices = findMatchingArguments($argToCheck, $arrayRef);
965     return scalar @matchingIndices > 0;
966 }
967
968 sub checkForArgumentAndRemoveFromArrayRef
969 {
970     my ($argToCheck, $arrayRef) = @_;
971     my @indicesToRemove = findMatchingArguments($argToCheck, $arrayRef);
972     my $removeOffset = 0;
973     foreach my $index (@indicesToRemove) {
974         splice(@$arrayRef, $index - $removeOffset++, 1);
975     }
976     return scalar @indicesToRemove > 0;
977 }
978
979 sub isWK2()
980 {
981     if (defined($isWK2)) {
982         return $isWK2;
983     }
984     if (checkForArgumentAndRemoveFromARGV("-2")) {
985         $isWK2 = 1;
986     } else {
987         $isWK2 = 0;
988     }
989     return $isWK2;
990 }
991
992 sub determineIsEfl()
993 {
994     return if defined($isEfl);
995     $isEfl = checkForArgumentAndRemoveFromARGV("--efl");
996 }
997
998 sub isEfl()
999 {
1000     determineIsEfl();
1001     return $isEfl;
1002 }
1003
1004 sub determineIsGtk()
1005 {
1006     return if defined($isGtk);
1007     $isGtk = checkForArgumentAndRemoveFromARGV("--gtk");
1008 }
1009
1010 sub isGtk()
1011 {
1012     determineIsGtk();
1013     return $isGtk;
1014 }
1015
1016 # Determine if this is debian, ubuntu, linspire, or something similar.
1017 sub isDebianBased()
1018 {
1019     return -e "/etc/debian_version";
1020 }
1021
1022 sub isFedoraBased()
1023 {
1024     return -e "/etc/fedora-release";
1025 }
1026
1027 sub isWinCairo()
1028 {
1029     determineIsWinCairo();
1030     return $isWinCairo;
1031 }
1032
1033 sub determineIsWinCairo()
1034 {
1035     return if defined($isWinCairo);
1036     $isWinCairo = checkForArgumentAndRemoveFromARGV("--wincairo");
1037 }
1038
1039 sub isWin64()
1040 {
1041     determineIsWin64();
1042     return $isWin64;
1043 }
1044
1045 sub determineIsWin64()
1046 {
1047     return if defined($isWin64);
1048     $isWin64 = checkForArgumentAndRemoveFromARGV("--64-bit");
1049 }
1050
1051 sub isCygwin()
1052 {
1053     return ($^O eq "cygwin") || 0;
1054 }
1055
1056 sub isAnyWindows()
1057 {
1058     return isWindows() || isCygwin();
1059 }
1060
1061 sub determineWinVersion()
1062 {
1063     return if $winVersion;
1064
1065     if (!isAnyWindows()) {
1066         $winVersion = -1;
1067         return;
1068     }
1069
1070     my $versionString = `cmd /c ver`;
1071     $versionString =~ /(\d)\.(\d)\.(\d+)/;
1072
1073     $winVersion = {
1074         major => $1,
1075         minor => $2,
1076         build => $3,
1077     };
1078 }
1079
1080 sub winVersion()
1081 {
1082     determineWinVersion();
1083     return $winVersion;
1084 }
1085
1086 sub isWindows7SP0()
1087 {
1088     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 1 && winVersion()->{build} == 7600;
1089 }
1090
1091 sub isWindowsVista()
1092 {
1093     return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 0;
1094 }
1095
1096 sub isWindowsXP()
1097 {
1098     return isAnyWindows() && winVersion()->{major} == 5 && winVersion()->{minor} == 1;
1099 }
1100
1101 sub isDarwin()
1102 {
1103     return ($^O eq "darwin") || 0;
1104 }
1105
1106 sub isWindows()
1107 {
1108     return ($^O eq "MSWin32") || 0;
1109 }
1110
1111 sub isLinux()
1112 {
1113     return ($^O eq "linux") || 0;
1114 }
1115
1116 sub isBSD()
1117 {
1118     return ($^O eq "freebsd") || ($^O eq "openbsd") || ($^O eq "netbsd") || 0;
1119 }
1120
1121 sub isARM()
1122 {
1123     return ($Config{archname} =~ /^arm[v\-]/) || ($Config{archname} =~ /^aarch64[v\-]/);
1124 }
1125
1126 sub isCrossCompilation()
1127 {
1128   my $compiler = "";
1129   $compiler = $ENV{'CC'} if (defined($ENV{'CC'}));
1130   if ($compiler =~ /gcc/) {
1131       my $compiler_options = `$compiler -v 2>&1`;
1132       my @host = $compiler_options =~ m/--host=(.*?)\s/;
1133       my @target = $compiler_options =~ m/--target=(.*?)\s/;
1134
1135       return ($host[0] ne "" && $target[0] ne "" && $host[0] ne $target[0]);
1136   }
1137   return 0;
1138 }
1139
1140 sub isAppleWebKit()
1141 {
1142     return isAppleMacWebKit() || isAppleWinWebKit();
1143 }
1144
1145 sub isAppleMacWebKit()
1146 {
1147     return isDarwin() && !isGtk();
1148 }
1149
1150 sub isAppleWinWebKit()
1151 {
1152     return (isCygwin() || isWindows()) && !isWinCairo() && !isGtk();
1153 }
1154
1155 sub iOSSimulatorDevicesPath
1156 {
1157     return "$ENV{HOME}/Library/Developer/CoreSimulator/Devices";
1158 }
1159
1160 sub iOSSimulatorDevices
1161 {
1162     eval "require Foundation";
1163     my $devicesPath = iOSSimulatorDevicesPath();
1164     opendir(DEVICES, $devicesPath);
1165     my @udids = grep {
1166         $_ =~ m/[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}/;
1167     } readdir(DEVICES);
1168     close(DEVICES);
1169
1170     # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
1171     #        dictionary keys so as to decouple our representation of the plist from the actual structure
1172     #        of the plist, which may change.
1173     my @devices = map {
1174         Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_("$devicesPath/$_/device.plist"));
1175     } @udids;
1176
1177     return @devices;
1178 }
1179
1180 sub createiOSSimulatorDevice
1181 {
1182     my $name = shift;
1183     my $deviceTypeId = shift;
1184     my $runtimeId = shift;
1185
1186     my $created = system("xcrun", "--sdk", "iphonesimulator", "simctl", "create", $name, $deviceTypeId, $runtimeId) == 0;
1187     die "Couldn't create simulator device: $name $deviceTypeId $runtimeId" if not $created;
1188
1189     system("xcrun", "--sdk", "iphonesimulator", "simctl", "list");
1190
1191     print "Waiting for device to be created ...\n";
1192     sleep 5;
1193     for (my $tries = 0; $tries < 5; $tries++){
1194         my @devices = iOSSimulatorDevices();
1195         foreach my $device (@devices) {
1196             return $device if $device->{name} eq $name and $device->{deviceType} eq $deviceTypeId and $device->{runtime} eq $runtimeId;
1197         }
1198         sleep 5;
1199     }
1200     die "Device $name $deviceTypeId $runtimeId wasn't found in " . iOSSimulatorDevicesPath();
1201 }
1202
1203 sub willUseIOSDeviceSDKWhenBuilding()
1204 {
1205     return xcodeSDKPlatformName() eq "iphoneos";
1206 }
1207
1208 sub willUseIOSSimulatorSDKWhenBuilding()
1209 {
1210     return xcodeSDKPlatformName() eq "iphonesimulator";
1211 }
1212
1213 sub isIOSWebKit()
1214 {
1215     determineXcodeSDK();
1216     return isAppleMacWebKit() && (willUseIOSDeviceSDKWhenBuilding() || willUseIOSSimulatorSDKWhenBuilding());
1217 }
1218
1219 sub determineNmPath()
1220 {
1221     return if $nmPath;
1222
1223     if (isAppleMacWebKit()) {
1224         $nmPath = `xcrun -find nm`;
1225         chomp $nmPath;
1226     }
1227     $nmPath = "nm" if !$nmPath;
1228 }
1229
1230 sub nmPath()
1231 {
1232     determineNmPath();
1233     return $nmPath;
1234 }
1235
1236 sub determineOSXVersion()
1237 {
1238     return if $osXVersion;
1239
1240     if (!isDarwin()) {
1241         $osXVersion = -1;
1242         return;
1243     }
1244
1245     my $version = `sw_vers -productVersion`;
1246     my @splitVersion = split(/\./, $version);
1247     @splitVersion >= 2 or die "Invalid version $version";
1248     $osXVersion = {
1249             "major" => $splitVersion[0],
1250             "minor" => $splitVersion[1],
1251             "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0),
1252     };
1253 }
1254
1255 sub osXVersion()
1256 {
1257     determineOSXVersion();
1258     return $osXVersion;
1259 }
1260
1261 sub isWindowsNT()
1262 {
1263     return $ENV{'OS'} eq 'Windows_NT';
1264 }
1265
1266 sub shouldTargetWebProcess
1267 {
1268     determineShouldTargetWebProcess();
1269     return $shouldTargetWebProcess;
1270 }
1271
1272 sub determineShouldTargetWebProcess
1273 {
1274     return if defined($shouldTargetWebProcess);
1275     $shouldTargetWebProcess = checkForArgumentAndRemoveFromARGV("--target-web-process");
1276 }
1277
1278 sub shouldUseXPCServiceForWebProcess
1279 {
1280     determineShouldUseXPCServiceForWebProcess();
1281     return $shouldUseXPCServiceForWebProcess;
1282 }
1283
1284 sub determineShouldUseXPCServiceForWebProcess
1285 {
1286     return if defined($shouldUseXPCServiceForWebProcess);
1287     $shouldUseXPCServiceForWebProcess = checkForArgumentAndRemoveFromARGV("--use-web-process-xpc-service");
1288 }
1289
1290 sub debugger
1291 {
1292     determineDebugger();
1293     return $debugger;
1294 }
1295
1296 sub determineDebugger
1297 {
1298     return if defined($debugger);
1299
1300     determineXcodeVersion();
1301     if (eval "v$xcodeVersion" ge v4.5) {
1302         $debugger = "lldb";
1303     } else {
1304         $debugger = "gdb";
1305     }
1306
1307     if (checkForArgumentAndRemoveFromARGV("--use-lldb")) {
1308         $debugger = "lldb";
1309     }
1310
1311     if (checkForArgumentAndRemoveFromARGV("--use-gdb")) {
1312         $debugger = "gdb";
1313     }
1314 }
1315
1316 sub appendToEnvironmentVariableList
1317 {
1318     my ($environmentVariableName, $value) = @_;
1319
1320     if (defined($ENV{$environmentVariableName})) {
1321         $ENV{$environmentVariableName} .= ":" . $value;
1322     } else {
1323         $ENV{$environmentVariableName} = $value;
1324     }
1325 }
1326
1327 sub sharedCommandLineOptions()
1328 {
1329     return (
1330         "g|guard-malloc" => \$shouldUseGuardMalloc,
1331     );
1332 }
1333
1334 sub sharedCommandLineOptionsUsage
1335 {
1336     my %opts = @_;
1337
1338     my %switches = (
1339         '-g|--guard-malloc' => 'Use guardmalloc when running executable',
1340     );
1341
1342     my $indent = " " x ($opts{indent} || 2);
1343     my $switchWidth = List::Util::max(int($opts{switchWidth}), List::Util::max(map { length($_) } keys %switches) + ($opts{brackets} ? 2 : 0));
1344
1345     my $result = "Common switches:\n";
1346
1347     for my $switch (keys %switches) {
1348         my $switchName = $opts{brackets} ? "[" . $switch . "]" : $switch;
1349         $result .= sprintf("%s%-" . $switchWidth . "s %s\n", $indent, $switchName, $switches{$switch});
1350     }
1351
1352     return $result;
1353 }
1354
1355 sub setUpGuardMallocIfNeeded
1356 {
1357     if (!isDarwin()) {
1358         return;
1359     }
1360
1361     if (!defined($shouldUseGuardMalloc)) {
1362         $shouldUseGuardMalloc = checkForArgumentAndRemoveFromARGV("-g") || checkForArgumentAndRemoveFromARGV("--guard-malloc");
1363     }
1364
1365     if ($shouldUseGuardMalloc) {
1366         appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib");
1367     }
1368 }
1369
1370 sub relativeScriptsDir()
1371 {
1372     my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel($FindBin::Bin, getcwd()), "");
1373     if ($scriptDir eq "") {
1374         $scriptDir = ".";
1375     }
1376     return $scriptDir;
1377 }
1378
1379 sub launcherPath()
1380 {
1381     my $relativeScriptsPath = relativeScriptsDir();
1382     if (isGtk() || isEfl()) {
1383         return "$relativeScriptsPath/run-launcher";
1384     } elsif (isAppleWebKit()) {
1385         return "$relativeScriptsPath/run-safari";
1386     }
1387 }
1388
1389 sub launcherName()
1390 {
1391     if (isGtk() || isEfl()) {
1392         return "MiniBrowser";
1393     } elsif (isAppleMacWebKit()) {
1394         return "Safari";
1395     } elsif (isAppleWinWebKit()) {
1396         return "WinLauncher";
1397     }
1398 }
1399
1400 sub checkRequiredSystemConfig
1401 {
1402     if (isDarwin()) {
1403         chomp(my $productVersion = `sw_vers -productVersion`);
1404         if (eval "v$productVersion" lt v10.7.5) {
1405             print "*************************************************************\n";
1406             print "Mac OS X Version 10.7.5 or later is required to build WebKit.\n";
1407             print "You have " . $productVersion . ", thus the build will most likely fail.\n";
1408             print "*************************************************************\n";
1409         }
1410         my $xcodebuildVersionOutput = `xcodebuild -version`;
1411         my $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : undef;
1412         if (!$xcodeVersion || $xcodeVersion && eval "v$xcodeVersion" lt v4.6) {
1413             print "*************************************************************\n";
1414             print "Xcode Version 4.6 or later is required to build WebKit.\n";
1415             print "You have an earlier version of Xcode, thus the build will\n";
1416             print "most likely fail. The latest Xcode is available from the App Store.\n";
1417             print "*************************************************************\n";
1418         }
1419     } elsif (isGtk() or isEfl() or isWindows()) {
1420         my @cmds = qw(bison gperf flex);
1421         my @missing = ();
1422         my $oldPath = $ENV{PATH};
1423         foreach my $cmd (@cmds) {
1424             push @missing, $cmd if not commandExists($cmd);
1425         }
1426
1427         if (@missing) {
1428             my $list = join ", ", @missing;
1429             die "ERROR: $list missing but required to build WebKit.\n";
1430         }
1431     }
1432     # Win32 and other platforms may want to check for minimum config
1433 }
1434
1435 sub determineWindowsSourceDir()
1436 {
1437     return if $windowsSourceDir;
1438     $windowsSourceDir = sourceDir();
1439     chomp($windowsSourceDir = `cygpath -w '$windowsSourceDir'`) if isCygwin();
1440 }
1441
1442 sub windowsSourceDir()
1443 {
1444     determineWindowsSourceDir();
1445     return $windowsSourceDir;
1446 }
1447
1448 sub windowsSourceSourceDir()
1449 {
1450     return windowsSourceDir() . "\\Source";
1451 }
1452
1453 sub windowsLibrariesDir()
1454 {
1455     return windowsSourceDir() . "\\WebKitLibraries\\win";
1456 }
1457
1458 sub windowsOutputDir()
1459 {
1460     return windowsSourceDir() . "\\WebKitBuild";
1461 }
1462
1463 sub fontExists($)
1464 {
1465     my $font = shift;
1466     my $val = system qw(regtool get), '\\HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\' . $font . ' (TrueType)';
1467     return 0 == $val;
1468 }
1469
1470 sub checkInstalledTools()
1471 {
1472     # SVN 1.7.10 is known to be compatible with current servers. SVN 1.8.x seems to be missing some authentication
1473     # protocols we use for svn.webkit.org:
1474     my $svnVersion = `svn --version | grep "\\sversion"`;
1475     chomp($svnVersion);
1476     if (!$? and $svnVersion =~ /1\.8\./) {
1477         print "svn 1.7.10 is known to be compatible with our servers. You are running $svnVersion,\nwhich may not work properly.\n"
1478     }
1479
1480     # environment variables. Avoid until this is corrected.
1481     my $pythonVer = `python --version 2>&1`;
1482     die "You must have Python installed to build WebKit.\n" if ($?);
1483
1484     # cURL 7.34.0 has a bug that prevents authentication with opensource.apple.com (and other things using SSL3).
1485     my $curlVer = `curl --version | grep "curl"`;
1486     chomp($curlVer);
1487     if (!$? and $curlVer =~ /libcurl\/7\.34\.0/) {
1488         print "cURL version 7.34.0 has a bug that prevents authentication with SSL v2 or v3.\n";
1489         print "cURL 7.33.0 is known to work. The cURL projects is preparing an update to\n";
1490         print "correct this problem.\n\n";
1491         die "Please install a working cURL and try again.\n";
1492     }
1493
1494     # MathML requires fonts that do not ship with Windows (at least through Windows 8). Warn the user if they are missing
1495     my @fonts = qw(STIXGeneral-Regular MathJax_Main-Regular);
1496     my @missing = ();
1497     foreach my $font (@fonts) {
1498         push @missing, $font if not fontExists($font);
1499     }
1500
1501     if (scalar @missing > 0) {
1502         print "*************************************************************\n";
1503         print "Mathematical fonts, such as STIX and MathJax, are needed to\n";
1504         print "use the MathML feature.  You do not appear to have these fonts\n";
1505         print "on your system.\n\n";
1506         print "You can download a suitable set of fonts from the following URL:\n";
1507         print "https://developer.mozilla.org/Mozilla/MathML_Projects/Fonts\n";
1508         print "*************************************************************\n";
1509     }
1510
1511     print "Installed tools are correct for the WebKit build.\n";
1512 }
1513
1514 sub setupAppleWinEnv()
1515 {
1516     return unless isAppleWinWebKit();
1517
1518     checkInstalledTools();
1519
1520     if (isWindowsNT()) {
1521         my $restartNeeded = 0;
1522         my %variablesToSet = ();
1523
1524         # FIXME: We should remove this explicit version check for cygwin once we stop supporting Cygwin 1.7.9 or older versions. 
1525         # https://bugs.webkit.org/show_bug.cgi?id=85791
1526         my $uname_version = (POSIX::uname())[2];
1527         $uname_version =~ s/\(.*\)//;  # Remove the trailing cygwin version, if any.
1528         $uname_version =~ s/\-.*$//; # Remove trailing dash-version content, if any
1529         if (version->parse($uname_version) < version->parse("1.7.10")) {
1530             # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios)
1531             # for UNIX-like ttys in the Windows console
1532             $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN};
1533         }
1534         
1535         # Those environment variables must be set to be able to build inside Visual Studio.
1536         $variablesToSet{WEBKIT_LIBRARIES} = windowsLibrariesDir() unless $ENV{WEBKIT_LIBRARIES};
1537         $variablesToSet{WEBKIT_OUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKIT_OUTPUTDIR};
1538         $variablesToSet{MSBUILDDISABLENODEREUSE} = "1" unless $ENV{MSBUILDDISABLENODEREUSE};
1539
1540         foreach my $variable (keys %variablesToSet) {
1541             print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n";
1542             system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable};
1543             $restartNeeded ||=  $variable eq "WEBKIT_LIBRARIES" || $variable eq "WEBKIT_OUTPUTDIR";
1544         }
1545
1546         if ($restartNeeded) {
1547             print "Please restart your computer before attempting to build inside Visual Studio.\n\n";
1548         }
1549     } else {
1550         if (!defined $ENV{'WEBKIT_LIBRARIES'} || !$ENV{'WEBKIT_LIBRARIES'}) {
1551             print "Warning: You must set the 'WebKit_Libraries' environment variable\n";
1552             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n";
1553             print "         Make sure that 'WebKit_Libraries' points to the\n";
1554             print "         'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n";
1555         }
1556         if (!defined $ENV{'WEBKIT_OUTPUTDIR'} || !$ENV{'WEBKIT_OUTPUTDIR'}) {
1557             print "Warning: You must set the 'WebKit_OutputDir' environment variable\n";
1558             print "         to be able build WebKit from within Visual Studio 2013 and newer.\n\n";
1559         }
1560         if (!defined $ENV{'MSBUILDDISABLENODEREUSE'} || !$ENV{'MSBUILDDISABLENODEREUSE'}) {
1561             print "Warning: You should set the 'MSBUILDDISABLENODEREUSE' environment variable to '1'\n";
1562             print "         to avoid periodic locked log files when building.\n\n";
1563         }
1564     }
1565     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1566     if (isWin64() and !$ENV{'WEBKIT_64_SUPPORT'}) {
1567         print "Warning: You must set the 'WEBKIT_64_SUPPORT' environment variable\n";
1568         print "         to be able run WebKit or JavaScriptCore tests.\n\n";
1569     }
1570 }
1571
1572 sub setupCygwinEnv()
1573 {
1574     return if !isCygwin() && !isWindows();
1575     return if $vcBuildPath;
1576
1577     my $programFilesPath = programFilesPath();
1578     my $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.com));
1579     if (-e $visualStudioPath) {
1580         # Visual Studio is installed;
1581         if (visualStudioVersion() eq "12") {
1582             $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.exe));
1583         }
1584     } else {
1585         # Visual Studio not found, try VC++ Express
1586         $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE WDExpress.exe));
1587         if (! -e $visualStudioPath) {
1588             print "*************************************************************\n";
1589             print "Cannot find '$visualStudioPath'\n";
1590             print "Please execute the file 'vcvars32.bat' from\n";
1591             print "'$programFilesPath\\Microsoft Visual Studio 12.0\\VC\\bin\\'\n";
1592             print "to setup the necessary environment variables.\n";
1593             print "*************************************************************\n";
1594             die;
1595         }
1596         $willUseVCExpressWhenBuilding = 1;
1597     }
1598
1599     print "Building results into: ", baseProductDir(), "\n";
1600     print "WEBKIT_OUTPUTDIR is set to: ", $ENV{"WEBKIT_OUTPUTDIR"}, "\n";
1601     print "WEBKIT_LIBRARIES is set to: ", $ENV{"WEBKIT_LIBRARIES"}, "\n";
1602     # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
1603     print "WEBKIT_64_SUPPORT is set to: ", $ENV{"WEBKIT_64_SUPPORT"}, "\n" if isWin64();
1604
1605     # We will actually use MSBuild to build WebKit, but we need to find the Visual Studio install (above) to make
1606     # sure we use the right options.
1607     $vcBuildPath = File::Spec->catfile(msBuildInstallDir(), qw(MSBuild.exe));
1608     if (! -e $vcBuildPath) {
1609         print "*************************************************************\n";
1610         print "Cannot find '$vcBuildPath'\n";
1611         print "Please make sure execute that the Microsoft .NET Framework SDK\n";
1612         print "is installed on this machine.\n";
1613         print "*************************************************************\n";
1614         die;
1615     }
1616 }
1617
1618 sub dieIfWindowsPlatformSDKNotInstalled
1619 {
1620     my $registry32Path = "/proc/registry/";
1621     my $registry64Path = "/proc/registry64/";
1622     my @windowsPlatformSDKRegistryEntries = (
1623         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0A",
1624         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0",
1625         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.1A",
1626         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.0A",
1627         "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1",
1628     );
1629
1630     # FIXME: It would be better to detect whether we are using 32- or 64-bit Windows
1631     # and only check the appropriate entry. But for now we just blindly check both.
1632     my $recommendedPlatformSDK = $windowsPlatformSDKRegistryEntries[0];
1633
1634     while (@windowsPlatformSDKRegistryEntries) {
1635         my $windowsPlatformSDKRegistryEntry = shift @windowsPlatformSDKRegistryEntries;
1636         return if (-e $registry32Path . $windowsPlatformSDKRegistryEntry) || (-e $registry64Path . $windowsPlatformSDKRegistryEntry);
1637     }
1638
1639     print "*************************************************************\n";
1640     print "Cannot find registry entry '$recommendedPlatformSDK'.\n";
1641     print "Please download and install the Microsoft Windows SDK\n";
1642     print "from <http://www.microsoft.com/en-us/download/details.aspx?id=8279>.\n\n";
1643     print "Then follow step 2 in the Windows section of the \"Installing Developer\n";
1644     print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n";
1645     print "*************************************************************\n";
1646     die;
1647 }
1648
1649 sub buildXCodeProject($$@)
1650 {
1651     my ($project, $clean, @extraOptions) = @_;
1652
1653     if ($clean) {
1654         push(@extraOptions, "-alltargets");
1655         push(@extraOptions, "clean");
1656     }
1657
1658     push(@extraOptions, ("-sdk", xcodeSDK())) if isIOSWebKit();
1659
1660     chomp($ENV{DSYMUTIL_NUM_THREADS} = `sysctl -n hw.activecpu`);
1661     return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions;
1662 }
1663
1664 sub usingVisualStudioExpress()
1665 {
1666     setupCygwinEnv();
1667     return $willUseVCExpressWhenBuilding;
1668 }
1669
1670 sub buildVisualStudioProject
1671 {
1672     my ($project, $clean) = @_;
1673     setupCygwinEnv();
1674
1675     my $config = configurationForVisualStudio();
1676
1677     dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding;
1678
1679     chomp($project = `cygpath -w "$project"`) if isCygwin();
1680
1681     my $action = "/t:build";
1682     if ($clean) {
1683         $action = "/t:clean";
1684     }
1685
1686     my $platform = "/p:Platform=" . (isWin64() ? "x64" : "Win32");
1687     my $logPath = File::Spec->catdir($baseProductDir, $configuration);
1688     File::Path->make_path($logPath) unless -d $logPath;
1689
1690     my $errorLogFile = File::Spec->catfile($logPath, "webkit_errors.log");
1691     chomp($errorLogFile = `cygpath -w "$errorLogFile"`) if isCygwin();
1692     my $errorLogging = "/flp:LogFile=" . $errorLogFile . ";ErrorsOnly";
1693
1694     my $warningLogFile = File::Spec->catfile($logPath, "webkit_warnings.log");
1695     chomp($warningLogFile = `cygpath -w "$warningLogFile"`) if isCygwin();
1696     my $warningLogging = "/flp1:LogFile=" . $warningLogFile . ";WarningsOnly";
1697
1698     my @command = ($vcBuildPath, "/verbosity:minimal", $project, $action, $config, $platform, "/fl", $errorLogging, "/fl1", $warningLogging);
1699
1700     print join(" ", @command), "\n";
1701     return system @command;
1702 }
1703
1704 sub getJhbuildPath()
1705 {
1706     my @jhbuildPath = File::Spec->splitdir(baseProductDir());
1707     if (isGit() && isGitBranchBuild() && gitBranch()) {
1708         pop(@jhbuildPath);
1709     }
1710     if (isEfl()) {
1711         push(@jhbuildPath, "DependenciesEFL");
1712     } elsif (isGtk()) {
1713         push(@jhbuildPath, "DependenciesGTK");
1714     } else {
1715         die "Cannot get JHBuild path for platform that isn't GTK+ or EFL.\n";
1716     }
1717     return File::Spec->catdir(@jhbuildPath);
1718 }
1719
1720 sub isCachedArgumentfileOutOfDate($@)
1721 {
1722     my ($filename, $currentContents) = @_;
1723
1724     if (! -e $filename) {
1725         return 1;
1726     }
1727
1728     open(CONTENTS_FILE, $filename);
1729     chomp(my $previousContents = <CONTENTS_FILE>);
1730     close(CONTENTS_FILE);
1731
1732     if ($previousContents ne $currentContents) {
1733         print "Contents for file $filename have changed.\n";
1734         print "Previous contents were: $previousContents\n\n";
1735         print "New contents are: $currentContents\n";
1736         return 1;
1737     }
1738
1739     return 0;
1740 }
1741
1742 sub jhbuildWrapperPrefixIfNeeded()
1743 {
1744     if (-e getJhbuildPath()) {
1745         my @prefix = (File::Spec->catfile(sourceDir(), "Tools", "jhbuild", "jhbuild-wrapper"));
1746         if (isEfl()) {
1747             push(@prefix, "--efl");
1748         } elsif (isGtk()) {
1749             push(@prefix, "--gtk");
1750         }
1751         push(@prefix, "run");
1752
1753         return @prefix;
1754     }
1755
1756     return ();
1757 }
1758
1759 sub cmakeCachePath()
1760 {
1761     return File::Spec->catdir(baseProductDir(), configuration(), "CMakeCache.txt");
1762 }
1763
1764 sub shouldRemoveCMakeCache(@)
1765 {
1766     my ($cacheFilePath, @buildArgs) = @_;
1767
1768     # We check this first, because we always want to create this file for a fresh build.
1769     my $productDir = File::Spec->catdir(baseProductDir(), configuration());
1770     my $optionsCache = File::Spec->catdir($productDir, "build-webkit-options.txt");
1771     my $joinedBuildArgs = join(" ", @buildArgs);
1772     if (isCachedArgumentfileOutOfDate($optionsCache, $joinedBuildArgs)) {
1773         File::Path::mkpath($productDir) unless -d $productDir;
1774         open(CACHED_ARGUMENTS, ">", $optionsCache);
1775         print CACHED_ARGUMENTS $joinedBuildArgs;
1776         close(CACHED_ARGUMENTS);
1777
1778         return 1;
1779     }
1780
1781     my $cmakeCache = cmakeCachePath();
1782     unless (-e $cmakeCache) {
1783         return 0;
1784     }
1785
1786     my $cacheFileModifiedTime = stat($cmakeCache)->mtime;
1787     my $platformConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "Options" . cmakeBasedPortName() . ".cmake");
1788     if ($cacheFileModifiedTime < stat($platformConfiguration)->mtime) {
1789         return 1;
1790     }
1791
1792     my $globalConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsCommon.cmake");
1793     if ($cacheFileModifiedTime < stat($globalConfiguration)->mtime) {
1794         return 1;
1795     }
1796
1797     return 0;
1798 }
1799
1800 sub removeCMakeCache(@)
1801 {
1802     my (@buildArgs) = @_;
1803     if (shouldRemoveCMakeCache(@buildArgs)) {
1804         my $cmakeCache = cmakeCachePath();
1805         unlink($cmakeCache) if -e $cmakeCache;
1806     }
1807 }
1808
1809 sub canUseNinja(@)
1810 {
1811     # Test both ninja and ninja-build. Fedora uses ninja-build and has patched CMake to also call ninja-build.
1812     return commandExists("ninja") || commandExists("ninja-build");
1813 }
1814
1815 sub canUseEclipse(@)
1816 {
1817     return commandExists("eclipse");
1818 }
1819
1820 sub cmakeGeneratedBuildfile(@)
1821 {
1822     my ($willUseNinja) = @_;
1823     if ($willUseNinja) {
1824         return File::Spec->catfile(baseProductDir(), configuration(), "build.ninja")
1825     } else {
1826         return File::Spec->catfile(baseProductDir(), configuration(), "Makefile")
1827     }
1828 }
1829
1830 sub generateBuildSystemFromCMakeProject
1831 {
1832     my ($port, $prefixPath, @cmakeArgs, $additionalCMakeArgs) = @_;
1833     my $config = configuration();
1834     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1835     File::Path::mkpath($buildPath) unless -d $buildPath;
1836     my $originalWorkingDirectory = getcwd();
1837     chdir($buildPath) or die;
1838
1839     # We try to be smart about when to rerun cmake, so that we can have faster incremental builds.
1840     my $willUseNinja = canUseNinja();
1841     if (-e cmakeCachePath() && -e cmakeGeneratedBuildfile($willUseNinja)) {
1842         return 0;
1843     }
1844
1845     my @args;
1846     push @args, "-DPORT=\"$port\"";
1847     push @args, "-DCMAKE_INSTALL_PREFIX=\"$prefixPath\"" if $prefixPath;
1848     push @args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" if isGtk();
1849     if ($config =~ /release/i) {
1850         push @args, "-DCMAKE_BUILD_TYPE=Release";
1851     } elsif ($config =~ /debug/i) {
1852         push @args, "-DCMAKE_BUILD_TYPE=Debug";
1853     }
1854
1855     if ($willUseNinja) {
1856         push @args, "-G";
1857         if (canUseEclipse()) {
1858             push @args, "'Eclipse CDT4 - Ninja'";
1859         } else {
1860             push @args, "Ninja";
1861         }
1862     }
1863
1864     # GTK+ has a production mode, but build-webkit should always use developer mode.
1865     push @args, "-DDEVELOPER_MODE=ON" if isEfl() || isGtk();
1866
1867     # Don't warn variables which aren't used by cmake ports.
1868     push @args, "--no-warn-unused-cli";
1869     push @args, @cmakeArgs if @cmakeArgs;
1870     push @args, $additionalCMakeArgs if $additionalCMakeArgs;
1871
1872     push @args, '"' . sourceDir() . '"';
1873
1874     # Compiler options to keep floating point values consistent
1875     # between 32-bit and 64-bit architectures.
1876     determineArchitecture();
1877     if ($architecture ne "x86_64" && !isARM() && !isCrossCompilation()) {
1878         $ENV{'CXXFLAGS'} = "-march=pentium4 -msse2 -mfpmath=sse " . ($ENV{'CXXFLAGS'} || "");
1879     }
1880
1881     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
1882     # parsed for shell metacharacters.
1883     my $wrapper = join(" ", jhbuildWrapperPrefixIfNeeded()) . " ";
1884     my $returnCode = system($wrapper . "cmake @args");
1885
1886     chdir($originalWorkingDirectory);
1887     return $returnCode;
1888 }
1889
1890 sub buildCMakeGeneratedProject($)
1891 {
1892     my ($makeArgs) = @_;
1893     my $config = configuration();
1894     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1895     if (! -d $buildPath) {
1896         die "Must call generateBuildSystemFromCMakeProject() before building CMake project.";
1897     }
1898
1899     my $command = "cmake";
1900     my @args = ("--build", $buildPath, "--config", $config);
1901     push @args, ("--", $makeArgs) if $makeArgs;
1902
1903     # GTK can use a build script to preserve colors and pretty-printing.
1904     if (isGtk() && -e "$buildPath/build.sh") {
1905         chdir "$buildPath" or die;
1906         $command = "$buildPath/build.sh";
1907         @args = ($makeArgs);
1908     }
1909
1910     if ($ENV{VERBOSE} && canUseNinja()) {
1911         push @args, "-v";
1912         push @args, "-d keeprsp" if (version->parse(determineNinjaVersion()) >= version->parse("1.4.0"));
1913     }
1914
1915     # We call system("cmake @args") instead of system("cmake", @args) so that @args is
1916     # parsed for shell metacharacters. In particular, $makeArgs may contain such metacharacters.
1917     my $wrapper = join(" ", jhbuildWrapperPrefixIfNeeded()) . " ";
1918     return system($wrapper . "$command @args");
1919
1920 }
1921
1922 sub cleanCMakeGeneratedProject()
1923 {
1924     my $config = configuration();
1925     my $buildPath = File::Spec->catdir(baseProductDir(), $config);
1926     if (-d $buildPath) {
1927         return system("cmake", "--build", $buildPath, "--config", $config, "--target", "clean");
1928     }
1929     return 0;
1930 }
1931
1932 sub buildCMakeProjectOrExit($$$$@)
1933 {
1934     my ($clean, $port, $prefixPath, $makeArgs, @cmakeArgs) = @_;
1935     my $returnCode;
1936
1937     exit(exitStatus(cleanCMakeGeneratedProject())) if $clean;
1938
1939     if (isEfl() && checkForArgumentAndRemoveFromARGV("--update-efl")) {
1940         system("perl", "$sourceDir/Tools/Scripts/update-webkitefl-libs") == 0 or die $!;
1941     }
1942
1943     if (isGtk() && checkForArgumentAndRemoveFromARGV("--update-gtk")) {
1944         system("perl", "$sourceDir/Tools/Scripts/update-webkitgtk-libs") == 0 or die $!;
1945     }
1946
1947     $returnCode = exitStatus(generateBuildSystemFromCMakeProject($port, $prefixPath, @cmakeArgs));
1948     exit($returnCode) if $returnCode;
1949
1950     $returnCode = exitStatus(buildCMakeGeneratedProject($makeArgs));
1951     exit($returnCode) if $returnCode;
1952     return 0;
1953 }
1954
1955 sub cmakeBasedPortArguments()
1956 {
1957     return ();
1958 }
1959
1960 sub cmakeBasedPortName()
1961 {
1962     return "Efl" if isEfl();
1963     return "GTK" if isGtk();
1964     return "";
1965 }
1966
1967 sub isCMakeBuild()
1968 {
1969     return isEfl() || isGtk();
1970 }
1971
1972 sub promptUser
1973 {
1974     my ($prompt, $default) = @_;
1975     my $defaultValue = $default ? "[$default]" : "";
1976     print "$prompt $defaultValue: ";
1977     chomp(my $input = <STDIN>);
1978     return $input ? $input : $default;
1979 }
1980
1981 sub appleApplicationSupportPath
1982 {
1983     open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir";
1984     my $path = <INSTALL_DIR>;
1985     $path =~ s/[\r\n\x00].*//;
1986     close INSTALL_DIR;
1987
1988     my $unixPath = `cygpath -u '$path'`;
1989     chomp $unixPath;
1990     return $unixPath;
1991 }
1992
1993 sub setPathForRunningWebKitApp
1994 {
1995     my ($env) = @_;
1996
1997     if (isAppleWinWebKit()) {
1998         $env->{PATH} = join(':', productDir(), appleApplicationSupportPath(), $env->{PATH} || "");
1999     } elsif (isWinCairo()) {
2000         my $winCairoBin = sourceDir() . "/WebKitLibraries/win/" . (isWin64() ? "bin64/" : "bin32/");
2001         my $gstreamerBin = isWin64() ? $ENV{"GSTREAMER_1_0_ROOT_X86_64"} . "bin" : $ENV{"GSTREAMER_1_0_ROOT_X86"} . "bin";
2002         $env->{PATH} = join(':', productDir(), $winCairoBin, $gstreamerBin, $env->{PATH} || "");
2003     }
2004 }
2005
2006 sub printHelpAndExitForRunAndDebugWebKitAppIfNeeded
2007 {
2008     return unless checkForArgumentAndRemoveFromARGV("--help");
2009
2010     my ($includeOptionsForDebugging) = @_;
2011
2012     print STDERR <<EOF;
2013 Usage: @{[basename($0)]} [options] [args ...]
2014   --help                            Show this help message
2015   --no-saved-state                  Launch the application without state restoration (OS X 10.7 and later)
2016   -g|--guard-malloc                 Enable Guard Malloc (OS X only)
2017   --use-web-process-xpc-service     Launch the Web Process as an XPC Service (OS X only)
2018 EOF
2019
2020     if ($includeOptionsForDebugging) {
2021         print STDERR <<EOF;
2022   --target-web-process              Debug the web process
2023   --use-gdb                         Use GDB (this is the default when using Xcode 4.4 or earlier)
2024   --use-lldb                        Use LLDB (this is the default when using Xcode 4.5 or later)
2025 EOF
2026     }
2027
2028     exit(1);
2029 }
2030
2031 sub argumentsForRunAndDebugMacWebKitApp()
2032 {
2033     my @args = ();
2034     if (checkForArgumentAndRemoveFromARGV("--no-saved-state")) {
2035         push @args, ("-ApplePersistenceIgnoreStateQuietly", "YES");
2036         # FIXME: Don't set ApplePersistenceIgnoreState once all supported OS versions respect ApplePersistenceIgnoreStateQuietly (rdar://15032886).
2037         push @args, ("-ApplePersistenceIgnoreState", "YES");
2038     }
2039     push @args, ("-WebKit2UseXPCServiceForWebProcess", "YES") if shouldUseXPCServiceForWebProcess();
2040     unshift @args, @ARGV;
2041
2042     return @args;
2043 }
2044
2045 sub setupMacWebKitEnvironment($)
2046 {
2047     my ($dyldFrameworkPath) = @_;
2048
2049     $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
2050
2051     $ENV{DYLD_FRAMEWORK_PATH} = $ENV{DYLD_FRAMEWORK_PATH} ? join(":", $dyldFrameworkPath, $ENV{DYLD_FRAMEWORK_PATH}) : $dyldFrameworkPath;
2052     $ENV{__XPC_DYLD_FRAMEWORK_PATH} = $dyldFrameworkPath;
2053     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
2054
2055     setUpGuardMallocIfNeeded();
2056 }
2057
2058 sub setupIOSWebKitEnvironment($)
2059 {
2060     my ($dyldFrameworkPath) = @_;
2061     $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
2062
2063     $ENV{DYLD_FRAMEWORK_PATH} = $dyldFrameworkPath;
2064     $ENV{DYLD_LIBRARY_PATH} = $dyldFrameworkPath;
2065
2066     setUpGuardMallocIfNeeded();
2067 }
2068
2069 sub iosSimulatorApplicationsPath()
2070 {
2071     return File::Spec->catdir(XcodeSDKPath(), "Applications");
2072 }
2073
2074 sub installedMobileSafariBundle()
2075 {
2076     return File::Spec->catfile(iosSimulatorApplicationsPath(), "MobileSafari.app");
2077 }
2078
2079 sub mobileSafariBundle()
2080 {
2081     determineConfigurationProductDir();
2082
2083     # Use MobileSafari.app in product directory if present.
2084     if (isAppleMacWebKit() && -d "$configurationProductDir/MobileSafari.app") {
2085         return "$configurationProductDir/MobileSafari.app";
2086     }
2087     return installedMobileSafariBundle();
2088 }
2089
2090 sub plistPathFromBundle($)
2091 {
2092     my ($appBundle) = @_;
2093     return "$appBundle/Info.plist" if -f "$appBundle/Info.plist"; # iOS app bundle
2094     return "$appBundle/Contents/Info.plist" if -f "$appBundle/Contents/Info.plist"; # Mac app bundle
2095     return "";
2096 }
2097
2098 sub appIdentifierFromBundle($)
2099 {
2100     my ($appBundle) = @_;
2101     my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
2102     chomp(my $bundleIdentifier = `defaults read '$plistPath' CFBundleIdentifier 2> /dev/null`);
2103     return $bundleIdentifier;
2104 }
2105
2106 sub appDisplayNameFromBundle($)
2107 {
2108     my ($appBundle) = @_;
2109     my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
2110     chomp(my $bundleDisplayName = `defaults read '$plistPath' CFBundleDisplayName 2> /dev/null`);
2111     return $bundleDisplayName;
2112 }
2113
2114 sub waitUntilIOSSimulatorDeviceIsInState($$)
2115 {
2116     my ($deviceUDID, $waitUntilState) = @_;
2117     my $device = iosSimulatorDeviceByUDID($deviceUDID);
2118     while ($device->{state} ne $waitUntilState) {
2119         usleep(500 * 1000); # Waiting 500ms between file system polls does not make script run-safari feel sluggish.
2120         $device = iosSimulatorDeviceByUDID($deviceUDID);
2121     }
2122 }
2123
2124 sub relaunchIOSSimulator($)
2125 {
2126     my ($simulatedDevice) = @_;
2127     quitIOSSimulator($simulatedDevice->{UDID});
2128
2129     chomp(my $developerDirectory = $ENV{DEVELOPER_DIR} || `xcode-select --print-path`);
2130     my $iosSimulatorPath = File::Spec->catfile($developerDirectory, "Applications", "iOS Simulator.app");
2131
2132     system("open", "-a", $iosSimulatorPath, "--args", "-CurrentDeviceUDID", $simulatedDevice->{UDID}) == 0 or die "Failed to open $iosSimulatorPath: $!";
2133     waitUntilIOSSimulatorDeviceIsInState($simulatedDevice->{UDID}, SIMULATOR_DEVICE_STATE_BOOTED);
2134 }
2135
2136 sub quitIOSSimulator(;$)
2137 {
2138     my ($waitForShutdownOfSimulatedDeviceUDID) = @_;
2139     exitStatus(system {"osascript"} "osascript", "-e", 'tell application "iOS Simulator" to quit') == 0 or die "Failed to quit iOS Simulator: $!";
2140     if (!defined($waitForShutdownOfSimulatedDeviceUDID)) {
2141         return;
2142     }
2143     # FIXME: We assume that $waitForShutdownOfSimulatedDeviceUDID was not booted using the simctl command line tool.
2144     #        Otherwise we will spin indefinitely since quiting the iOS Simulator will not shutdown this device. We
2145     #        should add a maximum time limit to wait for a device to shutdown and either return an error or die()
2146     #        on expiration of the time limit.
2147     waitUntilIOSSimulatorDeviceIsInState($waitForShutdownOfSimulatedDeviceUDID, SIMULATOR_DEVICE_STATE_SHUTDOWN);
2148 }
2149
2150 sub iosSimulatorDeviceByName($)
2151 {
2152     my ($simulatorName) = @_;
2153     my $simulatorRuntime = iosSimulatorRuntime();
2154     my @devices = iOSSimulatorDevices();
2155     for my $device (@devices) {
2156         if ($device->{name} eq $simulatorName && $device->{runtime} eq $simulatorRuntime) {
2157             return $device;
2158         }
2159     }
2160     return undef;
2161 }
2162
2163 sub iosSimulatorDeviceByUDID($)
2164 {
2165     my ($simulatedDeviceUDID) = @_;
2166     my $devicePlistPath = File::Spec->catfile(iOSSimulatorDevicesPath(), $simulatedDeviceUDID, "device.plist");
2167     if (!-f $devicePlistPath) {
2168         return;
2169     }
2170     # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
2171     #        dictionary keys so as to decouple our representation of the plist from the actual structure
2172     #        of the plist, which may change.
2173     eval "require Foundation";
2174     return Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_($devicePlistPath));
2175 }
2176
2177 sub iosSimulatorRuntime()
2178 {
2179     my $xcodeSDKVersion = xcodeSDKVersion();
2180     $xcodeSDKVersion =~ s/\./-/;
2181     return "com.apple.CoreSimulator.SimRuntime.iOS-$xcodeSDKVersion";
2182 }
2183
2184 sub findOrCreateSimulatorForIOSDevice($)
2185 {
2186     my ($simulatorNameSuffix) = @_;
2187     my $simulatorName;
2188     my $simulatorDeviceType;
2189     if (architecture() eq "x86_64") {
2190         $simulatorName = "iPhone 5s " . $simulatorNameSuffix;
2191         $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5s";
2192     } else {
2193         $simulatorName = "iPhone 5 " . $simulatorNameSuffix;
2194         $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5";
2195     }
2196     my $simulatedDevice = iosSimulatorDeviceByName($simulatorName);
2197     return $simulatedDevice if $simulatedDevice;
2198     return createiOSSimulatorDevice($simulatorName, $simulatorDeviceType, iosSimulatorRuntime());
2199 }
2200
2201 sub isIOSSimulatorSystemInstalledApp($)
2202 {
2203     my ($appBundle) = @_;
2204     my $simulatorApplicationsPath = realpath(iosSimulatorApplicationsPath());
2205     return substr(realpath($appBundle), 0, length($simulatorApplicationsPath)) eq $simulatorApplicationsPath;
2206 }
2207
2208 sub hasUserInstalledAppInSimulatorDevice($$)
2209 {
2210     my ($appIdentifier, $simulatedDeviceUDID) = @_;
2211     my $userInstalledAppPath = File::Spec->catfile($ENV{HOME}, "Library", "Developer", "CoreSimulator", "Devices", $simulatedDeviceUDID, "data", "Containers", "Bundle", "Application");
2212     if (!-d $userInstalledAppPath) {
2213         return 0; # No user installed apps.
2214     }
2215     local @::userInstalledAppBundles;
2216     my $wantedFunction = sub {
2217         my $file = $_;
2218
2219         # Ignore hidden files and directories.
2220         if ($file =~ /^\../) {
2221             $File::Find::prune = 1;
2222             return;
2223         }
2224
2225         return if !-d $file || $file !~ /\.app$/;
2226         push @::userInstalledAppBundles, $File::Find::name;
2227         $File::Find::prune = 1; # Do not traverse contents of app bundle.
2228     };
2229     find($wantedFunction, $userInstalledAppPath);
2230     for my $userInstalledAppBundle (@::userInstalledAppBundles) {
2231         if (appIdentifierFromBundle($userInstalledAppBundle) eq $appIdentifier) {
2232             return 1; # Has user installed app.
2233         }
2234     }
2235     return 0; # Does not have user installed app.
2236 }
2237
2238 sub isSimulatorDeviceBooted($)
2239 {
2240     my ($simulatedDeviceUDID) = @_;
2241     my $device = iosSimulatorDeviceByUDID($simulatedDeviceUDID);
2242     return $device && $device->{state} eq SIMULATOR_DEVICE_STATE_BOOTED;
2243 }
2244
2245 sub runIOSWebKitAppInSimulator($;$)
2246 {
2247     my ($appBundle, $simulatorOptions) = @_;
2248     my $productDir = productDir();
2249     my $appDisplayName = appDisplayNameFromBundle($appBundle);
2250     my $appIdentifier = appIdentifierFromBundle($appBundle);
2251     my $simulatedDevice = findOrCreateSimulatorForIOSDevice("For WebKit Development");
2252     my $simulatedDeviceUDID = $simulatedDevice->{UDID};
2253
2254     my $willUseSystemInstalledApp = isIOSSimulatorSystemInstalledApp($appBundle);
2255     if ($willUseSystemInstalledApp) {
2256         if (hasUserInstalledAppInSimulatorDevice($appIdentifier, $simulatedDeviceUDID)) {
2257             # Restore the system-installed app in the simulator device corresponding to $appBundle as it
2258             # was previously overwritten with a custom built version of the app.
2259             # FIXME: Only restore the system-installed version of the app instead of erasing all contents and settings.
2260             print "Quitting iOS Simulator...\n";
2261             quitIOSSimulator($simulatedDeviceUDID);
2262             print "Erasing contents and settings for simulator device \"$simulatedDevice->{name}\".\n";
2263             exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "erase", $simulatedDeviceUDID)) == 0 or die;
2264         }
2265         # FIXME: We assume that if $simulatedDeviceUDID is not booted then iOS Simulator is not open. However
2266         #        $simulatedDeviceUDID may have been booted using the simctl command line tool. If $simulatedDeviceUDID
2267         #        was booted using simctl then we should shutdown the device and launch iOS Simulator to boot it again.
2268         if (!isSimulatorDeviceBooted($simulatedDeviceUDID)) {
2269             print "Launching iOS Simulator...\n";
2270             relaunchIOSSimulator($simulatedDevice);
2271         }
2272     } else {
2273         # FIXME: We should killall(1) any running instances of $appBundle before installing it to ensure
2274         #        that simctl launch opens the latest installed version of the app. For now we quit and
2275         #        launch the iOS Simulator again to ensure there are no running instances of $appBundle.
2276         print "Quitting and launching iOS Simulator...\n";
2277         relaunchIOSSimulator($simulatedDevice);
2278
2279         print "Installing $appBundle.\n";
2280         # Install custom built app, overwriting an app with the same app identifier if one exists.
2281         exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "install", $simulatedDeviceUDID, $appBundle)) == 0 or die;
2282
2283     }
2284
2285     $simulatorOptions = {} unless $simulatorOptions;
2286
2287     my %simulatorENV;
2288     %simulatorENV = %{$simulatorOptions->{applicationEnvironment}} if $simulatorOptions->{applicationEnvironment};
2289     {
2290         local %ENV; # Shadow global-scope %ENV so that changes to it will not be seen outside of this scope.
2291         setupIOSWebKitEnvironment($productDir);
2292         %simulatorENV = %ENV;
2293     }
2294     my $applicationArguments = \@ARGV;
2295     $applicationArguments = $simulatorOptions->{applicationArguments} if $simulatorOptions && $simulatorOptions->{applicationArguments};
2296
2297     # Prefix the environment variables with SIMCTL_CHILD_ per `xcrun simctl help launch`.
2298     foreach my $key (keys %simulatorENV) {
2299         $ENV{"SIMCTL_CHILD_$key"} = $simulatorENV{$key};
2300     }
2301
2302     print "Starting $appDisplayName with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2303     return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "launch", $simulatedDeviceUDID, $appIdentifier, @$applicationArguments));
2304 }
2305
2306 sub runIOSWebKitApp($)
2307 {
2308     my ($appBundle) = @_;
2309     if (willUseIOSDeviceSDKWhenBuilding()) {
2310         die "Only running Safari in iOS Simulator is supported now.";
2311     }
2312     if (willUseIOSSimulatorSDKWhenBuilding()) {
2313         return runIOSWebKitAppInSimulator($appBundle);
2314     }
2315     die "Not using an iOS SDK."
2316 }
2317
2318 sub runMacWebKitApp($;$)
2319 {
2320     my ($appPath, $useOpenCommand) = @_;
2321     my $productDir = productDir();
2322     print "Starting @{[basename($appPath)]} with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2323
2324     local %ENV = %ENV;
2325     setupMacWebKitEnvironment($productDir);
2326
2327     if (defined($useOpenCommand) && $useOpenCommand == USE_OPEN_COMMAND) {
2328         return system("open", "-W", "-a", $appPath, "--args", argumentsForRunAndDebugMacWebKitApp());
2329     }
2330     if (architecture()) {
2331         return system "arch", "-" . architecture(), $appPath, argumentsForRunAndDebugMacWebKitApp();
2332     }
2333     return system { $appPath } $appPath, argumentsForRunAndDebugMacWebKitApp();
2334 }
2335
2336 sub execMacWebKitAppForDebugging($)
2337 {
2338     my ($appPath) = @_;
2339     my $architectureSwitch;
2340     my $argumentsSeparator;
2341
2342     if (debugger() eq "lldb") {
2343         $architectureSwitch = "--arch";
2344         $argumentsSeparator = "--";
2345     } elsif (debugger() eq "gdb") {
2346         $architectureSwitch = "-arch";
2347         $argumentsSeparator = "--args";
2348     } else {
2349         die "Unknown debugger $debugger.\n";
2350     }
2351
2352     my $debuggerPath = `xcrun -find $debugger`;
2353     chomp $debuggerPath;
2354     die "Can't find the $debugger executable.\n" unless -x $debuggerPath;
2355
2356     my $productDir = productDir();
2357     setupMacWebKitEnvironment($productDir);
2358
2359     my @architectureFlags = ($architectureSwitch, architecture());
2360     if (!shouldTargetWebProcess()) {
2361         print "Starting @{[basename($appPath)]} under $debugger with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2362         exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $appPath, argumentsForRunAndDebugMacWebKitApp() or die;
2363     } else {
2364         if (shouldUseXPCServiceForWebProcess()) {
2365             die "Targetting the Web Process is not compatible with using an XPC Service for the Web Process at this time.";
2366         }
2367         
2368         my $webProcessShimPath = File::Spec->catfile($productDir, "SecItemShim.dylib");
2369         my $webProcessPath = File::Spec->catdir($productDir, "WebProcess.app");
2370         my $webKit2ExecutablePath = File::Spec->catfile($productDir, "WebKit2.framework", "WebKit2");
2371
2372         appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", $webProcessShimPath);
2373
2374         print "Starting WebProcess under $debugger with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
2375         exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $webProcessPath, $webKit2ExecutablePath, "-type", "webprocess", "-client-executable", $appPath or die;
2376     }
2377 }
2378
2379 sub debugSafari
2380 {
2381     if (isAppleMacWebKit()) {
2382         checkFrameworks();
2383         execMacWebKitAppForDebugging(safariPath());
2384     }
2385
2386     return 1; # Unsupported platform; can't debug Safari on this platform.
2387 }
2388
2389 sub runSafari
2390 {
2391     if (isIOSWebKit()) {
2392         return runIOSWebKitApp(mobileSafariBundle());
2393     }
2394
2395     if (isAppleMacWebKit()) {
2396         return runMacWebKitApp(safariPath());
2397     }
2398
2399     if (isAppleWinWebKit()) {
2400         my $result;
2401         my $productDir = productDir();
2402         my $webKitLauncherPath = File::Spec->catfile(productDir(), "WinLauncher.exe");
2403         return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
2404     }
2405
2406     return 1; # Unsupported platform; can't run Safari on this platform.
2407 }
2408
2409 sub runMiniBrowser
2410 {
2411     if (isAppleMacWebKit()) {
2412         return runMacWebKitApp(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2413     }
2414
2415     return 1;
2416 }
2417
2418 sub debugMiniBrowser
2419 {
2420     if (isAppleMacWebKit()) {
2421         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
2422     }
2423     
2424     return 1;
2425 }
2426
2427 sub runWebKitTestRunner
2428 {
2429     if (isAppleMacWebKit()) {
2430         return runMacWebKitApp(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2431     }
2432
2433     return 1;
2434 }
2435
2436 sub debugWebKitTestRunner
2437 {
2438     if (isAppleMacWebKit()) {
2439         execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "WebKitTestRunner"));
2440     }
2441
2442     return 1;
2443 }
2444
2445 sub readRegistryString
2446 {
2447     my ($valueName) = @_;
2448     chomp(my $string = `regtool --wow32 get "$valueName"`);
2449     return $string;
2450 }
2451
2452 sub writeRegistryString
2453 {
2454     my ($valueName, $string) = @_;
2455
2456     my $error = system "regtool", "--wow32", "set", "-s", $valueName, $string;
2457
2458     # On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still
2459     # return a successful exit code. So we double-check here that the value we tried to write to the
2460     # registry was really written.
2461     return !$error && readRegistryString($valueName) eq $string;
2462 }
2463
2464 sub formatBuildTime($)
2465 {
2466     my ($buildTime) = @_;
2467
2468     my $buildHours = int($buildTime / 3600);
2469     my $buildMins = int(($buildTime - $buildHours * 3600) / 60);
2470     my $buildSecs = $buildTime - $buildHours * 3600 - $buildMins * 60;
2471
2472     if ($buildHours) {
2473         return sprintf("%dh:%02dm:%02ds", $buildHours, $buildMins, $buildSecs);
2474     }
2475     return sprintf("%02dm:%02ds", $buildMins, $buildSecs);
2476 }
2477
2478 sub runSvnUpdateAndResolveChangeLogs(@)
2479 {
2480     my @svnOptions = @_;
2481     open UPDATE, "-|", "svn", "update", @svnOptions or die;
2482     my @conflictedChangeLogs;
2483     while (my $line = <UPDATE>) {
2484         print $line;
2485         $line =~ m/^C\s+(.+?)[\r\n]*$/;
2486         if ($1) {
2487           my $filename = normalizePath($1);
2488           push @conflictedChangeLogs, $filename if basename($filename) eq "ChangeLog";
2489         }
2490     }
2491     close UPDATE or die;
2492
2493     if (@conflictedChangeLogs) {
2494         print "Attempting to merge conflicted ChangeLogs.\n";
2495         my $resolveChangeLogsPath = File::Spec->catfile(sourceDir(), "Tools", "Scripts", "resolve-ChangeLogs");
2496         (system($resolveChangeLogsPath, "--no-warnings", @conflictedChangeLogs) == 0)
2497             or die "Could not open resolve-ChangeLogs script: $!.\n";
2498     }
2499 }
2500
2501 sub runGitUpdate()
2502 {
2503     # Doing a git fetch first allows setups with svn-remote.svn.fetch = trunk:refs/remotes/origin/master
2504     # to perform the rebase much much faster.
2505     system("git", "fetch");
2506     if (isGitSVN()) {
2507         system("git", "svn", "rebase") == 0 or die;
2508     } else {
2509         # This will die if branch.$BRANCHNAME.merge isn't set, which is
2510         # almost certainly what we want.
2511         system("git", "pull") == 0 or die;
2512     }
2513 }
2514
2515 1;