WebKitQt:
[WebKit-https.git] / WebKitTools / Scripts / webkitdirs.pm
1 # Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 #
7 # 1.  Redistributions of source code must retain the above copyright
8 #     notice, this list of conditions and the following disclaimer. 
9 # 2.  Redistributions in binary form must reproduce the above copyright
10 #     notice, this list of conditions and the following disclaimer in the
11 #     documentation and/or other materials provided with the distribution. 
12 # 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
13 #     its contributors may be used to endorse or promote products derived
14 #     from this software without specific prior written permission. 
15 #
16 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
17 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
20 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 # Module to share code to get to WebKit directories.
28
29 use strict;
30 use warnings;
31 use FindBin;
32
33 BEGIN {
34    use Exporter   ();
35    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
36    $VERSION     = 1.00;
37    @ISA         = qw(Exporter);
38    @EXPORT      = qw(&chdirWebKit &baseProductDir &productDir &XcodeOptions &XcodeOptionString &XcodeOptionStringNoConfig &passedConfiguration &setConfiguration &safariPath &checkFrameworks &currentSVNRevision);
39    %EXPORT_TAGS = ( );
40    @EXPORT_OK   = ();
41 }
42
43 our @EXPORT_OK;
44
45 my $baseProductDir;
46 my @baseProductDirOption;
47 my $configuration;
48 my $configurationProductDir;
49 my $sourceDir;
50 my $currentSVNRevision;
51 my $osXVersion;
52
53 # Variables for Win32 support
54 my $vcBuildPath;
55 my $windowsTmpPath;
56
57 sub determineSourceDir
58 {
59     return if $sourceDir;
60     $sourceDir = $FindBin::Bin;
61     
62     # walks up path checking each directory to see if it is the main WebKit project dir, 
63     # defined by containing JavaScriptCore, WebCore, and WebKit
64     until ((-d "$sourceDir/JavaScriptCore" && -d "$sourceDir/WebCore" && -d "$sourceDir/WebKit") || (-d "$sourceDir/Internal" && -d "$sourceDir/OpenSource"))
65     {
66         if ($sourceDir !~ s|/[^/]+$||) {
67             die "Could not find top level webkit directory above source directory using FindBin.\n";
68         }
69     }
70
71     $sourceDir = "$sourceDir/OpenSource" if -d "$sourceDir/OpenSource";
72 }
73
74 # used for scripts which are stored in a non-standard location
75 sub setSourceDir($)
76 {
77     ($sourceDir) = @_;
78 }
79
80 sub determineBaseProductDir
81 {
82     return if defined $baseProductDir;
83     determineSourceDir();
84     if (isOSX()) {
85         open PRODUCT, "defaults read com.apple.Xcode PBXProductDirectory 2> /dev/null |" or die;
86         $baseProductDir = <PRODUCT>;
87         close PRODUCT;
88         if ($baseProductDir) {
89             chomp $baseProductDir;
90             undef $baseProductDir unless $baseProductDir =~ /^\//;
91         }
92     } else {
93         $baseProductDir = $ENV{"WEBKITOUTPUTDIR"};
94         if (isCygwin() && $baseProductDir) {
95             my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`;
96             chomp $unixBuildPath;
97             $baseProductDir = $unixBuildPath;
98         }
99     }
100
101     if ($baseProductDir && isOSX()) {
102         $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|;
103         $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|;
104         $baseProductDir =~ s|^~/|$ENV{HOME}/|;
105         die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/;
106         die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/;
107         @baseProductDirOption = ();
108     }
109
110     if (!defined($baseProductDir)) {
111         $baseProductDir = "$sourceDir/WebKitBuild";
112         @baseProductDirOption = ("SYMROOT=$baseProductDir") if (isOSX());
113         if (isCygwin()) {
114             my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`;
115             chomp $dosBuildPath;
116             $ENV{"WEBKITOUTPUTDIR"} = $dosBuildPath;
117         }
118     }
119 }
120
121 sub setBaseProductDir($)
122 {
123     ($baseProductDir) = @_;
124 }
125
126 sub determineConfiguration
127 {
128     return if defined $configuration;
129     determineBaseProductDir();
130     if (open CONFIGURATION, "$baseProductDir/Configuration") {
131         $configuration = <CONFIGURATION>;
132         close CONFIGURATION;
133     }
134     if ($configuration) {
135         chomp $configuration;
136         # compatibility for people who have old Configuration files
137         $configuration = "Release" if $configuration eq "Deployment";
138         $configuration = "Debug" if $configuration eq "Development";
139     } else {
140         $configuration = "Release";
141     }
142 }
143
144 sub determineConfigurationProductDir
145 {
146     return if defined $configurationProductDir;
147     determineBaseProductDir();
148     if(isCygwin()) {
149         $configurationProductDir = "$baseProductDir/bin";
150     } else {
151         determineConfiguration();
152         $configurationProductDir = "$baseProductDir/$configuration";
153     }
154 }
155
156 sub setConfigurationProductDir($)
157 {
158     ($configurationProductDir) = @_;
159 }
160
161 sub determineCurrentSVNRevision
162 {
163     return if defined $currentSVNRevision;
164     determineSourceDir();
165     my $svnInfo = `LC_ALL=C svn info $sourceDir | grep Revision:`;
166     ($currentSVNRevision) = ($svnInfo =~ m/Revision: (\d+).*/g);
167     die "Unable to determine current SVN revision in $sourceDir" unless (defined $currentSVNRevision);
168     return $currentSVNRevision;
169 }
170
171
172 sub chdirWebKit
173 {
174     determineSourceDir();
175     chdir $sourceDir or die;
176 }
177
178 sub baseProductDir
179 {
180     determineBaseProductDir();
181     return $baseProductDir;
182 }
183
184 sub sourceDir
185 {
186     determineSourceDir();
187     return $sourceDir;
188 }
189
190 sub productDir
191 {
192     determineConfigurationProductDir();
193     return $configurationProductDir;
194 }
195
196 sub configuration()
197 {
198     determineConfiguration();
199     return $configuration;
200 }
201
202 sub currentSVNRevision
203 {
204     determineCurrentSVNRevision();
205     return $currentSVNRevision;
206 }
207
208 sub XcodeOptions
209 {
210     determineBaseProductDir();
211     determineConfiguration();
212     return (@baseProductDirOption, "-configuration", $configuration);
213 }
214
215 sub XcodeOptionString
216 {
217     return join " ", XcodeOptions();
218 }
219
220 sub XcodeOptionStringNoConfig
221 {
222     return join " ", @baseProductDirOption;
223 }
224
225 my $passedConfiguration;
226 my $searchedForPassedConfiguration;
227 sub determinePassedConfiguration
228 {
229     return if $searchedForPassedConfiguration;
230     $searchedForPassedConfiguration = 1;
231     for my $i (0 .. $#ARGV) {
232         my $opt = $ARGV[$i];
233         if ($opt =~ /^--debug$/i || $opt =~ /^--devel/i) {
234             splice(@ARGV, $i, 1);
235             $passedConfiguration = "Debug";
236             return;
237         }
238         if ($opt =~ /^--release$/i || $opt =~ /^--deploy/i) {
239             splice(@ARGV, $i, 1);
240             $passedConfiguration = "Release";
241             return;
242         }
243     }
244     $passedConfiguration = undef;
245 }
246
247 sub passedConfiguration
248 {
249     determinePassedConfiguration();
250     return $passedConfiguration;
251 }
252
253 sub setConfiguration
254 {
255     if (my $config = shift @_) {
256         $configuration = $config;
257         return;
258     }
259
260     determinePassedConfiguration();
261     $configuration = $passedConfiguration if $passedConfiguration;
262 }
263
264 # Locate Safari.
265 sub safariPath
266 {
267     # Use WEBKIT_SAFARI environment variable if present.
268     my $safariBundle = $ENV{WEBKIT_SAFARI};
269     if (!$safariBundle) {
270         determineConfigurationProductDir();
271         # Use Safari.app in product directory if present (good for Safari development team).
272         if (isOSX() && -d "$configurationProductDir/Safari.app") {
273             $safariBundle = "$configurationProductDir/Safari.app";
274         } elsif (isCygwin() && -x "$configurationProductDir/bin/Safari.exe") {
275             $safariBundle = "$configurationProductDir/bin/Safari.exe";
276         } else {
277             # Otherwise use the installed Safari
278             if (isOSX()) {
279                 $safariBundle = "/Applications/Safari.app";
280             } elsif (isCygwin()) {
281                 chomp(my $programFiles = `cygpath -u '$ENV{"PROGRAMFILES"}'`);
282                 $safariBundle = "$programFiles/Safari/Safari.exe";
283             }
284         }
285     }
286     my $safariPath;
287     if (isOSX()) {
288         $safariPath = "$safariBundle/Contents/MacOS/Safari";
289     } elsif (isCygwin()) {
290         $safariPath = $safariBundle;
291     }
292     die "Can't find executable at $safariPath.\n" if isOSX() && !-x $safariPath;
293     return $safariPath;
294 }
295
296 sub builtDylibPathForName
297 {
298     my $framework = shift;
299     determineConfigurationProductDir();
300     if (isQt() or isGdk()) {
301         return "$configurationProductDir/$framework";
302     }
303     if (isOSX()) {
304         return "$configurationProductDir/$framework.framework/Versions/A/$framework";
305     }
306     if (isCygwin()) {
307         if ($framework eq "JavaScriptCore") {
308                 return "$baseProductDir/lib/$framework.lib";
309         } else {
310             return "$baseProductDir/$framework.intermediate/$configuration/$framework.intermediate/$framework.lib";
311         }
312     }
313
314     die "Unsupported platform, can't determine built library locations.";
315 }
316
317 # Check to see that all the frameworks are built.
318 sub checkFrameworks
319 {
320     return if isCygwin();
321     my @frameworks = ("JavaScriptCore", "WebCore");
322     push(@frameworks, "WebKit") if isOSX() and not isGdk() and not isQt();
323     for my $framework (@frameworks) {
324         my $path = builtDylibPathForName($framework);
325         die "Can't find built framework at \"$path\".\n" unless -x $path;
326     }
327 }
328
329 sub hasSVGSupport
330 {
331     return 0 if isCygwin();
332
333     my $path = shift;
334
335     if (isQt()) {
336         return 1;
337     }
338
339     if (isGdk() and $path =~ /WebCore/) {
340         $path .= "/../lib/libWebKitGdk.so";
341     }
342
343     open NM, "-|", "nm", $path or die;
344     my $hasSVGSupport = 0;
345     while (<NM>) {
346         $hasSVGSupport = 1 if /SVGElement/;
347     }
348     close NM;
349     return $hasSVGSupport;
350 }
351
352 sub removeLibraryDependingOnSVG
353 {
354     my $frameworkName = shift;
355     my $shouldHaveSVG = shift;
356
357     my $path = builtDylibPathForName($frameworkName);
358     return unless -x $path;
359
360     my $hasSVG = hasSVGSupport($path);
361     system "rm -f $path" if ($shouldHaveSVG xor $hasSVG);
362 }
363
364 sub checkWebCoreSVGSupport
365 {
366     my $required = shift;
367     my $framework = "WebCore";
368     my $path = builtDylibPathForName($framework);
369     my $hasSVG = hasSVGSupport($path);
370     if ($required && !$hasSVG) {
371         die "$framework at \"$path\" does not include SVG Support, please run build-webkit --svg\n";
372     }
373     return $hasSVG;
374 }
375
376 sub isQt()
377 {
378     # Allow override in case QTDIR is not set.
379     for my $i (0 .. $#ARGV) {
380         my $opt = $ARGV[$i];
381         if ($opt =~ /^--qt/i ) {
382             return 1;
383         }
384     }
385     return defined($ENV{'QTDIR'})
386 }
387
388 sub isGdk()
389 {
390     for my $i (0 .. $#ARGV) {
391         my $opt = $ARGV[$i];
392         if ($opt =~ /^--gdk$/i ) {
393             return 1;
394         }
395     }
396     return 0;
397 }
398
399 sub isCygwin()
400 {
401     return ($^O eq "cygwin");
402 }
403
404 sub isOSX()
405 {
406     return ($^O eq "darwin");
407 }
408
409 sub determineOSXVersion()
410 {
411     return if $osXVersion;
412
413     if (!isOSX()) {
414         $osXVersion = -1;
415         return;
416     }
417
418     my $version = `sw_vers -productVersion`;
419     my @splitVersion = split(/\./, $version);
420     @splitVersion >= 2 or die "Invalid version $version";
421     $osXVersion = {
422             "major" => $splitVersion[0],
423             "minor" => $splitVersion[1],
424             "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0),
425     };
426 }
427
428 sub osXVersion()
429 {
430     determineOSXVersion();
431     return $osXVersion;
432 }
433
434 sub isTiger()
435 {
436     return isOSX() && osXVersion()->{"minor"} == 4;
437 }
438
439 sub isLeopard()
440 {
441     return isOSX() && osXVersion()->{"minor"} == 5;
442 }
443
444 sub launcherPath()
445 {
446     my $relativeScriptsPath = File::Spec->catpath("", File::Spec->abs2rel(dirname($0), getcwd()), "");
447     if (isGdk() || isQt()) {
448         return "$relativeScriptsPath/run-launcher";
449     } elsif (isOSX() || isCygwin()) {
450         return "$relativeScriptsPath/run-safari";
451     }
452 }
453
454 sub launcherName()
455 {
456     if (isGdk()) {
457         return "GdkLauncher";
458     } elsif (isQt()) {
459         return "QtLauncher";
460     } elsif (isOSX() || isCygwin()) {
461         return "Safari";
462     }
463 }
464
465 sub checkRequiredSystemConfig
466 {
467     if (isOSX()) {
468         chomp(my $productVersion = `sw_vers -productVersion`);
469         if ($productVersion lt "10.4") {
470             print "*************************************************************\n";
471             print "Mac OS X Version 10.4.0 or later is required to build WebKit.\n";
472             print "You have " . $productVersion . ", thus the build will most likely fail.\n";
473             print "*************************************************************\n";
474         }
475         my $xcodeVersion = `xcodebuild -version`;
476         if ($xcodeVersion !~ /DevToolsCore-(\d+)/ || $1 < 747) {
477             print "*************************************************************\n";
478             print "Xcode Version 2.3 or later is required to build WebKit.\n";
479             print "You have an earlier version of Xcode, thus the build will\n";
480             print "most likely fail.  The latest Xcode is available from the web:\n";
481             print "http://developer.apple.com/tools/xcode\n";
482             print "*************************************************************\n";
483         }
484     }
485     # Win32 and other platforms may want to check for minimum config
486 }
487
488 sub setupCygwinEnv()
489 {
490     return if !isCygwin();
491     return if $vcBuildPath;
492
493     my $programFilesPath = `cygpath "$ENV{'PROGRAMFILES'}"`;
494     chomp $programFilesPath;
495     $vcBuildPath = "$programFilesPath/Microsoft Visual Studio 8/Common7/IDE/devenv.com";
496     if (! -e $vcBuildPath) {
497         # VC++ not found, try VC++ Express
498         my $vsInstallDir;
499         if ($ENV{'VSINSTALLDIR'}) {
500             $vsInstallDir = $ENV{'VSINSTALLDIR'};
501         } else {
502             $programFilesPath = $ENV{'PROGRAMFILES'} || "C:\\Program Files";
503             $vsInstallDir = "$programFilesPath/Microsoft Visual Studio 8";
504         }
505         $vsInstallDir = `cygpath "$vsInstallDir"`;
506         chomp $vsInstallDir;
507         $vcBuildPath = "$vsInstallDir/Common7/IDE/VCExpress.exe";
508         if (! -e $vcBuildPath) {
509             print "*************************************************************\n";
510             print "Cannot find '$vcBuildPath'\n";
511             print "Please execute the file 'vcvars32.bat' from\n";
512             print "'$programFilesPath\\Microsoft Visual Studio 8\\VC\\bin\\'\n";
513             print "to setup the necessary environment variables.\n";
514             print "*************************************************************\n";
515             die;
516         }
517     }
518
519     chomp($ENV{'WEBKITLIBRARIESDIR'} = `cygpath -wa "$sourceDir/WebKitLibraries/win"`) unless $ENV{'WEBKITLIBRARIESDIR'};
520
521     $windowsTmpPath = `cygpath -w /tmp`;
522     chomp $windowsTmpPath;
523     print "Building results into: ", baseProductDir(), "\n";
524     print "WEBKITOUTPUTDIR is set to: ", $ENV{"WEBKITOUTPUTDIR"}, "\n";
525     print "WEBKITLIBRARIESDIR is set to: ", $ENV{"WEBKITLIBRARIESDIR"}, "\n";
526 }
527
528 sub buildVisualStudioProject($)
529 {
530     my ($project) = @_;
531     setupCygwinEnv();
532
533     my $config = configuration();
534
535     chomp(my $winProjectPath = `cygpath -w "$project"`);
536
537     print "$vcBuildPath $winProjectPath /build $config";
538     my $result = system $vcBuildPath, $winProjectPath, "/build", $config;
539     return $result;
540 }
541
542 sub qtMakeCommand()
543 {
544     chomp(my $mkspec = `qmake -query QMAKE_MKSPECS`);
545     $mkspec .= "/default";
546
547     my $compiler = "";
548     open SPEC, "<$mkspec/qmake.conf" or return "make";
549     while (<SPEC>) {
550         if ($_ =~ /QMAKE_CC\s*=\s*([^\s]+)/) {
551             $compiler = $1;
552         }
553     }
554     close SPEC;
555
556     #print "default spec: " . $mkspec . "\n";
557     #print "compiler found: " . $compiler . "\n";
558
559     if ($compiler eq "cl") {
560         return "nmake";
561     }
562
563     return "make";
564 }
565
566 sub buildQMakeProject($$)
567 {
568     my ($project, $colorize) = @_;
569
570     my @buildArgs = ("-r");
571     my $make = qtMakeCommand();
572
573     my $qmakebin = "qmake"; # Allow override of the qmake binary from $PATH
574     for my $i (0 .. $#ARGV) {
575         my $opt = $ARGV[$i];
576         if ($opt =~ /^--qmake=(.*)/i ) {
577             $qmakebin = $1;
578         } elsif ($opt =~ /^--qmakearg=(.*)/i ) {
579             push @buildArgs, $1;
580         }
581     }
582
583     if ($project ne "WebKit") {
584         die "Qt/Linux builds JavaScriptCore/WebCore/WebKitQt in one shot! Only call it for 'WebKit'.\n";
585     }
586
587     my $config = configuration();
588     my $prefix = $ENV{"WebKitInstallationPrefix"};
589
590     push @buildArgs, "OUTPUT_DIR=" . baseProductDir() . "/$config";
591     push @buildArgs, "CONFIG+=qt-port";
592     push @buildArgs, sourceDir() . "/WebKit.pro";
593
594     print "Calling '$qmakebin @buildArgs' in " . baseProductDir() . "/$config ...\n\n";
595     print "Installation directory: $prefix\n" if(defined($prefix));
596
597     my $dir = baseProductDir();
598     if (! -d $dir) {
599         mkdir $dir or die "Failed to create product directory " . $dir;
600     }
601     $dir = $dir . "/$config";
602     if (! -d $dir) {
603         mkdir $dir or die "Failed to create build directory " . $dir;
604     }
605
606     chdir $dir or die "Failed to cd into " . $dir . "\n";
607
608     my $result = system $qmakebin, @buildArgs;
609     if($result ne 0) {
610        die "Failed to setup build environment using $qmakebin!\n";
611     }
612
613     my $clean = $ENV{"WEBKIT_FULLBUILD"};
614
615     if(defined $clean) {
616       system "$make clean";
617     }
618
619     $result = system "$make";
620     chdir ".." or die;
621     return $result;
622 }
623
624 sub buildQMakeGdkProject($$)
625 {
626     my ($project, $colorize) = @_;
627
628     if ($project ne "WebKit") {
629         die "The Gdk portbuilds JavaScriptCore/WebCore/WebKitQt in one shot! Only call it for 'WebKit'.\n";
630     }
631
632     my $config = configuration();
633     my $prefix = $ENV{"WebKitInstallationPrefix"};
634
635     my @buildArgs = ("-r");
636
637     foreach my $opt (@ARGV) {
638         if ($opt =~ /^--qmakearg=(.*)/i ) {
639             push @buildArgs, $1;
640         }
641     }
642
643     push @buildArgs, "OUTPUT_DIR=" . baseProductDir() . "/$config";
644     push @buildArgs, "CONFIG-=qt";
645     push @buildArgs, "CONFIG+=gdk-port";
646     push @buildArgs, sourceDir() . "/WebKit.pro";
647
648     print "Calling 'qmake @buildArgs' in " . baseProductDir() . "/$config ...\n\n";
649     print "Installation directory: $prefix\n" if(defined($prefix));
650
651     system "mkdir -p " . baseProductDir() . "/$config";
652     chdir baseProductDir() . "/$config" or die "Failed to cd into " . baseProductDir() . "/$config \n";
653
654     my $result = system "qmake-qt4", @buildArgs;
655     $result =  system "qmake", @buildArgs if ($result ne 0);
656     if ($result ne 0) {
657        die "Failed to setup build environment using qmake!\n";
658     }
659
660     my $clean = $ENV{"WEBKIT_FULLBUILD"};
661
662     if (defined $clean) {
663       system "make clean";
664     }
665
666     $result = system "make";
667     chdir ".." or die;
668     return $result;
669 }
670
671 1;