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