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

Side by Side Diff: build/config/ios/codesign.py

Issue 2187343003: Embeds the entitlements file in the binary in simulator builds. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix compilation on device when code signing is disabled. Created 4 years, 4 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
« no previous file with comments | « no previous file | build/config/ios/entitlements.plist » ('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 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 The Chromium Authors. 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 import argparse 5 import argparse
6 import fnmatch 6 import fnmatch
7 import glob 7 import glob
8 import os 8 import os
9 import plistlib 9 import plistlib
10 import shutil 10 import shutil
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 return self._data.get('Entitlements', {}).get('application-identifier', '') 83 return self._data.get('Entitlements', {}).get('application-identifier', '')
84 84
85 @property 85 @property
86 def team_identifier(self): 86 def team_identifier(self):
87 return self._data.get('TeamIdentifier', [''])[0] 87 return self._data.get('TeamIdentifier', [''])[0]
88 88
89 @property 89 @property
90 def entitlements(self): 90 def entitlements(self):
91 return self._data.get('Entitlements', {}) 91 return self._data.get('Entitlements', {})
92 92
93 def ValidToSignBundle(self, bundle): 93 def ValidToSignBundle(self, bundle_identifier):
94 """Checks whether the provisioning profile can sign bundle_identifier. 94 """Checks whether the provisioning profile can sign bundle_identifier.
95 95
96 Args: 96 Args:
97 bundle: the Bundle object that needs to be signed. 97 bundle_identifier: the identifier of the bundle that needs to be signed.
98 98
99 Returns: 99 Returns:
100 True if the mobile provisioning profile can be used to sign a bundle 100 True if the mobile provisioning profile can be used to sign a bundle
101 with the corresponding bundle_identifier, False otherwise. 101 with the corresponding bundle_identifier, False otherwise.
102 """ 102 """
103 return fnmatch.fnmatch( 103 return fnmatch.fnmatch(
104 '%s.%s' % (self.team_identifier, bundle.identifier), 104 '%s.%s' % (self.team_identifier, bundle_identifier),
105 self.application_identifier_pattern) 105 self.application_identifier_pattern)
106 106
107 def Install(self, bundle): 107 def Install(self, bundle):
108 """Copies mobile provisioning profile info the bundle.""" 108 """Copies mobile provisioning profile info the bundle."""
109 installation_path = os.path.join(bundle.path, 'embedded.mobileprovision') 109 installation_path = os.path.join(bundle.path, 'embedded.mobileprovision')
110 shutil.copy2(self.path, installation_path) 110 shutil.copy2(self.path, installation_path)
111 111
112 112
113 class Entitlements(object): 113 class Entitlements(object):
114 """Wraps an Entitlement plist file.""" 114 """Wraps an Entitlement plist file."""
(...skipping 29 matching lines...) Expand all
144 144
145 def LoadDefaults(self, defaults): 145 def LoadDefaults(self, defaults):
146 for key, value in defaults.iteritems(): 146 for key, value in defaults.iteritems():
147 if key not in self._data: 147 if key not in self._data:
148 self._data[key] = value 148 self._data[key] = value
149 149
150 def WriteTo(self, target_path): 150 def WriteTo(self, target_path):
151 plistlib.writePlist(self._data, target_path) 151 plistlib.writePlist(self._data, target_path)
152 152
153 153
154 def FindProvisioningProfile(bundle, provisioning_profile_short_name): 154 def FindProvisioningProfile(bundle_identifier, provisioning_profile_short_name):
155 """Finds mobile provisioning profile to use to sign bundle. 155 """Finds mobile provisioning profile to use to sign bundle.
156 156
157 Args: 157 Args:
158 bundle: the Bundle object to sign. 158 bundle_identifier: the identifier of the bundle to sign.
159 provisioning_profile_short_path: optional short name of the mobile 159 provisioning_profile_short_path: optional short name of the mobile
160 provisioning profile file to use to sign (will still be checked 160 provisioning profile file to use to sign (will still be checked
161 to see if it can sign bundle). 161 to see if it can sign bundle).
162 162
163 Returns: 163 Returns:
164 The ProvisioningProfile object that can be used to sign the Bundle 164 The ProvisioningProfile object that can be used to sign the Bundle
165 object. 165 object.
166 166
167 Raises: 167 Raises:
168 InstallationError if no mobile provisioning profile can be used to 168 InstallationError if no mobile provisioning profile can be used to
(...skipping 15 matching lines...) Expand all
184 184
185 if not provisioning_profile_paths: 185 if not provisioning_profile_paths:
186 provisioning_profile_paths = glob.glob( 186 provisioning_profile_paths = glob.glob(
187 os.path.join(provisioning_profiles_dir, '*.mobileprovision')) 187 os.path.join(provisioning_profiles_dir, '*.mobileprovision'))
188 188
189 # Iterate over all installed mobile provisioning profiles and filter those 189 # Iterate over all installed mobile provisioning profiles and filter those
190 # that can be used to sign the bundle. 190 # that can be used to sign the bundle.
191 valid_provisioning_profiles = [] 191 valid_provisioning_profiles = []
192 for provisioning_profile_path in provisioning_profile_paths: 192 for provisioning_profile_path in provisioning_profile_paths:
193 provisioning_profile = ProvisioningProfile(provisioning_profile_path) 193 provisioning_profile = ProvisioningProfile(provisioning_profile_path)
194 if provisioning_profile.ValidToSignBundle(bundle): 194 if provisioning_profile.ValidToSignBundle(bundle_identifier):
195 valid_provisioning_profiles.append(provisioning_profile) 195 valid_provisioning_profiles.append(provisioning_profile)
196 196
197 if not valid_provisioning_profiles: 197 if not valid_provisioning_profiles:
198 raise InstallationError( 198 raise InstallationError(
199 'no mobile provisioning profile for "%s"', 199 'no mobile provisioning profile for "%s"',
200 bundle.identifier) 200 bundle_identifier)
201 201
202 # Select the most specific mobile provisioning profile, i.e. the one with 202 # Select the most specific mobile provisioning profile, i.e. the one with
203 # the longest application identifier pattern. 203 # the longest application identifier pattern.
204 return max( 204 return max(
205 valid_provisioning_profiles, 205 valid_provisioning_profiles,
206 key=lambda p: len(p.application_identifier_pattern)) 206 key=lambda p: len(p.application_identifier_pattern))
207 207
208 208
209 def InstallFramework(framework_path, bundle, args): 209 def InstallFramework(framework_path, bundle, args):
210 """Install framework from |framework_path| to |bundle| and code-re-sign it.""" 210 """Install framework from |framework_path| to |bundle| and code-re-sign it."""
211 installed_framework_path = os.path.join( 211 installed_framework_path = os.path.join(
212 bundle.path, 'Frameworks', os.path.basename(framework_path)) 212 bundle.path, 'Frameworks', os.path.basename(framework_path))
213 213
214 if os.path.exists(installed_framework_path): 214 if os.path.exists(installed_framework_path):
215 shutil.rmtree(installed_framework_path) 215 shutil.rmtree(installed_framework_path)
216 216
217 shutil.copytree(framework_path, installed_framework_path) 217 shutil.copytree(framework_path, installed_framework_path)
218 218
219 framework_bundle = Bundle(installed_framework_path) 219 framework_bundle = Bundle(installed_framework_path)
220 CodeSignBundle(framework_bundle.binary_path, framework_bundle, args, True) 220 CodeSignBundle(framework_bundle.binary_path, framework_bundle, args, True)
221 221
222 222
223 def GenerateEntitlements(bundle_identifier, provisioning_profile, args):
justincohen 2016/07/29 22:28:10 If the upstream bots can have an empty provisionin
sdefresne 2016/08/02 16:58:57 Done.
224 """Generates an entitlements file.
225
226 Args:
227 bundle_identifier: identifier of the bundle to sign.
228 provisioning_profile: ProvisioningProfile object.
229 output_path: path to the generated entitlements file.
230 """
231 entitlements = Entitlements(args.entitlements_path)
232 entitlements.LoadDefaults(provisioning_profile.entitlements)
233 entitlements.ExpandVariables({
234 'CFBundleIdentifier': bundle_identifier,
235 'AppIdentifierPrefix': '%s.' % (provisioning_profile.team_identifier,)
236 })
237 return entitlements
238
239
223 def CodeSignBundle(binary, bundle, args, preserve=False): 240 def CodeSignBundle(binary, bundle, args, preserve=False):
224 """Cryptographically signs bundle. 241 """Cryptographically signs bundle.
225 242
226 Args: 243 Args:
227 bundle: the Bundle object to sign. 244 bundle: the Bundle object to sign.
228 args: a dictionary with configuration settings for the code signature, 245 args: a dictionary with configuration settings for the code signature,
229 need to define 'entitlements_path', 'provisioning_profile_short_name', 246 need to define 'entitlements_path', 'provisioning_profile_short_name',
230 'deep_signature' and 'identify' keys. 247 'deep_signature' and 'identify' keys.
231 """ 248 """
232 provisioning_profile = FindProvisioningProfile( 249 provisioning_profile = FindProvisioningProfile(
233 bundle, args.provisioning_profile_short_name) 250 bundle.identifier, args.provisioning_profile_short_name)
234 provisioning_profile.Install(bundle) 251 provisioning_profile.Install(bundle)
235 252
236 if preserve: 253 if preserve:
237 process = subprocess.Popen([ 254 process = subprocess.Popen([
238 'xcrun', 'codesign', '--force', '--sign', args.identity, 255 'xcrun', 'codesign', '--force', '--sign', args.identity,
239 '--deep', '--preserve-metadata=identifier,entitlements', 256 '--deep', '--preserve-metadata=identifier,entitlements',
240 '--timestamp=none', bundle.path], stderr=subprocess.PIPE) 257 '--timestamp=none', bundle.path], stderr=subprocess.PIPE)
241 _, stderr = process.communicate() 258 _, stderr = process.communicate()
242 if process.returncode: 259 if process.returncode:
243 sys.stderr.write(stderr) 260 sys.stderr.write(stderr)
244 sys.exit(process.returncode) 261 sys.exit(process.returncode)
245 for line in stderr.splitlines(): 262 for line in stderr.splitlines():
246 # Ignore expected warning as we are replacing the signature on purpose. 263 # Ignore expected warning as we are replacing the signature on purpose.
247 if not line.endswith(': replacing existing signature'): 264 if not line.endswith(': replacing existing signature'):
248 sys.stderr.write(line + '\n') 265 sys.stderr.write(line + '\n')
249 else: 266 else:
250 signature_file = os.path.join( 267 signature_file = os.path.join(
251 bundle.path, '_CodeSignature', 'CodeResources') 268 bundle.path, '_CodeSignature', 'CodeResources')
252 if os.path.isfile(signature_file): 269 if os.path.isfile(signature_file):
253 os.unlink(signature_file) 270 os.unlink(signature_file)
254 271
255 if os.path.isfile(bundle.binary_path): 272 if os.path.isfile(bundle.binary_path):
256 os.unlink(bundle.binary_path) 273 os.unlink(bundle.binary_path)
257 shutil.copy(binary, bundle.binary_path) 274 shutil.copy(binary, bundle.binary_path)
258 275
259 entitlements = Entitlements(args.entitlements_path) 276 entitlements = GenerateEntitlements(
260 entitlements.LoadDefaults(provisioning_profile.entitlements) 277 bundle.identifier, provisioning_profile, args)
261 entitlements.ExpandVariables({
262 'CFBundleIdentifier': bundle.identifier,
263 'AppIdentifierPrefix': '%s.' % (provisioning_profile.team_identifier,)
264 })
265 278
266 with tempfile.NamedTemporaryFile(suffix='.xcent') as temporary_file_path: 279 with tempfile.NamedTemporaryFile(suffix='.xcent') as temporary_file_path:
267 entitlements.WriteTo(temporary_file_path.name) 280 entitlements.WriteTo(temporary_file_path.name)
268 subprocess.check_call([ 281 subprocess.check_call([
269 'xcrun', 'codesign', '--force', '--sign', args.identity, 282 'xcrun', 'codesign', '--force', '--sign', args.identity,
270 '--entitlements', temporary_file_path.name, '--timestamp=none', 283 '--entitlements', temporary_file_path.name, '--timestamp=none',
271 bundle.path]) 284 bundle.path])
272 285
273 286
287 def MainCodeSignBundle(args):
288 """Adapter to call CodeSignBundle from Main."""
289 bundle = Bundle(args.path)
290 for framework in args.frameworks:
291 InstallFramework(framework, bundle, args)
292 CodeSignBundle(args.binary, bundle, args)
293
294
295 def MainGenerateEntitlements(args):
296 """Adapter to call GenerateEntitlements from Main."""
297 info_plist = LoadPlistFile(args.info_plist)
298 bundle_identifier = info_plist['CFBundleIdentifier']
299 provisioning_profile = FindProvisioningProfile(
300 bundle_identifier, args.provisioning_profile_short_name)
301 entitlements = GenerateEntitlements(
302 bundle_identifier, provisioning_profile, args)
303 entitlements.WriteTo(args.path)
304
305
274 def Main(): 306 def Main():
275 parser = argparse.ArgumentParser('codesign iOS bundles') 307 parser = argparse.ArgumentParser('codesign iOS bundles')
276 parser.add_argument( 308 parser.add_argument(
277 'path', help='path to the iOS bundle to codesign')
278 parser.add_argument(
279 '--binary', '-b', required=True,
280 help='path to the iOS bundle binary')
281 parser.add_argument(
282 '--provisioning-profile', '-p', dest='provisioning_profile_short_name', 309 '--provisioning-profile', '-p', dest='provisioning_profile_short_name',
283 help='short name of the mobile provisioning profile to use (' 310 help='short name of the mobile provisioning profile to use ('
284 'if undefined, will autodetect the mobile provisioning ' 311 'if undefined, will autodetect the mobile provisioning '
285 'to use)') 312 'to use)')
286 parser.add_argument( 313 parser.add_argument(
314 '--entitlements', '-e', dest='entitlements_path',
315 help='path to the entitlements file to use')
316 subparsers = parser.add_subparsers()
317
318 code_sign_bundle_parser = subparsers.add_parser(
319 'code-sign-bundle',
320 help='code sign a bundle')
321 code_sign_bundle_parser.add_argument(
322 'path', help='path to the iOS bundle to codesign')
323 code_sign_bundle_parser.add_argument(
287 '--identity', '-i', required=True, 324 '--identity', '-i', required=True,
288 help='identity to use to codesign') 325 help='identity to use to codesign')
289 parser.add_argument( 326 code_sign_bundle_parser.add_argument(
290 '--entitlements', '-e', dest='entitlements_path', 327 '--binary', '-b', required=True,
291 help='path to the entitlements file to use') 328 help='path to the iOS bundle binary')
292 parser.add_argument( 329 code_sign_bundle_parser.add_argument(
293 '--framework', '-F', action='append', default=[], dest="frameworks", 330 '--framework', '-F', action='append', default=[], dest="frameworks",
294 help='install and resign system framework') 331 help='install and resign system framework')
332 code_sign_bundle_parser.set_defaults(func=MainCodeSignBundle)
333
334 generate_entitlements_parser = subparsers.add_parser(
335 'generate-entitlements',
336 help='generate entitlements file')
337 generate_entitlements_parser.add_argument(
338 'path', help='path to the entitlements file to generate')
339 generate_entitlements_parser.add_argument(
340 '--info-plist', '-p', required=True,
341 help='path to the bundle Info.plist')
342 generate_entitlements_parser.set_defaults(
343 func=MainGenerateEntitlements)
344
295 args = parser.parse_args() 345 args = parser.parse_args()
296 346 args.func(args)
297 bundle = Bundle(args.path)
298 for framework in args.frameworks:
299 InstallFramework(framework, bundle, args)
300
301 CodeSignBundle(args.binary, bundle, args)
302 347
303 348
304 if __name__ == '__main__': 349 if __name__ == '__main__':
305 sys.exit(Main()) 350 sys.exit(Main())
OLDNEW
« no previous file with comments | « no previous file | build/config/ios/entitlements.plist » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698