Improve support for Python3 in Mac, iOS build

- Decode the result of subprocess.Popen() so that the stdout/stderr
  result is str, not bytes.
- Use dict.items() instead of dict.iteritems().
- If the return of map() needs to be a list, use list comprehension.
- Use range() instead of xrange().
- Tuple parameter unpacking is removed on Python3, refactor its usage.
- struct.pack() doesn't accept str, convert arguments as bytes.
- plistlib has several API changes. Replace removed.
  functions (readPlistFromString). Still have some warnings like this:
  "The readPlist function is deprecated, use load() instead"
- _GetOutputNoError() in tweak_info_plist.py has no usage. Removed.

No intended behavior change.

Bug: 941669
Change-Id: Ibc204fad8b56e864a083046559133403dcf99fe3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2392289
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805790}
GitOrigin-RevId: 1fdde535bbe5fb06abdb35a7aadf5e5903ab3913
This commit is contained in:
Byoungchan Lee 2020-09-10 16:38:39 +00:00 коммит произвёл Copybara-Service
Родитель 82f5d003b9
Коммит 820101cf3c
10 изменённых файлов: 101 добавлений и 70 удалений

Просмотреть файл

@ -43,22 +43,8 @@ def _GetOutput(args):
"""Runs a subprocess and waits for termination. Returns (stdout, returncode) """Runs a subprocess and waits for termination. Returns (stdout, returncode)
of the process. stderr is attached to the parent.""" of the process. stderr is attached to the parent."""
proc = subprocess.Popen(args, stdout=subprocess.PIPE) proc = subprocess.Popen(args, stdout=subprocess.PIPE)
(stdout, stderr) = proc.communicate() stdout, _ = proc.communicate()
return (stdout, proc.returncode) return stdout.decode('UTF-8'), proc.returncode
def _GetOutputNoError(args):
"""Similar to _GetOutput() but ignores stderr. If there's an error launching
the child (like file not found), the exception will be caught and (None, 1)
will be returned to mimic quiet failure."""
try:
proc = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
except OSError:
return (None, 1)
(stdout, stderr) = proc.communicate()
return (stdout, proc.returncode)
def _RemoveKeys(plist, *keys): def _RemoveKeys(plist, *keys):
@ -194,9 +180,9 @@ def _TagSuffixes():
components_len = len(components) components_len = len(components)
combinations = 1 << components_len combinations = 1 << components_len
tag_suffixes = [] tag_suffixes = []
for combination in xrange(0, combinations): for combination in range(0, combinations):
tag_suffix = '' tag_suffix = ''
for component_index in xrange(0, components_len): for component_index in range(0, components_len):
if combination & (1 << component_index): if combination & (1 << component_index):
tag_suffix += '-' + components[component_index] tag_suffix += '-' + components[component_index]
tag_suffixes.append(tag_suffix) tag_suffixes.append(tag_suffix)

Просмотреть файл

