| #!/usr/bin/perl -w |
| |
| # Copyright (C) 2014 Apple Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
| # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| # Checks if Xcode supports building a command line tool for the iOS Simulator. |
| # If not, then creates xcspec files in the iOS Simulator SDK for a command line |
| # tool product- and package- type using the definitions in the OS X SDK for the |
| # same types. |
| |
| # FIXME: We should also configure Xcode to support building a command line tool for an iOS device. |
| |
| use strict; |
| use warnings; |
| |
| use English; |
| use File::Basename; |
| use File::Find; |
| use File::Spec; |
| use File::Temp qw(tempfile); |
| |
| sub createXcodeSpecificationFromSpecificationAndId($$$); |
| sub readXcodeSpecificationById($$); |
| sub xcodeSDKSpecificationsPath($); |
| |
| use constant COMMAND_LINE_PACKAGE_TYPE => "com.apple.package-type.mach-o-executable"; |
| use constant COMMAND_LINE_PRODUCT_TYPE => "com.apple.product-type.tool"; |
| |
| # FIXME: We should only require running as root if needed. It's not necessary to run as root if |
| # Xcode was installed by the user, say via a download from <http://developer.apple.com>. |
| if ($EFFECTIVE_USER_ID) { |
| print STDERR basename($0) . " must be run as root.\n"; |
| exit 1; |
| } |
| |
| my $iosSimulatorSDKSpecificationsPath = xcodeSDKSpecificationsPath("iphonesimulator"); |
| |
| my @xcodeSpecificationFiles; |
| sub wanted |
| { |
| my $file = $_; |
| |
| # Ignore hidden files/directories. |
| if ($file =~ /^\../) { |
| $File::Find::prune = 1; |
| return; |
| } |
| |
| if (!-f $file || $file !~ /\.xcspec$/) { |
| return; |
| } |
| |
| push @xcodeSpecificationFiles, $File::Find::name; |
| } |
| |
| find(\&wanted, $iosSimulatorSDKSpecificationsPath); |
| |
| my $hasPackageTypeForCommandLineTool; |
| my $hasProductTypeForCommandLineTool; |
| foreach my $specificationFile (@xcodeSpecificationFiles) { |
| last if $hasPackageTypeForCommandLineTool && $hasProductTypeForCommandLineTool; |
| if (!$hasPackageTypeForCommandLineTool && readXcodeSpecificationById($specificationFile, COMMAND_LINE_PACKAGE_TYPE)) { |
| $hasPackageTypeForCommandLineTool = 1; |
| next; |
| } |
| if (!$hasProductTypeForCommandLineTool && readXcodeSpecificationById($specificationFile, COMMAND_LINE_PRODUCT_TYPE)) { |
| $hasProductTypeForCommandLineTool = 1; |
| next; |
| } |
| } |
| |
| if ($hasPackageTypeForCommandLineTool && $hasProductTypeForCommandLineTool) { |
| exit 0; # Xcode knows how to build a command line tool for the iOS Simulator. |
| } |
| |
| my $macosxSDKSpecificationsPath = xcodeSDKSpecificationsPath("macosx"); |
| if (!$hasPackageTypeForCommandLineTool) { |
| my $packageTypesForMacOSXPath = File::Spec->catfile($macosxSDKSpecificationsPath, "MacOSX Package Types.xcspec"); |
| my $packageTypesForWebKitDevelopmentPath = File::Spec->catfile($iosSimulatorSDKSpecificationsPath, "iPhone Simulator PackageTypes For WebKit Development.xcspec"); |
| createXcodeSpecificationFromSpecificationAndId($packageTypesForWebKitDevelopmentPath, $packageTypesForMacOSXPath, COMMAND_LINE_PACKAGE_TYPE); |
| } |
| |
| if (!$hasProductTypeForCommandLineTool) { |
| my $productTypesForMacOSXPath = File::Spec->catfile($macosxSDKSpecificationsPath, "MacOSX Product Types.xcspec"); |
| my $productTypesForWebKitDevelopmentPath = File::Spec->catfile($iosSimulatorSDKSpecificationsPath, "iPhone Simulator ProductTypes For WebKit Development.xcspec"); |
| createXcodeSpecificationFromSpecificationAndId($productTypesForWebKitDevelopmentPath, $productTypesForMacOSXPath, COMMAND_LINE_PRODUCT_TYPE); |
| } |
| |
| exit 0; |
| |
| sub writeXcodeSpecification($$) |
| { |
| my ($xcodeSpecificationFile, $specification) = @_; |
| my ($tempFileHandle, $tempFilename) = tempfile("webkit-xcspecXXXXXXX", UNLINK => 1); |
| print $tempFileHandle $specification; |
| close($tempFileHandle); |
| system("/usr/libexec/PlistBuddy -x -c 'clear array' '$xcodeSpecificationFile' > /dev/null") == 0 or die "PlistBuddy exited with $?: $!"; |
| system("/usr/libexec/PlistBuddy -x -c 'add 0 dict' '$xcodeSpecificationFile' > /dev/null") == 0 or die "PlistBuddy exited with $?: $!"; |
| system("/usr/libexec/PlistBuddy -x -c 'merge $tempFilename 0' '$xcodeSpecificationFile' > /dev/null") == 0 or die "PlistBuddy exited with $?: $!"; |
| } |
| |
| sub readXcodeSpecificationById($$) |
| { |
| my ($xcodeSpecificationFile, $id) = @_; |
| open(PLIST_BUDDY, "-|", "/usr/libexec/PlistBuddy", "-x", "-c", "Print", $xcodeSpecificationFile) or die "Failed to run PlistBuddy: $!"; |
| my $foundStartOfSpecificationsArray; |
| while (<PLIST_BUDDY>) { |
| if (/^<array>$/) { |
| $foundStartOfSpecificationsArray = 1; |
| last; |
| } |
| } |
| if (!$foundStartOfSpecificationsArray) { |
| return ""; # Not a Xcode specification file. |
| } |
| my $position = -1; |
| my $foundIdentfierKey = 0; |
| my $foundSpecification = 0; |
| while (<PLIST_BUDDY>) { |
| if (/^\s<dict>$/) { |
| ++$position; |
| next; |
| } |
| if (!$foundIdentfierKey && /^\s\s<key>Identifier<\/key>$/) { |
| $foundIdentfierKey = 1; |
| next; |
| } |
| if ($foundIdentfierKey && /^\s\s<string>([^<]+)<\/string>$/) { |
| if ($1 eq $id) { |
| $foundSpecification = 1; |
| last; |
| } |
| $foundIdentfierKey = 0; |
| next; |
| } |
| } |
| close(PLIST_BUDDY); |
| if ($foundSpecification && $position >= 0) { |
| chomp(my $result = `/usr/libexec/PlistBuddy -x -c 'Print $position' '$xcodeSpecificationFile'`); |
| die "Failed to run PlistBuddy" if $?; |
| return $result; |
| } |
| return ""; # Did not find id. |
| } |
| |
| sub xcodeSDKSpecificationsPath($) |
| { |
| my ($sdkName) = @_; |
| chomp(my $sdkPlatformDirectory = `xcrun --sdk '$sdkName' --show-sdk-platform-path`); |
| die "Failed to get SDK platform path from xcrun" if $?; |
| |
| return File::Spec->catdir($sdkPlatformDirectory, "Developer", "Library", "Xcode", "Specifications"); |
| } |
| |
| sub createXcodeSpecificationFromSpecificationAndId($$$) |
| { |
| my ($targetXcodeSpecificationFile, $sourceXcodeSpecificationFile, $id) = @_; |
| my $specification = readXcodeSpecificationById($sourceXcodeSpecificationFile, $id); |
| if (!$specification) { |
| die "Failed to find '$id' in '$sourceXcodeSpecificationFile'.\n"; |
| } |
| writeXcodeSpecification($targetXcodeSpecificationFile, $specification); |
| print "Successfully created '$targetXcodeSpecificationFile'.\n"; |
| } |