1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-01-31 02:00:45 +00:00

utils/android: Add support for using aapt2

aapt is now depreciated in favour of aapt2 therefore prefer using the
newer binary if it is found on the system. If not present fallback to
the old implementation.
Currently all invocations of aapt within devlib are compatible with
aapt2 however expose the `aapt_version` attribute to indicate which
version has been selected to allow for allow maintaining future
compatibility.
This commit is contained in:
Marc Bonnici 2020-05-20 17:45:53 +01:00
parent b52462440c
commit 45ee68fdd4

View File

@ -46,6 +46,7 @@ logger = logging.getLogger('android')
MAX_ATTEMPTS = 5 MAX_ATTEMPTS = 5
AM_START_ERROR = re.compile(r"Error: Activity.*") AM_START_ERROR = re.compile(r"Error: Activity.*")
AAPT_BADGING_OUTPUT = re.compile(r"no dump ((file)|(apk)) specified", re.IGNORECASE)
# See: # See:
# http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels # http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels
@ -93,6 +94,7 @@ android_home = None
platform_tools = None platform_tools = None
adb = None adb = None
aapt = None aapt = None
aapt_version = None
fastboot = None fastboot = None
@ -201,7 +203,9 @@ class ApkInfo(object):
@property @property
def activities(self): def activities(self):
if self._activities is None: if self._activities is None:
cmd = [aapt, 'dump', 'xmltree', self._apk_path, file_flag = '--file' if aapt_version == 2 else ''
cmd = [aapt, 'dump', 'xmltree',
self._apk_path, '{}'.format(file_flag),
'AndroidManifest.xml'] 'AndroidManifest.xml']
matched_activities = self.activity_regex.finditer(self._run(cmd)) matched_activities = self.activity_regex.finditer(self._run(cmd))
self._activities = [m.group('name') for m in matched_activities] self._activities = [m.group('name') for m in matched_activities]
@ -644,6 +648,7 @@ class _AndroidEnvironment(object):
self.build_tools = None self.build_tools = None
self.adb = None self.adb = None
self.aapt = None self.aapt = None
self.aapt_version = None
self.fastboot = None self.fastboot = None
@ -676,31 +681,66 @@ def _init_common(env):
def _discover_build_tools(env): def _discover_build_tools(env):
logger.debug('ANDROID_HOME: {}'.format(env.android_home)) logger.debug('ANDROID_HOME: {}'.format(env.android_home))
build_tools_directory = os.path.join(env.android_home, 'build-tools') build_tools_directory = os.path.join(env.android_home, 'build-tools')
if not os.path.isdir(build_tools_directory): if os.path.isdir(build_tools_directory):
msg = '''ANDROID_HOME ({}) does not appear to have valid Android SDK install env.build_tools = build_tools_directory
(cannot find build-tools)'''
raise HostError(msg.format(env.android_home)) def _check_supported_aapt2(binary):
env.build_tools = build_tools_directory # At time of writing the version argument of aapt2 is not helpful as
# the output is only a placeholder that does not distinguish between versions
# with and without support for badging. Unfortunately aapt has been
# deprecated and fails to parse some valid apks so we will try to favour
# aapt2 if possible else will fall back to aapt.
# Try to execute the badging command and check if we get an expected error
# message as opposed to an unknown command error to determine if we have a
# suitable version.
cmd = '{} dump badging'.format(binary)
result = subprocess.run(cmd.encode('utf-8'), shell=True, stderr=subprocess.PIPE)
supported = bool(AAPT_BADGING_OUTPUT.search(result.stderr.decode('utf-8')))
msg = 'Found a {} aapt2 binary at: {}'
logger.debug(msg.format('supported' if supported else 'unsupported', binary))
return supported
def _discover_aapt(env): def _discover_aapt(env):
if env.build_tools: if env.build_tools:
aapt_path = ''
aapt2_path = ''
versions = os.listdir(env.build_tools) versions = os.listdir(env.build_tools)
for version in reversed(sorted(versions)): for version in reversed(sorted(versions)):
aapt_path = os.path.join(env.build_tools, version, 'aapt') if not aapt2_path and not os.path.isfile(aapt2_path):
if os.path.isfile(aapt_path): aapt2_path = os.path.join(env.build_tools, version, 'aapt2')
logger.debug('Using aapt for version {}'.format(version)) if not aapt_path and not os.path.isfile(aapt_path):
env.aapt = aapt_path aapt_path = os.path.join(env.build_tools, version, 'aapt')
aapt_version = 1
break break
# Use aapt2 only if present and we have a suitable version
if aapt2_path and _check_supported_aapt2(aapt2_path):
aapt_path = aapt2_path
aapt_version = 2
# Use the aapt version discoverted from build tools.
if aapt_path:
logger.debug('Using {} for version {}'.format(aapt_path, version))
env.aapt = aapt_path
env.aapt_version = aapt_version
return
# Try detecting aapt2 and aapt from PATH
if not env.aapt: if not env.aapt:
env.aapt = which(aapt) aapt2_path = which('aapt2')
if _check_supported_aapt2(aapt2_path):
env.aapt = aapt2_path
env.aapt_version = 2
else:
env.aapt = which('aapt')
env.aapt_version = 1
if not env.aapt: if not env.aapt:
raise HostError('aapt not found. Please make sure it is avaliable in PATH' raise HostError('aapt/aapt2 not found. Please make sure it is avaliable in PATH'
' or at least one Android platform is installed') ' or at least one Android platform is installed')
def _check_env(): def _check_env():
global android_home, platform_tools, adb, aapt # pylint: disable=W0603 global android_home, platform_tools, adb, aapt, aapt_version # pylint: disable=W0603
if not android_home: if not android_home:
android_home = os.getenv('ANDROID_HOME') android_home = os.getenv('ANDROID_HOME')
if android_home: if android_home:
@ -711,6 +751,7 @@ def _check_env():
platform_tools = _env.platform_tools platform_tools = _env.platform_tools
adb = _env.adb adb = _env.adb
aapt = _env.aapt aapt = _env.aapt
aapt_version = _env.aapt_version
class LogcatMonitor(object): class LogcatMonitor(object):
""" """