@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
from __future__ import print_function
import argparse import argparse
import codecs import codecs
import datetime import datetime
@ -15,6 +17,11 @@ import subprocess
import sys import sys
import tempfile import tempfile
if sys.version_info.major < 3:
basestring_compat = basestring
else:
basestring_compat = str
def GetProvisioningProfilesDir(): def GetProvisioningProfilesDir():
"""Returns the location of the installed mobile provisioning profiles. """Returns the location of the installed mobile provisioning profiles.
@ -27,6 +34,21 @@ def GetProvisioningProfilesDir():
os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles')
def ReadPlistFromString(plist_bytes):
"""Parse property list from given |plist_bytes|.
Args:
plist_bytes: contents of property list to load. Must be bytes in python 3.
Returns:
The contents of property list as a python object.
"""
if sys.version_info.major == 2:
return plistlib.readPlistFromString(plist_bytes)
else:
return plistlib.loads(plist_bytes)
def LoadPlistFile(plist_path): def LoadPlistFile(plist_path):
"""Loads property list file at |plist_path|. """Loads property list file at |plist_path|.
@ -36,8 +58,9 @@ def LoadPlistFile(plist_path):
Returns: Returns:
The content of the property list file as a python object. The content of the property list file as a python object.
""" """
return plistlib.readPlistFromString(subprocess.check_output([ return ReadPlistFromString(
'xcrun', 'plutil', '-convert', 'xml1', '-o', '-', plist_path])) subprocess.check_output(
['xcrun', 'plutil', '-convert', 'xml1', '-o', '-', plist_path]))
class Bundle(object): class Bundle(object):
@ -74,7 +97,7 @@ class Bundle(object):
error message. The dictionary will be empty if there are no errors. error message. The dictionary will be empty if there are no errors.
""" """
errors = {} errors = {}
for key, expected_value in expected_mappings.iteritems(): for key, expected_value in expected_mappings.items():
if key in self._data: if key in self._data:
value = self._data[key] value = self._data[key]
if value != expected_value: if value != expected_value:
@ -88,9 +111,11 @@ class ProvisioningProfile(object):
def __init__(self, provisioning_profile_path): def __init__(self, provisioning_profile_path):
"""Initializes the ProvisioningProfile with data from profile file.""" """Initializes the ProvisioningProfile with data from profile file."""
self._path = provisioning_profile_path self._path = provisioning_profile_path
self._data = plistlib.readPlistFromString(subprocess.check_output([ self._data = ReadPlistFromString(
'xcrun', 'security', 'cms', '-D', '-u', 'certUsageAnyCA', subprocess.check_output([
'-i', provisioning_profile_path])) 'xcrun', 'security', 'cms', '-D', '-u', 'certUsageAnyCA', '-i',
provisioning_profile_path
]))
@property @property
def path(self): def path(self):
@ -155,13 +180,13 @@ class Entitlements(object):
self._data = self._ExpandVariables(self._data, substitutions) self._data = self._ExpandVariables(self._data, substitutions)
def _ExpandVariables(self, data, substitutions): def _ExpandVariables(self, data, substitutions):
if isinstance(data, str): if isinstance(data, basestring_compat):
for key, substitution in substitutions.iteritems(): for key, substitution in substitutions.items():
data = data.replace('$(%s)' % (key,), substitution) data = data.replace('$(%s)' % (key,), substitution)
return data return data
if isinstance(data, dict): if isinstance(data, dict):
for key, value in data.iteritems(): for key, value in data.items():
data[key] = self._ExpandVariables(value, substitutions) data[key] = self._ExpandVariables(value, substitutions)
return data return data
@ -172,7 +197,7 @@ class Entitlements(object):
return data return data
def LoadDefaults(self, defaults): def LoadDefaults(self, defaults):
for key, value in defaults.iteritems(): for key, value in defaults.items():
if key not in self._data: if key not in self._data:
self._data[key] = value self._data[key] = value

Просмотреть файл

