Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(805)

Side by Side Diff: pylib/gyp/xcode_emulation.py

Issue 26895006: ninja/mac: Support iOS codesign for ninja builds. (Closed) Base URL: http://gyp.googlecode.com/svn/trunk/
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pylib/gyp/generator/ninja.py ('k') | test/ios/app-bundle/TestApp/check_no_signature.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « pylib/gyp/generator/ninja.py ('k') | test/ios/app-bundle/TestApp/check_no_signature.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698