6c447564d2e0380e83c6d36e30773f637f3d97aa
[WebKit-https.git] / Tools / Scripts / configure-xcode-for-ios-development
1 #!/usr/bin/perl -w
2
3 # Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 # 1.  Redistributions of source code must retain the above copyright
9 #     notice, this list of conditions and the following disclaimer.
10 # 2.  Redistributions in binary form must reproduce the above copyright
11 #     notice, this list of conditions and the following disclaimer in the
12 #     documentation and/or other materials provided with the distribution.
13 #
14 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
15 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25 # Checks if Xcode supports building a command line tool for the iOS Simulator.
26 # If not, then creates xcspec files in the iOS Simulator SDK for a command line
27 # tool product- and package- type using the definitions in the OS X SDK for the
28 # same types.
29
30 use strict;
31 use warnings;
32
33 use English;
34 use File::Basename;
35 use File::Find;
36 use File::Spec;
37 use File::Temp qw(tempfile);
38 use FindBin;
39 use lib $FindBin::Bin;
40 use webkitdirs;
41
42 sub copyMissingHeadersToIPhoneOSSDKIfNeeded();
43 sub createXcodeSpecificationFilesForSDKIfNeeded($);
44 sub createXcodeSpecificationFromSpecificationAndId($$$);
45 sub sdkDirectory($);
46 sub sdkPlatformDirectory($);
47 sub readXcodeSpecificationById($$);
48 sub xcodeSDKSpecificationsPath($);
49
50 use constant COMMAND_LINE_PACKAGE_TYPE => "com.apple.package-type.mach-o-executable";
51 use constant COMMAND_LINE_PRODUCT_TYPE => "com.apple.product-type.tool";
52 use constant SDK_TO_XCSPEC_NAME_MAP => +{ "iphoneos" => "iPhoneOS", "iphonesimulator" => "iPhone Simulator " };
53
54 # FIXME: We should only require running as root if needed. It's not necessary to run as root if
55 #        Xcode was installed by the user, say via a download from <http://developer.apple.com>.
56 if ($EFFECTIVE_USER_ID) {
57     print STDERR basename($0) . " must be run as root.\n";
58     exit 1;
59 }
60
61 for my $sdk (qw(iphoneos iphonesimulator)) {
62     createXcodeSpecificationFilesForSDKIfNeeded($sdk);
63 }
64
65 copyMissingHeadersToIPhoneOSSDKIfNeeded();
66
67 exit 0;
68
69 sub copyMissingHeadersToIPhoneOSSDKIfNeeded()
70 {
71     my @missingHeaders = qw(
72         /usr/include/crt_externs.h
73         /usr/include/MacErrors.h
74         /usr/include/mach/mach_types.defs
75         /usr/include/mach/machine/machine_types.defs
76         /usr/include/mach/std_types.defs
77         /usr/include/objc/objc-class.h
78         /usr/include/objc/objc-runtime.h
79         /usr/include/objc/Protocol.h
80         /usr/include/readline/history.h
81         /usr/include/readline/readline.h
82         /usr/include/sqlite3_private.h
83     );
84
85     my $iphoneosSDKDirectory = sdkDirectory("iphoneos");
86     my $iphonesimulatorSDKDirectory = sdkDirectory("iphonesimulator");
87
88     for my $header (@missingHeaders) {
89         my $iphoneosSDKPath = File::Spec->canonpath(File::Spec->catfile($iphoneosSDKDirectory, $header));
90         next if (-f $iphoneosSDKPath);
91
92         my $iphonesimulatorSDKPath = File::Spec->canonpath(File::Spec->catfile($iphonesimulatorSDKDirectory, $header));
93         system("/usr/bin/ditto", $iphonesimulatorSDKPath, $iphoneosSDKPath);
94         die "Could not copy $iphonesimulatorSDKPath to $iphoneosSDKPath: $!" if exitStatus($?);
95         print "Successfully copied $iphonesimulatorSDKPath to $iphoneosSDKPath.\n";
96     }
97 }
98
99 sub createXcodeSpecificationFilesForSDKIfNeeded($)
100 {
101     my ($sdk) = @_;
102     my $sdkSpecificationsPath = xcodeSDKSpecificationsPath($sdk);
103
104     local @::xcodeSpecificationFiles;
105     sub wanted
106     {
107         my $file = $_;
108
109         # Ignore hidden files/directories.
110         if ($file =~ /^\../) {
111             $File::Find::prune = 1;
112             return;
113         }
114
115         if (!-f $file || $file !~ /\.xcspec$/) {
116             return;
117         }
118
119         push @::xcodeSpecificationFiles, $File::Find::name;
120     }
121
122     find(\&wanted, $sdkSpecificationsPath);
123
124     my $hasPackageTypeForCommandLineTool;
125     my $hasProductTypeForCommandLineTool;
126     foreach my $specificationFile (@::xcodeSpecificationFiles) {
127         last if $hasPackageTypeForCommandLineTool && $hasProductTypeForCommandLineTool;
128         if (!$hasPackageTypeForCommandLineTool && readXcodeSpecificationById($specificationFile, COMMAND_LINE_PACKAGE_TYPE)) {
129             $hasPackageTypeForCommandLineTool = 1;
130             next;
131         }
132         if (!$hasProductTypeForCommandLineTool && readXcodeSpecificationById($specificationFile, COMMAND_LINE_PRODUCT_TYPE)) {
133             $hasProductTypeForCommandLineTool = 1;
134             next;
135         }
136     }
137
138     if ($hasPackageTypeForCommandLineTool && $hasProductTypeForCommandLineTool) {
139         return; # Xcode knows how to build a command line tool for $sdk.
140     }
141
142     my $fileNamePrefix = SDK_TO_XCSPEC_NAME_MAP->{$sdk};
143
144     my $macosxSDKSpecificationsPath = xcodeSDKSpecificationsPath("macosx");
145     if (!$hasPackageTypeForCommandLineTool) {
146         my $packageTypesForMacOSXPath = File::Spec->catfile($macosxSDKSpecificationsPath, "MacOSX Package Types.xcspec");
147         my $packageTypesForWebKitDevelopmentPath = File::Spec->catfile($sdkSpecificationsPath, "${fileNamePrefix}PackageTypes For WebKit Development.xcspec");
148         createXcodeSpecificationFromSpecificationAndId($packageTypesForWebKitDevelopmentPath, $packageTypesForMacOSXPath, COMMAND_LINE_PACKAGE_TYPE);
149     }
150
151     if (!$hasProductTypeForCommandLineTool) {
152         my $productTypesForMacOSXPath = File::Spec->catfile($macosxSDKSpecificationsPath, "MacOSX Product Types.xcspec");
153         my $productTypesForWebKitDevelopmentPath = File::Spec->catfile($sdkSpecificationsPath, "${fileNamePrefix}ProductTypes For WebKit Development.xcspec");
154         createXcodeSpecificationFromSpecificationAndId($productTypesForWebKitDevelopmentPath, $productTypesForMacOSXPath, COMMAND_LINE_PRODUCT_TYPE);
155     }
156 }
157
158 sub sdkDirectory($)
159 {
160     my ($sdkName) = @_;
161     chomp(my $sdkDirectory = `xcrun --sdk '$sdkName' --show-sdk-path`);
162     die "Failed to get SDK path from xcrun: $!" if exitStatus($?);
163     return $sdkDirectory;
164 }
165
166 sub sdkPlatformDirectory($)
167 {
168     my ($sdkName) = @_;
169     chomp(my $sdkPlatformDirectory = `xcrun --sdk '$sdkName' --show-sdk-platform-path`);
170     die "Failed to get SDK platform path from xcrun: $!" if exitStatus($?);
171     return $sdkPlatformDirectory;
172 }
173
174 sub writeXcodeSpecification($$)
175 {
176     my ($xcodeSpecificationFile, $specification) = @_;
177     my ($tempFileHandle, $tempFilename) = tempfile("webkit-xcspecXXXXXXX", UNLINK => 1);
178     print $tempFileHandle $specification;
179     close($tempFileHandle);
180     system("/usr/libexec/PlistBuddy -x -c 'clear array' '$xcodeSpecificationFile' > /dev/null") == 0 or die "PlistBuddy exited with $?: $!";
181     system("/usr/libexec/PlistBuddy -x -c 'add 0 dict' '$xcodeSpecificationFile' > /dev/null") == 0 or die "PlistBuddy exited with $?: $!";
182     system("/usr/libexec/PlistBuddy -x -c 'merge $tempFilename 0' '$xcodeSpecificationFile' > /dev/null") == 0 or die "PlistBuddy exited with $?: $!";
183 }
184
185 sub readXcodeSpecificationById($$)
186 {
187     my ($xcodeSpecificationFile, $id) = @_;
188     open(PLIST_BUDDY, "-|", "/usr/libexec/PlistBuddy", "-x", "-c", "Print", $xcodeSpecificationFile) or die "Failed to run PlistBuddy: $!";
189     my $foundStartOfSpecificationsArray;
190     while (<PLIST_BUDDY>) {
191         if (/^<array>$/) {
192             $foundStartOfSpecificationsArray = 1;
193             last;
194         }
195     }
196     if (!$foundStartOfSpecificationsArray) {
197        return ""; # Not a Xcode specification file.
198     }
199     my $position = -1;
200     my $foundIdentfierKey = 0;
201     my $foundSpecification = 0;
202     while (<PLIST_BUDDY>) {
203         if (/^\s<dict>$/) {
204             ++$position;
205             next;
206         }
207         if (!$foundIdentfierKey && /^\s\s<key>Identifier<\/key>$/) {
208             $foundIdentfierKey = 1;
209             next;
210         }
211         if ($foundIdentfierKey && /^\s\s<string>([^<]+)<\/string>$/) {
212             if ($1 eq $id) {
213                 $foundSpecification = 1;
214                 last;
215             }
216             $foundIdentfierKey = 0;
217             next;
218         }
219     }
220     close(PLIST_BUDDY);
221     if ($foundSpecification && $position >= 0) {
222         chomp(my $result = `/usr/libexec/PlistBuddy -x -c 'Print $position' '$xcodeSpecificationFile'`);
223         die "Failed to run PlistBuddy" if $?;
224         return $result;
225     }
226     return ""; # Did not find id.
227 }
228
229 sub xcodeSDKSpecificationsPath($)
230 {
231     my ($sdkName) = @_;
232
233     return File::Spec->catdir(sdkPlatformDirectory($sdkName), "Developer", "Library", "Xcode", "Specifications");
234 }
235
236 sub createXcodeSpecificationFromSpecificationAndId($$$)
237 {
238     my ($targetXcodeSpecificationFile, $sourceXcodeSpecificationFile, $id) = @_;
239     my $specification = readXcodeSpecificationById($sourceXcodeSpecificationFile, $id);
240     if (!$specification) {
241         die "Failed to find '$id' in '$sourceXcodeSpecificationFile'.\n";
242     }
243     writeXcodeSpecification($targetXcodeSpecificationFile, $specification);
244     print "Successfully created '$targetXcodeSpecificationFile'.\n";
245 }