@ -32,16 +32,18 @@ NOTICE_SECTION = 'com.apple.actool.compilation-results'
# Regular expressions matching spurious messages from actool that should be # Regular expressions matching spurious messages from actool that should be
# ignored (as they are bogus). Generally a bug should be filed with Apple # ignored (as they are bogus). Generally a bug should be filed with Apple
# when adding a pattern here. # when adding a pattern here.
SPURIOUS_PATTERNS = map(re.compile, [ SPURIOUS_PATTERNS = [
# crbug.com/770634, likely a bug in Xcode 9.1 beta, remove once build re.compile(v) for v in [
# requires a version of Xcode with a fix. # crbug.com/770634, likely a bug in Xcode 9.1 beta, remove once build
r'\[\]\[ipad\]\[76x76\]\[\]\[\]\[1x\]\[\]\[\]: notice: \(null\)', # requires a version of Xcode with a fix.
r'\[\]\[ipad\]\[76x76\]\[\]\[\]\[1x\]\[\]\[\]: notice: \(null\)',
# crbug.com/770634, likely a bug in Xcode 9.2 beta, remove once build # crbug.com/770634, likely a bug in Xcode 9.2 beta, remove once build
# requires a version of Xcode with a fix. # requires a version of Xcode with a fix.
r'\[\]\[ipad\]\[76x76\]\[\]\[\]\[1x\]\[\]\[\]: notice: 76x76@1x app icons' r'\[\]\[ipad\]\[76x76\]\[\]\[\]\[1x\]\[\]\[\]: notice: 76x76@1x app icons'
' only apply to iPad apps targeting releases of iOS prior to 10.0.', ' only apply to iPad apps targeting releases of iOS prior to 10.0.',
]) ]
]
# Map special type of asset catalog to the corresponding command-line # Map special type of asset catalog to the corresponding command-line
# parameter that need to be passed to actool. # parameter that need to be passed to actool.
@ -193,7 +195,7 @@ def CompileAssetCatalog(output, platform, product_type, min_deployment_target,
stdout, _ = process.communicate() stdout, _ = process.communicate()
# Filter the output to remove all garbarge and to fix the paths. # Filter the output to remove all garbarge and to fix the paths.
stdout = FilterCompilerOutput(stdout, relative_paths) stdout = FilterCompilerOutput(stdout.decode('UTF-8'), relative_paths)
if process.returncode or stdout: if process.returncode or stdout:
sys.stderr.write(stdout) sys.stderr.write(stdout)

Просмотреть файл

@ -18,9 +18,9 @@ def check_output(command):
if process.returncode: if process.returncode:
sys.stderr.write('error: command failed with retcode %d: %s\n\n' % sys.stderr.write('error: command failed with retcode %d: %s\n\n' %
(process.returncode, ' '.join(map(repr, command)))) (process.returncode, ' '.join(map(repr, command))))
sys.stderr.write(errs) sys.stderr.write(errs.decode('UTF-8', errors='ignore'))
sys.exit(process.returncode) sys.exit(process.returncode)
return outs return outs.decode('UTF-8')
def check_call(command): def check_call(command):

Просмотреть файл

@ -50,7 +50,7 @@ def WriteHmap(output_name, filelist):
count = len(filelist) count = len(filelist)
capacity = NextGreaterPowerOf2(count) capacity = NextGreaterPowerOf2(count)
strings_offset = 24 + (12 * capacity) strings_offset = 24 + (12 * capacity)
max_value_length = len(max(filelist.items(), key=lambda (k,v):len(v))[1]) max_value_length = len(max(filelist.values(), key=lambda v: len(v)))
out = open(output_name, 'wb') out = open(output_name, 'wb')
out.write(struct.pack('<LHHLLLL', magic, version, _reserved, strings_offset, out.write(struct.pack('<LHHLLLL', magic, version, _reserved, strings_offset,
@ -86,14 +86,17 @@ def WriteHmap(output_name, filelist):
for bucket in buckets: for bucket in buckets:
if bucket is not None: if bucket is not None:
(file, path) = bucket (file, path) = bucket
out.write(struct.pack('<%ds' % len(file), file))
out.write(struct.pack('<s', '\0'))
base = os.path.dirname(path) + os.sep base = os.path.dirname(path) + os.sep
out.write(struct.pack('<%ds' % len(base), base))
out.write(struct.pack('<s', '\0'))
path = os.path.basename(path) path = os.path.basename(path)
file = file.encode('UTF-8')
base = base.encode('UTF-8')
path = path.encode('UTF-8')
out.write(struct.pack('<%ds' % len(file), file))
out.write(struct.pack('<s', b'\0'))
out.write(struct.pack('<%ds' % len(base), base))
out.write(struct.pack('<s', b'\0'))
out.write(struct.pack('<%ds' % len(path), path)) out.write(struct.pack('<%ds' % len(path), path))
out.write(struct.pack('<s', '\0')) out.write(struct.pack('<s', b'\0'))
if __name__ == '__main__': if __name__ == '__main__':

Просмотреть файл

@ -23,7 +23,7 @@ def Main():
# Foo.framework/Versions/Current symlink to it. # Foo.framework/Versions/Current symlink to it.
if args.version: if args.version:
try: try:
os.makedirs(os.path.join(args.framework, VERSIONS, args.version), 0755) os.makedirs(os.path.join(args.framework, VERSIONS, args.version), 0o755)
except OSError as e: except OSError as e:
if e.errno != errno.EEXIST: if e.errno != errno.EEXIST:
raise e raise e

Просмотреть файл

@ -12,6 +12,10 @@ import sys
import tempfile import tempfile
import shlex import shlex
if sys.version_info.major < 3:
basestring_compat = basestring
else:
basestring_compat = str
# Xcode substitutes variables like ${PRODUCT_NAME} or $(PRODUCT_NAME) when # Xcode substitutes variables like ${PRODUCT_NAME} or $(PRODUCT_NAME) when
# compiling Info.plist. It also supports supports modifiers like :identifier # compiling Info.plist. It also supports supports modifiers like :identifier
@ -80,10 +84,10 @@ def Interpolate(value, substitutions):
substitution. substitution.
""" """
if isinstance(value, dict): if isinstance(value, dict):
return {k: Interpolate(v, substitutions) for k, v in value.iteritems()} return {k: Interpolate(v, substitutions) for k, v in value.items()}
if isinstance(value, list): if isinstance(value, list):
return [Interpolate(v, substitutions) for v in value] return [Interpolate(v, substitutions) for v in value]
if isinstance(value, str): if isinstance(value, basestring_compat):
return InterpolateString(value, substitutions) return InterpolateString(value, substitutions)
return value return value
@ -93,7 +97,7 @@ def LoadPList(path):
fd, name = tempfile.mkstemp() fd, name = tempfile.mkstemp()
try: try:
subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path])
with os.fdopen(fd, 'r') as f: with os.fdopen(fd, 'rb') as f:
return plistlib.readPlist(f) return plistlib.readPlist(f)
finally: finally:
os.unlink(name) os.unlink(name)
@ -109,7 +113,7 @@ def SavePList(path, format, data):
# it does exist. # it does exist.
if os.path.exists(path): if os.path.exists(path):
os.unlink(path) os.unlink(path)
with os.fdopen(fd, 'w') as f: with os.fdopen(fd, 'wb') as f:
plistlib.writePlist(data, f) plistlib.writePlist(data, f)
subprocess.check_call(['plutil', '-convert', format, '-o', path, name]) subprocess.check_call(['plutil', '-convert', format, '-o', path, name])
finally: finally:
@ -134,7 +138,7 @@ def MergePList(plist1, plist2):
are concatenated. are concatenated.
""" """
result = plist1.copy() result = plist1.copy()
for key, value in plist2.iteritems(): for key, value in plist2.items():
if isinstance(value, dict): if isinstance(value, dict):
old_value = result.get(key) old_value = result.get(key)
if isinstance(old_value, dict): if isinstance(old_value, dict):

Просмотреть файл

@ -13,6 +13,11 @@ import re
import subprocess import subprocess
import sys import sys
if sys.version_info.major < 3:
basestring_compat = basestring
else:
basestring_compat = str
# src directory # src directory
ROOT_SRC_DIR = os.path.dirname( ROOT_SRC_DIR = os.path.dirname(
os.path.dirname( os.path.dirname(
@ -61,7 +66,8 @@ def FillXcodeVersion(settings, developer_dir):
settings['xcode_build'] = version_plist['ProductBuildVersion'] settings['xcode_build'] = version_plist['ProductBuildVersion']
return return
lines = subprocess.check_output(['xcodebuild', '-version']).splitlines() lines = subprocess.check_output(['xcodebuild',
'-version']).decode('UTF-8').splitlines()
settings['xcode_version'] = FormatVersion(lines[0].split()[-1]) settings['xcode_version'] = FormatVersion(lines[0].split()[-1])
settings['xcode_version_int'] = int(settings['xcode_version'], 10) settings['xcode_version_int'] = int(settings['xcode_version'], 10)
settings['xcode_build'] = lines[-1].split()[-1] settings['xcode_build'] = lines[-1].split()[-1]
@ -69,8 +75,8 @@ def FillXcodeVersion(settings, developer_dir):
def FillMachineOSBuild(settings): def FillMachineOSBuild(settings):
"""Fills OS build number into |settings|.""" """Fills OS build number into |settings|."""
machine_os_build = subprocess.check_output(['sw_vers', '-buildVersion'], machine_os_build = subprocess.check_output(['sw_vers', '-buildVersion'
universal_newlines=True).strip() ]).decode('UTF-8').strip()
settings['machine_os_build'] = machine_os_build settings['machine_os_build'] = machine_os_build
# The reported build number is made up from the kernel major version number, # The reported build number is made up from the kernel major version number,
@ -91,14 +97,17 @@ def FillMachineOSBuild(settings):
def FillSDKPathAndVersion(settings, platform, xcode_version): def FillSDKPathAndVersion(settings, platform, xcode_version):
"""Fills the SDK path and version for |platform| into |settings|.""" """Fills the SDK path and version for |platform| into |settings|."""
settings['sdk_path'] = subprocess.check_output([ settings['sdk_path'] = subprocess.check_output(
'xcrun', '-sdk', platform, '--show-sdk-path']).strip() ['xcrun', '-sdk', platform, '--show-sdk-path']).decode('UTF-8').strip()
settings['sdk_version'] = subprocess.check_output([ settings['sdk_version'] = subprocess.check_output(
'xcrun', '-sdk', platform, '--show-sdk-version']).strip() ['xcrun', '-sdk', platform,
settings['sdk_platform_path'] = subprocess.check_output([ '--show-sdk-version']).decode('UTF-8').strip()
'xcrun', '-sdk', platform, '--show-sdk-platform-path']).strip() settings['sdk_platform_path'] = subprocess.check_output(
['xcrun', '-sdk', platform,
'--show-sdk-platform-path']).decode('UTF-8').strip()
settings['sdk_build'] = subprocess.check_output( settings['sdk_build'] = subprocess.check_output(
['xcrun', '-sdk', platform, '--show-sdk-build-version']).strip() ['xcrun', '-sdk', platform,
'--show-sdk-build-version']).decode('UTF-8').strip()
def CreateXcodeSymlinkAt(src, dst): def CreateXcodeSymlinkAt(src, dst):
@ -157,6 +166,6 @@ if __name__ == '__main__':
value = settings[key] value = settings[key]
if args.create_symlink_at and '_path' in key: if args.create_symlink_at and '_path' in key:
value = CreateXcodeSymlinkAt(value, args.create_symlink_at) value = CreateXcodeSymlinkAt(value, args.create_symlink_at)
if isinstance(value, str): if isinstance(value, basestring_compat):
value = '"%s"' % value value = '"%s"' % value
print('%s=%s' % (key, value)) print('%s=%s' % (key, value))

Просмотреть файл

@ -65,7 +65,7 @@ def main():
print(out, file=sys.stderr) print(out, file=sys.stderr)
print(err, file=sys.stderr) print(err, file=sys.stderr)
raise Exception('Error %d running xcode-select' % job.returncode) raise Exception('Error %d running xcode-select' % job.returncode)
dev_dir = out.rstrip() dev_dir = out.decode('UTF-8').rstrip()
sdk_dir = os.path.join( sdk_dir = os.path.join(
dev_dir, 'Platforms/MacOSX.platform/Developer/SDKs') dev_dir, 'Platforms/MacOSX.platform/Developer/SDKs')

Просмотреть файл

@ -12,14 +12,16 @@ import sys
# This script executes libool and filters out logspam lines like: # This script executes libool and filters out logspam lines like:
# '/path/to/libtool: file: foo.o has no symbols' # '/path/to/libtool: file: foo.o has no symbols'
BLACKLIST_PATTERNS = map(re.compile, [ BLACKLIST_PATTERNS = [
r'^.*libtool: (?:for architecture: \S* )?file: .* has no symbols$', re.compile(v) for v in [
r'^.*libtool: warning for library: .* the table of contents is empty ' r'^.*libtool: (?:for architecture: \S* )?file: .* has no symbols$',
r'^.*libtool: warning for library: .* the table of contents is empty '
r'\(no object file members in the library define global symbols\)$', r'\(no object file members in the library define global symbols\)$',
r'^.*libtool: warning same member name \(\S*\) in output file used for ' r'^.*libtool: warning same member name \(\S*\) in output file used for '
r'input files: \S* and: \S* \(due to use of basename, truncation, ' r'input files: \S* and: \S* \(due to use of basename, truncation, '
r'blank padding or duplicate input files\)$', r'blank padding or duplicate input files\)$',
]) ]
]
def IsBlacklistedLine(line): def IsBlacklistedLine(line):
@ -34,7 +36,7 @@ def Main(cmd_list):
env = os.environ.copy() env = os.environ.copy()
libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env)
_, err = libtoolout.communicate() _, err = libtoolout.communicate()
for line in err.splitlines(): for line in err.decode('UTF-8').splitlines():
if not IsBlacklistedLine(line): if not IsBlacklistedLine(line):
print(line, file=sys.stderr) print(line, file=sys.stderr)
return libtoolout.returncode return libtoolout.returncode