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

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 typo breaking code signing (not exercised by the bots). 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(path, defaults, bundle_identifier, team_identifier):
224 """Generates an entitlements file.
225
226 Args:
227 path: path to the entitlements template file
228 defaults: dictionary with defaults to embeds in the generated entitlements
229 bundle_identifier: identifier of the bundle to sign.
230 team_identifier: identifier for the team (for code signing)
231 """
232 entitlements = Entitlements(path)
233 entitlements.LoadDefaults(defaults)
234 entitlements.ExpandVariables({
235 'CFBundleIdentifier': bundle_identifier,
236 'AppIdentifierPrefix': '%s.' % team_identifier,
237 })
238 return entitlements
239
240
223 def CodeSignBundle(binary, bundle, args, preserve=False): 241 def CodeSignBundle(binary, bundle, args, preserve=False):
224 """Cryptographically signs bundle. 242 """Cryptographically signs bundle.
225 243
226 Args: 244 Args:
227 bundle: the Bundle object to sign. 245 bundle: the Bundle object to sign.
228 args: a dictionary with configuration settings for the code signature, 246 args: a dictionary with configuration settings for the code signature,
229 need to define 'entitlements_path', 'provisioning_profile_short_name', 247 need to define 'entitlements_path', 'provisioning_profile_short_name',
230 'deep_signature' and 'identify' keys. 248 'deep_signature' and 'identify' keys.
231 """ 249 """
232 provisioning_profile = FindProvisioningProfile( 250 provisioning_profile = FindProvisioningProfile(
233 bundle, args.provisioning_profile_short_name) 251 bundle.identifier, args.provisioning_profile_short_name)
234 provisioning_profile.Install(bundle) 252 provisioning_profile.Install(bundle)
235 253
236 if preserve: 254 if preserve:
237 process = subprocess.Popen([ 255 process = subprocess.Popen([
238 'xcrun', 'codesign', '--force', '--sign', args.identity, 256 'xcrun', 'codesign', '--force', '--sign', args.identity,
239 '--deep', '--preserve-metadata=identifier,entitlements', 257 '--deep', '--preserve-metadata=identifier,entitlements',
240 '--timestamp=none', bundle.path], stderr=subprocess.PIPE) 258 '--timestamp=none', bundle.path], stderr=subprocess.PIPE)
241 _, stderr = process.communicate() 259 _, stderr = process.communicate()
242 if process.returncode: 260 if process.returncode:
243 sys.stderr.write(stderr) 261 sys.stderr.write(stderr)
244 sys.exit(process.returncode) 262 sys.exit(process.returncode)
245 for line in stderr.splitlines(): 263 for line in stderr.splitlines():
246 # Ignore expected warning as we are replacing the signature on purpose. 264 # Ignore expected warning as we are replacing the signature on purpose.
247 if not line.endswith(': replacing existing signature'): 265 if not line.endswith(': replacing existing signature'):
248 sys.stderr.write(line + '\n') 266 sys.stderr.write(line + '\n')
249 else: 267 else:
250 signature_file = os.path.join( 268 signature_file = os.path.join(
251 bundle.path, '_CodeSignature', 'CodeResources') 269 bundle.path, '_CodeSignature', 'CodeResources')
252 if os.path.isfile(signature_file): 270 if os.path.isfile(signature_file):
253 os.unlink(signature_file) 271 os.unlink(signature_file)
254 272
255 if os.path.isfile(bundle.binary_path): 273 if os.path.isfile(bundle.binary_path):
256 os.unlink(bundle.binary_path) 274 os.unlink(bundle.binary_path)
257 shutil.copy(binary, bundle.binary_path) 275 shutil.copy(binary, bundle.binary_path)
258 276
259 entitlements = Entitlements(args.entitlements_path) 277 entitlements = GenerateEntitlements(
260 entitlements.LoadDefaults(provisioning_profile.entitlements) 278 args.entitlements_path, provisioning_profile.entitlements,
261 entitlements.ExpandVariables({ 279 bundle.identifier, provisioning_profile.team_identifier)
262 'CFBundleIdentifier': bundle.identifier,
263 'AppIdentifierPrefix': '%s.' % (provisioning_profile.team_identifier,)
264 })
265 280
266 with tempfile.NamedTemporaryFile(suffix='.xcent') as temporary_file_path: 281 with tempfile.NamedTemporaryFile(suffix='.xcent') as temporary_file_path:
267 entitlements.WriteTo(temporary_file_path.name) 282 entitlements.WriteTo(temporary_file_path.name)
268 subprocess.check_call([ 283 subprocess.check_call([
269 'xcrun', 'codesign', '--force', '--sign', args.identity, 284 'xcrun', 'codesign', '--force', '--sign', args.identity,
270 '--entitlements', temporary_file_path.name, '--timestamp=none', 285 '--entitlements', temporary_file_path.name, '--timestamp=none',
271 bundle.path]) 286 bundle.path])
272 287
273 288
289 def MainCodeSignBundle(args):
290 """Adapter to call CodeSignBundle from Main."""
291 bundle = Bundle(args.path)
292 for framework in args.frameworks:
293 InstallFramework(framework, bundle, args)
294 CodeSignBundle(args.binary, bundle, args)
295
296
297 def MainGenerateEntitlements(args):
298 """Adapter to call GenerateEntitlements from Main."""
299 info_plist = LoadPlistFile(args.info_plist)
300 bundle_identifier = info_plist['CFBundleIdentifier']
301 try:
302 provisioning_profile = FindProvisioningProfile(
303 bundle_identifier, args.provisioning_profile_short_name)
304 default_entitlements, team_identifier = (
305 provisioning_profile.entitlements,
306 provisioning_profile.team_identifier)
307 except InstallationError:
308 # Do not fail if no mobile provisioning is available, but instead embeds
309 # fake entitlements into the binary. This is necessary as some of the bots
310 # do not have mobile provisioning but still need to run the tests. In that
311 # case, use dummy values.
312 default_entitlements, team_identifier = {}, '*'
313
314 entitlements = GenerateEntitlements(
315 args.entitlements_path, default_entitlements, bundle_identifier,
316 team_identifier)
317 entitlements.WriteTo(args.path)
318
319
274 def Main(): 320 def Main():
275 parser = argparse.ArgumentParser('codesign iOS bundles') 321 parser = argparse.ArgumentParser('codesign iOS bundles')
276 parser.add_argument( 322 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', 323 '--provisioning-profile', '-p', dest='provisioning_profile_short_name',
283 help='short name of the mobile provisioning profile to use (' 324 help='short name of the mobile provisioning profile to use ('
284 'if undefined, will autodetect the mobile provisioning ' 325 'if undefined, will autodetect the mobile provisioning '
285 'to use)') 326 'to use)')
286 parser.add_argument( 327 parser.add_argument(
328 '--entitlements', '-e', dest='entitlements_path',
329 help='path to the entitlements file to use')
330 subparsers = parser.add_subparsers()
331
332 code_sign_bundle_parser = subparsers.add_parser(
333 'code-sign-bundle',
334 help='code sign a bundle')
335 code_sign_bundle_parser.add_argument(
336 'path', help='path to the iOS bundle to codesign')
337 code_sign_bundle_parser.add_argument(
287 '--identity', '-i', required=True, 338 '--identity', '-i', required=True,
288 help='identity to use to codesign') 339 help='identity to use to codesign')
289 parser.add_argument( 340 code_sign_bundle_parser.add_argument(
290 '--entitlements', '-e', dest='entitlements_path', 341 '--binary', '-b', required=True,
291 help='path to the entitlements file to use') 342 help='path to the iOS bundle binary')
292 parser.add_argument( 343 code_sign_bundle_parser.add_argument(
293 '--framework', '-F', action='append', default=[], dest="frameworks", 344 '--framework', '-F', action='append', default=[], dest="frameworks",
294 help='install and resign system framework') 345 help='install and resign system framework')
346 code_sign_bundle_parser.set_defaults(func=MainCodeSignBundle)
347
348 generate_entitlements_parser = subparsers.add_parser(
349 'generate-entitlements',
350 help='generate entitlements file')
351 generate_entitlements_parser.add_argument(
352 'path', help='path to the entitlements file to generate')
353 generate_entitlements_parser.add_argument(
354 '--info-plist', '-p', required=True,
355 help='path to the bundle Info.plist')
356 generate_entitlements_parser.set_defaults(
357 func=MainGenerateEntitlements)
358
295 args = parser.parse_args() 359 args = parser.parse_args()
296 360 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 361
303 362
304 if __name__ == '__main__': 363 if __name__ == '__main__':
305 sys.exit(Main()) 364 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