OLD | NEW |
---|---|
1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """ | 5 """ |
6 This module contains classes that help to emulate xcodebuild behavior on top of | 6 This module contains classes that help to emulate xcodebuild behavior on top of |
7 other build systems, such as make and ninja. | 7 other build systems, such as make and ninja. |
8 """ | 8 """ |
9 | 9 |
10 import copy | 10 import copy |
11 import gyp.common | 11 import gyp.common |
12 import os | 12 import os |
13 import os.path | 13 import os.path |
14 import re | 14 import re |
15 import shlex | 15 import shlex |
16 import subprocess | 16 import subprocess |
17 import sys | 17 import sys |
18 import tempfile | 18 import tempfile |
19 from gyp.common import GypError | 19 from gyp.common import GypError |
20 | 20 |
21 # Populated lazily by XcodeVersion, for efficiency, and to fix an issue when | |
22 # "xcodebuild" is called too quickly (it has been found to return incorrect | |
23 # version number). | |
24 XCODE_VERSION_CACHE = [] | |
25 | |
21 class XcodeSettings(object): | 26 class XcodeSettings(object): |
22 """A class that understands the gyp 'xcode_settings' object.""" | 27 """A class that understands the gyp 'xcode_settings' object.""" |
23 | 28 |
24 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached | 29 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached |
25 # at class-level for efficiency. | 30 # at class-level for efficiency. |
26 _sdk_path_cache = {} | 31 _sdk_path_cache = {} |
27 _sdk_root_cache = {} | 32 _sdk_root_cache = {} |
28 | 33 |
29 # Populated lazily by GetExtraPlistItems(). Shared by all XcodeSettings, so | 34 # Populated lazily by GetExtraPlistItems(). Shared by all XcodeSettings, so |
30 # cached at class-level for efficiency. | 35 # cached at class-level for efficiency. |
31 _plist_cache = {} | 36 _plist_cache = {} |
32 | 37 |
33 # Populated lazily by GetIOSPostbuilds. Shared by all XcodeSettings, so | 38 # Populated lazily by GetIOSPostbuilds. Shared by all XcodeSettings, so |
34 # cached at class-level for efficiency. | 39 # cached at class-level for efficiency. |
35 _codesigning_key_cache = {} | 40 _codesigning_key_cache = {} |
36 | 41 |
37 # Populated lazily by _XcodeVersion. Shared by all XcodeSettings, so cached | |
38 # at class-level for efficiency. | |
39 _xcode_version_cache = () | |
40 | |
41 def __init__(self, spec): | 42 def __init__(self, spec): |
42 self.spec = spec | 43 self.spec = spec |
43 | 44 |
44 self.isIOS = False | 45 self.isIOS = False |
45 | 46 |
46 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp. | 47 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp. |
47 # This means self.xcode_settings[config] always contains all settings | 48 # This means self.xcode_settings[config] always contains all settings |
48 # for that config -- the per-target settings as well. Settings that are | 49 # for that config -- the per-target settings as well. Settings that are |
49 # the same for all configs are implicitly per-target settings. | 50 # the same for all configs are implicitly per-target settings. |
50 self.xcode_settings = {} | 51 self.xcode_settings = {} |
(...skipping 810 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
861 """Transforms entries like 'Cocoa.framework' in libraries into entries like | 862 """Transforms entries like 'Cocoa.framework' in libraries into entries like |
862 '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc. | 863 '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc. |
863 """ | 864 """ |
864 libraries = [self._AdjustLibrary(library, config_name) | 865 libraries = [self._AdjustLibrary(library, config_name) |
865 for library in libraries] | 866 for library in libraries] |
866 return libraries | 867 return libraries |
867 | 868 |
868 def _BuildMachineOSBuild(self): | 869 def _BuildMachineOSBuild(self): |
869 return GetStdout(['sw_vers', '-buildVersion']) | 870 return GetStdout(['sw_vers', '-buildVersion']) |
870 | 871 |
871 def _XcodeVersion(self): | |
872 if len(XcodeSettings._xcode_version_cache) == 0: | |
873 XcodeSettings._xcode_version_cache = XcodeVersion() | |
874 return XcodeSettings._xcode_version_cache | |
875 | |
876 def _XcodeIOSDeviceFamily(self, configname): | 872 def _XcodeIOSDeviceFamily(self, configname): |
877 family = self.xcode_settings[configname].get('TARGETED_DEVICE_FAMILY', '1') | 873 family = self.xcode_settings[configname].get('TARGETED_DEVICE_FAMILY', '1') |
878 return [int(x) for x in family.split(',')] | 874 return [int(x) for x in family.split(',')] |
879 | 875 |
880 def GetExtraPlistItems(self, configname=None): | 876 def GetExtraPlistItems(self, configname=None): |
881 """Returns a dictionary with extra items to insert into Info.plist.""" | 877 """Returns a dictionary with extra items to insert into Info.plist.""" |
882 if configname not in XcodeSettings._plist_cache: | 878 if configname not in XcodeSettings._plist_cache: |
883 cache = {} | 879 cache = {} |
884 cache['BuildMachineOSBuild'] = self._BuildMachineOSBuild() | 880 cache['BuildMachineOSBuild'] = self._BuildMachineOSBuild() |
885 | 881 |
886 xcode, xcode_build = self._XcodeVersion() | 882 xcode, xcode_build = XcodeVersion() |
887 cache['DTXcode'] = xcode | 883 cache['DTXcode'] = xcode |
888 cache['DTXcodeBuild'] = xcode_build | 884 cache['DTXcodeBuild'] = xcode_build |
889 | 885 |
890 sdk_root = self._SdkRoot(configname) | 886 sdk_root = self._SdkRoot(configname) |
891 if not sdk_root: | 887 if not sdk_root: |
892 sdk_root = self._DefaultSdkRoot() | 888 sdk_root = self._DefaultSdkRoot() |
893 cache['DTSDKName'] = sdk_root | 889 cache['DTSDKName'] = sdk_root |
894 if xcode >= '0430': | 890 if xcode >= '0430': |
895 cache['DTSDKBuild'] = self._GetSdkVersionInfoItem( | 891 cache['DTSDKBuild'] = self._GetSdkVersionInfoItem( |
896 sdk_root, 'ProductBuildVersion') | 892 sdk_root, 'ProductBuildVersion') |
(...skipping 17 matching lines...) Expand all Loading... | |
914 items['UIDeviceFamily'] = self._XcodeIOSDeviceFamily(configname) | 910 items['UIDeviceFamily'] = self._XcodeIOSDeviceFamily(configname) |
915 return items | 911 return items |
916 | 912 |
917 def _DefaultSdkRoot(self): | 913 def _DefaultSdkRoot(self): |
918 """Returns the default SDKROOT to use. | 914 """Returns the default SDKROOT to use. |
919 | 915 |
920 Prior to version 5.0.0, if SDKROOT was not explicitly set in the Xcode | 916 Prior to version 5.0.0, if SDKROOT was not explicitly set in the Xcode |
921 project, then the environment variable was empty. Starting with this | 917 project, then the environment variable was empty. Starting with this |
922 version, Xcode uses the name of the newest SDK installed. | 918 version, Xcode uses the name of the newest SDK installed. |
923 """ | 919 """ |
924 xcode_version, xcode_build = self._XcodeVersion() | 920 xcode_version, xcode_build = XcodeVersion() |
925 if xcode_version < '0500': | 921 if xcode_version < '0500': |
926 return '' | 922 return '' |
927 default_sdk_path = self._XcodeSdkPath('') | 923 default_sdk_path = self._XcodeSdkPath('') |
928 default_sdk_root = XcodeSettings._sdk_root_cache.get(default_sdk_path) | 924 default_sdk_root = XcodeSettings._sdk_root_cache.get(default_sdk_path) |
929 if default_sdk_root: | 925 if default_sdk_root: |
930 return default_sdk_root | 926 return default_sdk_root |
931 try: | 927 try: |
932 all_sdks = GetStdout(['xcodebuild', '-showsdks']) | 928 all_sdks = GetStdout(['xcodebuild', '-showsdks']) |
933 except: | 929 except: |
934 # If xcodebuild fails, there will be no valid SDKs | 930 # If xcodebuild fails, there will be no valid SDKs |
(...skipping 18 matching lines...) Expand all Loading... | |
953 # For new projects, ARCHS is set to $(ARCHS_STANDARD_INCLUDING_64_BIT), | 949 # For new projects, ARCHS is set to $(ARCHS_STANDARD_INCLUDING_64_BIT), |
954 # which correspond to "armv7 armv7s arm64", and when building the simulator | 950 # which correspond to "armv7 armv7s arm64", and when building the simulator |
955 # the architecture is either "i386" or "x86_64" depending on the simulated | 951 # the architecture is either "i386" or "x86_64" depending on the simulated |
956 # device (respectively 32-bit or 64-bit device). | 952 # device (respectively 32-bit or 64-bit device). |
957 # | 953 # |
958 # Since the value returned by this function is only used when ARCHS is not | 954 # Since the value returned by this function is only used when ARCHS is not |
959 # set, then on iOS we return "i386", as the default xcode project generator | 955 # set, then on iOS we return "i386", as the default xcode project generator |
960 # does not set ARCHS if it is not set in the .gyp file. | 956 # does not set ARCHS if it is not set in the .gyp file. |
961 if self.isIOS: | 957 if self.isIOS: |
962 return 'i386' | 958 return 'i386' |
963 version, build = self._XcodeVersion() | 959 version, build = XcodeVersion() |
964 if version >= '0500': | 960 if version >= '0500': |
965 return 'x86_64' | 961 return 'x86_64' |
966 return 'i386' | 962 return 'i386' |
967 | 963 |
968 class MacPrefixHeader(object): | 964 class MacPrefixHeader(object): |
969 """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. | 965 """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. |
970 | 966 |
971 This feature consists of several pieces: | 967 This feature consists of several pieces: |
972 * If GCC_PREFIX_HEADER is present, all compilations in that project get an | 968 * If GCC_PREFIX_HEADER is present, all compilations in that project get an |
973 additional |-include path_to_prefix_header| cflag. | 969 additional |-include path_to_prefix_header| cflag. |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1064 if not self.header or not self.compile_headers: | 1060 if not self.header or not self.compile_headers: |
1065 return [] | 1061 return [] |
1066 return [ | 1062 return [ |
1067 (self._Gch('c', arch), '-x c-header', 'c', self.header), | 1063 (self._Gch('c', arch), '-x c-header', 'c', self.header), |
1068 (self._Gch('cc', arch), '-x c++-header', 'cc', self.header), | 1064 (self._Gch('cc', arch), '-x c++-header', 'cc', self.header), |
1069 (self._Gch('m', arch), '-x objective-c-header', 'm', self.header), | 1065 (self._Gch('m', arch), '-x objective-c-header', 'm', self.header), |
1070 (self._Gch('mm', arch), '-x objective-c++-header', 'mm', self.header), | 1066 (self._Gch('mm', arch), '-x objective-c++-header', 'mm', self.header), |
1071 ] | 1067 ] |
1072 | 1068 |
1073 | 1069 |
1074 def XcodeVersion(): | 1070 def XcodeVersion(): |
Nico
2014/02/18 17:11:57
Huh, is this new? I don't remember there being a g
sdefresne
2014/02/18 17:28:33
I extracted the function out of the XcodeSettings
| |
1075 """Returns a tuple of version and build version of installed Xcode.""" | 1071 """Returns a tuple of version and build version of installed Xcode.""" |
1076 # `xcodebuild -version` output looks like | 1072 # `xcodebuild -version` output looks like |
1077 # Xcode 4.6.3 | 1073 # Xcode 4.6.3 |
1078 # Build version 4H1503 | 1074 # Build version 4H1503 |
1079 # or like | 1075 # or like |
1080 # Xcode 3.2.6 | 1076 # Xcode 3.2.6 |
1081 # Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0 | 1077 # Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0 |
1082 # BuildVersion: 10M2518 | 1078 # BuildVersion: 10M2518 |
1083 # Convert that to '0463', '4H1503'. | 1079 # Convert that to '0463', '4H1503'. |
1080 if XCODE_VERSION_CACHE: | |
1081 assert len(XCODE_VERSION_CACHE) >= 2 | |
1082 return tuple(XCODE_VERSION_CACHE[:2]) | |
1084 try: | 1083 try: |
1085 version_list = GetStdout(['xcodebuild', '-version']).splitlines() | 1084 version_list = GetStdout(['xcodebuild', '-version']).splitlines() |
1086 # In some circumstances xcodebuild exits 0 but doesn't return | 1085 # In some circumstances xcodebuild exits 0 but doesn't return |
1087 # the right results; for example, a user on 10.7 or 10.8 with | 1086 # the right results; for example, a user on 10.7 or 10.8 with |
1088 # a bogus path set via xcode-select | 1087 # a bogus path set via xcode-select |
1089 # In that case this may be a CLT-only install so fall back to | 1088 # In that case this may be a CLT-only install so fall back to |
1090 # checking that version. | 1089 # checking that version. |
1091 if len(version_list) < 2: | 1090 if len(version_list) < 2: |
1092 raise GypError, "xcodebuild returned unexpected results" | 1091 raise GypError, "xcodebuild returned unexpected results" |
1093 except: | 1092 except: |
1094 version = CLTVersion() | 1093 version = CLTVersion() |
1095 if version: | 1094 if version: |
1096 version = re.match('(\d\.\d\.?\d*)', version).groups()[0] | 1095 version = re.match('(\d\.\d\.?\d*)', version).groups()[0] |
1097 else: | 1096 else: |
1098 raise GypError, "No Xcode or CLT version detected!" | 1097 raise GypError, "No Xcode or CLT version detected!" |
1099 # The CLT has no build information, so we return an empty string. | 1098 # The CLT has no build information, so we return an empty string. |
1100 version_list = [version, ''] | 1099 version_list = [version, ''] |
1101 version = version_list[0] | 1100 version = version_list[0] |
1102 build = version_list[-1] | 1101 build = version_list[-1] |
1103 # Be careful to convert "4.2" to "0420": | 1102 # Be careful to convert "4.2" to "0420": |
1104 version = version.split()[-1].replace('.', '') | 1103 version = version.split()[-1].replace('.', '') |
1105 version = (version + '0' * (3 - len(version))).zfill(4) | 1104 version = (version + '0' * (3 - len(version))).zfill(4) |
1106 if build: | 1105 if build: |
1107 build = build.split()[-1] | 1106 build = build.split()[-1] |
1107 XCODE_VERSION_CACHE.extend((version, build)) | |
1108 return version, build | 1108 return version, build |
1109 | 1109 |
1110 | 1110 |
1111 # This function ported from the logic in Homebrew's CLT version check | 1111 # This function ported from the logic in Homebrew's CLT version check |
1112 def CLTVersion(): | 1112 def CLTVersion(): |
1113 """Returns the version of command-line tools from pkgutil.""" | 1113 """Returns the version of command-line tools from pkgutil.""" |
1114 # pkgutil output looks like | 1114 # pkgutil output looks like |
1115 # package-id: com.apple.pkg.CLTools_Executables | 1115 # package-id: com.apple.pkg.CLTools_Executables |
1116 # version: 5.0.1.0.1.1382131676 | 1116 # version: 5.0.1.0.1.1382131676 |
1117 # volume: / | 1117 # volume: / |
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1490 _FilterIOSArchitectureForSDKROOT(iphoneos_config_dict['xcode_settings']) | 1490 _FilterIOSArchitectureForSDKROOT(iphoneos_config_dict['xcode_settings']) |
1491 _FilterIOSArchitectureForSDKROOT(config_dict['xcode_settings']) | 1491 _FilterIOSArchitectureForSDKROOT(config_dict['xcode_settings']) |
1492 return targets | 1492 return targets |
1493 | 1493 |
1494 def CloneConfigurationForDeviceAndEmulator(target_dicts): | 1494 def CloneConfigurationForDeviceAndEmulator(target_dicts): |
1495 """If |target_dicts| contains any iOS targets, automatically create -iphoneos | 1495 """If |target_dicts| contains any iOS targets, automatically create -iphoneos |
1496 targets for iOS device builds.""" | 1496 targets for iOS device builds.""" |
1497 if _HasIOSTarget(target_dicts): | 1497 if _HasIOSTarget(target_dicts): |
1498 return _AddIOSDeviceConfigurations(target_dicts) | 1498 return _AddIOSDeviceConfigurations(target_dicts) |
1499 return target_dicts | 1499 return target_dicts |
OLD | NEW |