| 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 gyp.common | 10 import gyp.common |
| 11 import os.path | 11 import os.path |
| 12 import re | 12 import re |
| 13 import shlex | 13 import shlex |
| 14 import subprocess | 14 import subprocess |
| 15 import sys | 15 import sys |
| 16 from gyp.common import GypError | 16 from gyp.common import GypError |
| 17 | 17 |
| 18 class XcodeSettings(object): | 18 class XcodeSettings(object): |
| 19 """A class that understands the gyp 'xcode_settings' object.""" | 19 """A class that understands the gyp 'xcode_settings' object.""" |
| 20 | 20 |
| 21 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached | 21 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached |
| 22 # at class-level for efficiency. | 22 # at class-level for efficiency. |
| 23 _sdk_path_cache = {} | 23 _sdk_path_cache = {} |
| 24 | 24 |
| 25 # Populated lazily by GetExtraPlistItems(). Shared by all XcodeSettings, so |
| 26 # cached at class-level for efficiency. |
| 27 _plist_cache = {} |
| 28 |
| 25 def __init__(self, spec): | 29 def __init__(self, spec): |
| 26 self.spec = spec | 30 self.spec = spec |
| 27 | 31 |
| 28 self.isIOS = False | 32 self.isIOS = False |
| 29 | 33 |
| 30 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp. | 34 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp. |
| 31 # This means self.xcode_settings[config] always contains all settings | 35 # This means self.xcode_settings[config] always contains all settings |
| 32 # for that config -- the per-target settings as well. Settings that are | 36 # for that config -- the per-target settings as well. Settings that are |
| 33 # the same for all configs are implicitly per-target settings. | 37 # the same for all configs are implicitly per-target settings. |
| 34 self.xcode_settings = {} | 38 self.xcode_settings = {} |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 239 return self._GetBundleBinaryPath() | 243 return self._GetBundleBinaryPath() |
| 240 else: | 244 else: |
| 241 return self._GetStandaloneBinaryPath() | 245 return self._GetStandaloneBinaryPath() |
| 242 | 246 |
| 243 def GetActiveArchs(self, configname): | 247 def GetActiveArchs(self, configname): |
| 244 """Returns the architectures this target should be built for.""" | 248 """Returns the architectures this target should be built for.""" |
| 245 # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set | 249 # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set |
| 246 # CURRENT_ARCH / NATIVE_ARCH env vars? | 250 # CURRENT_ARCH / NATIVE_ARCH env vars? |
| 247 return self.xcode_settings[configname].get('ARCHS', ['i386']) | 251 return self.xcode_settings[configname].get('ARCHS', ['i386']) |
| 248 | 252 |
| 249 def _GetSdkVersionInfoItem(self, sdk, infoitem): | 253 def _GetStdout(self, cmdlist): |
| 250 job = subprocess.Popen(['xcodebuild', '-version', '-sdk', sdk, infoitem], | 254 job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE) |
| 251 stdout=subprocess.PIPE) | |
| 252 out = job.communicate()[0] | 255 out = job.communicate()[0] |
| 253 if job.returncode != 0: | 256 if job.returncode != 0: |
| 254 sys.stderr.write(out + '\n') | 257 sys.stderr.write(out + '\n') |
| 255 raise GypError('Error %d running xcodebuild' % job.returncode) | 258 raise GypError('Error %d running %s' % (job.returncode, cmdlist[0])) |
| 256 return out.rstrip('\n') | 259 return out.rstrip('\n') |
| 257 | 260 |
| 261 def _GetSdkVersionInfoItem(self, sdk, infoitem): |
| 262 return self._GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem]) |
| 263 |
| 264 def _SdkRoot(self): |
| 265 return self.GetPerTargetSetting('SDKROOT', default='') |
| 266 |
| 258 def _SdkPath(self): | 267 def _SdkPath(self): |
| 259 sdk_root = self.GetPerTargetSetting('SDKROOT', default='macosx') | 268 sdk_root = self._SdkRoot() |
| 260 if sdk_root.startswith('/'): | 269 if sdk_root.startswith('/'): |
| 261 return sdk_root | 270 return sdk_root |
| 262 if sdk_root not in XcodeSettings._sdk_path_cache: | 271 if sdk_root not in XcodeSettings._sdk_path_cache: |
| 263 XcodeSettings._sdk_path_cache[sdk_root] = self._GetSdkVersionInfoItem( | 272 XcodeSettings._sdk_path_cache[sdk_root] = self._GetSdkVersionInfoItem( |
| 264 sdk_root, 'Path') | 273 sdk_root, 'Path') |
| 265 return XcodeSettings._sdk_path_cache[sdk_root] | 274 return XcodeSettings._sdk_path_cache[sdk_root] |
| 266 | 275 |
| 267 def _AppendPlatformVersionMinFlags(self, lst): | 276 def _AppendPlatformVersionMinFlags(self, lst): |
| 268 self._Appendf(lst, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s') | 277 self._Appendf(lst, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s') |
| 269 if 'IPHONEOS_DEPLOYMENT_TARGET' in self._Settings(): | 278 if 'IPHONEOS_DEPLOYMENT_TARGET' in self._Settings(): |
| (...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 for key, value in self.xcode_settings[configname].iteritems(): | 661 for key, value in self.xcode_settings[configname].iteritems(): |
| 653 if key not in result: | 662 if key not in result: |
| 654 continue | 663 continue |
| 655 elif result[key] != value: | 664 elif result[key] != value: |
| 656 del result[key] | 665 del result[key] |
| 657 return result | 666 return result |
| 658 | 667 |
| 659 def GetPerTargetSetting(self, setting, default=None): | 668 def GetPerTargetSetting(self, setting, default=None): |
| 660 """Tries to get xcode_settings.setting from spec. Assumes that the setting | 669 """Tries to get xcode_settings.setting from spec. Assumes that the setting |
| 661 has the same value in all configurations and throws otherwise.""" | 670 has the same value in all configurations and throws otherwise.""" |
| 662 first_pass = True | 671 is_first_pass = True |
| 663 result = None | 672 result = None |
| 664 for configname in sorted(self.xcode_settings.keys()): | 673 for configname in sorted(self.xcode_settings.keys()): |
| 665 if first_pass: | 674 if is_first_pass: |
| 666 result = self.xcode_settings[configname].get(setting, None) | 675 result = self.xcode_settings[configname].get(setting, None) |
| 667 first_pass = False | 676 is_first_pass = False |
| 668 else: | 677 else: |
| 669 assert result == self.xcode_settings[configname].get(setting, None), ( | 678 assert result == self.xcode_settings[configname].get(setting, None), ( |
| 670 "Expected per-target setting for '%s', got per-config setting " | 679 "Expected per-target setting for '%s', got per-config setting " |
| 671 "(target %s)" % (setting, spec['target_name'])) | 680 "(target %s)" % (setting, spec['target_name'])) |
| 672 if result is None: | 681 if result is None: |
| 673 return default | 682 return default |
| 674 return result | 683 return result |
| 675 | 684 |
| 676 def _GetStripPostbuilds(self, configname, output_binary, quiet): | 685 def _GetStripPostbuilds(self, configname, output_binary, quiet): |
| 677 """Returns a list of shell commands that contain the shell commands | 686 """Returns a list of shell commands that contain the shell commands |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 745 l = library | 754 l = library |
| 746 return l.replace('$(SDKROOT)', self._SdkPath()) | 755 return l.replace('$(SDKROOT)', self._SdkPath()) |
| 747 | 756 |
| 748 def AdjustLibraries(self, libraries): | 757 def AdjustLibraries(self, libraries): |
| 749 """Transforms entries like 'Cocoa.framework' in libraries into entries like | 758 """Transforms entries like 'Cocoa.framework' in libraries into entries like |
| 750 '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc. | 759 '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc. |
| 751 """ | 760 """ |
| 752 libraries = [ self._AdjustLibrary(library) for library in libraries] | 761 libraries = [ self._AdjustLibrary(library) for library in libraries] |
| 753 return libraries | 762 return libraries |
| 754 | 763 |
| 764 def _BuildMachineOSBuild(self): |
| 765 return self._GetStdout(['sw_vers', '-buildVersion']) |
| 766 |
| 767 def _XcodeVersion(self): |
| 768 # `xcodebuild -version` output looks like |
| 769 # Xcode 4.6.3 |
| 770 # Build version 4H1503 |
| 771 # Convert that to '0463', '4H1503'. |
| 772 version, build = self._GetStdout(['xcodebuild', '-version']).splitlines() |
| 773 # Be careful to convert "4.2" to "0420": |
| 774 version = version.split()[-1].replace('.', '') |
| 775 version = (version + '0' * (3 - len(version))).zfill(4) |
| 776 build = build.split()[-1] |
| 777 return version, build |
| 778 |
| 779 def GetExtraPlistItems(self): |
| 780 """Returns a dictionary with extra items to insert into Info.plist.""" |
| 781 if not XcodeSettings._plist_cache: |
| 782 cache = XcodeSettings._plist_cache |
| 783 cache['BuildMachineOSBuild'] = self._BuildMachineOSBuild() |
| 784 |
| 785 xcode, xcode_build = self._XcodeVersion() |
| 786 cache['DTXcode'] = xcode |
| 787 cache['DTXcodeBuild'] = xcode_build |
| 788 |
| 789 sdk_root = self._SdkRoot() |
| 790 cache['DTSDKName'] = sdk_root |
| 791 if xcode >= '0430': |
| 792 cache['DTSDKBuild'] = self._GetSdkVersionInfoItem( |
| 793 sdk_root, 'ProductBuildVersion') |
| 794 else: |
| 795 cache['DTSDKBuild'] = cache['BuildMachineOSBuild'] |
| 796 |
| 797 return XcodeSettings._plist_cache |
| 798 |
| 755 | 799 |
| 756 class MacPrefixHeader(object): | 800 class MacPrefixHeader(object): |
| 757 """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. | 801 """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. |
| 758 | 802 |
| 759 This feature consists of several pieces: | 803 This feature consists of several pieces: |
| 760 * If GCC_PREFIX_HEADER is present, all compilations in that project get an | 804 * If GCC_PREFIX_HEADER is present, all compilations in that project get an |
| 761 additional |-include path_to_prefix_header| cflag. | 805 additional |-include path_to_prefix_header| cflag. |
| 762 * If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is | 806 * If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is |
| 763 instead compiled, and all other compilations in the project get an | 807 instead compiled, and all other compilations in the project get an |
| 764 additional |-include path_to_compiled_header| instead. | 808 additional |-include path_to_compiled_header| instead. |
| (...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1124 def GetSpecPostbuildCommands(spec, quiet=False): | 1168 def GetSpecPostbuildCommands(spec, quiet=False): |
| 1125 """Returns the list of postbuilds explicitly defined on |spec|, in a form | 1169 """Returns the list of postbuilds explicitly defined on |spec|, in a form |
| 1126 executable by a shell.""" | 1170 executable by a shell.""" |
| 1127 postbuilds = [] | 1171 postbuilds = [] |
| 1128 for postbuild in spec.get('postbuilds', []): | 1172 for postbuild in spec.get('postbuilds', []): |
| 1129 if not quiet: | 1173 if not quiet: |
| 1130 postbuilds.append('echo POSTBUILD\\(%s\\) %s' % ( | 1174 postbuilds.append('echo POSTBUILD\\(%s\\) %s' % ( |
| 1131 spec['target_name'], postbuild['postbuild_name'])) | 1175 spec['target_name'], postbuild['postbuild_name'])) |
| 1132 postbuilds.append(gyp.common.EncodePOSIXShellList(postbuild['action'])) | 1176 postbuilds.append(gyp.common.EncodePOSIXShellList(postbuild['action'])) |
| 1133 return postbuilds | 1177 return postbuilds |
| OLD | NEW |