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

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

Issue 1454433002: Python 3 compatibility Base URL: https://chromium.googlesource.com/external/gyp.git@master
Patch Set: Rebase with master (4ec6c4e3a94bd04a6da2858163d40b2429b8aad1) Created 4 years, 8 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
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 Google Inc. All rights reserved. 2 # Copyright (c) 2012 Google Inc. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Utility functions to perform Xcode-style build steps. 6 """Utility functions to perform Xcode-style build steps.
7 7
8 These functions are executed via gyp-mac-tool when using the Makefile generator. 8 These functions are executed via gyp-mac-tool when using the Makefile generator.
9 """ 9 """
10 10
11 from __future__ import print_function
12
11 import fcntl 13 import fcntl
12 import fnmatch 14 import fnmatch
13 import glob 15 import glob
14 import json 16 import json
15 import os 17 import os
16 import plistlib 18 import plistlib
17 import re 19 import re
18 import shutil 20 import shutil
19 import string
20 import struct 21 import struct
21 import subprocess 22 import subprocess
22 import sys 23 import sys
23 import tempfile 24 import tempfile
24 25
25 26
26 def main(args): 27 def main(args):
27 executor = MacTool() 28 executor = MacTool()
28 exit_code = executor.Dispatch(args) 29 exit_code = executor.Dispatch(args)
29 if exit_code is not None: 30 if exit_code is not None:
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 def _DetectInputEncoding(self, file_name): 144 def _DetectInputEncoding(self, file_name):
144 """Reads the first few bytes from file_name and tries to guess the text 145 """Reads the first few bytes from file_name and tries to guess the text
145 encoding. Returns None as a guess if it can't detect it.""" 146 encoding. Returns None as a guess if it can't detect it."""
146 fp = open(file_name, 'rb') 147 fp = open(file_name, 'rb')
147 try: 148 try:
148 header = fp.read(3) 149 header = fp.read(3)
149 except e: 150 except e:
150 fp.close() 151 fp.close()
151 return None 152 return None
152 fp.close() 153 fp.close()
153 if header.startswith("\xFE\xFF"): 154 if header.startswith(b"\xFE\xFF"):
154 return "UTF-16" 155 return "UTF-16"
155 elif header.startswith("\xFF\xFE"): 156 elif header.startswith(b"\xFF\xFE"):
156 return "UTF-16" 157 return "UTF-16"
157 elif header.startswith("\xEF\xBB\xBF"): 158 elif header.startswith(b"\xEF\xBB\xBF"):
158 return "UTF-8" 159 return "UTF-8"
159 else: 160 else:
160 return None 161 return None
161 162
162 def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys): 163 def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys):
163 """Copies the |source| Info.plist to the destination directory |dest|.""" 164 """Copies the |source| Info.plist to the destination directory |dest|."""
164 # Read the source Info.plist into memory. 165 # Read the source Info.plist into memory.
165 fd = open(source, 'r') 166 fd = open(source, 'r')
166 lines = fd.read() 167 lines = fd.read()
167 fd.close() 168 fd.close()
168 169
169 # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). 170 # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild).
170 plist = plistlib.readPlistFromString(lines) 171 plist = plistlib.readPlistFromString(lines)
171 if keys: 172 if keys:
172 plist = dict(plist.items() + json.loads(keys[0]).items()) 173 plist.update(json.loads(keys[0]))
173 lines = plistlib.writePlistToString(plist) 174 lines = plistlib.writePlistToString(plist)
174 175
175 # Go through all the environment variables and replace them as variables in 176 # Go through all the environment variables and replace them as variables in
176 # the file. 177 # the file.
177 IDENT_RE = re.compile(r'[/\s]') 178 IDENT_RE = re.compile(r'[/\s]')
178 for key in os.environ: 179 for key in os.environ:
179 if key.startswith('_'): 180 if key.startswith('_'):
180 continue 181 continue
181 evar = '${%s}' % key 182 evar = '${%s}' % key
182 evalue = os.environ[key] 183 evalue = os.environ[key]
183 lines = string.replace(lines, evar, evalue) 184 lines = lines.replace(evar, evalue)
184 185
185 # Xcode supports various suffices on environment variables, which are 186 # Xcode supports various suffices on environment variables, which are
186 # all undocumented. :rfc1034identifier is used in the standard project 187 # all undocumented. :rfc1034identifier is used in the standard project
187 # template these days, and :identifier was used earlier. They are used to 188 # template these days, and :identifier was used earlier. They are used to
188 # convert non-url characters into things that look like valid urls -- 189 # convert non-url characters into things that look like valid urls --
189 # except that the replacement character for :identifier, '_' isn't valid 190 # except that the replacement character for :identifier, '_' isn't valid
190 # in a URL either -- oops, hence :rfc1034identifier was born. 191 # in a URL either -- oops, hence :rfc1034identifier was born.
191 evar = '${%s:identifier}' % key 192 evar = '${%s:identifier}' % key
192 evalue = IDENT_RE.sub('_', os.environ[key]) 193 evalue = IDENT_RE.sub('_', os.environ[key])
193 lines = string.replace(lines, evar, evalue) 194 lines = lines.replace(evar, evalue)
194 195
195 evar = '${%s:rfc1034identifier}' % key 196 evar = '${%s:rfc1034identifier}' % key
196 evalue = IDENT_RE.sub('-', os.environ[key]) 197 evalue = IDENT_RE.sub('-', os.environ[key])
197 lines = string.replace(lines, evar, evalue) 198 lines = lines.replace(evar, evalue)
198 199
199 # Remove any keys with values that haven't been replaced. 200 # Remove any keys with values that haven't been replaced.
200 lines = lines.split('\n') 201 lines = lines.split('\n')
201 for i in range(len(lines)): 202 for i in range(len(lines)):
202 if lines[i].strip().startswith("<string>${"): 203 if lines[i].strip().startswith("<string>${"):
203 lines[i] = None 204 lines[i] = None
204 lines[i - 1] = None 205 lines[i - 1] = None
205 lines = '\n'.join(filter(lambda x: x is not None, lines)) 206 lines = '\n'.join(filter(lambda x: x is not None, lines))
206 207
207 # Write out the file with variables replaced. 208 # Write out the file with variables replaced.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
258 env = os.environ.copy() 259 env = os.environ.copy()
259 # Ref: 260 # Ref:
260 # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c 261 # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c
261 # The problem with this flag is that it resets the file mtime on the file to 262 # The problem with this flag is that it resets the file mtime on the file to
262 # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone. 263 # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone.
263 env['ZERO_AR_DATE'] = '1' 264 env['ZERO_AR_DATE'] = '1'
264 libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) 265 libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env)
265 _, err = libtoolout.communicate() 266 _, err = libtoolout.communicate()
266 for line in err.splitlines(): 267 for line in err.splitlines():
267 if not libtool_re.match(line) and not libtool_re5.match(line): 268 if not libtool_re.match(line) and not libtool_re5.match(line):
268 print >>sys.stderr, line 269 print(line, file=sys.stderr)
269 # Unconditionally touch the output .a file on the command line if present 270 # Unconditionally touch the output .a file on the command line if present
270 # and the command succeeded. A bit hacky. 271 # and the command succeeded. A bit hacky.
271 if not libtoolout.returncode: 272 if not libtoolout.returncode:
272 for i in range(len(cmd_list) - 1): 273 for i in range(len(cmd_list) - 1):
273 if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'): 274 if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'):
274 os.utime(cmd_list[i+1], None) 275 os.utime(cmd_list[i+1], None)
275 break 276 break
276 return libtoolout.returncode 277 return libtoolout.returncode
277 278
278 def ExecPackageIosFramework(self, framework): 279 def ExecPackageIosFramework(self, framework):
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 ]) 374 ])
374 else: 375 else:
375 command_line.extend([ 376 command_line.extend([
376 '--platform', 'macosx', '--target-device', 'mac', 377 '--platform', 'macosx', '--target-device', 'mac',
377 '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'], 378 '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'],
378 '--compile', 379 '--compile',
379 os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']), 380 os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']),
380 ]) 381 ])
381 if keys: 382 if keys:
382 keys = json.loads(keys) 383 keys = json.loads(keys)
383 for key, value in keys.iteritems(): 384 for key, value in keys.items():
384 arg_name = '--' + key 385 arg_name = '--' + key
385 if isinstance(value, bool): 386 if isinstance(value, bool):
386 if value: 387 if value:
387 command_line.append(arg_name) 388 command_line.append(arg_name)
388 elif isinstance(value, list): 389 elif isinstance(value, list):
389 for v in value: 390 for v in value:
390 command_line.append(arg_name) 391 command_line.append(arg_name)
391 command_line.append(str(v)) 392 command_line.append(str(v))
392 else: 393 else:
393 command_line.append(arg_name) 394 command_line.append(arg_name)
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
465 A tuple of the path to the selected provisioning profile, the data of 466 A tuple of the path to the selected provisioning profile, the data of
466 the embedded plist in the provisioning profile and the team identifier 467 the embedded plist in the provisioning profile and the team identifier
467 to use for code signing. 468 to use for code signing.
468 469
469 Raises: 470 Raises:
470 SystemExit: if no .mobileprovision can be used to sign the bundle. 471 SystemExit: if no .mobileprovision can be used to sign the bundle.
471 """ 472 """
472 profiles_dir = os.path.join( 473 profiles_dir = os.path.join(
473 os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') 474 os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles')
474 if not os.path.isdir(profiles_dir): 475 if not os.path.isdir(profiles_dir):
475 print >>sys.stderr, ( 476 print((
476 'cannot find mobile provisioning for %s' % bundle_identifier) 477 'cannot find mobile provisioning for %s' % bundle_identifier),
478 file=sys.stderr)
477 sys.exit(1) 479 sys.exit(1)
478 provisioning_profiles = None 480 provisioning_profiles = None
479 if profile: 481 if profile:
480 profile_path = os.path.join(profiles_dir, profile + '.mobileprovision') 482 profile_path = os.path.join(profiles_dir, profile + '.mobileprovision')
481 if os.path.exists(profile_path): 483 if os.path.exists(profile_path):
482 provisioning_profiles = [profile_path] 484 provisioning_profiles = [profile_path]
483 if not provisioning_profiles: 485 if not provisioning_profiles:
484 provisioning_profiles = glob.glob( 486 provisioning_profiles = glob.glob(
485 os.path.join(profiles_dir, '*.mobileprovision')) 487 os.path.join(profiles_dir, '*.mobileprovision'))
486 valid_provisioning_profiles = {} 488 valid_provisioning_profiles = {}
487 for profile_path in provisioning_profiles: 489 for profile_path in provisioning_profiles:
488 profile_data = self._LoadProvisioningProfile(profile_path) 490 profile_data = self._LoadProvisioningProfile(profile_path)
489 app_id_pattern = profile_data.get( 491 app_id_pattern = profile_data.get(
490 'Entitlements', {}).get('application-identifier', '') 492 'Entitlements', {}).get('application-identifier', '')
491 for team_identifier in profile_data.get('TeamIdentifier', []): 493 for team_identifier in profile_data.get('TeamIdentifier', []):
492 app_id = '%s.%s' % (team_identifier, bundle_identifier) 494 app_id = '%s.%s' % (team_identifier, bundle_identifier)
493 if fnmatch.fnmatch(app_id, app_id_pattern): 495 if fnmatch.fnmatch(app_id, app_id_pattern):
494 valid_provisioning_profiles[app_id_pattern] = ( 496 valid_provisioning_profiles[app_id_pattern] = (
495 profile_path, profile_data, team_identifier) 497 profile_path, profile_data, team_identifier)
496 if not valid_provisioning_profiles: 498 if not valid_provisioning_profiles:
497 print >>sys.stderr, ( 499 print((
498 'cannot find mobile provisioning for %s' % bundle_identifier) 500 'cannot find mobile provisioning for %s' % bundle_identifier),
501 file=sys.stderr)
499 sys.exit(1) 502 sys.exit(1)
500 # If the user has multiple provisioning profiles installed that can be 503 # If the user has multiple provisioning profiles installed that can be
501 # used for ${bundle_identifier}, pick the most specific one (ie. the 504 # used for ${bundle_identifier}, pick the most specific one (ie. the
502 # provisioning profile whose pattern is the longest). 505 # provisioning profile whose pattern is the longest).
503 selected_key = max(valid_provisioning_profiles, key=lambda v: len(v)) 506 selected_key = max(valid_provisioning_profiles, key=lambda v: len(v))
504 return valid_provisioning_profiles[selected_key] 507 return valid_provisioning_profiles[selected_key]
505 508
506 def _LoadProvisioningProfile(self, profile_path): 509 def _LoadProvisioningProfile(self, profile_path):
507 """Extracts the plist embedded in a provisioning profile. 510 """Extracts the plist embedded in a provisioning profile.
508 511
509 Args: 512 Args:
510 profile_path: string, path to the .mobileprovision file 513 profile_path: string, path to the .mobileprovision file
511 514
512 Returns: 515 Returns:
513 Content of the plist embedded in the provisioning profile as a dictionary. 516 Content of the plist embedded in the provisioning profile as a dictionary.
514 """ 517 """
515 with tempfile.NamedTemporaryFile() as temp: 518 with tempfile.NamedTemporaryFile() as temp:
516 subprocess.check_call([ 519 subprocess.check_call([
517 'security', 'cms', '-D', '-i', profile_path, '-o', temp.name]) 520 'security', 'cms', '-D', '-i', profile_path, '-o', temp.name])
518 return self._LoadPlistMaybeBinary(temp.name) 521 return self._LoadPlistMaybeBinary(temp.name)
519 522
520 def _MergePlist(self, merged_plist, plist): 523 def _MergePlist(self, merged_plist, plist):
521 """Merge |plist| into |merged_plist|.""" 524 """Merge |plist| into |merged_plist|."""
522 for key, value in plist.iteritems(): 525 for key, value in plist.items():
523 if isinstance(value, dict): 526 if isinstance(value, dict):
524 merged_value = merged_plist.get(key, {}) 527 merged_value = merged_plist.get(key, {})
525 if isinstance(merged_value, dict): 528 if isinstance(merged_value, dict):
526 self._MergePlist(merged_value, value) 529 self._MergePlist(merged_value, value)
527 merged_plist[key] = merged_value 530 merged_plist[key] = merged_value
528 else: 531 else:
529 merged_plist[key] = value 532 merged_plist[key] = value
530 else: 533 else:
531 merged_plist[key] = value 534 merged_plist[key] = value
532 535
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
622 Args: 625 Args:
623 data: object, can be either string, list or dictionary 626 data: object, can be either string, list or dictionary
624 substitutions: dictionary, variable substitutions to perform 627 substitutions: dictionary, variable substitutions to perform
625 628
626 Returns: 629 Returns:
627 Copy of data where each references to "$(variable)" has been replaced 630 Copy of data where each references to "$(variable)" has been replaced
628 by the corresponding value found in substitutions, or left intact if 631 by the corresponding value found in substitutions, or left intact if
629 the key was not found. 632 the key was not found.
630 """ 633 """
631 if isinstance(data, str): 634 if isinstance(data, str):
632 for key, value in substitutions.iteritems(): 635 for key, value in substitutions.items():
633 data = data.replace('$(%s)' % key, value) 636 data = data.replace('$(%s)' % key, value)
634 return data 637 return data
635 if isinstance(data, list): 638 if isinstance(data, list):
636 return [self._ExpandVariables(v, substitutions) for v in data] 639 return [self._ExpandVariables(v, substitutions) for v in data]
637 if isinstance(data, dict): 640 if isinstance(data, dict):
638 return {k: self._ExpandVariables(data[k], substitutions) for k in data} 641 return {k: self._ExpandVariables(data[k], substitutions) for k in data}
639 return data 642 return data
640 643
641 def NextGreaterPowerOf2(x): 644 def NextGreaterPowerOf2(x):
642 return 2**(x-1).bit_length() 645 return 2**(x-1).bit_length()
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
699 out.write(struct.pack('<s', '\0')) 702 out.write(struct.pack('<s', '\0'))
700 base = os.path.dirname(path) + os.sep 703 base = os.path.dirname(path) + os.sep
701 out.write(struct.pack('<%ds' % len(base), base)) 704 out.write(struct.pack('<%ds' % len(base), base))
702 out.write(struct.pack('<s', '\0')) 705 out.write(struct.pack('<s', '\0'))
703 path = os.path.basename(path) 706 path = os.path.basename(path)
704 out.write(struct.pack('<%ds' % len(path), path)) 707 out.write(struct.pack('<%ds' % len(path), path))
705 out.write(struct.pack('<s', '\0')) 708 out.write(struct.pack('<s', '\0'))
706 709
707 if __name__ == '__main__': 710 if __name__ == '__main__':
708 sys.exit(main(sys.argv[1:])) 711 sys.exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698