| 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.path | 12 import os.path |
| 13 import re | 13 import re |
| 14 import shlex | 14 import shlex |
| 15 import subprocess | 15 import subprocess |
| 16 import sys | 16 import sys |
| 17 from gyp.common import GypError | 17 from gyp.common import GypError |
| 18 | 18 |
| 19 class XcodeSettings(object): | 19 class XcodeSettings(object): |
| 20 """A class that understands the gyp 'xcode_settings' object.""" | 20 """A class that understands the gyp 'xcode_settings' object.""" |
| 21 | 21 |
| 22 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached | 22 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached |
| 23 # at class-level for efficiency. | 23 # at class-level for efficiency. |
| 24 _sdk_path_cache = {} | 24 _sdk_path_cache = {} |
| 25 | 25 |
| 26 # Populated lazily by GetExtraPlistItems(). Shared by all XcodeSettings, so | 26 # Populated lazily by GetExtraPlistItems(). Shared by all XcodeSettings, so |
| 27 # cached at class-level for efficiency. | 27 # cached at class-level for efficiency. |
| 28 _plist_cache = {} | 28 _plist_cache = {} |
| 29 | 29 |
| 30 # Populated lazily by GetIOSPostbuilds. Shared by all XcodeSettings, so |
| 31 # cached at class-level for efficiency. |
| 32 _codesigning_key_cache = {} |
| 33 |
| 30 def __init__(self, spec): | 34 def __init__(self, spec): |
| 31 self.spec = spec | 35 self.spec = spec |
| 32 | 36 |
| 33 self.isIOS = False | 37 self.isIOS = False |
| 34 | 38 |
| 35 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp. | 39 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp. |
| 36 # This means self.xcode_settings[config] always contains all settings | 40 # This means self.xcode_settings[config] always contains all settings |
| 37 # for that config -- the per-target settings as well. Settings that are | 41 # for that config -- the per-target settings as well. Settings that are |
| 38 # the same for all configs are implicitly per-target settings. | 42 # the same for all configs are implicitly per-target settings. |
| 39 self.xcode_settings = {} | 43 self.xcode_settings = {} |
| 40 configs = spec['configurations'] | 44 configs = spec['configurations'] |
| 41 for configname, config in configs.iteritems(): | 45 for configname, config in configs.iteritems(): |
| 42 self.xcode_settings[configname] = config.get('xcode_settings', {}) | 46 self.xcode_settings[configname] = config.get('xcode_settings', {}) |
| 47 self._ConvertConditionalKeys(configname) |
| 43 if self.xcode_settings[configname].get('IPHONEOS_DEPLOYMENT_TARGET', | 48 if self.xcode_settings[configname].get('IPHONEOS_DEPLOYMENT_TARGET', |
| 44 None): | 49 None): |
| 45 self.isIOS = True | 50 self.isIOS = True |
| 46 | 51 |
| 47 # If you need this, speak up at http://crbug.com/122592 | |
| 48 conditional_keys = [key for key in self.xcode_settings[configname] | |
| 49 if key.endswith(']')] | |
| 50 if conditional_keys: | |
| 51 print 'Warning: Conditional keys not implemented, ignoring:', \ | |
| 52 ' '.join(conditional_keys) | |
| 53 for key in conditional_keys: | |
| 54 del self.xcode_settings[configname][key] | |
| 55 | |
| 56 # This is only non-None temporarily during the execution of some methods. | 52 # This is only non-None temporarily during the execution of some methods. |
| 57 self.configname = None | 53 self.configname = None |
| 58 | 54 |
| 59 # Used by _AdjustLibrary to match .a and .dylib entries in libraries. | 55 # Used by _AdjustLibrary to match .a and .dylib entries in libraries. |
| 60 self.library_re = re.compile(r'^lib([^/]+)\.(a|dylib)$') | 56 self.library_re = re.compile(r'^lib([^/]+)\.(a|dylib)$') |
| 61 | 57 |
| 58 def _ConvertConditionalKeys(self, configname): |
| 59 """Converts or warns on conditional keys. Xcode supports conditional keys, |
| 60 such as CODE_SIGN_IDENTITY[sdk=iphoneos*]. This is a partial implementation |
| 61 with some keys converted while the rest force a warning.""" |
| 62 settings = self.xcode_settings[configname] |
| 63 conditional_keys = [key for key in settings if key.endswith(']')] |
| 64 for key in conditional_keys: |
| 65 # If you need more, speak up at http://crbug.com/122592 |
| 66 if key.endswith("[sdk=iphoneos*]"): |
| 67 if configname.endswith("iphoneos"): |
| 68 new_key = key.split("[")[0] |
| 69 settings[new_key] = settings[key] |
| 70 else: |
| 71 print 'Warning: Conditional keys not implemented, ignoring:', \ |
| 72 ' '.join(conditional_keys) |
| 73 del settings[key] |
| 74 |
| 62 def _Settings(self): | 75 def _Settings(self): |
| 63 assert self.configname | 76 assert self.configname |
| 64 return self.xcode_settings[self.configname] | 77 return self.xcode_settings[self.configname] |
| 65 | 78 |
| 66 def _Test(self, test_key, cond_key, default): | 79 def _Test(self, test_key, cond_key, default): |
| 67 return self._Settings().get(test_key, default) == cond_key | 80 return self._Settings().get(test_key, default) == cond_key |
| 68 | 81 |
| 69 def _Appendf(self, lst, test_key, format_str, default=None): | 82 def _Appendf(self, lst, test_key, format_str, default=None): |
| 70 if test_key in self._Settings(): | 83 if test_key in self._Settings(): |
| 71 lst.append(format_str % str(self._Settings()[test_key])) | 84 lst.append(format_str % str(self._Settings()[test_key])) |
| (...skipping 665 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 737 self._Test( | 750 self._Test( |
| 738 'DEBUG_INFORMATION_FORMAT', 'dwarf-with-dsym', default='dwarf') and | 751 'DEBUG_INFORMATION_FORMAT', 'dwarf-with-dsym', default='dwarf') and |
| 739 self.spec['type'] != 'static_library'): | 752 self.spec['type'] != 'static_library'): |
| 740 if not quiet: | 753 if not quiet: |
| 741 result.append('echo DSYMUTIL\\(%s\\)' % self.spec['target_name']) | 754 result.append('echo DSYMUTIL\\(%s\\)' % self.spec['target_name']) |
| 742 result.append('dsymutil %s -o %s' % (output_binary, output + '.dSYM')) | 755 result.append('dsymutil %s -o %s' % (output_binary, output + '.dSYM')) |
| 743 | 756 |
| 744 self.configname = None | 757 self.configname = None |
| 745 return result | 758 return result |
| 746 | 759 |
| 747 def GetTargetPostbuilds(self, configname, output, output_binary, quiet=False): | 760 def _GetTargetPostbuilds(self, configname, output, output_binary, |
| 761 quiet=False): |
| 748 """Returns a list of shell commands that contain the shell commands | 762 """Returns a list of shell commands that contain the shell commands |
| 749 to run as postbuilds for this target, before the actual postbuilds.""" | 763 to run as postbuilds for this target, before the actual postbuilds.""" |
| 750 # dSYMs need to build before stripping happens. | 764 # dSYMs need to build before stripping happens. |
| 751 return ( | 765 return ( |
| 752 self._GetDebugInfoPostbuilds(configname, output, output_binary, quiet) + | 766 self._GetDebugInfoPostbuilds(configname, output, output_binary, quiet) + |
| 753 self._GetStripPostbuilds(configname, output_binary, quiet)) | 767 self._GetStripPostbuilds(configname, output_binary, quiet)) |
| 754 | 768 |
| 769 def _GetIOSPostbuilds(self, configname, output_binary): |
| 770 """Return a shell command to codesign the iOS output binary so it can |
| 771 be deployed to a device. This should be run as the very last step of the |
| 772 build.""" |
| 773 if not (self.isIOS and self.spec['type'] == "executable"): |
| 774 return [] |
| 775 |
| 776 identity = self.xcode_settings[configname].get('CODE_SIGN_IDENTITY', '') |
| 777 if identity == '': |
| 778 return [] |
| 779 if identity not in XcodeSettings._codesigning_key_cache: |
| 780 proc = subprocess.Popen(['security', 'find-identity', '-p', 'codesigning', |
| 781 '-v'], stdout=subprocess.PIPE) |
| 782 output = proc.communicate()[0].strip() |
| 783 key = None |
| 784 for item in output.split("\n"): |
| 785 if identity in item: |
| 786 assert key == None, ( |
| 787 "Multiple codesigning identities for identity: %s" % |
| 788 identity) |
| 789 key = item.split(' ')[1] |
| 790 XcodeSettings._codesigning_key_cache[identity] = key |
| 791 key = XcodeSettings._codesigning_key_cache[identity] |
| 792 if key: |
| 793 # Warn for any unimplemented signing xcode keys. |
| 794 unimpl = ['CODE_SIGN_RESOURCE_RULES_PATH', 'OTHER_CODE_SIGN_FLAGS', |
| 795 'CODE_SIGN_ENTITLEMENTS'] |
| 796 keys = set(self.xcode_settings[configname].keys()) |
| 797 unimpl = set(unimpl) & keys |
| 798 if unimpl: |
| 799 print 'Warning: Some codesign keys not implemented, ignoring:', \ |
| 800 ' '.join(unimpl) |
| 801 return ['codesign --force --sign %s %s' % (key, output_binary)] |
| 802 return [] |
| 803 |
| 804 def AddImplicitPostbuilds(self, configname, output, output_binary, |
| 805 postbuilds=[], quiet=False): |
| 806 """Returns a list of shell commands that should run before and after |
| 807 |postbuilds|.""" |
| 808 assert output_binary is not None |
| 809 pre = self._GetTargetPostbuilds(configname, output, output_binary, quiet) |
| 810 post = self._GetIOSPostbuilds(configname, output_binary) |
| 811 return pre + postbuilds + post |
| 812 |
| 755 def _AdjustLibrary(self, library, config_name=None): | 813 def _AdjustLibrary(self, library, config_name=None): |
| 756 if library.endswith('.framework'): | 814 if library.endswith('.framework'): |
| 757 l = '-framework ' + os.path.splitext(os.path.basename(library))[0] | 815 l = '-framework ' + os.path.splitext(os.path.basename(library))[0] |
| 758 else: | 816 else: |
| 759 m = self.library_re.match(library) | 817 m = self.library_re.match(library) |
| 760 if m: | 818 if m: |
| 761 l = '-l' + m.group(1) | 819 l = '-l' + m.group(1) |
| 762 else: | 820 else: |
| 763 l = library | 821 l = library |
| 764 return l.replace('$(SDKROOT)', self._SdkPath(config_name)) | 822 return l.replace('$(SDKROOT)', self._SdkPath(config_name)) |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 808 | 866 |
| 809 sdk_root = self._SdkRoot(configname) | 867 sdk_root = self._SdkRoot(configname) |
| 810 cache['DTSDKName'] = sdk_root | 868 cache['DTSDKName'] = sdk_root |
| 811 if xcode >= '0430': | 869 if xcode >= '0430': |
| 812 cache['DTSDKBuild'] = self._GetSdkVersionInfoItem( | 870 cache['DTSDKBuild'] = self._GetSdkVersionInfoItem( |
| 813 sdk_root, 'ProductBuildVersion') | 871 sdk_root, 'ProductBuildVersion') |
| 814 else: | 872 else: |
| 815 cache['DTSDKBuild'] = cache['BuildMachineOSBuild'] | 873 cache['DTSDKBuild'] = cache['BuildMachineOSBuild'] |
| 816 | 874 |
| 817 if self.isIOS: | 875 if self.isIOS: |
| 818 cache['UIDeviceFamily'] = self._XcodeIOSDeviceFamily(configname) | |
| 819 if configname.endswith("iphoneos"): | 876 if configname.endswith("iphoneos"): |
| 820 cache['CFBundleSupportedPlatforms'] = ['iPhoneOS'] | 877 cache['CFBundleSupportedPlatforms'] = ['iPhoneOS'] |
| 821 else: | 878 else: |
| 822 cache['CFBundleSupportedPlatforms'] = ['iPhoneSimulator'] | 879 cache['CFBundleSupportedPlatforms'] = ['iPhoneSimulator'] |
| 823 XcodeSettings._plist_cache[configname] = cache | 880 XcodeSettings._plist_cache[configname] = cache |
| 824 return XcodeSettings._plist_cache[configname] | 881 |
| 882 # Include extra plist items that are per-target, not per global |
| 883 # XcodeSettings. |
| 884 items = dict(XcodeSettings._plist_cache[configname]) |
| 885 if self.isIOS: |
| 886 items['UIDeviceFamily'] = self._XcodeIOSDeviceFamily(configname) |
| 887 return items |
| 825 | 888 |
| 826 | 889 |
| 827 class MacPrefixHeader(object): | 890 class MacPrefixHeader(object): |
| 828 """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. | 891 """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. |
| 829 | 892 |
| 830 This feature consists of several pieces: | 893 This feature consists of several pieces: |
| 831 * If GCC_PREFIX_HEADER is present, all compilations in that project get an | 894 * If GCC_PREFIX_HEADER is present, all compilations in that project get an |
| 832 additional |-include path_to_prefix_header| cflag. | 895 additional |-include path_to_prefix_header| cflag. |
| 833 * If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is | 896 * If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is |
| 834 instead compiled, and all other compilations in the project get an | 897 instead compiled, and all other compilations in the project get an |
| (...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1227 new_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos' | 1290 new_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos' |
| 1228 target_dict['configurations'][new_config_name] = new_config_dict | 1291 target_dict['configurations'][new_config_name] = new_config_dict |
| 1229 return targets | 1292 return targets |
| 1230 | 1293 |
| 1231 def CloneConfigurationForDeviceAndEmulator(target_dicts): | 1294 def CloneConfigurationForDeviceAndEmulator(target_dicts): |
| 1232 """If |target_dicts| contains any iOS targets, automatically create -iphoneos | 1295 """If |target_dicts| contains any iOS targets, automatically create -iphoneos |
| 1233 targets for iOS device builds.""" | 1296 targets for iOS device builds.""" |
| 1234 if _HasIOSTarget(target_dicts): | 1297 if _HasIOSTarget(target_dicts): |
| 1235 return _AddIOSDeviceConfigurations(target_dicts) | 1298 return _AddIOSDeviceConfigurations(target_dicts) |
| 1236 return target_dicts | 1299 return target_dicts |
| OLD | NEW |