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 |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 return self._GetBundleBinaryPath() | 264 return self._GetBundleBinaryPath() |
265 else: | 265 else: |
266 return self._GetStandaloneBinaryPath() | 266 return self._GetStandaloneBinaryPath() |
267 | 267 |
268 def GetActiveArchs(self, configname): | 268 def GetActiveArchs(self, configname): |
269 """Returns the architectures this target should be built for.""" | 269 """Returns the architectures this target should be built for.""" |
270 # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set | 270 # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set |
271 # CURRENT_ARCH / NATIVE_ARCH env vars? | 271 # CURRENT_ARCH / NATIVE_ARCH env vars? |
272 return self.xcode_settings[configname].get('ARCHS', [self._DefaultArch()]) | 272 return self.xcode_settings[configname].get('ARCHS', [self._DefaultArch()]) |
273 | 273 |
274 def _GetStdout(self, cmdlist): | |
275 job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE) | |
276 out = job.communicate()[0] | |
277 if job.returncode != 0: | |
278 sys.stderr.write(out + '\n') | |
279 raise GypError('Error %d running %s' % (job.returncode, cmdlist[0])) | |
280 return out.rstrip('\n') | |
281 | |
282 def _GetSdkVersionInfoItem(self, sdk, infoitem): | 274 def _GetSdkVersionInfoItem(self, sdk, infoitem): |
283 # xcodebuild requires Xcode and can't run on Command Line Tools-only | 275 # xcodebuild requires Xcode and can't run on Command Line Tools-only |
284 # systems from 10.7 onward. | 276 # systems from 10.7 onward. |
285 # Since the CLT has no SDK paths anyway, returning None is the | 277 # Since the CLT has no SDK paths anyway, returning None is the |
286 # most sensible route and should still do the right thing. | 278 # most sensible route and should still do the right thing. |
287 try: | 279 try: |
288 return self._GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem]) | 280 return GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem]) |
289 except: | 281 except: |
290 pass | 282 pass |
291 | 283 |
292 def _SdkRoot(self, configname): | 284 def _SdkRoot(self, configname): |
293 if configname is None: | 285 if configname is None: |
294 configname = self.configname | 286 configname = self.configname |
295 return self.GetPerConfigSetting('SDKROOT', configname, default='') | 287 return self.GetPerConfigSetting('SDKROOT', configname, default='') |
296 | 288 |
297 def _SdkPath(self, configname=None): | 289 def _SdkPath(self, configname=None): |
298 sdk_root = self._SdkRoot(configname) | 290 sdk_root = self._SdkRoot(configname) |
(...skipping 568 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
867 | 859 |
868 def AdjustLibraries(self, libraries, config_name=None): | 860 def AdjustLibraries(self, libraries, config_name=None): |
869 """Transforms entries like 'Cocoa.framework' in libraries into entries like | 861 """Transforms entries like 'Cocoa.framework' in libraries into entries like |
870 '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc. | 862 '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc. |
871 """ | 863 """ |
872 libraries = [self._AdjustLibrary(library, config_name) | 864 libraries = [self._AdjustLibrary(library, config_name) |
873 for library in libraries] | 865 for library in libraries] |
874 return libraries | 866 return libraries |
875 | 867 |
876 def _BuildMachineOSBuild(self): | 868 def _BuildMachineOSBuild(self): |
877 return self._GetStdout(['sw_vers', '-buildVersion']) | 869 return GetStdout(['sw_vers', '-buildVersion']) |
878 | |
879 # This method ported from the logic in Homebrew's CLT version check | |
880 def _CLTVersion(self): | |
881 # pkgutil output looks like | |
882 # package-id: com.apple.pkg.CLTools_Executables | |
883 # version: 5.0.1.0.1.1382131676 | |
884 # volume: / | |
885 # location: / | |
886 # install-time: 1382544035 | |
887 # groups: com.apple.FindSystemFiles.pkg-group com.apple.DevToolsBoth.pkg-g
roup com.apple.DevToolsNonRelocatableShared.pkg-group | |
888 STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo" | |
889 FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI" | |
890 MAVERICKS_PKG_ID = "com.apple.pkg.CLTools_Executables" | |
891 | |
892 regex = re.compile('version: (?P<version>.+)') | |
893 for key in [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID]: | |
894 try: | |
895 output = self._GetStdout(['/usr/sbin/pkgutil', '--pkg-info', key]) | |
896 return re.search(regex, output).groupdict()['version'] | |
897 except: | |
898 continue | |
899 | 870 |
900 def _XcodeVersion(self): | 871 def _XcodeVersion(self): |
901 # `xcodebuild -version` output looks like | |
902 # Xcode 4.6.3 | |
903 # Build version 4H1503 | |
904 # or like | |
905 # Xcode 3.2.6 | |
906 # Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0 | |
907 # BuildVersion: 10M2518 | |
908 # Convert that to '0463', '4H1503'. | |
909 if len(XcodeSettings._xcode_version_cache) == 0: | 872 if len(XcodeSettings._xcode_version_cache) == 0: |
910 try: | 873 XcodeSettings._xcode_version_cache = XcodeVersion() |
911 version_list = self._GetStdout(['xcodebuild', '-version']).splitlines() | |
912 # In some circumstances xcodebuild exits 0 but doesn't return | |
913 # the right results; for example, a user on 10.7 or 10.8 with | |
914 # a bogus path set via xcode-select | |
915 # In that case this may be a CLT-only install so fall back to | |
916 # checking that version. | |
917 if len(version_list) < 2: | |
918 raise GypError, "xcodebuild returned unexpected results" | |
919 except: | |
920 version = self._CLTVersion() | |
921 if version: | |
922 version = re.match('(\d\.\d\.?\d*)', version).groups()[0] | |
923 else: | |
924 raise GypError, "No Xcode or CLT version detected!" | |
925 # The CLT has no build information, so we return an empty string. | |
926 version_list = [version, ''] | |
927 version = version_list[0] | |
928 build = version_list[-1] | |
929 # Be careful to convert "4.2" to "0420": | |
930 version = version.split()[-1].replace('.', '') | |
931 version = (version + '0' * (3 - len(version))).zfill(4) | |
932 if build: | |
933 build = build.split()[-1] | |
934 XcodeSettings._xcode_version_cache = (version, build) | |
935 return XcodeSettings._xcode_version_cache | 874 return XcodeSettings._xcode_version_cache |
936 | 875 |
937 def _XcodeIOSDeviceFamily(self, configname): | 876 def _XcodeIOSDeviceFamily(self, configname): |
938 family = self.xcode_settings[configname].get('TARGETED_DEVICE_FAMILY', '1') | 877 family = self.xcode_settings[configname].get('TARGETED_DEVICE_FAMILY', '1') |
939 return [int(x) for x in family.split(',')] | 878 return [int(x) for x in family.split(',')] |
940 | 879 |
941 def GetExtraPlistItems(self, configname=None): | 880 def GetExtraPlistItems(self, configname=None): |
942 """Returns a dictionary with extra items to insert into Info.plist.""" | 881 """Returns a dictionary with extra items to insert into Info.plist.""" |
943 if configname not in XcodeSettings._plist_cache: | 882 if configname not in XcodeSettings._plist_cache: |
944 cache = {} | 883 cache = {} |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
982 project, then the environment variable was empty. Starting with this | 921 project, then the environment variable was empty. Starting with this |
983 version, Xcode uses the name of the newest SDK installed. | 922 version, Xcode uses the name of the newest SDK installed. |
984 """ | 923 """ |
985 if self._XcodeVersion() < '0500': | 924 if self._XcodeVersion() < '0500': |
986 return '' | 925 return '' |
987 default_sdk_path = self._XcodeSdkPath('') | 926 default_sdk_path = self._XcodeSdkPath('') |
988 default_sdk_root = XcodeSettings._sdk_root_cache.get(default_sdk_path) | 927 default_sdk_root = XcodeSettings._sdk_root_cache.get(default_sdk_path) |
989 if default_sdk_root: | 928 if default_sdk_root: |
990 return default_sdk_root | 929 return default_sdk_root |
991 try: | 930 try: |
992 all_sdks = self._GetStdout(['xcodebuild', '-showsdks']) | 931 all_sdks = GetStdout(['xcodebuild', '-showsdks']) |
993 except: | 932 except: |
994 # If xcodebuild fails, there will be no valid SDKs | 933 # If xcodebuild fails, there will be no valid SDKs |
995 return '' | 934 return '' |
996 for line in all_sdks.splitlines(): | 935 for line in all_sdks.splitlines(): |
997 items = line.split() | 936 items = line.split() |
998 if len(items) >= 3 and items[-2] == '-sdk': | 937 if len(items) >= 3 and items[-2] == '-sdk': |
999 sdk_root = items[-1] | 938 sdk_root = items[-1] |
1000 sdk_path = self._XcodeSdkPath(sdk_root) | 939 sdk_path = self._XcodeSdkPath(sdk_root) |
1001 if sdk_path == default_sdk_path: | 940 if sdk_path == default_sdk_path: |
1002 return sdk_root | 941 return sdk_root |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1124 if not self.header or not self.compile_headers: | 1063 if not self.header or not self.compile_headers: |
1125 return [] | 1064 return [] |
1126 return [ | 1065 return [ |
1127 (self._Gch('c', arch), '-x c-header', 'c', self.header), | 1066 (self._Gch('c', arch), '-x c-header', 'c', self.header), |
1128 (self._Gch('cc', arch), '-x c++-header', 'cc', self.header), | 1067 (self._Gch('cc', arch), '-x c++-header', 'cc', self.header), |
1129 (self._Gch('m', arch), '-x objective-c-header', 'm', self.header), | 1068 (self._Gch('m', arch), '-x objective-c-header', 'm', self.header), |
1130 (self._Gch('mm', arch), '-x objective-c++-header', 'mm', self.header), | 1069 (self._Gch('mm', arch), '-x objective-c++-header', 'mm', self.header), |
1131 ] | 1070 ] |
1132 | 1071 |
1133 | 1072 |
| 1073 def XcodeVersion(): |
| 1074 """Returns a tuple of version and build version of installed Xcode.""" |
| 1075 # `xcodebuild -version` output looks like |
| 1076 # Xcode 4.6.3 |
| 1077 # Build version 4H1503 |
| 1078 # or like |
| 1079 # Xcode 3.2.6 |
| 1080 # Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0 |
| 1081 # BuildVersion: 10M2518 |
| 1082 # Convert that to '0463', '4H1503'. |
| 1083 try: |
| 1084 version_list = GetStdout(['xcodebuild', '-version']).splitlines() |
| 1085 # In some circumstances xcodebuild exits 0 but doesn't return |
| 1086 # the right results; for example, a user on 10.7 or 10.8 with |
| 1087 # a bogus path set via xcode-select |
| 1088 # In that case this may be a CLT-only install so fall back to |
| 1089 # checking that version. |
| 1090 if len(version_list) < 2: |
| 1091 raise GypError, "xcodebuild returned unexpected results" |
| 1092 except: |
| 1093 version = CLTVersion() |
| 1094 if version: |
| 1095 version = re.match('(\d\.\d\.?\d*)', version).groups()[0] |
| 1096 else: |
| 1097 raise GypError, "No Xcode or CLT version detected!" |
| 1098 # The CLT has no build information, so we return an empty string. |
| 1099 version_list = [version, ''] |
| 1100 version = version_list[0] |
| 1101 build = version_list[-1] |
| 1102 # Be careful to convert "4.2" to "0420": |
| 1103 version = version.split()[-1].replace('.', '') |
| 1104 version = (version + '0' * (3 - len(version))).zfill(4) |
| 1105 if build: |
| 1106 build = build.split()[-1] |
| 1107 return version, build |
| 1108 |
| 1109 |
| 1110 # This function ported from the logic in Homebrew's CLT version check |
| 1111 def CLTVersion(): |
| 1112 """Returns the version of command-line tools from pkgutil.""" |
| 1113 # pkgutil output looks like |
| 1114 # package-id: com.apple.pkg.CLTools_Executables |
| 1115 # version: 5.0.1.0.1.1382131676 |
| 1116 # volume: / |
| 1117 # location: / |
| 1118 # install-time: 1382544035 |
| 1119 # groups: com.apple.FindSystemFiles.pkg-group com.apple.DevToolsBoth.pkg-gro
up com.apple.DevToolsNonRelocatableShared.pkg-group |
| 1120 STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo" |
| 1121 FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI" |
| 1122 MAVERICKS_PKG_ID = "com.apple.pkg.CLTools_Executables" |
| 1123 |
| 1124 regex = re.compile('version: (?P<version>.+)') |
| 1125 for key in [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID]: |
| 1126 try: |
| 1127 output = GetStdout(['/usr/sbin/pkgutil', '--pkg-info', key]) |
| 1128 return re.search(regex, output).groupdict()['version'] |
| 1129 except: |
| 1130 continue |
| 1131 |
| 1132 |
| 1133 def GetStdout(cmdlist): |
| 1134 """Returns the content of standard output returned by invoking |cmdlist|. |
| 1135 Raises |GypError| if the command return with a non-zero return code.""" |
| 1136 job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE) |
| 1137 out = job.communicate()[0] |
| 1138 if job.returncode != 0: |
| 1139 sys.stderr.write(out + '\n') |
| 1140 raise GypError('Error %d running %s' % (job.returncode, cmdlist[0])) |
| 1141 return out.rstrip('\n') |
| 1142 |
| 1143 |
1134 def MergeGlobalXcodeSettingsToSpec(global_dict, spec): | 1144 def MergeGlobalXcodeSettingsToSpec(global_dict, spec): |
1135 """Merges the global xcode_settings dictionary into each configuration of the | 1145 """Merges the global xcode_settings dictionary into each configuration of the |
1136 target represented by spec. For keys that are both in the global and the local | 1146 target represented by spec. For keys that are both in the global and the local |
1137 xcode_settings dict, the local key gets precendence. | 1147 xcode_settings dict, the local key gets precendence. |
1138 """ | 1148 """ |
1139 # The xcode generator special-cases global xcode_settings and does something | 1149 # The xcode generator special-cases global xcode_settings and does something |
1140 # that amounts to merging in the global xcode_settings into each local | 1150 # that amounts to merging in the global xcode_settings into each local |
1141 # xcode_settings dict. | 1151 # xcode_settings dict. |
1142 global_xcode_settings = global_dict.get('xcode_settings', {}) | 1152 global_xcode_settings = global_dict.get('xcode_settings', {}) |
1143 for config in spec['configurations'].values(): | 1153 for config in spec['configurations'].values(): |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1431 new_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos' | 1441 new_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos' |
1432 target_dict['configurations'][new_config_name] = new_config_dict | 1442 target_dict['configurations'][new_config_name] = new_config_dict |
1433 return targets | 1443 return targets |
1434 | 1444 |
1435 def CloneConfigurationForDeviceAndEmulator(target_dicts): | 1445 def CloneConfigurationForDeviceAndEmulator(target_dicts): |
1436 """If |target_dicts| contains any iOS targets, automatically create -iphoneos | 1446 """If |target_dicts| contains any iOS targets, automatically create -iphoneos |
1437 targets for iOS device builds.""" | 1447 targets for iOS device builds.""" |
1438 if _HasIOSTarget(target_dicts): | 1448 if _HasIOSTarget(target_dicts): |
1439 return _AddIOSDeviceConfigurations(target_dicts) | 1449 return _AddIOSDeviceConfigurations(target_dicts) |
1440 return target_dicts | 1450 return target_dicts |
OLD | NEW |