summaryrefslogtreecommitdiff
path: root/chromium/build/android
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/build/android')
-rw-r--r--chromium/build/android/BUILD.gn18
-rw-r--r--chromium/build/android/PRESUBMIT.py1
-rwxr-xr-xchromium/build/android/apk_operations.py237
-rwxr-xr-xchromium/build/android/asan_symbolize.py1
-rw-r--r--chromium/build/android/devil_chromium.py1
-rw-r--r--chromium/build/android/docs/android_app_bundles.md199
-rwxr-xr-xchromium/build/android/gradle/generate_gradle.py65
-rw-r--r--chromium/build/android/gradle/root.jinja2
-rw-r--r--chromium/build/android/gyp/aar.pydeps7
-rwxr-xr-xchromium/build/android/gyp/aidl.py4
-rw-r--r--chromium/build/android/gyp/aidl.pydeps7
-rw-r--r--chromium/build/android/gyp/apkbuilder.pydeps8
-rw-r--r--chromium/build/android/gyp/app_bundle_to_apks.pydeps8
-rw-r--r--chromium/build/android/gyp/bytecode_processor.pydeps7
-rwxr-xr-xchromium/build/android/gyp/compile_resources.py3
-rw-r--r--chromium/build/android/gyp/compile_resources.pydeps29
-rwxr-xr-xchromium/build/android/gyp/copy_ex.py10
-rw-r--r--chromium/build/android/gyp/copy_ex.pydeps7
-rw-r--r--chromium/build/android/gyp/create_apk_operations_script.pydeps3
-rw-r--r--chromium/build/android/gyp/create_app_bundle.pydeps30
-rwxr-xr-xchromium/build/android/gyp/create_bundle_wrapper_script.py94
-rw-r--r--chromium/build/android/gyp/create_bundle_wrapper_script.pydeps7
-rwxr-xr-xchromium/build/android/gyp/create_dist_jar.py3
-rw-r--r--chromium/build/android/gyp/create_dist_jar.pydeps7
-rw-r--r--chromium/build/android/gyp/create_java_binary_script.pydeps7
-rw-r--r--chromium/build/android/gyp/create_stack_script.pydeps7
-rw-r--r--chromium/build/android/gyp/create_test_runner_script.pydeps7
-rw-r--r--chromium/build/android/gyp/create_tool_wrapper.pydeps3
-rw-r--r--chromium/build/android/gyp/desugar.pydeps7
-rw-r--r--chromium/build/android/gyp/dex.pydeps7
-rwxr-xr-xchromium/build/android/gyp/dist_aar.py3
-rw-r--r--chromium/build/android/gyp/dist_aar.pydeps7
-rw-r--r--chromium/build/android/gyp/emma_instr.pydeps7
-rw-r--r--chromium/build/android/gyp/filter_zip.pydeps7
-rwxr-xr-xchromium/build/android/gyp/gcc_preprocess.py2
-rw-r--r--chromium/build/android/gyp/gcc_preprocess.pydeps7
-rwxr-xr-xchromium/build/android/gyp/generate_proguarded_module_jar.py159
-rw-r--r--chromium/build/android/gyp/generate_proguarded_module_jar.pydeps7
-rwxr-xr-xchromium/build/android/gyp/generate_resource_rewriter.py1
-rw-r--r--chromium/build/android/gyp/ijar.pydeps7
-rwxr-xr-xchromium/build/android/gyp/java_cpp_enum.py2
-rw-r--r--chromium/build/android/gyp/java_cpp_enum.pydeps7
-rwxr-xr-xchromium/build/android/gyp/java_google_api_keys.py1
-rwxr-xr-xchromium/build/android/gyp/javac.py11
-rw-r--r--chromium/build/android/gyp/javac.pydeps15
-rwxr-xr-xchromium/build/android/gyp/jinja_template.py7
-rw-r--r--chromium/build/android/gyp/jinja_template.pydeps41
-rw-r--r--chromium/build/android/gyp/lint.pydeps7
-rwxr-xr-xchromium/build/android/gyp/main_dex_list.py1
-rw-r--r--chromium/build/android/gyp/main_dex_list.pydeps8
-rwxr-xr-xchromium/build/android/gyp/merge_jar_info_files.py2
-rw-r--r--chromium/build/android/gyp/merge_jar_info_files.pydeps8
-rw-r--r--chromium/build/android/gyp/merge_manifest.pydeps7
-rwxr-xr-xchromium/build/android/gyp/prepare_resources.py3
-rw-r--r--chromium/build/android/gyp/prepare_resources.pydeps30
-rwxr-xr-xchromium/build/android/gyp/proguard.py92
-rw-r--r--chromium/build/android/gyp/proguard.pydeps8
-rw-r--r--chromium/build/android/gyp/util/__init__.py1
-rw-r--r--chromium/build/android/gyp/util/proguard_util.py6
-rwxr-xr-xchromium/build/android/gyp/write_build_config.py167
-rw-r--r--chromium/build/android/gyp/write_build_config.pydeps7
-rwxr-xr-xchromium/build/android/gyp/write_ordered_libraries.py5
-rw-r--r--chromium/build/android/gyp/write_ordered_libraries.pydeps7
-rw-r--r--chromium/build/android/incremental_install/__init__.py1
-rw-r--r--chromium/build/android/incremental_install/generate_android_manifest.pydeps7
-rwxr-xr-xchromium/build/android/incremental_install/write_installer_json.py4
-rw-r--r--chromium/build/android/incremental_install/write_installer_json.pydeps7
-rwxr-xr-xchromium/build/android/lighttpd_server.py2
-rw-r--r--chromium/build/android/lint/suppressions.xml4
-rwxr-xr-xchromium/build/android/method_count.py1
-rw-r--r--chromium/build/android/pylib/android/__init__.py1
-rw-r--r--chromium/build/android/pylib/base/__init__.py1
-rw-r--r--chromium/build/android/pylib/base/base_test_result.py1
-rw-r--r--chromium/build/android/pylib/base/test_exception.py1
-rw-r--r--chromium/build/android/pylib/base/test_instance.py1
-rw-r--r--chromium/build/android/pylib/base/test_run.py1
-rw-r--r--chromium/build/android/pylib/base/test_run_factory.py1
-rw-r--r--chromium/build/android/pylib/base/test_server.py1
-rw-r--r--chromium/build/android/pylib/constants/__init__.py2
-rw-r--r--chromium/build/android/pylib/gtest/__init__.py1
-rw-r--r--chromium/build/android/pylib/gtest/gtest_test_instance.py5
-rwxr-xr-xchromium/build/android/pylib/gtest/gtest_test_instance_test.py1
-rw-r--r--chromium/build/android/pylib/instrumentation/__init__.py1
-rw-r--r--chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py14
-rw-r--r--chromium/build/android/pylib/junit/__init__.py1
-rw-r--r--chromium/build/android/pylib/linker/__init__.py1
-rw-r--r--chromium/build/android/pylib/linker/linker_test_instance.py1
-rw-r--r--chromium/build/android/pylib/local/device/local_device_gtest_run.py4
-rw-r--r--chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py9
-rw-r--r--chromium/build/android/pylib/local/device/local_device_test_run.py2
-rw-r--r--chromium/build/android/pylib/local/local_test_server_spawner.py1
-rw-r--r--chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py2
-rw-r--r--chromium/build/android/pylib/results/json_results.py1
-rwxr-xr-xchromium/build/android/pylib/results/json_results_test.py1
-rwxr-xr-xchromium/build/android/pylib/results/presentation/test_results_presentation.py4
-rw-r--r--chromium/build/android/pylib/symbols/deobfuscator.py9
-rw-r--r--chromium/build/android/pylib/symbols/symbol_utils.py812
-rw-r--r--chromium/build/android/pylib/symbols/symbol_utils_unittest.py944
-rw-r--r--chromium/build/android/pylib/utils/app_bundle_utils.py66
-rw-r--r--chromium/build/android/pylib/utils/device_dependencies.py1
-rw-r--r--chromium/build/android/pylib/utils/google_storage_helper.py5
-rw-r--r--chromium/build/android/pylib/utils/logdog_helper.py1
-rwxr-xr-xchromium/build/android/pylib/utils/maven_downloader.py3
-rw-r--r--chromium/build/android/pylib/utils/proguard.py18
-rw-r--r--chromium/build/android/pylib/valgrind_tools.py1
-rw-r--r--chromium/build/android/pylintrc2
-rwxr-xr-xchromium/build/android/resource_sizes.py6
-rwxr-xr-xchromium/build/android/stacktrace/java_deobfuscate_test.py101
-rwxr-xr-xchromium/build/android/test_runner.py5
109 files changed, 3223 insertions, 282 deletions
diff --git a/chromium/build/android/BUILD.gn b/chromium/build/android/BUILD.gn
index 523636d137a..f6c9ab41ff5 100644
--- a/chromium/build/android/BUILD.gn
+++ b/chromium/build/android/BUILD.gn
@@ -54,6 +54,12 @@ if (enable_java_templates) {
"android_ndk_root=" + rebase_path(android_ndk_root, root_build_dir) + CR
_data += "android_tool_prefix=" +
rebase_path(android_tool_prefix, root_build_dir) + CR
+ if (defined(android_secondary_abi_cpu)) {
+ _secondary_label_info =
+ get_label_info(":foo($android_secondary_abi_toolchain)", "root_out_dir")
+ _data += "android_secondary_abi_toolchain=" +
+ rebase_path(_secondary_label_info, root_build_dir) + CR
+ }
write_file(android_build_vars, _data)
}
@@ -71,8 +77,8 @@ copy("cpplib_unstripped") {
action("cpplib_stripped") {
_strip_bin = "${android_tool_prefix}strip"
_soname = "libc++_shared.so"
- _input_so = "${root_shlib_dir}/lib.unstripped/${_soname}"
- _output_so = "${root_shlib_dir}/${_soname}"
+ _input_so = "${root_out_dir}/lib.unstripped/${_soname}"
+ _output_so = "${root_out_dir}/${_soname}"
deps = [
":cpplib_unstripped",
@@ -117,8 +123,14 @@ python_library("test_runner_py") {
"${android_sdk_root}/platform-tools/adb",
"//third_party/catapult/third_party/gsutil/",
"//third_party/catapult/devil/devil/devil_dependencies.json",
- "//third_party/proguard/lib/proguard.jar",
]
+
+ # Proguard is needed only when using apks (rather than native executables).
+ if (enable_java_templates) {
+ deps = [
+ "//third_party/proguard:proguard603_java",
+ ]
+ }
}
python_library("logdog_wrapper_py") {
diff --git a/chromium/build/android/PRESUBMIT.py b/chromium/build/android/PRESUBMIT.py
index 65d37d17a76..9617b5197cc 100644
--- a/chromium/build/android/PRESUBMIT.py
+++ b/chromium/build/android/PRESUBMIT.py
@@ -74,6 +74,7 @@ def CommonChecks(input_api, output_api):
J('pylib', 'results', 'json_results_test.py'),
J('pylib', 'symbols', 'apk_native_libs_unittest.py'),
J('pylib', 'symbols', 'elf_symbolizer_unittest.py'),
+ J('pylib', 'symbols', 'symbol_utils_unittest.py'),
J('pylib', 'utils', 'decorators_test.py'),
J('pylib', 'utils', 'device_dependencies_test.py'),
J('pylib', 'utils', 'dexdump_test.py'),
diff --git a/chromium/build/android/apk_operations.py b/chromium/build/android/apk_operations.py
index 33c3ef05559..2b17c19cdd4 100755
--- a/chromium/build/android/apk_operations.py
+++ b/chromium/build/android/apk_operations.py
@@ -16,6 +16,7 @@ import posixpath
import random
import re
import shlex
+import shutil
import sys
import tempfile
import textwrap
@@ -39,7 +40,11 @@ from incremental_install import installer
from pylib import constants
from pylib.symbols import deobfuscator
from pylib.utils import simpleperf
+from pylib.utils import app_bundle_utils
+with devil_env.SysPath(os.path.join(os.path.dirname(__file__), '..', '..',
+ 'build', 'android', 'gyp')):
+ import bundletool
# Matches messages only on pre-L (Dalvik) that are spammy and unimportant.
_DALVIK_IGNORE_PATTERN = re.compile('|'.join([
@@ -74,6 +79,55 @@ def _InstallApk(devices, apk, install_dict):
device_utils.DeviceUtils.parallel(devices).pMap(install)
+# A named tuple containing the information needed to convert a bundle into
+# an installable .apks archive.
+# Fields:
+# bundle_path: Path to input bundle file.
+# bundle_apk_path: Path to output bundle .apks archive file.
+# aapt2_path: Path to aapt2 tool.
+# keystore_path: Path to keystore file.
+# keystore_password: Password for the keystore file.
+# keystore_alias: Signing key name alias within the keystore file.
+BundleGenerationInfo = collections.namedtuple(
+ 'BundleGenerationInfo',
+ 'bundle_path,bundle_apks_path,aapt2_path,keystore_path,keystore_password,'
+ 'keystore_alias')
+
+
+def _GenerateBundleApks(info):
+ """Generate an .apks archive from a bundle on demand.
+
+ Args:
+ info: A BundleGenerationInfo instance.
+ Returns:
+ Path of output .apks archive.
+ """
+ app_bundle_utils.GenerateBundleApks(
+ info.bundle_path,
+ info.bundle_apks_path,
+ info.aapt2_path,
+ info.keystore_path,
+ info.keystore_password,
+ info.keystore_alias)
+ return info.bundle_apks_path
+
+
+def _InstallBundle(devices, bundle_apks):
+ def install(device):
+ # NOTE: For now, installation requires running 'bundletool install-apks'.
+ # TODO(digit): Add proper support for bundles to devil instead, then use it.
+ cmd_args = [
+ 'install-apks',
+ '--apks=' + bundle_apks,
+ '--adb=' + adb_wrapper.AdbWrapper.GetAdbPath(),
+ '--device-id=' + device.serial
+ ]
+ bundletool.RunBundleTool(cmd_args)
+
+ logging.info('Installing bundle.')
+ device_utils.DeviceUtils.parallel(devices).pMap(install)
+
+
def _UninstallApk(devices, install_dict, package_name):
def uninstall(device):
if install_dict:
@@ -735,21 +789,27 @@ class _Command(object):
supports_incremental = False
accepts_command_line_flags = False
accepts_args = False
+ need_device_args = True
all_devices_by_default = False
calls_exec = False
supports_multiple_devices = True
- def __init__(self, from_wrapper_script):
+ def __init__(self, from_wrapper_script, is_bundle):
self._parser = None
self._from_wrapper_script = from_wrapper_script
self.args = None
self.apk_helper = None
self.install_dict = None
self.devices = None
- # Do not support incremental install outside the context of wrapper scripts.
- if not from_wrapper_script:
+ self.is_bundle = is_bundle
+ self.bundle_generation_info = None
+ # Only support incremental install from APK wrapper scripts.
+ if is_bundle or not from_wrapper_script:
self.supports_incremental = False
+ def RegisterBundleGenerationInfo(self, bundle_generation_info):
+ self.bundle_generation_info = bundle_generation_info
+
def _RegisterExtraArgs(self, subp):
pass
@@ -760,17 +820,18 @@ class _Command(object):
formatter_class=argparse.RawDescriptionHelpFormatter)
self._parser = subp
subp.set_defaults(command=self)
- subp.add_argument('--all',
- action='store_true',
- default=self.all_devices_by_default,
- help='Operate on all connected devices.',)
- subp.add_argument('-d',
- '--device',
- action='append',
- default=[],
- dest='devices',
- help='Target device for script to work on. Enter '
- 'multiple times for multiple devices.')
+ if self.need_device_args:
+ subp.add_argument('--all',
+ action='store_true',
+ default=self.all_devices_by_default,
+ help='Operate on all connected devices.',)
+ subp.add_argument('-d',
+ '--device',
+ action='append',
+ default=[],
+ dest='devices',
+ help='Target device for script to work on. Enter '
+ 'multiple times for multiple devices.')
subp.add_argument('-v',
'--verbose',
action='count',
@@ -780,15 +841,29 @@ class _Command(object):
group = subp.add_argument_group('%s arguments' % self.name)
if self.needs_package_name:
- # Always gleaned from apk when using wrapper scripts.
- group.add_argument('--package-name',
- help=argparse.SUPPRESS if self._from_wrapper_script else (
- "App's package name."))
+ # Three cases to consider here, since later code assumes
+ # self.args.package_name always exists, even if None:
+ #
+ # - Called from a bundle wrapper script, the package_name is already
+ # set through parser.set_defaults(), so don't call add_argument()
+ # to avoid overriding its value.
+ #
+ # - Called from an apk wrapper script. The --package-name argument
+ # should not appear, but self.args.package_name will be gleaned from
+ # the --apk-path file later.
+ #
+ # - Called directly, then --package-name is required on the command-line.
+ #
+ if not self.is_bundle:
+ group.add_argument(
+ '--package-name',
+ help=argparse.SUPPRESS if self._from_wrapper_script else (
+ "App's package name."))
if self.needs_apk_path or self.needs_package_name:
# Adding this argument to the subparser would override the set_defaults()
# value set by on the parent parser (even if None).
- if not self._from_wrapper_script:
+ if not self._from_wrapper_script and not self.is_bundle:
group.add_argument('--apk-path',
required=self.needs_apk_path,
help='Path to .apk')
@@ -822,26 +897,27 @@ class _Command(object):
self._RegisterExtraArgs(group)
def ProcessArgs(self, args):
- devices = device_utils.DeviceUtils.HealthyDevices(
- device_arg=args.devices,
- enable_device_files_cache=bool(args.output_directory),
- default_retries=0)
self.args = args
- self.devices = devices
+ self.devices = []
+ if self.need_device_args:
+ self.devices = device_utils.DeviceUtils.HealthyDevices(
+ device_arg=args.devices,
+ enable_device_files_cache=bool(args.output_directory),
+ default_retries=0)
# TODO(agrieve): Device cache should not depend on output directory.
# Maybe put int /tmp?
- _LoadDeviceCaches(devices, args.output_directory)
+ _LoadDeviceCaches(self.devices, args.output_directory)
# Ensure these keys always exist. They are set by wrapper scripts, but not
# always added when not using wrapper scripts.
args.__dict__.setdefault('apk_path', None)
args.__dict__.setdefault('incremental_json', None)
try:
- if len(devices) > 1:
+ if len(self.devices) > 1:
if not self.supports_multiple_devices:
- self._parser.error(device_errors.MultipleDevicesError(devices))
+ self._parser.error(device_errors.MultipleDevicesError(self.devices))
if not args.all and not args.devices:
- self._parser.error(_GenerateMissingAllFlagMessage(devices))
+ self._parser.error(_GenerateMissingAllFlagMessage(self.devices))
incremental_apk_exists = False
@@ -869,7 +945,8 @@ class _Command(object):
self._parser.error('Both incremental and non-incremental apks exist. '
'Select using --incremental or --non-incremental')
- if self.needs_apk_path or args.apk_path or args.incremental_json:
+ if ((self.needs_apk_path and not self.is_bundle) or args.apk_path
+ or args.incremental_json):
if args.incremental_json:
if incremental_apk_exists:
self.install_dict = install_dict
@@ -893,9 +970,9 @@ class _Command(object):
# Save cache now if command will not get a chance to afterwards.
if self.calls_exec:
- _SaveDeviceCaches(devices, args.output_directory)
+ _SaveDeviceCaches(self.devices, args.output_directory)
except:
- _SaveDeviceCaches(devices, args.output_directory)
+ _SaveDeviceCaches(self.devices, args.output_directory)
raise
@@ -910,17 +987,21 @@ class _DevicesCommand(_Command):
class _InstallCommand(_Command):
name = 'install'
- description = 'Installs the APK to one or more devices.'
+ description = 'Installs the APK or bundle to one or more devices.'
needs_apk_path = True
supports_incremental = True
def Run(self):
- _InstallApk(self.devices, self.apk_helper, self.install_dict)
+ if self.is_bundle:
+ bundle_apks_path = _GenerateBundleApks(self.bundle_generation_info)
+ _InstallBundle(self.devices, bundle_apks_path)
+ else:
+ _InstallApk(self.devices, self.apk_helper, self.install_dict)
class _UninstallCommand(_Command):
name = 'uninstall'
- description = 'Removes the APK to one or more devices.'
+ description = 'Removes the APK or bundle from one or more devices.'
needs_package_name = True
def Run(self):
@@ -929,8 +1010,8 @@ class _UninstallCommand(_Command):
class _LaunchCommand(_Command):
name = 'launch'
- description = ('Sends a launch intent for the APK after first writing the '
- 'command-line flags file.')
+ description = ('Sends a launch intent for the APK or bundle after first '
+ 'writing the command-line flags file.')
needs_package_name = True
accepts_command_line_flags = True
all_devices_by_default = True
@@ -950,6 +1031,10 @@ class _LaunchCommand(_Command):
group.add_argument('url', nargs='?', help='A URL to launch with.')
def Run(self):
+ if self.args.url and self.is_bundle:
+ # TODO(digit): Support this, maybe by using 'dumpsys' as described
+ # in the _LaunchUrl() comment.
+ raise Exception('Launching with URL not supported for bundles yet!')
_LaunchUrl(self.devices, self.args.package_name, argv=self.args.args,
command_line_flags_file=self.args.command_line_flags_file,
url=self.args.url, apk=self.apk_helper,
@@ -1214,6 +1299,26 @@ class _RunCommand(_InstallCommand, _LaunchCommand, _LogcatCommand):
_LogcatCommand.Run(self)
+class _BuildBundleApks(_Command):
+ name = 'build-bundle-apks'
+ description = ('Build the .apks archive from an Android app bundle, and '
+ 'optionally copy it to a specific destination.')
+ need_device_args = False
+
+ def _RegisterExtraArgs(self, group):
+ group.add_argument('--output-apks',
+ help='Destination path for .apks archive copy.')
+
+ def Run(self):
+ bundle_apks_path = _GenerateBundleApks(self.bundle_generation_info)
+ if self.args.output_apks:
+ try:
+ shutil.copyfile(bundle_apks_path, self.args.output_apks)
+ except shutil.Error as e:
+ logging.exception('Failed to copy .apks archive: %s', e)
+
+
+# Shared commands for regular APKs and app bundles.
_COMMANDS = [
_DevicesCommand,
_InstallCommand,
@@ -1233,10 +1338,17 @@ _COMMANDS = [
_RunCommand,
]
+# Commands specific to app bundles.
+_BUNDLE_COMMANDS = [
+ _BuildBundleApks,
+]
-def _ParseArgs(parser, from_wrapper_script):
+
+def _ParseArgs(parser, from_wrapper_script, is_bundle):
subparsers = parser.add_subparsers()
- commands = [clazz(from_wrapper_script) for clazz in _COMMANDS]
+ command_list = _COMMANDS + (_BUNDLE_COMMANDS if is_bundle else [])
+ commands = [clazz(from_wrapper_script, is_bundle) for clazz in command_list]
+
for command in commands:
if from_wrapper_script or not command.needs_output_directory:
command.RegisterArgs(subparsers)
@@ -1249,21 +1361,21 @@ def _ParseArgs(parser, from_wrapper_script):
return parser.parse_args(argv)
-def _RunInternal(parser, output_directory=None):
+def _RunInternal(parser, output_directory=None, bundle_generation_info=None):
colorama.init()
parser.set_defaults(output_directory=output_directory)
from_wrapper_script = bool(output_directory)
- args = _ParseArgs(parser, from_wrapper_script)
+ args = _ParseArgs(parser, from_wrapper_script, bool(bundle_generation_info))
run_tests_helper.SetLogLevel(args.verbose_count)
args.command.ProcessArgs(args)
+ if bundle_generation_info:
+ args.command.RegisterBundleGenerationInfo(bundle_generation_info)
args.command.Run()
# Incremental install depends on the cache being cleared when uninstalling.
if args.command.name != 'uninstall':
_SaveDeviceCaches(args.command.devices, output_directory)
-# TODO(agrieve): Remove =None from target_cpu on or after October 2017.
-# It exists only so that stale wrapper scripts continue to work.
def Run(output_directory, apk_path, incremental_json, command_line_flags_file,
target_cpu, proguard_mapping_path):
"""Entry point for generated wrapper scripts."""
@@ -1280,6 +1392,47 @@ def Run(output_directory, apk_path, incremental_json, command_line_flags_file,
_RunInternal(parser, output_directory=output_directory)
+def RunForBundle(output_directory, bundle_path, bundle_apks_path,
+ aapt2_path, keystore_path, keystore_password,
+ keystore_alias, package_name, command_line_flags_file,
+ proguard_mapping_path, target_cpu):
+ """Entry point for generated app bundle wrapper scripts.
+
+ Args:
+ output_dir: Chromium output directory path.
+ bundle_path: Input bundle path.
+ bundle_apks_path: Output bundle .apks archive path.
+ aapt2_path: Aapt2 tool path.
+ keystore_path: Keystore file path.
+ keystore_password: Keystore password.
+ keystore_alias: Signing key name alias in keystore file.
+ package_name: Application's package name.
+ command_line_flags_file: Optional. Name of an on-device file that will be
+ used to store command-line flags for this bundle.
+ proguard_mapping_path: Input path to the Proguard mapping file, used to
+ deobfuscate Java stack traces.
+ target_cpu: Chromium target CPU name, used by the 'gdb' command.
+ """
+ constants.SetOutputDirectory(output_directory)
+ devil_chromium.Initialize(output_directory=output_directory)
+ bundle_generation_info = BundleGenerationInfo(
+ bundle_path=bundle_path,
+ bundle_apks_path=bundle_apks_path,
+ aapt2_path=aapt2_path,
+ keystore_path=keystore_path,
+ keystore_password=keystore_password,
+ keystore_alias=keystore_alias)
+
+ parser = argparse.ArgumentParser()
+ parser.set_defaults(
+ package_name=package_name,
+ command_line_flags_file=command_line_flags_file,
+ proguard_mapping_path=proguard_mapping_path,
+ target_cpu=target_cpu)
+ _RunInternal(parser, output_directory=output_directory,
+ bundle_generation_info=bundle_generation_info)
+
+
def main():
devil_chromium.Initialize()
_RunInternal(argparse.ArgumentParser(), output_directory=None)
diff --git a/chromium/build/android/asan_symbolize.py b/chromium/build/android/asan_symbolize.py
index 19d71ca3aff..9f2e88a60d6 100755
--- a/chromium/build/android/asan_symbolize.py
+++ b/chromium/build/android/asan_symbolize.py
@@ -14,6 +14,7 @@ import sys
from pylib import constants
from pylib.constants import host_paths
+# pylint: disable=wrong-import-order
# Uses symbol.py from third_party/android_platform, not python's.
with host_paths.SysPath(
host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH,
diff --git a/chromium/build/android/devil_chromium.py b/chromium/build/android/devil_chromium.py
index af2141a552d..2ae643674bd 100644
--- a/chromium/build/android/devil_chromium.py
+++ b/chromium/build/android/devil_chromium.py
@@ -167,4 +167,3 @@ def Initialize(output_directory=None, custom_deps=None, adb_path=None):
devil_env.config.Initialize(
configs=[devil_dynamic_config], config_files=[_DEVIL_CONFIG])
-
diff --git a/chromium/build/android/docs/android_app_bundles.md b/chromium/build/android/docs/android_app_bundles.md
new file mode 100644
index 00000000000..e9e82844940
--- /dev/null
+++ b/chromium/build/android/docs/android_app_bundles.md
@@ -0,0 +1,199 @@
+# Introduction
+
+This document describes how the Chromium build system supports Android app
+bundles.
+
+[TOC]
+
+# Overview of app bundles
+
+An Android app bundle is an alternative application distribution format for
+Android applications on the Google Play Store, that allows reducing the size
+of binaries sent for installation to individual devices that run on Android L
+and beyond. For more information about them, see the official Android
+documentation at:
+
+ https://developer.android.com/guide/app-bundle/
+
+For the context of this document, the most important points are:
+
+ - Unlike a regular APK (e.g. `foo.apk`), the bundle (e.g. `foo.aab`) cannot
+ be installed directly on a device.
+
+ - Instead, it must be processed into a set of installable split APKs, which
+ are stored inside a special zip archive (e.g. `foo.apks`).
+
+ - The splitting can be based on various criteria: e.g. language or screen
+ density for resources, or cpu ABI for native code.
+
+ - The bundle also uses the notion of modules to separate several application
+ features. Each module has its own code, assets and resources, and can be
+ installed separately from the rest of the application if needed.
+
+ - The main application itself is stored in the '`base`' module (this name
+ cannot be changed).
+
+
+# Declaring app bundles with GN templates
+
+Here's an example that shows how to declare a simple bundle that contains
+a single base module, which enables language-based splits:
+
+```gn
+
+ # First declare the first bundle module. The base module is the one
+ # that contains the main application's code, resources and assets.
+ android_app_bundle_module("foo_base_module") {
+ # Declaration are similar to android_apk here.
+ ...
+ }
+
+ # Second, declare the bundle itself.
+ android_app_bundle("foo_bundle") {
+ # Indicate the base module to use for this bundle
+ base_module_target = ":foo_base_module"
+
+ # The name of our bundle file (without any suffix). Default would
+ # be 'foo_bundle' otherwise.
+ bundle_name = "FooBundle"
+
+ # Signing your bundle is required to upload it to the Play Store
+ # but since signing is very slow, avoid doing it for non official
+ # builds. Signing the bundle is not required for local testing.
+ sign_bundle = is_official_build
+
+ # Enable language-based splits for this bundle. Which means that
+ # resources and assets specific to a given language will be placed
+ # into their own split APK in the final .apks archive.
+ enable_language_splits = true
+
+ # Proguard settings must be passed at the bundle, not module, target.
+ proguard_enabled = !is_java_debug
+ }
+```
+
+When generating the `foo_bundle` target with Ninja, you will end up with
+the following:
+
+ - The bundle file under `out/Release/apks/FooBundle.aab`
+
+ - A helper script called `out/Release/bin/foo_bundle`, which can be used
+ to install / launch / uninstall the bundle on local devices.
+
+ This works like an APK wrapper script (e.g. `foo_apk`). Use `--help`
+ to see all possible commands supported by the script.
+
+If you need more modules besides the base one, you will need to list all the
+extra ones using the extra_modules variable which takes a list of GN scopes,
+as in:
+
+```gn
+
+ android_app_bundle_module("foo_base_module") {
+ ...
+ }
+
+ android_app_bundle_module("foo_extra_module") {
+ ...
+ }
+
+ android_app_bundle("foo_bundle") {
+ base_module_target = ":foo_base_module"
+
+ extra_modules = [
+ { # NOTE: Scopes require one field per line, and no comma separators.
+ name = "my_module"
+ module_target = ":foo_extra_module"
+ }
+ ]
+
+ ...
+ }
+```
+
+Note that each extra module is identified by a unique name, which cannot
+be '`base`'.
+
+
+# Bundle signature issues
+
+Signing an app bundle is not necessary, unless you want to upload it to the
+Play Store. Since this process is very slow (it uses `jarsigner` instead of
+the much faster `apkbuilder`), you can control it with the `sign_bundle`
+variable, as described in the example above.
+
+The `.apks` archive however always contains signed split APKs. The keystore
+path/password/alias being used are the default ones, unless you use custom
+values when declaring the bundle itself, as in:
+
+```gn
+ android_app_bundle("foo_bundle") {
+ ...
+ keystore_path = "//path/to/keystore"
+ keystore_password = "K3y$t0Re-Pa$$w0rd"
+ keystore_name = "my-signing-key-name"
+ }
+```
+
+These values are not stored in the bundle itself, but in the wrapper script,
+which will use them to generate the `.apks` archive for you. This allows you
+to properly install updates on top of existing applications on any device.
+
+
+# Proguard and bundles
+
+When using an app bundle that is made of several modules, it is crucial to
+ensure that proguard, if enabled:
+
+- Keeps the obfuscated class names used by each module consistent.
+- Does not remove classes that are not used in one module, but referenced
+ by others.
+
+To achieve this, a special scheme called *synchronized proguarding* is
+performed, which consists of the following steps:
+
+- The list of unoptimized .jar files from all modules are sent to a single
+ proguard command. This generates a new temporary optimized *group* .jar file.
+
+- Each module extracts the optimized class files from the optimized *group*
+ .jar file, to generate its own, module-specific, optimized .jar.
+
+- Each module-specific optimized .jar is then sent to dex generation.
+
+This synchronized proguarding step is added by the `android_app_bundle()` GN
+template. In practice this means the following:
+
+ - If `proguard_enabled` and `proguard_jar_path` must be passed to
+ `android_app_bundle` targets, but not to `android_app_bundle_module` ones.
+
+ - `proguard_configs` can be still passed to individual modules, just
+ like regular APKs. All proguard configs will be merged during the
+ synchronized proguard step.
+
+
+# Manual generation and installation of .apks archives
+
+Note that the `foo_bundle` script knows how to generate the .apks archive
+from the bundle file, and install it to local devices for you. For example,
+to install and launch a bundle, use:
+
+```sh
+ out/Release/bin/foo_bundle run
+```
+
+If you want to manually look or use the `.apks` archive, use the following
+command to generate it:
+
+```sh
+ out/Release/bin/foo_bundle build-bundle-apks \
+ --output-apks=/tmp/BundleFoo.apks
+```
+
+All split APKs within the archive will be properly signed. And you will be
+able to look at its content (with `unzip -l`), or install it manually with:
+
+```sh
+ build/android/gyp/bundletool.py install-apks \
+ --apks=/tmp/BundleFoo.apks \
+ --adb=$(which adb)
+```
diff --git a/chromium/build/android/gradle/generate_gradle.py b/chromium/build/android/gradle/generate_gradle.py
index 302f0a679ea..dfc2f241549 100755
--- a/chromium/build/android/gradle/generate_gradle.py
+++ b/chromium/build/android/gradle/generate_gradle.py
@@ -169,7 +169,7 @@ class _ProjectEntry(object):
self._build_config = None
self._java_files = None
self._all_entries = None
- self.android_test_entries = None
+ self.android_test_entries = []
@classmethod
def FromGnTarget(cls, gn_target):
@@ -334,7 +334,7 @@ class _ProjectContextGenerator(object):
return _DEFAULT_ANDROID_MANIFEST_PATH
variables = {}
- variables['compile_sdk_version'] = self.build_vars['android_sdk_version']
+ variables['compile_sdk_version'] = self.build_vars['compile_sdk_version']
variables['package'] = resource_packages[0]
output_file = os.path.join(
@@ -373,21 +373,24 @@ class _ProjectContextGenerator(object):
res_zips += entry.ResZips()
return set(_RebasePath(res_zips))
- def GeneratedInputs(self, root_entry):
- generated_inputs = self.AllResZips(root_entry)
- generated_inputs.update(self.AllSrcjars(root_entry))
+ def GeneratedInputs(self, root_entry, fast=None):
+ generated_inputs = set()
+ if not fast:
+ generated_inputs.update(self.AllResZips(root_entry))
+ generated_inputs.update(self.AllSrcjars(root_entry))
for entry in self._GetEntries(root_entry):
generated_inputs.update(entry.GeneratedJavaFiles())
generated_inputs.update(entry.PrebuiltJars())
return generated_inputs
- def GeneratedZips(self, root_entry):
+ def GeneratedZips(self, root_entry, fast=None):
entry_output_dir = self.EntryOutputDir(root_entry)
tuples = []
- tuples.extend((s, os.path.join(entry_output_dir, _SRCJARS_SUBDIR))
- for s in self.AllSrcjars(root_entry))
- tuples.extend((s, os.path.join(entry_output_dir, _RES_SUBDIR))
- for s in self.AllResZips(root_entry))
+ if not fast:
+ tuples.extend((s, os.path.join(entry_output_dir, _SRCJARS_SUBDIR))
+ for s in self.AllSrcjars(root_entry))
+ tuples.extend((s, os.path.join(entry_output_dir, _RES_SUBDIR))
+ for s in self.AllResZips(root_entry))
return tuples
def GenerateManifest(self, root_entry):
@@ -550,7 +553,7 @@ def _GenerateBaseVars(generator, build_vars, source_properties):
variables = {}
variables['build_tools_version'] = source_properties['Pkg.Revision']
variables['compile_sdk_version'] = (
- 'android-%s' % build_vars['android_sdk_version'])
+ 'android-%s' % build_vars['compile_sdk_version'])
target_sdk_version = build_vars['android_sdk_version']
if target_sdk_version.isalpha():
target_sdk_version = '"{}"'.format(target_sdk_version)
@@ -820,6 +823,12 @@ def main():
action='append',
help='GN native targets to generate for. May be '
'repeated.')
+ parser.add_argument('--compile-sdk-version',
+ type=int,
+ default=0,
+ help='Override compileSdkVersion for android sdk docs. '
+ 'Useful when sources for android_sdk_version is '
+ 'not available in Android Studio.')
version_group = parser.add_mutually_exclusive_group()
version_group.add_argument('--beta',
action='store_true',
@@ -895,6 +904,10 @@ def main():
channel = 'canary'
else:
channel = 'stable'
+ if args.compile_sdk_version:
+ build_vars['compile_sdk_version'] = args.compile_sdk_version
+ else:
+ build_vars['compile_sdk_version'] = build_vars['android_sdk_version']
generator = _ProjectContextGenerator(_gradle_output_dir, build_vars,
args.use_gradle_process_resources, jinja_processor, args.split_projects,
channel)
@@ -962,24 +975,24 @@ def main():
_WriteFile(os.path.join(generator.project_dir, 'local.properties'),
_GenerateLocalProperties(sdk_path))
- if not args.fast:
- zip_tuples = []
- generated_inputs = set()
- for entry in entries:
+ zip_tuples = []
+ generated_inputs = set()
+ for entry in entries:
+ entries_to_gen = [entry]
+ entries_to_gen.extend(entry.android_test_entries)
+ for entry_to_gen in entries_to_gen:
# Build all paths references by .gradle that exist within output_dir.
- generated_inputs.update(generator.GeneratedInputs(entry))
- zip_tuples.extend(generator.GeneratedZips(entry))
- if generated_inputs:
- logging.warning('Building generated source files...')
- targets = _RebasePath(generated_inputs, output_dir)
- _RunNinja(output_dir, targets, args.j)
- if zip_tuples:
- _ExtractZips(generator.project_dir, zip_tuples)
+ generated_inputs.update(
+ generator.GeneratedInputs(entry_to_gen, args.fast))
+ zip_tuples.extend(generator.GeneratedZips(entry_to_gen, args.fast))
+ if generated_inputs:
+ logging.warning('Building generated source files...')
+ targets = _RebasePath(generated_inputs, output_dir)
+ _RunNinja(output_dir, targets, args.j)
+ if zip_tuples:
+ _ExtractZips(generator.project_dir, zip_tuples)
logging.warning('Generated projects for Android Studio %s', channel)
- if not args.fast:
- logging.warning('Run with --fast flag to skip generating files (faster, '
- 'but less correct)')
logging.warning('For more tips: https://chromium.googlesource.com/chromium'
'/src.git/+/master/docs/android_studio.md')
diff --git a/chromium/build/android/gradle/root.jinja b/chromium/build/android/gradle/root.jinja
index 76ffa8f63be..ff26840f00f 100644
--- a/chromium/build/android/gradle/root.jinja
+++ b/chromium/build/android/gradle/root.jinja
@@ -10,7 +10,7 @@ buildscript {
}
dependencies {
{% if channel == 'canary' %}
- classpath "com.android.tools.build:gradle:3.2.0-alpha16"
+ classpath "com.android.tools.build:gradle:3.3.0-alpha05"
{% elif channel == 'beta' %}
classpath "com.android.tools.build:gradle:3.1.0-beta4"
{% else %}
diff --git a/chromium/build/android/gyp/aar.pydeps b/chromium/build/android/gyp/aar.pydeps
new file mode 100644
index 00000000000..e08c5475e3d
--- /dev/null
+++ b/chromium/build/android/gyp/aar.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/aar.pydeps build/android/gyp/aar.py
+../../gn_helpers.py
+aar.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/aidl.py b/chromium/build/android/gyp/aidl.py
index 1591f24508c..64ad29041ad 100755
--- a/chromium/build/android/gyp/aidl.py
+++ b/chromium/build/android/gyp/aidl.py
@@ -18,7 +18,6 @@ from util import build_utils
def main(argv):
option_parser = optparse.OptionParser()
- build_utils.AddDepfileOption(option_parser)
option_parser.add_option('--aidl-path', help='Path to the aidl binary.')
option_parser.add_option('--imports', help='Files to import.')
option_parser.add_option('--includes',
@@ -54,9 +53,6 @@ def main(argv):
pkg_name.replace('.', '/'), os.path.basename(path))
build_utils.AddToZipHermetic(srcjar, arcname, data=data)
- if options.depfile:
- build_utils.WriteDepfile(options.depfile, options.srcjar)
-
if __name__ == '__main__':
sys.exit(main(sys.argv))
diff --git a/chromium/build/android/gyp/aidl.pydeps b/chromium/build/android/gyp/aidl.pydeps
new file mode 100644
index 00000000000..2dbce376f1e
--- /dev/null
+++ b/chromium/build/android/gyp/aidl.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/aidl.pydeps build/android/gyp/aidl.py
+../../gn_helpers.py
+aidl.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/apkbuilder.pydeps b/chromium/build/android/gyp/apkbuilder.pydeps
new file mode 100644
index 00000000000..3ae03319c90
--- /dev/null
+++ b/chromium/build/android/gyp/apkbuilder.pydeps
@@ -0,0 +1,8 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/apkbuilder.pydeps build/android/gyp/apkbuilder.py
+../../gn_helpers.py
+apkbuilder.py
+finalize_apk.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/app_bundle_to_apks.pydeps b/chromium/build/android/gyp/app_bundle_to_apks.pydeps
new file mode 100644
index 00000000000..49c2892b2f3
--- /dev/null
+++ b/chromium/build/android/gyp/app_bundle_to_apks.pydeps
@@ -0,0 +1,8 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/app_bundle_to_apks.pydeps build/android/gyp/app_bundle_to_apks.py
+../../gn_helpers.py
+app_bundle_to_apks.py
+bundletool.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/bytecode_processor.pydeps b/chromium/build/android/gyp/bytecode_processor.pydeps
new file mode 100644
index 00000000000..d8ff3964950
--- /dev/null
+++ b/chromium/build/android/gyp/bytecode_processor.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/bytecode_processor.pydeps build/android/gyp/bytecode_processor.py
+../../gn_helpers.py
+bytecode_processor.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/compile_resources.py b/chromium/build/android/gyp/compile_resources.py
index 412c6f7485d..cda03e60601 100755
--- a/chromium/build/android/gyp/compile_resources.py
+++ b/chromium/build/android/gyp/compile_resources.py
@@ -671,7 +671,8 @@ def main(args):
input_paths=input_paths,
input_strings=input_strings,
output_paths=output_paths,
- depfile_deps=options.dependencies_res_zips + options.extra_r_text_files)
+ depfile_deps=options.dependencies_res_zips + options.extra_r_text_files,
+ add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/compile_resources.pydeps b/chromium/build/android/gyp/compile_resources.pydeps
new file mode 100644
index 00000000000..2ffcb52a2c9
--- /dev/null
+++ b/chromium/build/android/gyp/compile_resources.pydeps
@@ -0,0 +1,29 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/compile_resources.pydeps build/android/gyp/compile_resources.py
+../../../third_party/jinja2/__init__.py
+../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/bccache.py
+../../../third_party/jinja2/compiler.py
+../../../third_party/jinja2/defaults.py
+../../../third_party/jinja2/environment.py
+../../../third_party/jinja2/exceptions.py
+../../../third_party/jinja2/filters.py
+../../../third_party/jinja2/idtracking.py
+../../../third_party/jinja2/lexer.py
+../../../third_party/jinja2/loaders.py
+../../../third_party/jinja2/nodes.py
+../../../third_party/jinja2/optimizer.py
+../../../third_party/jinja2/parser.py
+../../../third_party/jinja2/runtime.py
+../../../third_party/jinja2/tests.py
+../../../third_party/jinja2/utils.py
+../../../third_party/jinja2/visitor.py
+../../../third_party/markupsafe/__init__.py
+../../../third_party/markupsafe/_compat.py
+../../../third_party/markupsafe/_native.py
+../../gn_helpers.py
+compile_resources.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+util/resource_utils.py
diff --git a/chromium/build/android/gyp/copy_ex.py b/chromium/build/android/gyp/copy_ex.py
index a9f6a9f44bb..48d1b26df18 100755
--- a/chromium/build/android/gyp/copy_ex.py
+++ b/chromium/build/android/gyp/copy_ex.py
@@ -35,8 +35,14 @@ def CopyFile(f, dest, deps):
dest = os.path.join(dest, os.path.basename(f))
deps.append(f)
- if os.path.isfile(dest) and filecmp.cmp(dest, f, shallow=False):
- return
+
+ if os.path.isfile(dest):
+ if filecmp.cmp(dest, f, shallow=False):
+ return
+ # The shutil.copy() below would fail if the file does not have write
+ # permissions. Deleting the file has similar costs to modifying the
+ # permissions.
+ os.unlink(dest)
shutil.copy(f, dest)
diff --git a/chromium/build/android/gyp/copy_ex.pydeps b/chromium/build/android/gyp/copy_ex.pydeps
new file mode 100644
index 00000000000..e0fb31eaa9b
--- /dev/null
+++ b/chromium/build/android/gyp/copy_ex.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/copy_ex.pydeps build/android/gyp/copy_ex.py
+../../gn_helpers.py
+copy_ex.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/create_apk_operations_script.pydeps b/chromium/build/android/gyp/create_apk_operations_script.pydeps
new file mode 100644
index 00000000000..9d4dcb8fe5b
--- /dev/null
+++ b/chromium/build/android/gyp/create_apk_operations_script.pydeps
@@ -0,0 +1,3 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_apk_operations_script.pydeps build/android/gyp/create_apk_operations_script.py
+create_apk_operations_script.py
diff --git a/chromium/build/android/gyp/create_app_bundle.pydeps b/chromium/build/android/gyp/create_app_bundle.pydeps
new file mode 100644
index 00000000000..fef04fab53a
--- /dev/null
+++ b/chromium/build/android/gyp/create_app_bundle.pydeps
@@ -0,0 +1,30 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_app_bundle.pydeps build/android/gyp/create_app_bundle.py
+../../../third_party/jinja2/__init__.py
+../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/bccache.py
+../../../third_party/jinja2/compiler.py
+../../../third_party/jinja2/defaults.py
+../../../third_party/jinja2/environment.py
+../../../third_party/jinja2/exceptions.py
+../../../third_party/jinja2/filters.py
+../../../third_party/jinja2/idtracking.py
+../../../third_party/jinja2/lexer.py
+../../../third_party/jinja2/loaders.py
+../../../third_party/jinja2/nodes.py
+../../../third_party/jinja2/optimizer.py
+../../../third_party/jinja2/parser.py
+../../../third_party/jinja2/runtime.py
+../../../third_party/jinja2/tests.py
+../../../third_party/jinja2/utils.py
+../../../third_party/jinja2/visitor.py
+../../../third_party/markupsafe/__init__.py
+../../../third_party/markupsafe/_compat.py
+../../../third_party/markupsafe/_native.py
+../../gn_helpers.py
+bundletool.py
+create_app_bundle.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+util/resource_utils.py
diff --git a/chromium/build/android/gyp/create_bundle_wrapper_script.py b/chromium/build/android/gyp/create_bundle_wrapper_script.py
new file mode 100755
index 00000000000..04108363e3f
--- /dev/null
+++ b/chromium/build/android/gyp/create_bundle_wrapper_script.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Create a wrapper script to manage an Android App Bundle."""
+
+import argparse
+import os
+import string
+import sys
+
+from util import build_utils
+
+SCRIPT_TEMPLATE = string.Template("""\
+#!/usr/bin/env python
+#
+# This file was generated by build/android/gyp/create_bundle_wrapper_script.py
+
+import os
+import sys
+
+def main():
+ script_directory = os.path.dirname(__file__)
+ resolve = lambda p: p if p is None else os.path.abspath(os.path.join(
+ script_directory, p))
+ sys.path.append(resolve(${WRAPPED_SCRIPT_DIR}))
+ import apk_operations
+
+ apk_operations.RunForBundle(output_directory=resolve(${OUTPUT_DIR}),
+ bundle_path=resolve(${BUNDLE_PATH}),
+ bundle_apks_path=resolve(${BUNDLE_APKS_PATH}),
+ aapt2_path=resolve(${AAPT2_PATH}),
+ keystore_path=resolve(${KEYSTORE_PATH}),
+ keystore_password=${KEYSTORE_PASSWORD},
+ keystore_alias=${KEY_NAME},
+ package_name=${PACKAGE_NAME},
+ command_line_flags_file=${FLAGS_FILE},
+ proguard_mapping_path=resolve(${MAPPING_PATH}),
+ target_cpu=${TARGET_CPU})
+
+if __name__ == '__main__':
+ sys.exit(main())
+""")
+
+
+def main(args):
+ args = build_utils.ExpandFileArgs(args)
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--script-output-path', required=True,
+ help='Output path for executable script.')
+ parser.add_argument('--bundle-path', required=True)
+ parser.add_argument('--bundle-apks-path', required=True)
+ parser.add_argument('--package-name', required=True)
+ parser.add_argument('--aapt2-path', required=True)
+ parser.add_argument('--keystore-path', required=True)
+ parser.add_argument('--keystore-password', required=True)
+ parser.add_argument('--key-name', required=True)
+ parser.add_argument('--command-line-flags-file')
+ parser.add_argument('--proguard-mapping-path')
+ parser.add_argument('--target-cpu')
+ args = parser.parse_args(args)
+
+ def relativize(path):
+ """Returns the path relative to the output script directory."""
+ if path is None:
+ return path
+ return os.path.relpath(path, os.path.dirname(args.script_output_path))
+
+ wrapped_script_dir = os.path.join(os.path.dirname(__file__), os.path.pardir)
+ wrapped_script_dir = relativize(wrapped_script_dir)
+
+ with open(args.script_output_path, 'w') as script:
+ script_dict = {
+ 'WRAPPED_SCRIPT_DIR': repr(wrapped_script_dir),
+ 'OUTPUT_DIR': repr(relativize('.')),
+ 'BUNDLE_PATH': repr(relativize(args.bundle_path)),
+ 'BUNDLE_APKS_PATH': repr(relativize(args.bundle_apks_path)),
+ 'PACKAGE_NAME': repr(args.package_name),
+ 'AAPT2_PATH': repr(relativize(args.aapt2_path)),
+ 'KEYSTORE_PATH': repr(relativize(args.keystore_path)),
+ 'KEYSTORE_PASSWORD': repr(args.keystore_password),
+ 'KEY_NAME': repr(args.key_name),
+ 'MAPPING_PATH': repr(relativize(args.proguard_mapping_path)),
+ 'FLAGS_FILE': repr(args.command_line_flags_file),
+ 'TARGET_CPU': repr(args.target_cpu),
+ }
+ script.write(SCRIPT_TEMPLATE.substitute(script_dict))
+ os.chmod(args.script_output_path, 0750)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/chromium/build/android/gyp/create_bundle_wrapper_script.pydeps b/chromium/build/android/gyp/create_bundle_wrapper_script.pydeps
new file mode 100644
index 00000000000..5c87801e253
--- /dev/null
+++ b/chromium/build/android/gyp/create_bundle_wrapper_script.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_bundle_wrapper_script.pydeps build/android/gyp/create_bundle_wrapper_script.py
+../../gn_helpers.py
+create_bundle_wrapper_script.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/create_dist_jar.py b/chromium/build/android/gyp/create_dist_jar.py
index 2e06478fae8..7f78935a47d 100755
--- a/chromium/build/android/gyp/create_dist_jar.py
+++ b/chromium/build/android/gyp/create_dist_jar.py
@@ -24,7 +24,8 @@ def main(args):
build_utils.MergeZips(options.output, input_jars)
if options.depfile:
- build_utils.WriteDepfile(options.depfile, options.output, input_jars)
+ build_utils.WriteDepfile(options.depfile, options.output, input_jars,
+ add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/create_dist_jar.pydeps b/chromium/build/android/gyp/create_dist_jar.pydeps
new file mode 100644
index 00000000000..f4224d7f99d
--- /dev/null
+++ b/chromium/build/android/gyp/create_dist_jar.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_dist_jar.pydeps build/android/gyp/create_dist_jar.py
+../../gn_helpers.py
+create_dist_jar.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/create_java_binary_script.pydeps b/chromium/build/android/gyp/create_java_binary_script.pydeps
new file mode 100644
index 00000000000..96d79bf609e
--- /dev/null
+++ b/chromium/build/android/gyp/create_java_binary_script.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_java_binary_script.pydeps build/android/gyp/create_java_binary_script.py
+../../gn_helpers.py
+create_java_binary_script.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/create_stack_script.pydeps b/chromium/build/android/gyp/create_stack_script.pydeps
new file mode 100644
index 00000000000..7bddb156f40
--- /dev/null
+++ b/chromium/build/android/gyp/create_stack_script.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_stack_script.pydeps build/android/gyp/create_stack_script.py
+../../gn_helpers.py
+create_stack_script.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/create_test_runner_script.pydeps b/chromium/build/android/gyp/create_test_runner_script.pydeps
new file mode 100644
index 00000000000..4b8876bc943
--- /dev/null
+++ b/chromium/build/android/gyp/create_test_runner_script.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_test_runner_script.pydeps build/android/gyp/create_test_runner_script.py
+../../gn_helpers.py
+create_test_runner_script.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/create_tool_wrapper.pydeps b/chromium/build/android/gyp/create_tool_wrapper.pydeps
new file mode 100644
index 00000000000..75b8326e70b
--- /dev/null
+++ b/chromium/build/android/gyp/create_tool_wrapper.pydeps
@@ -0,0 +1,3 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_tool_wrapper.pydeps build/android/gyp/create_tool_wrapper.py
+create_tool_wrapper.py
diff --git a/chromium/build/android/gyp/desugar.pydeps b/chromium/build/android/gyp/desugar.pydeps
new file mode 100644
index 00000000000..a40f3aa7ddd
--- /dev/null
+++ b/chromium/build/android/gyp/desugar.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/desugar.pydeps build/android/gyp/desugar.py
+../../gn_helpers.py
+desugar.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/dex.pydeps b/chromium/build/android/gyp/dex.pydeps
new file mode 100644
index 00000000000..0e18d02a460
--- /dev/null
+++ b/chromium/build/android/gyp/dex.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/dex.pydeps build/android/gyp/dex.py
+../../gn_helpers.py
+dex.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/dist_aar.py b/chromium/build/android/gyp/dist_aar.py
index 6dc38c488a6..ed823f18b7b 100755
--- a/chromium/build/android/gyp/dist_aar.py
+++ b/chromium/build/android/gyp/dist_aar.py
@@ -124,7 +124,8 @@ def main(args):
if options.depfile:
all_inputs = (options.jars + options.dependencies_res_zips +
options.r_text_files + options.proguard_configs)
- build_utils.WriteDepfile(options.depfile, options.output, all_inputs)
+ build_utils.WriteDepfile(options.depfile, options.output, all_inputs,
+ add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/dist_aar.pydeps b/chromium/build/android/gyp/dist_aar.pydeps
new file mode 100644
index 00000000000..da5ea8da23d
--- /dev/null
+++ b/chromium/build/android/gyp/dist_aar.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/dist_aar.pydeps build/android/gyp/dist_aar.py
+../../gn_helpers.py
+dist_aar.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/emma_instr.pydeps b/chromium/build/android/gyp/emma_instr.pydeps
new file mode 100644
index 00000000000..88f752a0f9d
--- /dev/null
+++ b/chromium/build/android/gyp/emma_instr.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/emma_instr.pydeps build/android/gyp/emma_instr.py
+../../gn_helpers.py
+emma_instr.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/filter_zip.pydeps b/chromium/build/android/gyp/filter_zip.pydeps
new file mode 100644
index 00000000000..67c989cf885
--- /dev/null
+++ b/chromium/build/android/gyp/filter_zip.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/filter_zip.pydeps build/android/gyp/filter_zip.py
+../../gn_helpers.py
+filter_zip.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/gcc_preprocess.py b/chromium/build/android/gyp/gcc_preprocess.py
index 8c5c404c744..8b3444c2b01 100755
--- a/chromium/build/android/gyp/gcc_preprocess.py
+++ b/chromium/build/android/gyp/gcc_preprocess.py
@@ -47,7 +47,7 @@ def main(args):
DoGcc(options)
if options.depfile:
- build_utils.WriteDepfile(options.depfile, options.output)
+ build_utils.WriteDepfile(options.depfile, options.output, add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/gcc_preprocess.pydeps b/chromium/build/android/gyp/gcc_preprocess.pydeps
new file mode 100644
index 00000000000..64e776b6338
--- /dev/null
+++ b/chromium/build/android/gyp/gcc_preprocess.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/gcc_preprocess.pydeps build/android/gyp/gcc_preprocess.py
+../../gn_helpers.py
+gcc_preprocess.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/generate_proguarded_module_jar.py b/chromium/build/android/gyp/generate_proguarded_module_jar.py
new file mode 100755
index 00000000000..97b3027a51e
--- /dev/null
+++ b/chromium/build/android/gyp/generate_proguarded_module_jar.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Extracts a bundle module's classes from jar created in the synchronized
+proguarding step and packages them into a new jar.
+
+Synchronized proguarding means that, when several app modules are combined into
+an app bundle, all un-optimized jars for all modules are grouped and sent to a
+single proguard command, which generates a single, common, intermediate
+optimized jar, and its mapping file.
+
+This script is used to extract, from this synchronized proguard jar, all the
+optimized classes corresponding to a single module, into a new .jar file. The
+latter will be compiled later into the module's dex file.
+
+For this, the script reads the module's un-obfuscated class names from the
+module's unoptimized jars. Then, it maps those to obfuscated class names using
+the proguard mapping file. Finally, it extracts the module's class files from
+the proguarded jar and zips them into a new module jar. """
+
+import argparse
+import os
+import sys
+import zipfile
+
+from util import build_utils
+
+MANIFEST = """Manifest-Version: 1.0
+Created-By: generate_proguarded_module_jar.py
+"""
+
+
+# TODO(tiborg): Share with merge_jar_info_files.py.
+def _FullJavaNameFromClassFilePath(path):
+ if not path.endswith('.class'):
+ return ''
+ path = os.path.splitext(path)[0]
+ parts = []
+ while path:
+ # Use split to be platform independent.
+ head, tail = os.path.split(path)
+ path = head
+ parts.append(tail)
+ parts.reverse() # Package comes first
+ return '.'.join(parts)
+
+
+def main(args):
+ args = build_utils.ExpandFileArgs(args)
+ parser = argparse.ArgumentParser()
+ build_utils.AddDepfileOption(parser)
+ parser.add_argument(
+ '--proguarded-jar',
+ required=True,
+ help='Path to input jar produced by synchronized proguarding')
+ parser.add_argument(
+ '--proguard-mapping',
+ required=True,
+ help='Path to input proguard mapping produced by synchronized '
+ 'proguarding')
+ parser.add_argument(
+ '--module-input-jars',
+ required=True,
+ help='GN-list of input paths to un-optimized jar files for the current '
+ 'module. The optimized versions of their .class files will go into '
+ 'the output jar.')
+ parser.add_argument(
+ '--output-jar',
+ required=True,
+ help='Path to output jar file containing the module\'s optimized class '
+ 'files')
+ parser.add_argument(
+ '--is-base-module',
+ action='store_true',
+ help='Inidcates to extract class files for a base module')
+ options = parser.parse_args(args)
+ options.module_input_jars = build_utils.ParseGnList(options.module_input_jars)
+
+ # Read class names of the currently processed module.
+ classes = set()
+ for module_jar in options.module_input_jars:
+ with zipfile.ZipFile(module_jar) as zip_info:
+ for path in zip_info.namelist():
+ fully_qualified_name = _FullJavaNameFromClassFilePath(path)
+ if fully_qualified_name:
+ classes.add(fully_qualified_name)
+
+ # Parse the proguarding mapping to be able to map un-obfuscated to obfuscated
+ # names.
+ # Proguard mapping files have the following format:
+ #
+ # {un-obfuscated class name 1} -> {obfuscated class name 1}:
+ # {un-obfuscated member name 1} -> {obfuscated member name 1}
+ # ...
+ # {un-obfuscated class name 2} -> {obfuscated class name 2}:
+ # ...
+ # ...
+ obfuscation_map = {}
+ with open(options.proguard_mapping, 'r') as proguard_mapping_file:
+ for line in proguard_mapping_file:
+ # Skip indented lines since they map member names and not class names.
+ if line.startswith(' '):
+ continue
+ line = line.strip()
+ # Skip empty lines.
+ if not line:
+ continue
+ assert line.endswith(':')
+ full, obfuscated = line.strip(':').split(' -> ')
+ assert full
+ assert obfuscated
+ obfuscation_map[full] = obfuscated
+
+ # Collect the obfuscated names of classes, which should go into the currently
+ # processed module.
+ obfuscated_module_classes = set(
+ obfuscation_map[c] for c in classes if c in obfuscation_map)
+
+ # Collect horizontally merged classes to later make sure that those only go
+ # into the base module. Merging classes horizontally means that proguard took
+ # two classes that don't inherit from each other and merged them into one.
+ horiz_merged_classes = set()
+ obfuscated_classes = sorted(obfuscation_map.values())
+ prev_obfuscated_class = None
+ for obfuscated_class in obfuscated_classes:
+ if prev_obfuscated_class and obfuscated_class == prev_obfuscated_class:
+ horiz_merged_classes.add(obfuscated_class)
+ prev_obfuscated_class = obfuscated_class
+
+ # Move horizontally merged classes into the base module.
+ if options.is_base_module:
+ obfuscated_module_classes |= horiz_merged_classes
+ else:
+ obfuscated_module_classes -= horiz_merged_classes
+
+ # Extract module class files from proguarded jar and store them in a module
+ # split jar.
+ with zipfile.ZipFile(
+ os.path.abspath(options.output_jar), 'w',
+ zipfile.ZIP_DEFLATED) as output_jar:
+ with zipfile.ZipFile(os.path.abspath(options.proguarded_jar),
+ 'r') as proguarded_jar:
+ for obfuscated_class in obfuscated_module_classes:
+ class_path = obfuscated_class.replace('.', '/') + '.class'
+ class_file_content = proguarded_jar.read(class_path)
+ output_jar.writestr(class_path, class_file_content)
+ output_jar.writestr('META-INF/MANIFEST.MF', MANIFEST)
+
+ if options.depfile:
+ build_utils.WriteDepfile(
+ options.depfile, options.output_jar, options.module_input_jars +
+ [options.proguard_mapping, options.proguarded_jar], add_pydeps=False)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/chromium/build/android/gyp/generate_proguarded_module_jar.pydeps b/chromium/build/android/gyp/generate_proguarded_module_jar.pydeps
new file mode 100644
index 00000000000..6d52b4ec543
--- /dev/null
+++ b/chromium/build/android/gyp/generate_proguarded_module_jar.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/generate_proguarded_module_jar.pydeps build/android/gyp/generate_proguarded_module_jar.py
+../../gn_helpers.py
+generate_proguarded_module_jar.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/generate_resource_rewriter.py b/chromium/build/android/gyp/generate_resource_rewriter.py
index 82ddc21dc15..ba635a293d5 100755
--- a/chromium/build/android/gyp/generate_resource_rewriter.py
+++ b/chromium/build/android/gyp/generate_resource_rewriter.py
@@ -107,4 +107,3 @@ def main():
if __name__ == '__main__':
sys.exit(main())
-
diff --git a/chromium/build/android/gyp/ijar.pydeps b/chromium/build/android/gyp/ijar.pydeps
new file mode 100644
index 00000000000..ca10697c1f2
--- /dev/null
+++ b/chromium/build/android/gyp/ijar.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/ijar.pydeps build/android/gyp/ijar.py
+../../gn_helpers.py
+ijar.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/java_cpp_enum.py b/chromium/build/android/gyp/java_cpp_enum.py
index afb48430fa2..e737441005f 100755
--- a/chromium/build/android/gyp/java_cpp_enum.py
+++ b/chromium/build/android/gyp/java_cpp_enum.py
@@ -447,7 +447,7 @@ def DoMain(argv):
build_utils.AddToZipHermetic(srcjar, output_path, data=data)
if options.depfile:
- build_utils.WriteDepfile(options.depfile, options.srcjar)
+ build_utils.WriteDepfile(options.depfile, options.srcjar, add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/java_cpp_enum.pydeps b/chromium/build/android/gyp/java_cpp_enum.pydeps
new file mode 100644
index 00000000000..32c8de534db
--- /dev/null
+++ b/chromium/build/android/gyp/java_cpp_enum.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/java_cpp_enum.pydeps build/android/gyp/java_cpp_enum.py
+../../gn_helpers.py
+java_cpp_enum.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/java_google_api_keys.py b/chromium/build/android/gyp/java_google_api_keys.py
index f57e0b3a978..349821a8fce 100755
--- a/chromium/build/android/gyp/java_google_api_keys.py
+++ b/chromium/build/android/gyp/java_google_api_keys.py
@@ -121,4 +121,3 @@ def _DoMain(argv):
if __name__ == '__main__':
_DoMain(sys.argv[1:])
-
diff --git a/chromium/build/android/gyp/javac.py b/chromium/build/android/gyp/javac.py
index b5ae57a6d5a..0e1aad00922 100755
--- a/chromium/build/android/gyp/javac.py
+++ b/chromium/build/android/gyp/javac.py
@@ -96,6 +96,7 @@ ERRORPRONE_WARNINGS_TO_ERROR = [
'MissingFail',
'MissingOverride',
'NarrowingCompoundAssignment',
+ 'OrphanedFormatString',
'ParameterName',
'ParcelableCreator',
'ReferenceEquality',
@@ -248,7 +249,7 @@ def _CreateInfoFile(java_files, options, srcjar_files):
info_data[fully_qualified_name] = java_file
# Skip aidl srcjars since they don't indent code correctly.
source = srcjar_files.get(java_file, java_file)
- if source.endswith('_aidl.srcjar'):
+ if '_aidl.srcjar' in source:
continue
assert not options.chromium_code or len(class_names) == 1, (
'Chromium java files must only have one class: {}'.format(source))
@@ -309,7 +310,10 @@ def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
extracted_files = build_utils.ExtractAll(
srcjar, path=java_dir, pattern='*.java')
for path in extracted_files:
- srcjar_files[path] = srcjar
+ # We want the path inside the srcjar so the viewer can have a tree
+ # structure.
+ srcjar_files[path] = '{}/{}'.format(
+ srcjar, os.path.relpath(path, java_dir))
jar_srcs = build_utils.FindInDirectory(java_dir, '*.java')
java_files.extend(jar_srcs)
if changed_paths:
@@ -596,7 +600,8 @@ def main(argv):
input_strings=javac_cmd + classpath,
output_paths=output_paths,
force=force,
- pass_changes=True)
+ pass_changes=True,
+ add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/javac.pydeps b/chromium/build/android/gyp/javac.pydeps
new file mode 100644
index 00000000000..a9d257b95f8
--- /dev/null
+++ b/chromium/build/android/gyp/javac.pydeps
@@ -0,0 +1,15 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/javac.pydeps build/android/gyp/javac.py
+../../../third_party/colorama/src/colorama/__init__.py
+../../../third_party/colorama/src/colorama/ansi.py
+../../../third_party/colorama/src/colorama/ansitowin32.py
+../../../third_party/colorama/src/colorama/initialise.py
+../../../third_party/colorama/src/colorama/win32.py
+../../../third_party/colorama/src/colorama/winterm.py
+../../gn_helpers.py
+jar.py
+javac.py
+util/__init__.py
+util/build_utils.py
+util/jar_info_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/jinja_template.py b/chromium/build/android/gyp/jinja_template.py
index 434d281099e..4d5c403dfe8 100755
--- a/chromium/build/android/gyp/jinja_template.py
+++ b/chromium/build/android/gyp/jinja_template.py
@@ -124,7 +124,8 @@ def main():
parser.add_argument('--variables', help='Variables to be made available in '
'the template processing environment, as a GYP list '
'(e.g. --variables "channel=beta mstone=39")', default='')
- build_utils.AddDepfileOption(parser)
+ parser.add_argument('--check-includes', action='store_true',
+ help='Enable inputs and includes checks.')
options = parser.parse_args()
inputs = build_utils.ParseGnList(options.inputs)
@@ -146,15 +147,13 @@ def main():
_ProcessFiles(processor, inputs, options.inputs_base_dir,
options.outputs_zip)
- if options.depfile:
- output = options.output or options.outputs_zip
+ if options.check_includes:
all_inputs = set(processor.GetLoadedTemplates())
all_inputs.difference_update(inputs)
all_inputs.difference_update(includes)
if all_inputs:
raise Exception('Found files not listed via --includes:\n' +
'\n'.join(sorted(all_inputs)))
- build_utils.WriteDepfile(options.depfile, output)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/jinja_template.pydeps b/chromium/build/android/gyp/jinja_template.pydeps
new file mode 100644
index 00000000000..a2a38176bfe
--- /dev/null
+++ b/chromium/build/android/gyp/jinja_template.pydeps
@@ -0,0 +1,41 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/jinja_template.pydeps build/android/gyp/jinja_template.py
+../../../third_party/catapult/devil/devil/__init__.py
+../../../third_party/catapult/devil/devil/android/__init__.py
+../../../third_party/catapult/devil/devil/android/constants/__init__.py
+../../../third_party/catapult/devil/devil/android/constants/chrome.py
+../../../third_party/catapult/devil/devil/android/sdk/__init__.py
+../../../third_party/catapult/devil/devil/android/sdk/keyevent.py
+../../../third_party/catapult/devil/devil/android/sdk/version_codes.py
+../../../third_party/catapult/devil/devil/constants/__init__.py
+../../../third_party/catapult/devil/devil/constants/exit_codes.py
+../../../third_party/jinja2/__init__.py
+../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/bccache.py
+../../../third_party/jinja2/compiler.py
+../../../third_party/jinja2/defaults.py
+../../../third_party/jinja2/environment.py
+../../../third_party/jinja2/exceptions.py
+../../../third_party/jinja2/filters.py
+../../../third_party/jinja2/idtracking.py
+../../../third_party/jinja2/lexer.py
+../../../third_party/jinja2/loaders.py
+../../../third_party/jinja2/nodes.py
+../../../third_party/jinja2/optimizer.py
+../../../third_party/jinja2/parser.py
+../../../third_party/jinja2/runtime.py
+../../../third_party/jinja2/tests.py
+../../../third_party/jinja2/utils.py
+../../../third_party/jinja2/visitor.py
+../../../third_party/markupsafe/__init__.py
+../../../third_party/markupsafe/_compat.py
+../../../third_party/markupsafe/_native.py
+../../gn_helpers.py
+../pylib/__init__.py
+../pylib/constants/__init__.py
+../pylib/constants/host_paths.py
+jinja_template.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+util/resource_utils.py
diff --git a/chromium/build/android/gyp/lint.pydeps b/chromium/build/android/gyp/lint.pydeps
new file mode 100644
index 00000000000..a8616e4d378
--- /dev/null
+++ b/chromium/build/android/gyp/lint.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/lint.pydeps build/android/gyp/lint.py
+../../gn_helpers.py
+lint.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/main_dex_list.py b/chromium/build/android/gyp/main_dex_list.py
index 9ccfea00ff9..24358590992 100755
--- a/chromium/build/android/gyp/main_dex_list.py
+++ b/chromium/build/android/gyp/main_dex_list.py
@@ -172,4 +172,3 @@ def _OnStaleMd5(proguard_cmd, main_dex_list_cmd, paths, main_dex_list_path,
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
-
diff --git a/chromium/build/android/gyp/main_dex_list.pydeps b/chromium/build/android/gyp/main_dex_list.pydeps
new file mode 100644
index 00000000000..8c482dfa523
--- /dev/null
+++ b/chromium/build/android/gyp/main_dex_list.pydeps
@@ -0,0 +1,8 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/main_dex_list.pydeps build/android/gyp/main_dex_list.py
+../../gn_helpers.py
+main_dex_list.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+util/proguard_util.py
diff --git a/chromium/build/android/gyp/merge_jar_info_files.py b/chromium/build/android/gyp/merge_jar_info_files.py
index 22a40a801bd..e6160f68469 100755
--- a/chromium/build/android/gyp/merge_jar_info_files.py
+++ b/chromium/build/android/gyp/merge_jar_info_files.py
@@ -61,7 +61,7 @@ def _MergeInfoFiles(output, jar_paths):
for path in zip_info.namelist():
fully_qualified_name = _FullJavaNameFromClassFilePath(path)
if fully_qualified_name:
- info_data[fully_qualified_name] = jar_path
+ info_data[fully_qualified_name] = '{}/{}'.format(jar_path, path)
jar_info_utils.WriteJarInfoFile(output, info_data)
diff --git a/chromium/build/android/gyp/merge_jar_info_files.pydeps b/chromium/build/android/gyp/merge_jar_info_files.pydeps
new file mode 100644
index 00000000000..710091c42fa
--- /dev/null
+++ b/chromium/build/android/gyp/merge_jar_info_files.pydeps
@@ -0,0 +1,8 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/merge_jar_info_files.pydeps build/android/gyp/merge_jar_info_files.py
+../../gn_helpers.py
+merge_jar_info_files.py
+util/__init__.py
+util/build_utils.py
+util/jar_info_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/merge_manifest.pydeps b/chromium/build/android/gyp/merge_manifest.pydeps
new file mode 100644
index 00000000000..37901962ce3
--- /dev/null
+++ b/chromium/build/android/gyp/merge_manifest.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/merge_manifest.pydeps build/android/gyp/merge_manifest.py
+../../gn_helpers.py
+merge_manifest.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/chromium/build/android/gyp/prepare_resources.py b/chromium/build/android/gyp/prepare_resources.py
index 6237c7db58d..ae7f53d0c30 100755
--- a/chromium/build/android/gyp/prepare_resources.py
+++ b/chromium/build/android/gyp/prepare_resources.py
@@ -297,7 +297,8 @@ def main(args):
input_paths=input_paths,
input_strings=input_strings,
output_paths=output_paths,
- depfile_deps=depfile_deps)
+ depfile_deps=depfile_deps,
+ add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/prepare_resources.pydeps b/chromium/build/android/gyp/prepare_resources.pydeps
new file mode 100644
index 00000000000..0e9ccfbe5ed
--- /dev/null
+++ b/chromium/build/android/gyp/prepare_resources.pydeps
@@ -0,0 +1,30 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/prepare_resources.pydeps build/android/gyp/prepare_resources.py
+../../../third_party/jinja2/__init__.py
+../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/bccache.py
+../../../third_party/jinja2/compiler.py
+../../../third_party/jinja2/defaults.py
+../../../third_party/jinja2/environment.py
+../../../third_party/jinja2/exceptions.py
+../../../third_party/jinja2/filters.py
+../../../third_party/jinja2/idtracking.py
+../../../third_party/jinja2/lexer.py
+../../../third_party/jinja2/loaders.py
+../../../third_party/jinja2/nodes.py
+../../../third_party/jinja2/optimizer.py
+../../../third_party/jinja2/parser.py
+../../../third_party/jinja2/runtime.py
+../../../third_party/jinja2/tests.py
+../../../third_party/jinja2/utils.py
+../../../third_party/jinja2/visitor.py
+../../../third_party/markupsafe/__init__.py
+../../../third_party/markupsafe/_compat.py
+../../../third_party/markupsafe/_native.py
+../../gn_helpers.py
+generate_v14_compatible_resources.py
+prepare_resources.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+util/resource_utils.py
diff --git a/chromium/build/android/gyp/proguard.py b/chromium/build/android/gyp/proguard.py
index 5a61968c85c..90a461ac365 100755
--- a/chromium/build/android/gyp/proguard.py
+++ b/chromium/build/android/gyp/proguard.py
@@ -35,13 +35,16 @@ def _RemoveMethodMappings(orig_path, out_fd):
for line in in_fd:
if line[:1] != ' ':
out_fd.write(line)
+ out_fd.flush()
def _ParseOptions(args):
parser = optparse.OptionParser()
build_utils.AddDepfileOption(parser)
parser.add_option('--proguard-path',
- help='Path to the proguard executable.')
+ help='Path to the proguard.jar to use.')
+ parser.add_option('--r8-path',
+ help='Path to the R8.jar to use.')
parser.add_option('--input-paths',
help='Paths to the .jar files proguard should run on.')
parser.add_option('--output-path', help='Path to the generated .jar file.')
@@ -53,9 +56,6 @@ def _ParseOptions(args):
'included by --proguard-configs, but that should '
'not actually be included.')
parser.add_option('--mapping', help='Path to proguard mapping to apply.')
- parser.add_option('--is-test', action='store_true',
- help='If true, extra proguard options for instrumentation tests will be '
- 'added.')
parser.add_option('--classpath', action='append',
help='Classpath for proguard.')
parser.add_option('--enable-dangerous-optimizations', action='store_true',
@@ -82,6 +82,29 @@ def _ParseOptions(args):
return options
+def _CreateR8Command(options):
+ # TODO: R8 needs -applymapping equivalent.
+ cmd = [
+ 'java', '-jar', options.r8_path,
+ '--no-desugaring',
+ '--classfile',
+ '--output', options.output_path,
+ '--pg-map-output', options.output_path + '.mapping',
+ ]
+
+ classpath = [
+ p for p in set(options.classpath) if p not in options.input_paths
+ ]
+ for lib in classpath:
+ cmd += ['--lib', lib]
+
+ for config_file in options.proguard_configs:
+ cmd += ['--pg-conf', config_file]
+
+ cmd += options.input_paths
+ return cmd
+
+
def main(args):
args = build_utils.ExpandFileArgs(args)
options = _ParseOptions(args)
@@ -92,36 +115,47 @@ def main(args):
proguard.config_exclusions(options.proguard_config_exclusions)
proguard.outjar(options.output_path)
- classpath = list(set(options.classpath))
+ # If a jar is part of input no need to include it as library jar.
+ classpath = [
+ p for p in set(options.classpath) if p not in options.input_paths
+ ]
proguard.libraryjars(classpath)
proguard.verbose(options.verbose)
if not options.enable_dangerous_optimizations:
proguard.disable_optimizations(_DANGEROUS_OPTIMIZATIONS)
- # Do not consider the temp file as an input since its name is random.
- input_paths = proguard.GetInputs()
-
- with tempfile.NamedTemporaryFile() as f:
- if options.mapping:
- input_paths.append(options.mapping)
- # Maintain only class name mappings in the .mapping file in order to work
- # around what appears to be a ProGuard bug in -applymapping:
- # method 'int closed()' is not being kept as 'a', but remapped to 'c'
- _RemoveMethodMappings(options.mapping, f)
- proguard.mapping(f.name)
-
- input_strings = proguard.build()
- if f.name in input_strings:
- input_strings[input_strings.index(f.name)] = '$M'
-
- build_utils.CallAndWriteDepfileIfStale(
- proguard.CheckOutput,
- options,
- input_paths=input_paths,
- input_strings=input_strings,
- output_paths=proguard.GetOutputs(),
- depfile_deps=proguard.GetDepfileDeps(),
- add_pydeps=False)
+ # TODO(agrieve): Remove proguard usages.
+ if options.r8_path:
+ cmd = _CreateR8Command(options)
+ build_utils.CheckOutput(cmd)
+ build_utils.WriteDepfile(options.depfile, options.output_path,
+ inputs=proguard.GetDepfileDeps(),
+ add_pydeps=False)
+ else:
+ # Do not consider the temp file as an input since its name is random.
+ input_paths = proguard.GetInputs()
+
+ with tempfile.NamedTemporaryFile() as f:
+ if options.mapping:
+ input_paths.append(options.mapping)
+ # Maintain only class name mappings in the .mapping file in order to
+ # work around what appears to be a ProGuard bug in -applymapping:
+ # method 'int close()' is not being kept as 'a', but remapped to 'c'
+ _RemoveMethodMappings(options.mapping, f)
+ proguard.mapping(f.name)
+
+ input_strings = proguard.build()
+ if f.name in input_strings:
+ input_strings[input_strings.index(f.name)] = '$M'
+
+ build_utils.CallAndWriteDepfileIfStale(
+ proguard.CheckOutput,
+ options,
+ input_paths=input_paths,
+ input_strings=input_strings,
+ output_paths=proguard.GetOutputs(),
+ depfile_deps=proguard.GetDepfileDeps(),
+ add_pydeps=False)
if __name__ == '__main__':
diff --git a/chromium/build/android/gyp/proguard.pydeps b/chromium/build/android/gyp/proguard.pydeps
new file mode 100644
index 00000000000..6db3d7d0f78
--- /dev/null
+++ b/chromium/build/android/gyp/proguard.pydeps
@@ -0,0 +1,8 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/proguard.pydeps build/android/gyp/proguard.py
+../../gn_helpers.py
+proguard.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+util/proguard_util.py
diff --git a/chromium/build/android/gyp/util/__init__.py b/chromium/build/android/gyp/util/__init__.py
index 727e987e6b6..96196cffb27 100644
--- a/chromium/build/android/gyp/util/__init__.py
+++ b/chromium/build/android/gyp/util/__init__.py
@@ -1,4 +1,3 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/gyp/util/proguard_util.py b/chromium/build/android/gyp/util/proguard_util.py
index fd657e2aa71..8a6e785c891 100644
--- a/chromium/build/android/gyp/util/proguard_util.py
+++ b/chromium/build/android/gyp/util/proguard_util.py
@@ -165,7 +165,7 @@ class ProguardCmdBuilder(object):
# Quite useful for auditing proguard flags.
for config in sorted(self._configs):
out.write('#' * 80 + '\n')
- out.write(config + '\n')
+ out.write('# ' + config + '\n')
out.write('#' * 80 + '\n')
with open(config) as config_file:
contents = config_file.read().rstrip()
@@ -175,9 +175,9 @@ class ProguardCmdBuilder(object):
out.write(contents)
out.write('\n\n')
out.write('#' * 80 + '\n')
- out.write('Command-line\n')
+ out.write('# Command-line\n')
out.write('#' * 80 + '\n')
- out.write(' '.join(cmd) + '\n')
+ out.write('# ' + ' '.join(cmd) + '\n')
def CheckOutput(self):
cmd = self.build()
diff --git a/chromium/build/android/gyp/write_build_config.py b/chromium/build/android/gyp/write_build_config.py
index 734921c3334..a2387da778c 100755
--- a/chromium/build/android/gyp/write_build_config.py
+++ b/chromium/build/android/gyp/write_build_config.py
@@ -419,15 +419,15 @@ The list of all `deps_info['java_sources_file']` entries for all library
dependencies for this APK. Note: this is a list of files, where each file
contains a list of Java source files. This is used for JNI registration.
-* `deps_info['proguard_all_configs"]`:
+* `deps_info['proguard_all_configs']`:
The collection of all 'deps_info['proguard_configs']` values from this target
and all its dependencies.
-* `deps_info['proguard_all_extra_jars"]`:
+* `deps_info['proguard_classpath_jars']`:
The collection of all 'deps_info['extra_classpath_jars']` values from all
dependencies.
-* `deps_info['proguard_under_test_mapping"]`:
+* `deps_info['proguard_under_test_mapping']`:
Applicable to apks with proguard enabled that have an apk_under_test. This is
the path to the apk_under_test's output proguard .mapping file.
@@ -495,6 +495,10 @@ The classpath listing the jars used for annotation processors. I.e. sent as
The list of annotation processor main classes. I.e. sent as `-processor' when
invoking `javac`.
+## <a name="android_app_bundle">Target type `android_app_bundle`</a>:
+
+This type corresponds to an Android app bundle (`.aab` file).
+
--------------- END_MARKDOWN ---------------------------------------------------
"""
@@ -508,8 +512,8 @@ import xml.dom.minidom
from util import build_utils
# Types that should never be used as a dependency of another build config.
-_ROOT_TYPES = ('android_apk', 'java_binary',
- 'java_annotation_processor', 'junit_binary', 'resource_rewriter')
+_ROOT_TYPES = ('android_apk', 'java_binary', 'java_annotation_processor',
+ 'junit_binary', 'resource_rewriter', 'android_app_bundle')
# Types that should not allow code deps to pass through.
_RESOURCE_TYPES = ('android_assets', 'android_resources', 'system_java_library')
@@ -825,15 +829,22 @@ def main(argv):
parser.add_option('--shared-libraries-runtime-deps',
help='Path to file containing runtime deps for shared '
'libraries.')
+ parser.add_option('--native-libs',
+ action='append',
+ help='GN-list of native libraries for primary '
+ 'android-abi. Can be specified multiple times.',
+ default=[])
parser.add_option('--secondary-abi-shared-libraries-runtime-deps',
help='Path to file containing runtime deps for secondary '
'abi shared libraries.')
+ parser.add_option('--secondary-native-libs',
+ action='append',
+ help='GN-list of native libraries for secondary '
+ 'android-abi. Can be specified multiple times.',
+ default=[])
parser.add_option('--uncompress-shared-libraries', default=False,
action='store_true',
help='Whether to store native libraries uncompressed')
- parser.add_option('--extra-shared-libraries',
- help='GN-list of paths to extra native libraries stored '
- 'in the APK.')
# apk options
parser.add_option('--apk-path', help='Path to the target\'s apk output.')
parser.add_option('--incremental-apk-path',
@@ -846,7 +857,7 @@ def main(argv):
help='Path to the build config of the tested apk (for an instrumentation '
'test apk).')
parser.add_option('--proguard-enabled', action='store_true',
- help='Whether proguard is enabled for this apk.')
+ help='Whether proguard is enabled for this apk or bundle module.')
parser.add_option('--proguard-configs',
help='GN-list of proguard flag files to use in final apk.')
parser.add_option('--proguard-output-jar-path',
@@ -883,6 +894,8 @@ def main(argv):
required_options_map = {
'android_apk': ['build_config', 'dex_path', 'final_dex_path'] + \
jar_path_options,
+ 'android_app_bundle_module': ['build_config', 'dex_path',
+ 'final_dex_path'] + jar_path_options,
'android_assets': ['build_config'],
'android_resources': ['build_config', 'resources_zip'],
'dist_aar': ['build_config'],
@@ -894,6 +907,7 @@ def main(argv):
'junit_binary': ['build_config'],
'resource_rewriter': ['build_config'],
'system_java_library': ['build_config'],
+ 'android_app_bundle': ['build_config'],
}
required_options = required_options_map.get(options.type)
if not required_options:
@@ -902,14 +916,18 @@ def main(argv):
build_utils.CheckOptions(options, parser, required_options)
if options.apk_proto_resources:
- if options.type != 'android_apk':
+ if options.type != 'android_app_bundle_module':
raise Exception('--apk-proto-resources can only be used with '
- '--type=android_apk')
+ '--type=android_app_bundle_module')
+
+ is_apk_or_module_target = options.type in ('android_apk',
+ 'android_app_bundle_module')
if options.uncompress_shared_libraries:
- if options.type != 'android_apk':
+ if not is_apk_or_module_target:
raise Exception('--uncompressed-shared-libraries can only be used '
- 'with --type=android_apk')
+ 'with --type=android_apk or '
+ '--type=android_app_bundle_module')
if options.jar_path and options.supports_android and not options.dex_path:
raise Exception('java_library that supports Android requires a dex path.')
@@ -925,7 +943,7 @@ def main(argv):
is_java_target = options.type in (
'java_binary', 'junit_binary', 'java_annotation_processor',
'java_library', 'android_apk', 'dist_aar', 'dist_jar',
- 'system_java_library')
+ 'system_java_library', 'android_app_bundle_module')
deps = _DepsFromPaths(
build_utils.ParseGnList(options.deps_configs), options.type)
@@ -996,7 +1014,10 @@ def main(argv):
else:
gradle['dependent_java_projects'].append(c['path'])
- if options.type == 'android_apk':
+ # TODO(tiborg): Remove creation of JNI info for type group and java_library
+ # once we can generate the JNI registration based on APK / module targets as
+ # opposed to groups and libraries.
+ if is_apk_or_module_target or options.type in ('group', 'java_library'):
config['jni'] = {}
all_java_sources = [c['java_sources_file'] for c in all_library_deps
if 'java_sources_file' in c]
@@ -1115,7 +1136,7 @@ def main(argv):
if options.type in (
'android_resources', 'android_apk', 'junit_binary', 'resource_rewriter',
- 'dist_aar'):
+ 'dist_aar', 'android_app_bundle_module'):
config['resources'] = {}
config['resources']['dependency_zips'] = [
c['resources_zip'] for c in all_resources_deps]
@@ -1130,7 +1151,7 @@ def main(argv):
config['resources']['extra_package_names'] = extra_package_names
config['resources']['extra_r_text_files'] = extra_r_text_files
- if options.type == 'android_apk':
+ if is_apk_or_module_target:
deps_dex_files = [c['dex_path'] for c in all_library_deps]
if is_java_target:
@@ -1149,48 +1170,95 @@ def main(argv):
# The classpath used for bytecode-rewritting.
javac_full_classpath = [
c['unprocessed_jar_path'] for c in all_library_deps]
- # The classpath to use to run this target (or as an input to ProGuard).
- java_full_classpath = []
- if options.jar_path:
- java_full_classpath.append(options.jar_path)
- java_full_classpath.extend(c['jar_path'] for c in all_library_deps)
# Deps to add to the compile-time classpath (but not the runtime classpath).
# TODO(agrieve): Might be less confusing to fold these into bootclasspath.
- extra_jars = [c['unprocessed_jar_path']
+ javac_extra_jars = [c['unprocessed_jar_path']
+ for c in classpath_deps.Direct('java_library')]
+ extra_jars = [c['jar_path']
for c in classpath_deps.Direct('java_library')]
if options.extra_classpath_jars:
# These are .jars to add to javac classpath but not to runtime classpath.
+ javac_extra_jars.extend(
+ build_utils.ParseGnList(options.extra_classpath_jars))
extra_jars.extend(build_utils.ParseGnList(options.extra_classpath_jars))
- extra_jars = [p for p in extra_jars if p not in javac_classpath]
- javac_classpath.extend(extra_jars)
- javac_interface_classpath.extend(extra_jars)
- javac_full_interface_classpath.extend(
- p for p in extra_jars if p not in javac_full_classpath)
- javac_full_classpath.extend(
- p for p in extra_jars if p not in javac_full_classpath)
if extra_jars:
deps_info['extra_classpath_jars'] = extra_jars
+ javac_extra_jars = [p for p in javac_extra_jars if p not in javac_classpath]
+ javac_classpath.extend(javac_extra_jars)
+ javac_interface_classpath.extend(javac_extra_jars)
+ javac_full_interface_classpath.extend(
+ p for p in javac_extra_jars if p not in javac_full_classpath)
+ javac_full_classpath.extend(
+ p for p in javac_extra_jars if p not in javac_full_classpath)
+
+ if is_java_target or options.type == 'android_app_bundle':
+ # The classpath to use to run this target (or as an input to ProGuard).
+ java_full_classpath = []
+ if is_java_target and options.jar_path:
+ java_full_classpath.append(options.jar_path)
+ java_full_classpath.extend(c['jar_path'] for c in all_library_deps)
+ if options.type == 'android_app_bundle':
+ for d in deps.Direct('android_app_bundle_module'):
+ java_full_classpath.extend(
+ c for c in d.get('java_runtime_classpath', [])
+ if c not in java_full_classpath)
+
+ system_jars = [c['jar_path'] for c in system_library_deps]
+ system_interface_jars = [c['interface_jar_path'] for c in system_library_deps]
+ if system_library_deps:
+ config['android'] = {}
+ config['android']['sdk_interface_jars'] = system_interface_jars
+ config['android']['sdk_jars'] = system_jars
+ gradle['bootclasspath'] = system_jars
+
if options.proguard_configs:
deps_info['proguard_configs'] = (
build_utils.ParseGnList(options.proguard_configs))
- if options.type in ('android_apk', 'dist_aar', 'dist_jar'):
+ if options.type in ('android_apk', 'dist_aar',
+ 'dist_jar', 'android_app_bundle_module', 'android_app_bundle'):
all_configs = deps_info.get('proguard_configs', [])
- extra_jars = []
+ extra_jars = list()
for c in all_library_deps:
all_configs.extend(
p for p in c.get('proguard_configs', []) if p not in all_configs)
extra_jars.extend(
p for p in c.get('extra_classpath_jars', []) if p not in extra_jars)
+ if options.type == 'android_app_bundle':
+ for c in deps.Direct('android_app_bundle_module'):
+ all_configs.extend(
+ p for p in c.get('proguard_configs', []) if p not in all_configs)
deps_info['proguard_all_configs'] = all_configs
- deps_info['proguard_all_extra_jars'] = extra_jars
- deps_info['proguard_enabled'] = options.proguard_enabled
- if options.proguard_output_jar_path:
- deps_info['proguard_output_jar_path'] = options.proguard_output_jar_path
+ if options.type == 'android_app_bundle':
+ for d in deps.Direct('android_app_bundle_module'):
+ extra_jars.extend(
+ c for c in d.get('proguard_classpath_jars', [])
+ if c not in extra_jars)
+ deps_info['proguard_classpath_jars'] = extra_jars
+
+ if options.type == 'android_app_bundle':
+ deps_proguard_enabled = []
+ deps_proguard_disabled = []
+ for d in deps.Direct('android_app_bundle_module'):
+ if not d['java_runtime_classpath']:
+ # We don't care about modules that have no Java code for proguarding.
+ continue
+ if d['proguard_enabled']:
+ deps_proguard_enabled.append(d['name'])
+ else:
+ deps_proguard_disabled.append(d['name'])
+ if deps_proguard_enabled and deps_proguard_disabled:
+ raise Exception('Deps %s have proguard enabled while deps %s have '
+ 'proguard disabled' % (deps_proguard_enabled,
+ deps_proguard_disabled))
+ else:
+ deps_info['proguard_enabled'] = bool(options.proguard_enabled)
+ if options.proguard_output_jar_path:
+ deps_info['proguard_output_jar_path'] = options.proguard_output_jar_path
# The java code for an instrumentation test apk is assembled differently for
# ProGuard vs. non-ProGuard.
@@ -1215,7 +1283,7 @@ def main(argv):
# Mutating lists, so no need to explicitly re-assign to dict.
all_configs.extend(p for p in tested_apk_config['proguard_all_configs']
if p not in all_configs)
- extra_jars.extend(p for p in tested_apk_config['proguard_all_extra_jars']
+ extra_jars.extend(p for p in tested_apk_config['proguard_classpath_jars']
if p not in extra_jars)
tested_apk_config = GetDepConfig(options.tested_apk_config)
deps_info['proguard_under_test_mapping'] = (
@@ -1258,20 +1326,12 @@ def main(argv):
p for p in deps_dex_files if not p in tested_apk_deps_dex_files]
# Dependencies for the final dex file of an apk.
- if options.type == 'android_apk':
+ if is_apk_or_module_target:
config['final_dex'] = {}
dex_config = config['final_dex']
dex_config['dependency_dex_files'] = deps_dex_files
dex_config['path'] = options.final_dex_path
- system_jars = [c['jar_path'] for c in system_library_deps]
- system_interface_jars = [c['interface_jar_path'] for c in system_library_deps]
- if system_library_deps:
- config['android'] = {}
- config['android']['sdk_interface_jars'] = system_interface_jars
- config['android']['sdk_jars'] = system_jars
- gradle['bootclasspath'] = system_jars
-
if is_java_target:
config['javac']['bootclasspath'] = system_jars
config['javac']['classpath'] = javac_classpath
@@ -1285,9 +1345,9 @@ def main(argv):
deps_info['javac_full_classpath'] = javac_full_classpath
deps_info['javac_full_interface_classpath'] = javac_full_interface_classpath
- if options.type in (
- 'android_apk', 'dist_jar', 'java_binary', 'junit_binary'):
- deps_info['java_runtime_classpath'] = java_full_classpath
+ if options.type in ('android_apk', 'dist_jar', 'java_binary', 'junit_binary',
+ 'android_app_bundle_module', 'android_app_bundle'):
+ deps_info['java_runtime_classpath'] = java_full_classpath
if options.type in ('android_apk', 'dist_jar'):
all_interface_jars = []
@@ -1299,7 +1359,7 @@ def main(argv):
'all_interface_jars': all_interface_jars,
}
- if options.type == 'android_apk':
+ if is_apk_or_module_target:
manifest = AndroidManifest(options.android_manifest)
deps_info['package_name'] = manifest.GetPackageName()
if not options.tested_apk_config and manifest.GetInstrumentationElements():
@@ -1323,9 +1383,12 @@ def main(argv):
secondary_abi_runtime_deps_files)
secondary_abi_java_libraries_list = _CreateJavaLibrariesList(
secondary_abi_library_paths)
+ for gn_list in options.secondary_native_libs:
+ secondary_abi_library_paths.extend(build_utils.ParseGnList(gn_list))
- extra_shared_libraries = build_utils.ParseGnList(
- options.extra_shared_libraries)
+ extra_shared_libraries = []
+ for gn_list in options.native_libs:
+ extra_shared_libraries.extend(build_utils.ParseGnList(gn_list))
all_inputs.extend(runtime_deps_files)
config['native'] = {
diff --git a/chromium/build/android/gyp/write_build_config.pydeps b/chromium/build/android/gyp/write_build_config.pydeps
new file mode 100644
index 00000000000..e317c47cae4
--- /dev/null
+++ b/chromium/build/android/gyp/write_build_config.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/write_build_config.pydeps build/android/gyp/write_build_config.py
+../../gn_helpers.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+write_build_config.py
diff --git a/chromium/build/android/gyp/write_ordered_libraries.py b/chromium/build/android/gyp/write_ordered_libraries.py
index e3d87c16bb0..92c82bb4c8c 100755
--- a/chromium/build/android/gyp/write_ordered_libraries.py
+++ b/chromium/build/android/gyp/write_ordered_libraries.py
@@ -109,10 +109,9 @@ def main():
only_if_changed=True)
if options.depfile:
- build_utils.WriteDepfile(options.depfile, options.output, libraries)
+ build_utils.WriteDepfile(options.depfile, options.output, libraries,
+ add_pydeps=False)
if __name__ == '__main__':
sys.exit(main())
-
-
diff --git a/chromium/build/android/gyp/write_ordered_libraries.pydeps b/chromium/build/android/gyp/write_ordered_libraries.pydeps
new file mode 100644
index 00000000000..c2ed1fee36e
--- /dev/null
+++ b/chromium/build/android/gyp/write_ordered_libraries.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/write_ordered_libraries.pydeps build/android/gyp/write_ordered_libraries.py
+../../gn_helpers.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
+write_ordered_libraries.py
diff --git a/chromium/build/android/incremental_install/__init__.py b/chromium/build/android/incremental_install/__init__.py
index 1aaf0e179ff..50b23dff631 100644
--- a/chromium/build/android/incremental_install/__init__.py
+++ b/chromium/build/android/incremental_install/__init__.py
@@ -1,4 +1,3 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/incremental_install/generate_android_manifest.pydeps b/chromium/build/android/incremental_install/generate_android_manifest.pydeps
new file mode 100644
index 00000000000..de92fafa9bb
--- /dev/null
+++ b/chromium/build/android/incremental_install/generate_android_manifest.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/incremental_install --output build/android/incremental_install/generate_android_manifest.pydeps build/android/incremental_install/generate_android_manifest.py
+../../gn_helpers.py
+../gyp/util/__init__.py
+../gyp/util/build_utils.py
+../gyp/util/md5_check.py
+generate_android_manifest.py
diff --git a/chromium/build/android/incremental_install/write_installer_json.py b/chromium/build/android/incremental_install/write_installer_json.py
index b7e4e773adb..75bd6d1aab7 100755
--- a/chromium/build/android/incremental_install/write_installer_json.py
+++ b/chromium/build/android/incremental_install/write_installer_json.py
@@ -19,7 +19,6 @@ from util import build_utils
def _ParseArgs(args):
args = build_utils.ExpandFileArgs(args)
parser = argparse.ArgumentParser()
- build_utils.AddDepfileOption(parser)
parser.add_argument('--output-path',
help='Output path for .json file.',
required=True)
@@ -76,9 +75,6 @@ def main(args):
with build_utils.AtomicOutput(options.output_path) as f:
json.dump(data, f, indent=2, sort_keys=True)
- if options.depfile:
- build_utils.WriteDepfile(options.depfile, options.output_path)
-
if __name__ == '__main__':
main(sys.argv[1:])
diff --git a/chromium/build/android/incremental_install/write_installer_json.pydeps b/chromium/build/android/incremental_install/write_installer_json.pydeps
new file mode 100644
index 00000000000..851e6c5bd1b
--- /dev/null
+++ b/chromium/build/android/incremental_install/write_installer_json.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+# build/print_python_deps.py --root build/android/incremental_install --output build/android/incremental_install/write_installer_json.pydeps build/android/incremental_install/write_installer_json.py
+../../gn_helpers.py
+../gyp/util/__init__.py
+../gyp/util/build_utils.py
+../gyp/util/md5_check.py
+write_installer_json.py
diff --git a/chromium/build/android/lighttpd_server.py b/chromium/build/android/lighttpd_server.py
index 89a828ab54a..c77d740d66a 100755
--- a/chromium/build/android/lighttpd_server.py
+++ b/chromium/build/android/lighttpd_server.py
@@ -104,7 +104,7 @@ class LighttpdServer(object):
break
self.process.close()
- if self.fixed_port or not 'in use' in server_error:
+ if self.fixed_port or 'in use' not in server_error:
print 'Client error:', client_error
print 'Server error:', server_error
return False
diff --git a/chromium/build/android/lint/suppressions.xml b/chromium/build/android/lint/suppressions.xml
index 6e5562a5a70..c4b132f5ea3 100644
--- a/chromium/build/android/lint/suppressions.xml
+++ b/chromium/build/android/lint/suppressions.xml
@@ -333,10 +333,6 @@ Still reading?
<!-- 2 resource sets used by clank widgets for each channel -->
<ignore regexp="The resource `R.string.bookmark_widget_title.*` appears to be unused"/>
<ignore regexp="The resource `R.string.search_widget_title.*` appears to be unused"/>
- <!-- 3 Download location strings checked in before M68 deadline -->
- <!-- TODO(xingliu): Remove these. see crbug.com/840464. -->
- <ignore regexp="The resource `R.string.download_location_not_enough_space_sd_card_full` appears to be unused"/>
- <ignore regexp="The resource `R.string.download_location_notification_retry*` appears to be unused"/>
<!-- Endnote: Please specify number of suppressions when adding more -->
</issue>
<issue id="UseCompoundDrawables">
diff --git a/chromium/build/android/method_count.py b/chromium/build/android/method_count.py
index 68823e15482..490887adbca 100755
--- a/chromium/build/android/method_count.py
+++ b/chromium/build/android/method_count.py
@@ -114,4 +114,3 @@ def main():
if __name__ == '__main__':
sys.exit(main())
-
diff --git a/chromium/build/android/pylib/android/__init__.py b/chromium/build/android/pylib/android/__init__.py
index 7a90b532c39..a67c3501b20 100644
--- a/chromium/build/android/pylib/android/__init__.py
+++ b/chromium/build/android/pylib/android/__init__.py
@@ -1,4 +1,3 @@
# Copyright (c) 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/pylib/base/__init__.py b/chromium/build/android/pylib/base/__init__.py
index 727e987e6b6..96196cffb27 100644
--- a/chromium/build/android/pylib/base/__init__.py
+++ b/chromium/build/android/pylib/base/__init__.py
@@ -1,4 +1,3 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/pylib/base/base_test_result.py b/chromium/build/android/pylib/base/base_test_result.py
index af9f68f8e53..a5535322965 100644
--- a/chromium/build/android/pylib/base/base_test_result.py
+++ b/chromium/build/android/pylib/base/base_test_result.py
@@ -260,4 +260,3 @@ class TestRunResults(object):
def DidRunPass(self):
"""Return whether the test run was successful."""
return not self.GetNotPass() - self.GetSkip()
-
diff --git a/chromium/build/android/pylib/base/test_exception.py b/chromium/build/android/pylib/base/test_exception.py
index f00f0d0ee85..c98d2cb73eb 100644
--- a/chromium/build/android/pylib/base/test_exception.py
+++ b/chromium/build/android/pylib/base/test_exception.py
@@ -6,4 +6,3 @@
class TestException(Exception):
"""Base class for exceptions thrown by the test runner."""
pass
-
diff --git a/chromium/build/android/pylib/base/test_instance.py b/chromium/build/android/pylib/base/test_instance.py
index cdf678f2d28..f0ff83019bc 100644
--- a/chromium/build/android/pylib/base/test_instance.py
+++ b/chromium/build/android/pylib/base/test_instance.py
@@ -32,4 +32,3 @@ class TestInstance(object):
def __exit__(self, _exc_type, _exc_val, _exc_tb):
self.TearDown()
-
diff --git a/chromium/build/android/pylib/base/test_run.py b/chromium/build/android/pylib/base/test_run.py
index 9b16f890f69..59e595d926b 100644
--- a/chromium/build/android/pylib/base/test_run.py
+++ b/chromium/build/android/pylib/base/test_run.py
@@ -41,4 +41,3 @@ class TestRun(object):
def __exit__(self, exc_type, exc_val, exc_tb):
self.TearDown()
-
diff --git a/chromium/build/android/pylib/base/test_run_factory.py b/chromium/build/android/pylib/base/test_run_factory.py
index 1a2872819d9..1f63a059c98 100644
--- a/chromium/build/android/pylib/base/test_run_factory.py
+++ b/chromium/build/android/pylib/base/test_run_factory.py
@@ -54,4 +54,3 @@ def CreateTestRun(args, env, test_instance, error_func):
error_func('Unable to create test run for %s tests in %s environment'
% (str(test_instance), str(env)))
-
diff --git a/chromium/build/android/pylib/base/test_server.py b/chromium/build/android/pylib/base/test_server.py
index 085a51e9bae..763e1212c36 100644
--- a/chromium/build/android/pylib/base/test_server.py
+++ b/chromium/build/android/pylib/base/test_server.py
@@ -16,4 +16,3 @@ class TestServer(object):
def TearDown(self):
raise NotImplementedError
-
diff --git a/chromium/build/android/pylib/constants/__init__.py b/chromium/build/android/pylib/constants/__init__.py
index 2101b9c1ca1..cf6ca113390 100644
--- a/chromium/build/android/pylib/constants/__init__.py
+++ b/chromium/build/android/pylib/constants/__init__.py
@@ -104,8 +104,6 @@ ANDROID_SDK_TOOLS = os.path.join(ANDROID_SDK_ROOT,
ANDROID_NDK_ROOT = os.path.join(DIR_SOURCE_ROOT,
'third_party', 'android_ndk')
-PROGUARD_ROOT = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'proguard')
-
BAD_DEVICES_JSON = os.path.join(DIR_SOURCE_ROOT,
os.environ.get('CHROMIUM_OUT_DIR', 'out'),
'bad_devices.json')
diff --git a/chromium/build/android/pylib/gtest/__init__.py b/chromium/build/android/pylib/gtest/__init__.py
index 727e987e6b6..96196cffb27 100644
--- a/chromium/build/android/pylib/gtest/__init__.py
+++ b/chromium/build/android/pylib/gtest/__init__.py
@@ -1,4 +1,3 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/pylib/gtest/gtest_test_instance.py b/chromium/build/android/pylib/gtest/gtest_test_instance.py
index d93edc65070..cd8ddbc3921 100644
--- a/chromium/build/android/pylib/gtest/gtest_test_instance.py
+++ b/chromium/build/android/pylib/gtest/gtest_test_instance.py
@@ -533,7 +533,9 @@ class GtestTestInstance(test_instance.TestInstance):
disabled_filter_items = []
if disabled_prefixes is None:
- disabled_prefixes = ['FAILS_', 'PRE_', 'MANUAL_']
+ disabled_prefixes = ['FAILS_', 'PRE_']
+ if '--run-manual' not in self._flags:
+ disabled_prefixes += ['MANUAL_']
if not self._run_disabled:
disabled_prefixes += ['DISABLED_', 'FLAKY_']
@@ -555,4 +557,3 @@ class GtestTestInstance(test_instance.TestInstance):
def TearDown(self):
"""Do nothing."""
pass
-
diff --git a/chromium/build/android/pylib/gtest/gtest_test_instance_test.py b/chromium/build/android/pylib/gtest/gtest_test_instance_test.py
index 3466924a0d9..a34ab84c7dd 100755
--- a/chromium/build/android/pylib/gtest/gtest_test_instance_test.py
+++ b/chromium/build/android/pylib/gtest/gtest_test_instance_test.py
@@ -260,4 +260,3 @@ class GtestTestInstanceTests(unittest.TestCase):
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/chromium/build/android/pylib/instrumentation/__init__.py b/chromium/build/android/pylib/instrumentation/__init__.py
index 727e987e6b6..96196cffb27 100644
--- a/chromium/build/android/pylib/instrumentation/__init__.py
+++ b/chromium/build/android/pylib/instrumentation/__init__.py
@@ -1,4 +1,3 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py b/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py
index 452b259849c..809fce60105 100644
--- a/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -568,15 +568,15 @@ class InstrumentationTestInstance(test_instance.TestInstance):
self._test_package = self._test_apk.GetPackageName()
all_instrumentations = self._test_apk.GetAllInstrumentations()
all_junit3_runner_classes = [
- x for x in all_instrumentations if ('0xffffffff' not in x.get(
- 'chromium-junit4', ''))]
- all_junit4_test_runner_classes = [
x for x in all_instrumentations if ('0xffffffff' in x.get(
- 'chromium-junit4', ''))]
+ 'chromium-junit3', ''))]
+ all_junit4_runner_classes = [
+ x for x in all_instrumentations if ('0xffffffff' not in x.get(
+ 'chromium-junit3', ''))]
if len(all_junit3_runner_classes) > 1:
logging.warning('This test apk has more than one JUnit3 instrumentation')
- if len(all_junit4_test_runner_classes) > 1:
+ if len(all_junit4_runner_classes) > 1:
logging.warning('This test apk has more than one JUnit4 instrumentation')
self._junit3_runner_class = (
@@ -584,8 +584,8 @@ class InstrumentationTestInstance(test_instance.TestInstance):
if all_junit3_runner_classes else self.test_apk.GetInstrumentationName())
self._junit4_runner_class = (
- all_junit4_test_runner_classes[0]['android:name']
- if all_junit4_test_runner_classes else None)
+ all_junit4_runner_classes[0]['android:name']
+ if all_junit4_runner_classes else None)
if self._junit4_runner_class:
if self._test_apk_incremental_install_json:
diff --git a/chromium/build/android/pylib/junit/__init__.py b/chromium/build/android/pylib/junit/__init__.py
index 5cac026153c..4d6aabb953d 100644
--- a/chromium/build/android/pylib/junit/__init__.py
+++ b/chromium/build/android/pylib/junit/__init__.py
@@ -1,4 +1,3 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/pylib/linker/__init__.py b/chromium/build/android/pylib/linker/__init__.py
index af994371233..9228df89b0e 100644
--- a/chromium/build/android/pylib/linker/__init__.py
+++ b/chromium/build/android/pylib/linker/__init__.py
@@ -1,4 +1,3 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
diff --git a/chromium/build/android/pylib/linker/linker_test_instance.py b/chromium/build/android/pylib/linker/linker_test_instance.py
index 1de3e17f1df..6ace7a3633c 100644
--- a/chromium/build/android/pylib/linker/linker_test_instance.py
+++ b/chromium/build/android/pylib/linker/linker_test_instance.py
@@ -48,4 +48,3 @@ class LinkerTestInstance(test_instance.TestInstance):
def TestType(self):
return 'linker'
-
diff --git a/chromium/build/android/pylib/local/device/local_device_gtest_run.py b/chromium/build/android/pylib/local/device/local_device_gtest_run.py
index 21e9fe85121..47348834c99 100644
--- a/chromium/build/android/pylib/local/device/local_device_gtest_run.py
+++ b/chromium/build/android/pylib/local/device/local_device_gtest_run.py
@@ -152,6 +152,7 @@ class _ApkDelegate(object):
extras[gtest_test_instance.EXTRA_SHARD_NANO_TIMEOUT] = int(
kwargs['timeout'] * _SECONDS_TO_NANOS)
+ # pylint: disable=redefined-variable-type
command_line_file = _NullContextManager()
if flags:
if len(flags) > _MAX_INLINE_FLAGS_LENGTH:
@@ -169,6 +170,7 @@ class _ApkDelegate(object):
extras[_EXTRA_TEST_LIST] = test_list_file.name
else:
extras[_EXTRA_TEST] = test[0]
+ # pylint: enable=redefined-variable-type
stdout_file = device_temp_file.DeviceTempFile(
device.adb, dir=device.GetExternalStoragePath(), suffix='.gtest_out')
@@ -283,11 +285,13 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
assert isinstance(test_instance, gtest_test_instance.GtestTestInstance)
super(LocalDeviceGtestRun, self).__init__(env, test_instance)
+ # pylint: disable=redefined-variable-type
if self._test_instance.apk:
self._delegate = _ApkDelegate(self._test_instance, env.tool)
elif self._test_instance.exe_dist_dir:
self._delegate = _ExeDelegate(self, self._test_instance.exe_dist_dir,
self._env.tool)
+ # pylint: enable=redefined-variable-type
self._crashes = set()
self._servers = collections.defaultdict(list)
diff --git a/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 40b857d5fbf..de7ed933b5d 100644
--- a/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -153,7 +153,11 @@ class LocalDeviceInstrumentationTestRun(
self._replace_package_contextmanager = system_app.ReplaceSystemApp(
dev, self._test_instance.replace_system_package.package,
self._test_instance.replace_system_package.replacement_apk)
+ # Pylint is not smart enough to realize that this field has
+ # an __enter__ method, and will complain loudly.
+ # pylint: disable=no-member
self._replace_package_contextmanager.__enter__()
+ # pylint: enable=no-member
steps.append(replace_package)
@@ -308,12 +312,15 @@ class LocalDeviceInstrumentationTestRun(
valgrind_tools.SetChromeTimeoutScale(dev, None)
if self._replace_package_contextmanager:
+ # See pylint-related commend above with __enter__()
+ # pylint: disable=no-member
self._replace_package_contextmanager.__exit__(*sys.exc_info())
+ # pylint: enable=no-member
self._env.parallel_devices.pMap(individual_device_tear_down)
def _CreateFlagChangerIfNeeded(self, device):
- if not str(device) in self._flag_changers:
+ if str(device) not in self._flag_changers:
self._flag_changers[str(device)] = flag_changer.FlagChanger(
device, "test-cmdline-file")
diff --git a/chromium/build/android/pylib/local/device/local_device_test_run.py b/chromium/build/android/pylib/local/device/local_device_test_run.py
index 90e6b674cb0..00d715b3ade 100644
--- a/chromium/build/android/pylib/local/device/local_device_test_run.py
+++ b/chromium/build/android/pylib/local/device/local_device_test_run.py
@@ -220,7 +220,7 @@ class LocalDeviceTestRun(test_run.TestRun):
if hash(self._GetUniqueTestName(t)) % total_shards == shard_index]
def GetTool(self, device):
- if not str(device) in self._tools:
+ if str(device) not in self._tools:
self._tools[str(device)] = valgrind_tools.CreateTool(
self._env.tool, device)
return self._tools[str(device)]
diff --git a/chromium/build/android/pylib/local/local_test_server_spawner.py b/chromium/build/android/pylib/local/local_test_server_spawner.py
index 8e416fac5d0..6cd282e3a45 100644
--- a/chromium/build/android/pylib/local/local_test_server_spawner.py
+++ b/chromium/build/android/pylib/local/local_test_server_spawner.py
@@ -98,4 +98,3 @@ class LocalTestServerSpawner(test_server.TestServer):
self.Reset()
self._spawning_server.Stop()
forwarder.Forwarder.UnmapDevicePort(self.port, self._device)
-
diff --git a/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py b/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py
index 9693977e6bf..5e5f83f2a27 100644
--- a/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py
+++ b/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py
@@ -380,6 +380,7 @@ class JSONResultsGeneratorBase(object):
urllib2.quote(self._test_type),
urllib2.quote(self._master_name)))
+ # pylint: disable=redefined-variable-type
try:
# FIXME: We should talk to the network via a Host object.
results_file = urllib2.urlopen(results_file_url)
@@ -391,6 +392,7 @@ class JSONResultsGeneratorBase(object):
error = http_error
except urllib2.URLError, url_error:
error = url_error
+ # pylint: enable=redefined-variable-type
if old_results:
# Strip the prefix and suffix so we can get the actual JSON object.
diff --git a/chromium/build/android/pylib/results/json_results.py b/chromium/build/android/pylib/results/json_results.py
index 1243691b7f8..3f87b46d8c5 100644
--- a/chromium/build/android/pylib/results/json_results.py
+++ b/chromium/build/android/pylib/results/json_results.py
@@ -175,4 +175,3 @@ def ParseResultsFromJson(json_results):
duration=tr['elapsed_time_ms'])
for tr in test_runs])
return results_list
-
diff --git a/chromium/build/android/pylib/results/json_results_test.py b/chromium/build/android/pylib/results/json_results_test.py
index 276171353f4..e8b983b56ed 100755
--- a/chromium/build/android/pylib/results/json_results_test.py
+++ b/chromium/build/android/pylib/results/json_results_test.py
@@ -178,4 +178,3 @@ class JsonResultsTest(unittest.TestCase):
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/chromium/build/android/pylib/results/presentation/test_results_presentation.py b/chromium/build/android/pylib/results/presentation/test_results_presentation.py
index eaae68f85b7..82d6c884700 100755
--- a/chromium/build/android/pylib/results/presentation/test_results_presentation.py
+++ b/chromium/build/android/pylib/results/presentation/test_results_presentation.py
@@ -268,6 +268,7 @@ def create_suite_table(results_dict):
def feedback_url(result_details_link):
+ # pylint: disable=redefined-variable-type
url_args = [
('labels', 'Pri-2,Type-Bug,Restrict-View-Google'),
('summary', 'Result Details Feedback:'),
@@ -276,6 +277,7 @@ def feedback_url(result_details_link):
if result_details_link:
url_args.append(('comment', 'Please check out: %s' % result_details_link))
url_args = urllib.urlencode(url_args)
+ # pylint: enable=redefined-variable-type
return 'https://bugs.chromium.org/p/chromium/issues/entry?%s' % url_args
@@ -370,6 +372,7 @@ def ui_screenshot_set(json_path):
# This will be reported as an error by result_details, no need to duplicate.
return None
ui_screenshots = []
+ # pylint: disable=too-many-nested-blocks
for testsuite_run in json_object['per_iteration_data']:
for _, test_runs in testsuite_run.iteritems():
for test_run in test_runs:
@@ -388,6 +391,7 @@ def ui_screenshot_set(json_path):
test_screenshots = json.loads(
screenshot_string)
ui_screenshots.extend(test_screenshots)
+ # pylint: enable=too-many-nested-blocks
if ui_screenshots:
return json.dumps(ui_screenshots)
diff --git a/chromium/build/android/pylib/symbols/deobfuscator.py b/chromium/build/android/pylib/symbols/deobfuscator.py
index 9d4f5fdfb4b..ac4ff7e4b4b 100644
--- a/chromium/build/android/pylib/symbols/deobfuscator.py
+++ b/chromium/build/android/pylib/symbols/deobfuscator.py
@@ -25,6 +25,8 @@ class Deobfuscator(object):
cmd = [script_path, mapping_path]
# Allow only one thread to call TransformLines() at a time.
self._lock = threading.Lock()
+ # Ensure that only one thread attempts to kill self._proc in Close().
+ self._close_lock = threading.Lock()
self._closed_called = False
# Assign to None so that attribute exists if Popen() throws.
self._proc = None
@@ -114,8 +116,11 @@ class Deobfuscator(object):
return lines
def Close(self):
- self._closed_called = True
- if not self.IsClosed():
+ with self._close_lock:
+ needs_closing = not self.IsClosed()
+ self._closed_called = True
+
+ if needs_closing:
self._proc.stdin.close()
self._proc.kill()
self._proc.wait()
diff --git a/chromium/build/android/pylib/symbols/symbol_utils.py b/chromium/build/android/pylib/symbols/symbol_utils.py
new file mode 100644
index 00000000000..e4e3faac803
--- /dev/null
+++ b/chromium/build/android/pylib/symbols/symbol_utils.py
@@ -0,0 +1,812 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import bisect
+import collections
+import logging
+import os
+import re
+
+from pylib.constants import host_paths
+from pylib.symbols import elf_symbolizer
+
+
+def _AndroidAbiToCpuArch(android_abi):
+ """Return the Chromium CPU architecture name for a given Android ABI."""
+ _ARCH_MAP = {
+ 'armeabi': 'arm',
+ 'armeabi-v7a': 'arm',
+ 'arm64-v8a': 'arm64',
+ 'x86_64': 'x64',
+ }
+ return _ARCH_MAP.get(android_abi, android_abi)
+
+
+def _HexAddressRegexpFor(android_abi):
+ """Return a regexp matching hexadecimal addresses for a given Android ABI."""
+ if android_abi in ['x86_64', 'arm64-v8a', 'mips64']:
+ width = 16
+ else:
+ width = 8
+ return '[0-9a-f]{%d}' % width
+
+
+class HostLibraryFinder(object):
+ """Translate device library path to matching host unstripped library path.
+
+ Usage is the following:
+ 1) Create instance.
+ 2) Call AddSearchDir() once or more times to add host directory path to
+ look for unstripped native libraries.
+ 3) Call Find(device_libpath) repeatedly to translate a device-specific
+ library path into the corresponding host path to the unstripped
+ version.
+ """
+ def __init__(self):
+ """Initialize instance."""
+ self._search_dirs = []
+ self._lib_map = {} # Map of library name to host file paths.
+
+ def AddSearchDir(self, lib_dir):
+ """Add a directory to the search path for host native shared libraries.
+
+ Args:
+ lib_dir: host path containing native libraries.
+ """
+ if not os.path.exists(lib_dir):
+ logging.warning('Ignoring missing host library directory: %s', lib_dir)
+ return
+ if not os.path.isdir(lib_dir):
+ logging.warning('Ignoring invalid host library directory: %s', lib_dir)
+ return
+ self._search_dirs.append(lib_dir)
+ self._lib_map = {} # Reset the map.
+
+ def Find(self, device_libpath):
+ """Find the host file path matching a specific device library path.
+
+ Args:
+ device_libpath: device-specific file path to library or executable.
+ Returns:
+ host file path to the unstripped version of the library, or None.
+ """
+ host_lib_path = None
+ lib_name = os.path.basename(device_libpath)
+ host_lib_path = self._lib_map.get(lib_name)
+ if not host_lib_path:
+ for search_dir in self._search_dirs:
+ lib_path = os.path.join(search_dir, lib_name)
+ if os.path.exists(lib_path):
+ host_lib_path = lib_path
+ break
+
+ if not host_lib_path:
+ logging.debug('Could not find host library for: %s', lib_name)
+ self._lib_map[lib_name] = host_lib_path
+
+ return host_lib_path
+
+
+
+class SymbolResolver(object):
+ """A base class for objets that can symbolize library (path, offset)
+ pairs into symbol information strings. Usage is the following:
+
+ 1) Create new instance (by calling the constructor of a derived
+ class, since this is only the base one).
+
+ 2) Call SetAndroidAbi() before any call to FindSymbolInfo() in order
+ to set the Android CPU ABI used for symbolization.
+
+ 3) Before the first call to FindSymbolInfo(), one can call
+ AddLibraryOffset(), or AddLibraryOffsets() to record a set of offsets
+ that you will want to symbolize later through FindSymbolInfo(). Doing
+ so allows some SymbolResolver derived classes to work faster (e.g. the
+ one that invokes the 'addr2line' program, since the latter works faster
+ if the offsets provided as inputs are sorted in increasing order).
+
+ 3) Call FindSymbolInfo(path, offset) to return the corresponding
+ symbol information string, or None if this doesn't correspond
+ to anything the instance can handle.
+
+ Note that whether the path is specific to the device or to the
+ host depends on the derived class implementation.
+ """
+ def __init__(self):
+ self._android_abi = None
+ self._lib_offsets_map = collections.defaultdict(set)
+
+ def SetAndroidAbi(self, android_abi):
+ """Set the Android ABI value for this instance.
+
+ Calling this function before FindSymbolInfo() is required by some
+ derived class implementations.
+
+ Args:
+ android_abi: Native Android CPU ABI name (e.g. 'armeabi-v7a').
+ Raises:
+ Exception if the ABI was already set with a different value.
+ """
+ if self._android_abi and self._android_abi != android_abi:
+ raise Exception('Cannot reset Android ABI to new value %s, already set '
+ 'to %s' % (android_abi, self._android_abi))
+
+ self._android_abi = android_abi
+
+ def AddLibraryOffset(self, lib_path, offset):
+ """Associate a single offset to a given device library.
+
+ This must be called before FindSymbolInfo(), otherwise its input arguments
+ will be ignored.
+
+ Args:
+ lib_path: A library path.
+ offset: An integer offset within the corresponding library that will be
+ symbolized by future calls to FindSymbolInfo.
+ """
+ self._lib_offsets_map[lib_path].add(offset)
+
+ def AddLibraryOffsets(self, lib_path, lib_offsets):
+ """Associate a set of wanted offsets to a given device library.
+
+ This must be called before FindSymbolInfo(), otherwise its input arguments
+ will be ignored.
+
+ Args:
+ lib_path: A library path.
+ lib_offsets: An iterable of integer offsets within the corresponding
+ library that will be symbolized by future calls to FindSymbolInfo.
+ """
+ self._lib_offsets_map[lib_path].update(lib_offsets)
+
+ # pylint: disable=unused-argument,no-self-use
+ def FindSymbolInfo(self, lib_path, lib_offset):
+ """Symbolize a device library path and offset.
+
+ Args:
+ lib_path: Library path (device or host specific, depending on the
+ derived class implementation).
+ lib_offset: Integer offset within the library.
+ Returns:
+ Corresponding symbol information string, or None.
+ """
+ # The base implementation cannot symbolize anything.
+ return None
+ # pylint: enable=unused-argument,no-self-use
+
+
+class ElfSymbolResolver(SymbolResolver):
+ """A SymbolResolver that can symbolize host path + offset values using
+ an elf_symbolizer.ELFSymbolizer instance.
+ """
+ def __init__(self, addr2line_path_for_tests=None):
+ super(ElfSymbolResolver, self).__init__()
+ self._addr2line_path = addr2line_path_for_tests
+
+ # Used to cache one ELFSymbolizer instance per library path.
+ self._elf_symbolizer_cache = {}
+
+ # Used to cache FindSymbolInfo() results. Maps host library paths
+ # to (offset -> symbol info string) dictionaries.
+ self._symbol_info_cache = collections.defaultdict(dict)
+ self._allow_symbolizer = True
+
+ def _CreateSymbolizerFor(self, host_path):
+ """Create the ELFSymbolizer instance associated with a given lib path."""
+ addr2line_path = self._addr2line_path
+ if not addr2line_path:
+ if not self._android_abi:
+ raise Exception(
+ 'Android CPU ABI must be set before calling FindSymbolInfo!')
+
+ cpu_arch = _AndroidAbiToCpuArch(self._android_abi)
+ self._addr2line_path = host_paths.ToolPath('addr2line', cpu_arch)
+
+ return elf_symbolizer.ELFSymbolizer(
+ elf_file_path=host_path, addr2line_path=self._addr2line_path,
+ callback=ElfSymbolResolver._Callback, inlines=True)
+
+ def DisallowSymbolizerForTesting(self):
+ """Disallow FindSymbolInfo() from using a symbolizer.
+
+ This is used during unit-testing to ensure that the offsets that were
+ recorded via AddLibraryOffset()/AddLibraryOffsets() are properly
+ symbolized, but not anything else.
+ """
+ self._allow_symbolizer = False
+
+ def FindSymbolInfo(self, host_path, offset):
+ """Override SymbolResolver.FindSymbolInfo.
+
+ Args:
+ host_path: Host-specific path to the native shared library.
+ offset: Integer offset within the native library.
+ Returns:
+ A symbol info string, or None.
+ """
+ offset_map = self._symbol_info_cache[host_path]
+ symbol_info = offset_map.get(offset)
+ if symbol_info:
+ return symbol_info
+
+ # Create symbolizer on demand.
+ symbolizer = self._elf_symbolizer_cache.get(host_path)
+ if not symbolizer:
+ symbolizer = self._CreateSymbolizerFor(host_path)
+ self._elf_symbolizer_cache[host_path] = symbolizer
+
+ # If there are pre-recorded offsets for this path, symbolize them now.
+ offsets = self._lib_offsets_map.get(host_path)
+ if offsets:
+ offset_map = {}
+ for pre_offset in offsets:
+ symbolizer.SymbolizeAsync(
+ pre_offset, callback_arg=(offset_map, pre_offset))
+ symbolizer.WaitForIdle()
+ self._symbol_info_cache[host_path] = offset_map
+
+ symbol_info = offset_map.get(offset)
+ if symbol_info:
+ return symbol_info
+
+ if not self._allow_symbolizer:
+ return None
+
+ # Symbolize single offset. Slower if addresses are not provided in
+ # increasing order to addr2line.
+ symbolizer.SymbolizeAsync(offset,
+ callback_arg=(offset_map, offset))
+ symbolizer.WaitForIdle()
+ return offset_map.get(offset)
+
+ @staticmethod
+ def _Callback(sym_info, callback_arg):
+ offset_map, offset = callback_arg
+ offset_map[offset] = str(sym_info)
+
+
+class DeviceSymbolResolver(SymbolResolver):
+ """A SymbolResolver instance that accepts device-specific path.
+
+ Usage is the following:
+ 1) Create new instance, passing a parent SymbolResolver instance that
+ accepts host-specific paths, and a HostLibraryFinder instance.
+
+ 2) Optional: call AddApkOffsets() to add offsets from within an APK
+ that contains uncompressed native shared libraries.
+
+ 3) Use it as any SymbolResolver instance.
+ """
+ def __init__(self, host_resolver, host_lib_finder):
+ """Initialize instance.
+
+ Args:
+ host_resolver: A parent SymbolResolver instance that will be used
+ to resolve symbols from host library paths.
+ host_lib_finder: A HostLibraryFinder instance used to locate
+ unstripped libraries on the host.
+ """
+ super(DeviceSymbolResolver, self).__init__()
+ self._host_lib_finder = host_lib_finder
+ self._bad_device_lib_paths = set()
+ self._host_resolver = host_resolver
+
+ def SetAndroidAbi(self, android_abi):
+ super(DeviceSymbolResolver, self).SetAndroidAbi(android_abi)
+ self._host_resolver.SetAndroidAbi(android_abi)
+
+ def AddLibraryOffsets(self, device_lib_path, lib_offsets):
+ """Associate a set of wanted offsets to a given device library.
+
+ This must be called before FindSymbolInfo(), otherwise its input arguments
+ will be ignored.
+
+ Args:
+ device_lib_path: A device-specific library path.
+ lib_offsets: An iterable of integer offsets within the corresponding
+ library that will be symbolized by future calls to FindSymbolInfo.
+ want to symbolize.
+ """
+ if device_lib_path in self._bad_device_lib_paths:
+ return
+
+ host_lib_path = self._host_lib_finder.Find(device_lib_path)
+ if not host_lib_path:
+ # NOTE: self._bad_device_lib_paths is only used to only print this
+ # warning once per bad library.
+ logging.warning('Could not find host library matching device path: %s',
+ device_lib_path)
+ self._bad_device_lib_paths.add(device_lib_path)
+ return
+
+ self._host_resolver.AddLibraryOffsets(host_lib_path, lib_offsets)
+
+ def AddApkOffsets(self, device_apk_path, apk_offsets, apk_translator):
+ """Associate a set of wanted offsets to a given device APK path.
+
+ This converts the APK-relative offsets into offsets relative to the
+ uncompressed libraries it contains, then calls AddLibraryOffsets()
+ for each one of the libraries.
+
+ Must be called before FindSymbolInfo() as well, otherwise input arguments
+ will be ignored.
+
+ Args:
+ device_apk_path: Device-specific APK path.
+ apk_offsets: Iterable of offsets within the APK file.
+ apk_translator: An ApkLibraryPathTranslator instance used to extract
+ library paths from the APK.
+ """
+ libraries_map = collections.defaultdict(set)
+ for offset in apk_offsets:
+ lib_path, lib_offset = apk_translator.TranslatePath(device_apk_path,
+ offset)
+ libraries_map[lib_path].add(lib_offset)
+
+ for lib_path, lib_offsets in libraries_map.iteritems():
+ self.AddLibraryOffsets(lib_path, lib_offsets)
+
+ def FindSymbolInfo(self, device_path, offset):
+ """Overrides SymbolResolver.FindSymbolInfo.
+
+ Args:
+ device_path: Device-specific library path (e.g.
+ '/data/app/com.example.app-1/lib/x86/libfoo.so')
+ offset: Offset in device library path.
+ Returns:
+ Corresponding symbol information string, or None.
+ """
+ host_path = self._host_lib_finder.Find(device_path)
+ if not host_path:
+ return None
+
+ return self._host_resolver.FindSymbolInfo(host_path, offset)
+
+
+class MemoryMap(object):
+ """Models the memory map of a given process. Usage is:
+
+ 1) Create new instance, passing the Android ABI.
+
+ 2) Call TranslateLine() whenever you want to detect and translate any
+ memory map input line.
+
+ 3) Otherwise, it is possible to parse the whole memory map input with
+ ParseLines(), then call FindSectionForAddress() repeatedly in order
+ to translate a memory address into the corresponding mapping and
+ file information tuple (e.g. to symbolize stack entries).
+ """
+
+ # A named tuple describing interesting memory map line items.
+ # Fields:
+ # addr_start: Mapping start address in memory.
+ # file_offset: Corresponding file offset.
+ # file_size: Corresponding mapping size in bytes.
+ # file_path: Input file path.
+ # match: Corresponding regular expression match object.
+ LineTuple = collections.namedtuple('MemoryMapLineTuple',
+ 'addr_start,file_offset,file_size,'
+ 'file_path, match')
+
+ # A name tuple describing a memory map section.
+ # Fields:
+ # address: Memory address.
+ # size: Size in bytes in memory
+ # offset: Starting file offset.
+ # path: Input file path.
+ SectionTuple = collections.namedtuple('MemoryMapSection',
+ 'address,size,offset,path')
+
+ def __init__(self, android_abi):
+ """Initializes instance.
+
+ Args:
+ android_abi: Android CPU ABI name (e.g. 'armeabi-v7a')
+ """
+ hex_addr = _HexAddressRegexpFor(android_abi)
+
+ # pylint: disable=line-too-long
+ # A regular expression used to match memory map entries which look like:
+ # b278c000-b2790fff r-- 4fda000 5000 /data/app/com.google.android.apps.chrome-2/base.apk
+ # pylint: enable=line-too-long
+ self._re_map_section = re.compile(
+ r'\s*(?P<addr_start>' + hex_addr + r')-(?P<addr_end>' + hex_addr + ')' +
+ r'\s+' +
+ r'(?P<perm>...)\s+' +
+ r'(?P<file_offset>[0-9a-f]+)\s+' +
+ r'(?P<file_size>[0-9a-f]+)\s*' +
+ r'(?P<file_path>[^ \t]+)?')
+
+ self._addr_map = [] # Sorted list of (address, size, path, offset) tuples.
+ self._sorted_addresses = [] # Sorted list of address fields in _addr_map.
+ self._in_section = False
+
+ def TranslateLine(self, line, apk_path_translator):
+ """Try to translate a memory map input line, if detected.
+
+ This only takes care of converting mapped APK file path and offsets
+ into a corresponding uncompressed native library file path + new offsets,
+ e.g. '..... <offset> <size> /data/.../base.apk' gets
+ translated into '.... <new-offset> <size> /data/.../base.apk!lib/libfoo.so'
+
+ This function should always work, even if ParseLines() was not called
+ previously.
+
+ Args:
+ line: Input memory map / tombstone line.
+ apk_translator: An ApkLibraryPathTranslator instance, used to map
+ APK offsets into uncompressed native libraries + new offsets.
+ Returns:
+ Translated memory map line, if relevant, or unchanged input line
+ otherwise.
+ """
+ t = self._ParseLine(line.rstrip())
+ if not t:
+ return line
+
+ new_path, new_offset = apk_path_translator.TranslatePath(
+ t.file_path, t.file_offset)
+
+ if new_path == t.file_path:
+ return line
+
+ pos = t.match.start('file_path')
+ return '%s%s (offset 0x%x)%s' % (line[0:pos], new_path, new_offset,
+ line[t.match.end('file_path'):])
+
+ def ParseLines(self, input_lines, in_section=False):
+ """Parse a list of input lines and extract the APK memory map out of it.
+
+ Args:
+ input_lines: list, or iterable, of input lines.
+ in_section: Optional. If true, considers that the input lines are
+ already part of the memory map. Otherwise, wait until the start of
+ the section appears in the input before trying to record data.
+ Returns:
+ True iff APK-related memory map entries were found. False otherwise.
+ """
+ addr_list = [] # list of (address, size, file_path, file_offset) tuples.
+ self._in_section = in_section
+ for line in input_lines:
+ t = self._ParseLine(line.rstrip())
+ if not t:
+ continue
+
+ addr_list.append(t)
+
+ self._addr_map = sorted(addr_list,
+ lambda x, y: cmp(x.addr_start, y.addr_start))
+ self._sorted_addresses = [e.addr_start for e in self._addr_map]
+ return bool(self._addr_map)
+
+ def _ParseLine(self, line):
+ """Used internally to recognized memory map input lines.
+
+ Args:
+ line: Input logcat or tomstone line.
+ Returns:
+ A LineTuple instance on success, or None on failure.
+ """
+ if not self._in_section:
+ self._in_section = line.startswith('memory map:')
+ return None
+
+ m = self._re_map_section.match(line)
+ if not m:
+ self._in_section = False # End of memory map section
+ return None
+
+ # Only accept .apk and .so files that are not from the system partitions.
+ file_path = m.group('file_path')
+ if not file_path:
+ return None
+
+ if file_path.startswith('/system') or file_path.startswith('/vendor'):
+ return None
+
+ if not (file_path.endswith('.apk') or file_path.endswith('.so')):
+ return None
+
+ addr_start = int(m.group('addr_start'), 16)
+ file_offset = int(m.group('file_offset'), 16)
+ file_size = int(m.group('file_size'), 16)
+
+ return self.LineTuple(addr_start, file_offset, file_size, file_path, m)
+
+ def Dump(self):
+ """Print memory map for debugging."""
+ print 'MEMORY MAP ['
+ for t in self._addr_map:
+ print '[%08x-%08x %08x %08x %s]' % (
+ t.addr_start, t.addr_start + t.file_size, t.file_size, t.file_offset,
+ t.file_path)
+ print '] MEMORY MAP'
+
+ def FindSectionForAddress(self, addr):
+ """Find the map section corresponding to a specific memory address.
+
+ Call this method only after using ParseLines() was called to extract
+ relevant information from the memory map.
+
+ Args:
+ addr: Memory address
+ Returns:
+ A SectionTuple instance on success, or None on failure.
+ """
+ pos = bisect.bisect_right(self._sorted_addresses, addr)
+ if pos > 0:
+ # All values in [0,pos) are <= addr, just ensure that the last
+ # one contains the address as well.
+ entry = self._addr_map[pos - 1]
+ if entry.addr_start + entry.file_size > addr:
+ return self.SectionTuple(entry.addr_start, entry.file_size,
+ entry.file_offset, entry.file_path)
+ return None
+
+
+class BacktraceTranslator(object):
+ """Translates backtrace-related lines in a tombstone or crash report.
+
+ Usage is the following:
+ 1) Create new instance with appropriate arguments.
+ 2) If the tombstone / logcat input is available, one can call
+ FindLibraryOffsets() in order to detect which library offsets
+ will need to be symbolized during a future parse. Doing so helps
+ speed up the ELF symbolizer.
+ 3) For each tombstone/logcat input line, call TranslateLine() to
+ try to detect and symbolize backtrace lines.
+ """
+
+ # A named tuple for relevant input backtrace lines.
+ # Fields:
+ # rel_pc: Instruction pointer, relative to offset in library start.
+ # location: Library or APK file path.
+ # offset: Load base of executable code in library or apk file path.
+ # match: The corresponding regular expression match object.
+ # Note:
+ # The actual instruction pointer always matches the position at
+ # |offset + rel_pc| in |location|.
+ LineTuple = collections.namedtuple('BacktraceLineTuple',
+ 'rel_pc,location,offset,match')
+
+ def __init__(self, android_abi, apk_translator):
+ """Initialize instance.
+
+ Args:
+ android_abi: Android CPU ABI name (e.g. 'armeabi-v7a').
+ apk_translator: ApkLibraryPathTranslator instance used to convert
+ mapped APK file offsets into uncompressed library file paths with
+ new offsets.
+ """
+ hex_addr = _HexAddressRegexpFor(android_abi)
+
+ # A regular expression used to match backtrace lines.
+ self._re_backtrace = re.compile(
+ r'.*#(?P<frame>[0-9]{2})\s+' +
+ r'(..)\s+' +
+ r'(?P<rel_pc>' + hex_addr + r')\s+' +
+ r'(?P<location>[^ \t]+)' +
+ r'(\s+\(offset 0x(?P<offset>[0-9a-f]+)\))?')
+
+ # In certain cases, offset will be provided as <location>+0x<offset>
+ # instead of <location> (offset 0x<offset>). This is a regexp to detect
+ # this.
+ self._re_location_offset = re.compile(
+ r'.*\+0x(?P<offset>[0-9a-f]+)$')
+
+ self._apk_translator = apk_translator
+ self._in_section = False
+
+ def _ParseLine(self, line):
+ """Used internally to detect and decompose backtrace input lines.
+
+ Args:
+ line: input tombstone line.
+ Returns:
+ A LineTuple instance on success, None on failure.
+ """
+ if not self._in_section:
+ self._in_section = line.startswith('backtrace:')
+ return None
+
+ line = line.rstrip()
+ m = self._re_backtrace.match(line)
+ if not m:
+ self._in_section = False
+ return None
+
+ location = m.group('location')
+ offset = m.group('offset')
+ if not offset:
+ m2 = self._re_location_offset.match(location)
+ if m2:
+ offset = m2.group('offset')
+ location = location[0:m2.start('offset') - 3]
+
+ if not offset:
+ return None
+
+ offset = int(offset, 16)
+ rel_pc = int(m.group('rel_pc'), 16)
+
+ # Two cases to consider here:
+ #
+ # * If this is a library file directly mapped in memory, then |rel_pc|
+ # if the direct offset within the library, and doesn't need any kind
+ # of adjustement.
+ #
+ # * If this is a library mapped directly from an .apk file, then
+ # |rel_pc| is the offset in the APK, and |offset| happens to be the
+ # load base of the corresponding library.
+ #
+ if location.endswith('.so'):
+ # For a native library directly mapped from the file system,
+ return self.LineTuple(rel_pc, location, offset, m)
+
+ if location.endswith('.apk'):
+ # For a native library inside an memory-mapped APK file,
+ new_location, new_offset = self._apk_translator.TranslatePath(
+ location, offset)
+
+ return self.LineTuple(rel_pc, new_location, new_offset, m)
+
+ # Ignore anything else (e.g. .oat or .odex files).
+ return None
+
+ def FindLibraryOffsets(self, input_lines, in_section=False):
+ """Parse a tombstone's backtrace section and find all library offsets in it.
+
+ Args:
+ input_lines: List or iterables of intput tombstone lines.
+ in_section: Optional. If True, considers that the stack section has
+ already started.
+ Returns:
+ A dictionary mapping device library paths to sets of offsets within
+ then.
+ """
+ self._in_section = in_section
+ result = collections.defaultdict(set)
+ for line in input_lines:
+ t = self._ParseLine(line)
+ if not t:
+ continue
+
+ result[t.location].add(t.offset + t.rel_pc)
+ return result
+
+ def TranslateLine(self, line, symbol_resolver):
+ """Symbolize backtrace line if recognized.
+
+ Args:
+ line: input backtrace line.
+ symbol_resolver: symbol resolver instance to use. This method will
+ call its FindSymbolInfo(device_lib_path, lib_offset) method to
+ convert offsets into symbol informations strings.
+ Returns:
+ Translated line (unchanged if not recognized as a back trace).
+ """
+ t = self._ParseLine(line)
+ if not t:
+ return line
+
+ symbol_info = symbol_resolver.FindSymbolInfo(t.location,
+ t.offset + t.rel_pc)
+ if not symbol_info:
+ symbol_info = 'offset 0x%x' % t.offset
+
+ pos = t.match.start('location')
+ pos2 = t.match.end('offset') + 1
+ if pos2 <= 0:
+ pos2 = t.match.end('location')
+ return '%s%s (%s)%s' % (line[:pos], t.location, symbol_info, line[pos2:])
+
+
+class StackTranslator(object):
+ """Translates stack-related lines in a tombstone or crash report."""
+
+ # A named tuple describing relevant stack input lines.
+ # Fields:
+ # address: Address as it appears in the stack.
+ # lib_path: Library path where |address| is mapped.
+ # lib_offset: Library load base offset. for |lib_path|.
+ # match: Corresponding regular expression match object.
+ LineTuple = collections.namedtuple('StackLineTuple',
+ 'address, lib_path, lib_offset, match')
+
+ def __init__(self, android_abi, memory_map, apk_translator):
+ """Initialize instance."""
+ hex_addr = _HexAddressRegexpFor(android_abi)
+
+ # pylint: disable=line-too-long
+ # A regular expression used to recognize stack entries like:
+ #
+ # #05 bf89a180 bf89a1e4 [stack]
+ # bf89a1c8 a0c01c51 /data/app/com.google.android.apps.chrome-2/base.apk
+ # bf89a080 00000000
+ # ........ ........
+ # pylint: enable=line-too-long
+ self._re_stack_line = re.compile(
+ r'\s+(?P<frame_number>#[0-9]+)?\s*' +
+ r'(?P<stack_addr>' + hex_addr + r')\s+' +
+ r'(?P<stack_value>' + hex_addr + r')' +
+ r'(\s+(?P<location>[^ \t]+))?')
+
+ self._re_stack_abbrev = re.compile(r'\s+[.]+\s+[.]+')
+
+ self._memory_map = memory_map
+ self._apk_translator = apk_translator
+ self._in_section = False
+
+ def _ParseLine(self, line):
+ """Check a given input line for a relevant _re_stack_line match.
+
+ Args:
+ line: input tombstone line.
+ Returns:
+ A LineTuple instance on success, None on failure.
+ """
+ line = line.rstrip()
+ if not self._in_section:
+ self._in_section = line.startswith('stack:')
+ return None
+
+ m = self._re_stack_line.match(line)
+ if not m:
+ if not self._re_stack_abbrev.match(line):
+ self._in_section = False
+ return None
+
+ location = m.group('location')
+ if not location:
+ return None
+
+ if not location.endswith('.apk') and not location.endswith('.so'):
+ return None
+
+ addr = int(m.group('stack_value'), 16)
+ t = self._memory_map.FindSectionForAddress(addr)
+ if t is None:
+ return None
+
+ lib_path = t.path
+ lib_offset = t.offset + (addr - t.address)
+
+ if lib_path.endswith('.apk'):
+ lib_path, lib_offset = self._apk_translator.TranslatePath(
+ lib_path, lib_offset)
+
+ return self.LineTuple(addr, lib_path, lib_offset, m)
+
+ def FindLibraryOffsets(self, input_lines, in_section=False):
+ """Parse a tombstone's stack section and find all library offsets in it.
+
+ Args:
+ input_lines: List or iterables of intput tombstone lines.
+ in_section: Optional. If True, considers that the stack section has
+ already started.
+ Returns:
+ A dictionary mapping device library paths to sets of offsets within
+ then.
+ """
+ result = collections.defaultdict(set)
+ self._in_section = in_section
+ for line in input_lines:
+ t = self._ParseLine(line)
+ if t:
+ result[t.lib_path].add(t.lib_offset)
+ return result
+
+ def TranslateLine(self, line, symbol_resolver=None):
+ """Try to translate a line of the stack dump."""
+ t = self._ParseLine(line)
+ if not t:
+ return line
+
+ symbol_info = symbol_resolver.FindSymbolInfo(t.lib_path, t.lib_offset)
+ if not symbol_info:
+ return line
+
+ pos = t.match.start('location')
+ pos2 = t.match.end('location')
+ return '%s%s (%s)%s' % (line[:pos], t.lib_path, symbol_info, line[pos2:])
diff --git a/chromium/build/android/pylib/symbols/symbol_utils_unittest.py b/chromium/build/android/pylib/symbols/symbol_utils_unittest.py
new file mode 100644
index 00000000000..d350c17489b
--- /dev/null
+++ b/chromium/build/android/pylib/symbols/symbol_utils_unittest.py
@@ -0,0 +1,944 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import contextlib
+import logging
+import os
+import re
+import shutil
+import tempfile
+import unittest
+
+from pylib.symbols import apk_native_libs_unittest
+from pylib.symbols import mock_addr2line
+from pylib.symbols import symbol_utils
+
+_MOCK_ELF_DATA = apk_native_libs_unittest.MOCK_ELF_DATA
+
+_MOCK_A2L_PATH = os.path.join(os.path.dirname(mock_addr2line.__file__),
+ 'mock_addr2line')
+
+
+# pylint: disable=line-too-long
+
+# list of (start_offset, end_offset, size, libpath) tuples corresponding
+# to the content of base.apk. This was taken from an x86 ChromeModern.apk
+# component build.
+_TEST_APK_LIBS = [
+ (0x01331000, 0x013696bc, 0x000386bc, 'libaccessibility.cr.so'),
+ (0x0136a000, 0x013779c4, 0x0000d9c4, 'libanimation.cr.so'),
+ (0x01378000, 0x0137f7e8, 0x000077e8, 'libapdu.cr.so'),
+ (0x01380000, 0x0155ccc8, 0x001dccc8, 'libbase.cr.so'),
+ (0x0155d000, 0x015ab98c, 0x0004e98c, 'libbase_i18n.cr.so'),
+ (0x015ac000, 0x015dff4c, 0x00033f4c, 'libbindings.cr.so'),
+ (0x015e0000, 0x015f5a54, 0x00015a54, 'libbindings_base.cr.so'),
+ (0x015f6000, 0x0160d770, 0x00017770, 'libblink_android_mojo_bindings_shared.cr.so'),
+ (0x0160e000, 0x01731960, 0x00123960, 'libblink_common.cr.so'),
+ (0x01732000, 0x0174ce54, 0x0001ae54, 'libblink_controller.cr.so'),
+ (0x0174d000, 0x0318c528, 0x01a3f528, 'libblink_core.cr.so'),
+ (0x0318d000, 0x03191700, 0x00004700, 'libblink_core_mojo_bindings_shared.cr.so'),
+ (0x03192000, 0x03cd7918, 0x00b45918, 'libblink_modules.cr.so'),
+ (0x03cd8000, 0x03d137d0, 0x0003b7d0, 'libblink_mojo_bindings_shared.cr.so'),
+ (0x03d14000, 0x03d2670c, 0x0001270c, 'libblink_offscreen_canvas_mojo_bindings_shared.cr.so'),
+ (0x03d27000, 0x046c7054, 0x009a0054, 'libblink_platform.cr.so'),
+ (0x046c8000, 0x0473fbfc, 0x00077bfc, 'libbluetooth.cr.so'),
+ (0x04740000, 0x04878f40, 0x00138f40, 'libboringssl.cr.so'),
+ (0x04879000, 0x0498466c, 0x0010b66c, 'libc++_shared.so'),
+ (0x04985000, 0x0498d93c, 0x0000893c, 'libcaptive_portal.cr.so'),
+ (0x0498e000, 0x049947cc, 0x000067cc, 'libcapture_base.cr.so'),
+ (0x04995000, 0x04b39f18, 0x001a4f18, 'libcapture_lib.cr.so'),
+ (0x04b3a000, 0x04b488ec, 0x0000e8ec, 'libcbor.cr.so'),
+ (0x04b49000, 0x04e9ea5c, 0x00355a5c, 'libcc.cr.so'),
+ (0x04e9f000, 0x04ed6404, 0x00037404, 'libcc_animation.cr.so'),
+ (0x04ed7000, 0x04ef5ab4, 0x0001eab4, 'libcc_base.cr.so'),
+ (0x04ef6000, 0x04fd9364, 0x000e3364, 'libcc_blink.cr.so'),
+ (0x04fda000, 0x04fe2758, 0x00008758, 'libcc_debug.cr.so'),
+ (0x04fe3000, 0x0500ae0c, 0x00027e0c, 'libcc_ipc.cr.so'),
+ (0x0500b000, 0x05078f38, 0x0006df38, 'libcc_paint.cr.so'),
+ (0x05079000, 0x0507e734, 0x00005734, 'libcdm_manager.cr.so'),
+ (0x0507f000, 0x06f4d744, 0x01ece744, 'libchrome.cr.so'),
+ (0x06f54000, 0x06feb830, 0x00097830, 'libchromium_sqlite3.cr.so'),
+ (0x06fec000, 0x0706f554, 0x00083554, 'libclient.cr.so'),
+ (0x07070000, 0x0708da60, 0x0001da60, 'libcloud_policy_proto_generated_compile.cr.so'),
+ (0x0708e000, 0x07121f28, 0x00093f28, 'libcodec.cr.so'),
+ (0x07122000, 0x07134ab8, 0x00012ab8, 'libcolor_space.cr.so'),
+ (0x07135000, 0x07138614, 0x00003614, 'libcommon.cr.so'),
+ (0x07139000, 0x0717c938, 0x00043938, 'libcompositor.cr.so'),
+ (0x0717d000, 0x0923d78c, 0x020c078c, 'libcontent.cr.so'),
+ (0x0923e000, 0x092ae87c, 0x0007087c, 'libcontent_common_mojo_bindings_shared.cr.so'),
+ (0x092af000, 0x092be718, 0x0000f718, 'libcontent_public_common_mojo_bindings_shared.cr.so'),
+ (0x092bf000, 0x092d9a20, 0x0001aa20, 'libcrash_key.cr.so'),
+ (0x092da000, 0x092eda58, 0x00013a58, 'libcrcrypto.cr.so'),
+ (0x092ee000, 0x092f16e0, 0x000036e0, 'libdevice_base.cr.so'),
+ (0x092f2000, 0x092fe8d8, 0x0000c8d8, 'libdevice_event_log.cr.so'),
+ (0x092ff000, 0x093026a4, 0x000036a4, 'libdevice_features.cr.so'),
+ (0x09303000, 0x093f1220, 0x000ee220, 'libdevice_gamepad.cr.so'),
+ (0x093f2000, 0x09437f54, 0x00045f54, 'libdevice_vr_mojo_bindings.cr.so'),
+ (0x09438000, 0x0954c168, 0x00114168, 'libdevice_vr_mojo_bindings_blink.cr.so'),
+ (0x0954d000, 0x0955d720, 0x00010720, 'libdevice_vr_mojo_bindings_shared.cr.so'),
+ (0x0955e000, 0x0956b9c0, 0x0000d9c0, 'libdevices.cr.so'),
+ (0x0956c000, 0x0957cae8, 0x00010ae8, 'libdiscardable_memory_client.cr.so'),
+ (0x0957d000, 0x09588854, 0x0000b854, 'libdiscardable_memory_common.cr.so'),
+ (0x09589000, 0x0959cbb4, 0x00013bb4, 'libdiscardable_memory_service.cr.so'),
+ (0x0959d000, 0x095b6b90, 0x00019b90, 'libdisplay.cr.so'),
+ (0x095b7000, 0x095be930, 0x00007930, 'libdisplay_types.cr.so'),
+ (0x095bf000, 0x095c46c4, 0x000056c4, 'libdisplay_util.cr.so'),
+ (0x095c5000, 0x095f54a4, 0x000304a4, 'libdomain_reliability.cr.so'),
+ (0x095f6000, 0x0966fe08, 0x00079e08, 'libembedder.cr.so'),
+ (0x09670000, 0x096735f8, 0x000035f8, 'libembedder_switches.cr.so'),
+ (0x09674000, 0x096a3460, 0x0002f460, 'libevents.cr.so'),
+ (0x096a4000, 0x096b6d40, 0x00012d40, 'libevents_base.cr.so'),
+ (0x096b7000, 0x0981a778, 0x00163778, 'libffmpeg.cr.so'),
+ (0x0981b000, 0x09945c94, 0x0012ac94, 'libfido.cr.so'),
+ (0x09946000, 0x09a330dc, 0x000ed0dc, 'libfingerprint.cr.so'),
+ (0x09a34000, 0x09b53170, 0x0011f170, 'libfreetype_harfbuzz.cr.so'),
+ (0x09b54000, 0x09bc5c5c, 0x00071c5c, 'libgcm.cr.so'),
+ (0x09bc6000, 0x09cc8584, 0x00102584, 'libgeolocation.cr.so'),
+ (0x09cc9000, 0x09cdc8d4, 0x000138d4, 'libgeometry.cr.so'),
+ (0x09cdd000, 0x09cec8b4, 0x0000f8b4, 'libgeometry_skia.cr.so'),
+ (0x09ced000, 0x09d10e14, 0x00023e14, 'libgesture_detection.cr.so'),
+ (0x09d11000, 0x09d7595c, 0x0006495c, 'libgfx.cr.so'),
+ (0x09d76000, 0x09d7d7cc, 0x000077cc, 'libgfx_ipc.cr.so'),
+ (0x09d7e000, 0x09d82708, 0x00004708, 'libgfx_ipc_buffer_types.cr.so'),
+ (0x09d83000, 0x09d89748, 0x00006748, 'libgfx_ipc_color.cr.so'),
+ (0x09d8a000, 0x09d8f6f4, 0x000056f4, 'libgfx_ipc_geometry.cr.so'),
+ (0x09d90000, 0x09d94754, 0x00004754, 'libgfx_ipc_skia.cr.so'),
+ (0x09d95000, 0x09d9869c, 0x0000369c, 'libgfx_switches.cr.so'),
+ (0x09d99000, 0x09dba0ac, 0x000210ac, 'libgin.cr.so'),
+ (0x09dbb000, 0x09e0a8cc, 0x0004f8cc, 'libgl_in_process_context.cr.so'),
+ (0x09e0b000, 0x09e17a18, 0x0000ca18, 'libgl_init.cr.so'),
+ (0x09e18000, 0x09ee34e4, 0x000cb4e4, 'libgl_wrapper.cr.so'),
+ (0x09ee4000, 0x0a1a2e00, 0x002bee00, 'libgles2.cr.so'),
+ (0x0a1a3000, 0x0a24556c, 0x000a256c, 'libgles2_implementation.cr.so'),
+ (0x0a246000, 0x0a267038, 0x00021038, 'libgles2_utils.cr.so'),
+ (0x0a268000, 0x0a3288e4, 0x000c08e4, 'libgpu.cr.so'),
+ (0x0a329000, 0x0a3627ec, 0x000397ec, 'libgpu_ipc_service.cr.so'),
+ (0x0a363000, 0x0a388a18, 0x00025a18, 'libgpu_util.cr.so'),
+ (0x0a389000, 0x0a506d8c, 0x0017dd8c, 'libhost.cr.so'),
+ (0x0a507000, 0x0a6f0ec0, 0x001e9ec0, 'libicui18n.cr.so'),
+ (0x0a6f1000, 0x0a83b4c8, 0x0014a4c8, 'libicuuc.cr.so'),
+ (0x0a83c000, 0x0a8416e4, 0x000056e4, 'libinterfaces_shared.cr.so'),
+ (0x0a842000, 0x0a87e2a0, 0x0003c2a0, 'libipc.cr.so'),
+ (0x0a87f000, 0x0a88c98c, 0x0000d98c, 'libipc_mojom.cr.so'),
+ (0x0a88d000, 0x0a8926e4, 0x000056e4, 'libipc_mojom_shared.cr.so'),
+ (0x0a893000, 0x0a8a1e18, 0x0000ee18, 'libkeyed_service_content.cr.so'),
+ (0x0a8a2000, 0x0a8b4a30, 0x00012a30, 'libkeyed_service_core.cr.so'),
+ (0x0a8b5000, 0x0a930a80, 0x0007ba80, 'libleveldatabase.cr.so'),
+ (0x0a931000, 0x0a9b3908, 0x00082908, 'libmanager.cr.so'),
+ (0x0a9b4000, 0x0aea9bb4, 0x004f5bb4, 'libmedia.cr.so'),
+ (0x0aeaa000, 0x0b08cb88, 0x001e2b88, 'libmedia_blink.cr.so'),
+ (0x0b08d000, 0x0b0a4728, 0x00017728, 'libmedia_devices_mojo_bindings_shared.cr.so'),
+ (0x0b0a5000, 0x0b1943ec, 0x000ef3ec, 'libmedia_gpu.cr.so'),
+ (0x0b195000, 0x0b2d07d4, 0x0013b7d4, 'libmedia_mojo_services.cr.so'),
+ (0x0b2d1000, 0x0b2d4760, 0x00003760, 'libmessage_center.cr.so'),
+ (0x0b2d5000, 0x0b2e0938, 0x0000b938, 'libmessage_support.cr.so'),
+ (0x0b2e1000, 0x0b2f3ad0, 0x00012ad0, 'libmetrics_cpp.cr.so'),
+ (0x0b2f4000, 0x0b313bb8, 0x0001fbb8, 'libmidi.cr.so'),
+ (0x0b314000, 0x0b31b848, 0x00007848, 'libmojo_base_lib.cr.so'),
+ (0x0b31c000, 0x0b3329f8, 0x000169f8, 'libmojo_base_mojom.cr.so'),
+ (0x0b333000, 0x0b34b98c, 0x0001898c, 'libmojo_base_mojom_blink.cr.so'),
+ (0x0b34c000, 0x0b354700, 0x00008700, 'libmojo_base_mojom_shared.cr.so'),
+ (0x0b355000, 0x0b3608b0, 0x0000b8b0, 'libmojo_base_shared_typemap_traits.cr.so'),
+ (0x0b361000, 0x0b3ad454, 0x0004c454, 'libmojo_edk.cr.so'),
+ (0x0b3ae000, 0x0b3c4a20, 0x00016a20, 'libmojo_edk_ports.cr.so'),
+ (0x0b3c5000, 0x0b3d38a0, 0x0000e8a0, 'libmojo_mojom_bindings.cr.so'),
+ (0x0b3d4000, 0x0b3da6e8, 0x000066e8, 'libmojo_mojom_bindings_shared.cr.so'),
+ (0x0b3db000, 0x0b3e27f0, 0x000077f0, 'libmojo_public_system.cr.so'),
+ (0x0b3e3000, 0x0b3fa9fc, 0x000179fc, 'libmojo_public_system_cpp.cr.so'),
+ (0x0b3fb000, 0x0b407728, 0x0000c728, 'libmojom_core_shared.cr.so'),
+ (0x0b408000, 0x0b421744, 0x00019744, 'libmojom_platform_shared.cr.so'),
+ (0x0b422000, 0x0b43451c, 0x0001251c, 'libnative_theme.cr.so'),
+ (0x0b435000, 0x0baaa1bc, 0x006751bc, 'libnet.cr.so'),
+ (0x0baab000, 0x0bac3c08, 0x00018c08, 'libnet_with_v8.cr.so'),
+ (0x0bac4000, 0x0bb74670, 0x000b0670, 'libnetwork_cpp.cr.so'),
+ (0x0bb75000, 0x0bbaee8c, 0x00039e8c, 'libnetwork_cpp_base.cr.so'),
+ (0x0bbaf000, 0x0bd21844, 0x00172844, 'libnetwork_service.cr.so'),
+ (0x0bd22000, 0x0bd256e4, 0x000036e4, 'libnetwork_session_configurator.cr.so'),
+ (0x0bd26000, 0x0bd33734, 0x0000d734, 'libonc.cr.so'),
+ (0x0bd34000, 0x0bd9ce18, 0x00068e18, 'libperfetto.cr.so'),
+ (0x0bd9d000, 0x0bda4854, 0x00007854, 'libplatform.cr.so'),
+ (0x0bda5000, 0x0bec5ce4, 0x00120ce4, 'libpolicy_component.cr.so'),
+ (0x0bec6000, 0x0bf5ab58, 0x00094b58, 'libpolicy_proto.cr.so'),
+ (0x0bf5b000, 0x0bf86fbc, 0x0002bfbc, 'libprefs.cr.so'),
+ (0x0bf87000, 0x0bfa5d74, 0x0001ed74, 'libprinting.cr.so'),
+ (0x0bfa6000, 0x0bfe0e80, 0x0003ae80, 'libprotobuf_lite.cr.so'),
+ (0x0bfe1000, 0x0bff0a18, 0x0000fa18, 'libproxy_config.cr.so'),
+ (0x0bff1000, 0x0c0f6654, 0x00105654, 'libpublic.cr.so'),
+ (0x0c0f7000, 0x0c0fa6a4, 0x000036a4, 'librange.cr.so'),
+ (0x0c0fb000, 0x0c118058, 0x0001d058, 'libraster.cr.so'),
+ (0x0c119000, 0x0c133d00, 0x0001ad00, 'libresource_coordinator_cpp.cr.so'),
+ (0x0c134000, 0x0c1396a0, 0x000056a0, 'libresource_coordinator_cpp_base.cr.so'),
+ (0x0c13a000, 0x0c1973b8, 0x0005d3b8, 'libresource_coordinator_public_mojom.cr.so'),
+ (0x0c198000, 0x0c2033e8, 0x0006b3e8, 'libresource_coordinator_public_mojom_blink.cr.so'),
+ (0x0c204000, 0x0c219744, 0x00015744, 'libresource_coordinator_public_mojom_shared.cr.so'),
+ (0x0c21a000, 0x0c21e700, 0x00004700, 'libsandbox.cr.so'),
+ (0x0c21f000, 0x0c22f96c, 0x0001096c, 'libsandbox_services.cr.so'),
+ (0x0c230000, 0x0c249d58, 0x00019d58, 'libseccomp_bpf.cr.so'),
+ (0x0c24a000, 0x0c24e714, 0x00004714, 'libseccomp_starter_android.cr.so'),
+ (0x0c24f000, 0x0c4ae9f0, 0x0025f9f0, 'libservice.cr.so'),
+ (0x0c4af000, 0x0c4c3ae4, 0x00014ae4, 'libservice_manager_cpp.cr.so'),
+ (0x0c4c4000, 0x0c4cb708, 0x00007708, 'libservice_manager_cpp_types.cr.so'),
+ (0x0c4cc000, 0x0c4fbe30, 0x0002fe30, 'libservice_manager_mojom.cr.so'),
+ (0x0c4fc000, 0x0c532e78, 0x00036e78, 'libservice_manager_mojom_blink.cr.so'),
+ (0x0c533000, 0x0c53669c, 0x0000369c, 'libservice_manager_mojom_constants.cr.so'),
+ (0x0c537000, 0x0c53e85c, 0x0000785c, 'libservice_manager_mojom_constants_blink.cr.so'),
+ (0x0c53f000, 0x0c542668, 0x00003668, 'libservice_manager_mojom_constants_shared.cr.so'),
+ (0x0c543000, 0x0c54d700, 0x0000a700, 'libservice_manager_mojom_shared.cr.so'),
+ (0x0c54e000, 0x0c8fc6ec, 0x003ae6ec, 'libsessions.cr.so'),
+ (0x0c8fd000, 0x0c90a924, 0x0000d924, 'libshared_memory_support.cr.so'),
+ (0x0c90b000, 0x0c9148ec, 0x000098ec, 'libshell_dialogs.cr.so'),
+ (0x0c915000, 0x0cf8de70, 0x00678e70, 'libskia.cr.so'),
+ (0x0cf8e000, 0x0cf978bc, 0x000098bc, 'libsnapshot.cr.so'),
+ (0x0cf98000, 0x0cfb7d9c, 0x0001fd9c, 'libsql.cr.so'),
+ (0x0cfb8000, 0x0cfbe744, 0x00006744, 'libstartup_tracing.cr.so'),
+ (0x0cfbf000, 0x0d19b4e4, 0x001dc4e4, 'libstorage_browser.cr.so'),
+ (0x0d19c000, 0x0d2a773c, 0x0010b73c, 'libstorage_common.cr.so'),
+ (0x0d2a8000, 0x0d2ac6fc, 0x000046fc, 'libsurface.cr.so'),
+ (0x0d2ad000, 0x0d2baa98, 0x0000da98, 'libtracing.cr.so'),
+ (0x0d2bb000, 0x0d2f36b0, 0x000386b0, 'libtracing_cpp.cr.so'),
+ (0x0d2f4000, 0x0d326e70, 0x00032e70, 'libtracing_mojom.cr.so'),
+ (0x0d327000, 0x0d33270c, 0x0000b70c, 'libtracing_mojom_shared.cr.so'),
+ (0x0d333000, 0x0d46d804, 0x0013a804, 'libui_android.cr.so'),
+ (0x0d46e000, 0x0d4cb3f8, 0x0005d3f8, 'libui_base.cr.so'),
+ (0x0d4cc000, 0x0d4dbc40, 0x0000fc40, 'libui_base_ime.cr.so'),
+ (0x0d4dc000, 0x0d4e58d4, 0x000098d4, 'libui_data_pack.cr.so'),
+ (0x0d4e6000, 0x0d51d1e0, 0x000371e0, 'libui_devtools.cr.so'),
+ (0x0d51e000, 0x0d52b984, 0x0000d984, 'libui_message_center_cpp.cr.so'),
+ (0x0d52c000, 0x0d539a48, 0x0000da48, 'libui_touch_selection.cr.so'),
+ (0x0d53a000, 0x0d55bc60, 0x00021c60, 'liburl.cr.so'),
+ (0x0d55c000, 0x0d55f6b4, 0x000036b4, 'liburl_ipc.cr.so'),
+ (0x0d560000, 0x0d5af110, 0x0004f110, 'liburl_matcher.cr.so'),
+ (0x0d5b0000, 0x0d5e2fac, 0x00032fac, 'libuser_manager.cr.so'),
+ (0x0d5e3000, 0x0d5e66e4, 0x000036e4, 'libuser_prefs.cr.so'),
+ (0x0d5e7000, 0x0e3e1cc8, 0x00dfacc8, 'libv8.cr.so'),
+ (0x0e3e2000, 0x0e400ae0, 0x0001eae0, 'libv8_libbase.cr.so'),
+ (0x0e401000, 0x0e4d91d4, 0x000d81d4, 'libviz_common.cr.so'),
+ (0x0e4da000, 0x0e4df7e4, 0x000057e4, 'libviz_resource_format.cr.so'),
+ (0x0e4e0000, 0x0e5b7120, 0x000d7120, 'libweb_dialogs.cr.so'),
+ (0x0e5b8000, 0x0e5c7a18, 0x0000fa18, 'libwebdata_common.cr.so'),
+ (0x0e5c8000, 0x0e61bfe4, 0x00053fe4, 'libwtf.cr.so'),
+]
+
+
+# A small memory map fragment extracted from a tombstone for a process that
+# had loaded the APK corresponding to _TEST_APK_LIBS above.
+_TEST_MEMORY_MAP = r'''memory map:
+12c00000-12ccafff rw- 0 cb000 /dev/ashmem/dalvik-main space (deleted)
+12ccb000-130cafff rw- cb000 400000 /dev/ashmem/dalvik-main space (deleted)
+130cb000-32bfffff --- 4cb000 1fb35000 /dev/ashmem/dalvik-main space (deleted)
+32c00000-32c00fff rw- 0 1000 /dev/ashmem/dalvik-main space 1 (deleted)
+32c01000-52bfffff --- 1000 1ffff000 /dev/ashmem/dalvik-main space 1 (deleted)
+6f3b8000-6fd90fff rw- 0 9d9000 /data/dalvik-cache/x86/system@framework@boot.art
+6fd91000-71c42fff r-- 0 1eb2000 /data/dalvik-cache/x86/system@framework@boot.oat
+71c43000-7393efff r-x 1eb2000 1cfc000 /data/dalvik-cache/x86/system@framework@boot.oat (load base 0x71c43000)
+7393f000-7393ffff rw- 3bae000 1000 /data/dalvik-cache/x86/system@framework@boot.oat
+73940000-73a1bfff rw- 0 dc000 /dev/ashmem/dalvik-zygote space (deleted)
+73a1c000-73a1cfff rw- 0 1000 /dev/ashmem/dalvik-non moving space (deleted)
+73a1d000-73a2dfff rw- 1000 11000 /dev/ashmem/dalvik-non moving space (deleted)
+73a2e000-77540fff --- 12000 3b13000 /dev/ashmem/dalvik-non moving space (deleted)
+77541000-7793ffff rw- 3b25000 3ff000 /dev/ashmem/dalvik-non moving space (deleted)
+923aa000-92538fff r-- 8a9000 18f000 /data/app/com.example.app-2/base.apk
+92539000-9255bfff r-- 0 23000 /data/data/com.example.app/app_data/paks/es.pak@162db1c6689
+9255c000-92593fff r-- 213000 38000 /data/app/com.example.app-2/base.apk
+92594000-925c0fff r-- 87d000 2d000 /data/app/com.example.app-2/base.apk
+925c1000-927d3fff r-- a37000 213000 /data/app/com.example.app-2/base.apk
+927d4000-92e07fff r-- 24a000 634000 /data/app/com.example.app-2/base.apk
+92e08000-92e37fff r-- a931000 30000 /data/app/com.example.app-2/base.apk
+92e38000-92e86fff r-x a961000 4f000 /data/app/com.example.app-2/base.apk
+92e87000-92e8afff rw- a9b0000 4000 /data/app/com.example.app-2/base.apk
+92e8b000-92e8bfff rw- 0 1000
+92e8c000-92e9dfff r-- d5b0000 12000 /data/app/com.example.app-2/base.apk
+92e9e000-92ebcfff r-x d5c2000 1f000 /data/app/com.example.app-2/base.apk
+92ebd000-92ebefff rw- d5e1000 2000 /data/app/com.example.app-2/base.apk
+92ebf000-92ebffff rw- 0 1000
+'''
+
+# list of (address, size, path, offset) tuples that must appear in
+# _TEST_MEMORY_MAP. Not all sections need to be listed.
+_TEST_MEMORY_MAP_SECTIONS = [
+ (0x923aa000, 0x18f000, '/data/app/com.example.app-2/base.apk', 0x8a9000),
+ (0x9255c000, 0x038000, '/data/app/com.example.app-2/base.apk', 0x213000),
+ (0x92594000, 0x02d000, '/data/app/com.example.app-2/base.apk', 0x87d000),
+ (0x925c1000, 0x213000, '/data/app/com.example.app-2/base.apk', 0xa37000),
+]
+
+_EXPECTED_TEST_MEMORY_MAP = r'''memory map:
+12c00000-12ccafff rw- 0 cb000 /dev/ashmem/dalvik-main space (deleted)
+12ccb000-130cafff rw- cb000 400000 /dev/ashmem/dalvik-main space (deleted)
+130cb000-32bfffff --- 4cb000 1fb35000 /dev/ashmem/dalvik-main space (deleted)
+32c00000-32c00fff rw- 0 1000 /dev/ashmem/dalvik-main space 1 (deleted)
+32c01000-52bfffff --- 1000 1ffff000 /dev/ashmem/dalvik-main space 1 (deleted)
+6f3b8000-6fd90fff rw- 0 9d9000 /data/dalvik-cache/x86/system@framework@boot.art
+6fd91000-71c42fff r-- 0 1eb2000 /data/dalvik-cache/x86/system@framework@boot.oat
+71c43000-7393efff r-x 1eb2000 1cfc000 /data/dalvik-cache/x86/system@framework@boot.oat (load base 0x71c43000)
+7393f000-7393ffff rw- 3bae000 1000 /data/dalvik-cache/x86/system@framework@boot.oat
+73940000-73a1bfff rw- 0 dc000 /dev/ashmem/dalvik-zygote space (deleted)
+73a1c000-73a1cfff rw- 0 1000 /dev/ashmem/dalvik-non moving space (deleted)
+73a1d000-73a2dfff rw- 1000 11000 /dev/ashmem/dalvik-non moving space (deleted)
+73a2e000-77540fff --- 12000 3b13000 /dev/ashmem/dalvik-non moving space (deleted)
+77541000-7793ffff rw- 3b25000 3ff000 /dev/ashmem/dalvik-non moving space (deleted)
+923aa000-92538fff r-- 8a9000 18f000 /data/app/com.example.app-2/base.apk
+92539000-9255bfff r-- 0 23000 /data/data/com.example.app/app_data/paks/es.pak@162db1c6689
+9255c000-92593fff r-- 213000 38000 /data/app/com.example.app-2/base.apk
+92594000-925c0fff r-- 87d000 2d000 /data/app/com.example.app-2/base.apk
+925c1000-927d3fff r-- a37000 213000 /data/app/com.example.app-2/base.apk
+927d4000-92e07fff r-- 24a000 634000 /data/app/com.example.app-2/base.apk
+92e08000-92e37fff r-- a931000 30000 /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x0)
+92e38000-92e86fff r-x a961000 4f000 /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x30000)
+92e87000-92e8afff rw- a9b0000 4000 /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x7f000)
+92e8b000-92e8bfff rw- 0 1000
+92e8c000-92e9dfff r-- d5b0000 12000 /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x0)
+92e9e000-92ebcfff r-x d5c2000 1f000 /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x12000)
+92ebd000-92ebefff rw- d5e1000 2000 /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x31000)
+92ebf000-92ebffff rw- 0 1000
+'''
+
+# Example stack section, taken from the same tombstone that _TEST_MEMORY_MAP
+# was extracted from.
+_TEST_STACK = r'''stack:
+ bf89a070 b7439468 /system/lib/libc.so
+ bf89a074 bf89a1e4 [stack]
+ bf89a078 932d4000 /data/app/com.example.app-2/base.apk
+ bf89a07c b73bfbc9 /system/lib/libc.so (pthread_mutex_lock+65)
+ bf89a080 00000000
+ bf89a084 4000671c /dev/ashmem/dalvik-main space 1 (deleted)
+ bf89a088 932d1d86 /data/app/com.example.app-2/base.apk
+ bf89a08c b743671c /system/lib/libc.so
+ bf89a090 b77f8c00 /system/bin/linker
+ bf89a094 b743cc90
+ bf89a098 932d1d4a /data/app/com.example.app-2/base.apk
+ bf89a09c b73bf271 /system/lib/libc.so (__pthread_internal_find(long)+65)
+ bf89a0a0 b743cc90
+ bf89a0a4 bf89a0b0 [stack]
+ bf89a0a8 bf89a0b8 [stack]
+ bf89a0ac 00000008
+ ........ ........
+ #00 bf89a0b0 00000006
+ bf89a0b4 00000002
+ bf89a0b8 b743671c /system/lib/libc.so
+ bf89a0bc b73bf5d9 /system/lib/libc.so (pthread_kill+71)
+ #01 bf89a0c0 00006937
+ bf89a0c4 00006937
+ bf89a0c8 00000006
+ bf89a0cc b77fd3a9 /system/bin/app_process32 (sigprocmask+141)
+ bf89a0d0 00000002
+ bf89a0d4 bf89a0ec [stack]
+ bf89a0d8 00000000
+ bf89a0dc b743671c /system/lib/libc.so
+ bf89a0e0 bf89a12c [stack]
+ bf89a0e4 bf89a1e4 [stack]
+ bf89a0e8 932d1d4a /data/app/com.example.app-2/base.apk
+ bf89a0ec b7365206 /system/lib/libc.so (raise+37)
+ #02 bf89a0f0 b77f8c00 /system/bin/linker
+ bf89a0f4 00000006
+ bf89a0f8 b7439468 /system/lib/libc.so
+ bf89a0fc b743671c /system/lib/libc.so
+ bf89a100 bf89a12c [stack]
+ bf89a104 b743671c /system/lib/libc.so
+ bf89a108 bf89a12c [stack]
+ bf89a10c b735e9e5 /system/lib/libc.so (abort+81)
+ #03 bf89a110 00000006
+ bf89a114 bf89a12c [stack]
+ bf89a118 00000000
+ bf89a11c b55a3d3b /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::DefaultLogHandler(google::protobuf::LogLevel, char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+99)
+ bf89a120 b7439468 /system/lib/libc.so
+ bf89a124 b55ba38d /system/lib/libprotobuf-cpp-lite.so
+ bf89a128 b55ba408 /system/lib/libprotobuf-cpp-lite.so
+ bf89a12c ffffffdf
+ bf89a130 0000003d
+ bf89a134 adfedf00 [anon:libc_malloc]
+ bf89a138 bf89a158 [stack]
+ #04 bf89a13c a0cee7f0 /data/app/com.example.app-2/base.apk
+ bf89a140 b55c1cb0 /system/lib/libprotobuf-cpp-lite.so
+ bf89a144 bf89a1e4 [stack]
+'''
+
+# Expected value of _TEST_STACK after translation of addresses in the APK
+# into offsets into libraries.
+_EXPECTED_STACK = r'''stack:
+ bf89a070 b7439468 /system/lib/libc.so
+ bf89a074 bf89a1e4 [stack]
+ bf89a078 932d4000 /data/app/com.example.app-2/base.apk
+ bf89a07c b73bfbc9 /system/lib/libc.so (pthread_mutex_lock+65)
+ bf89a080 00000000
+ bf89a084 4000671c /dev/ashmem/dalvik-main space 1 (deleted)
+ bf89a088 932d1d86 /data/app/com.example.app-2/base.apk
+ bf89a08c b743671c /system/lib/libc.so
+ bf89a090 b77f8c00 /system/bin/linker
+ bf89a094 b743cc90
+ bf89a098 932d1d4a /data/app/com.example.app-2/base.apk
+ bf89a09c b73bf271 /system/lib/libc.so (__pthread_internal_find(long)+65)
+ bf89a0a0 b743cc90
+ bf89a0a4 bf89a0b0 [stack]
+ bf89a0a8 bf89a0b8 [stack]
+ bf89a0ac 00000008
+ ........ ........
+ #00 bf89a0b0 00000006
+ bf89a0b4 00000002
+ bf89a0b8 b743671c /system/lib/libc.so
+ bf89a0bc b73bf5d9 /system/lib/libc.so (pthread_kill+71)
+ #01 bf89a0c0 00006937
+ bf89a0c4 00006937
+ bf89a0c8 00000006
+ bf89a0cc b77fd3a9 /system/bin/app_process32 (sigprocmask+141)
+ bf89a0d0 00000002
+ bf89a0d4 bf89a0ec [stack]
+ bf89a0d8 00000000
+ bf89a0dc b743671c /system/lib/libc.so
+ bf89a0e0 bf89a12c [stack]
+ bf89a0e4 bf89a1e4 [stack]
+ bf89a0e8 932d1d4a /data/app/com.example.app-2/base.apk
+ bf89a0ec b7365206 /system/lib/libc.so (raise+37)
+ #02 bf89a0f0 b77f8c00 /system/bin/linker
+ bf89a0f4 00000006
+ bf89a0f8 b7439468 /system/lib/libc.so
+ bf89a0fc b743671c /system/lib/libc.so
+ bf89a100 bf89a12c [stack]
+ bf89a104 b743671c /system/lib/libc.so
+ bf89a108 bf89a12c [stack]
+ bf89a10c b735e9e5 /system/lib/libc.so (abort+81)
+ #03 bf89a110 00000006
+ bf89a114 bf89a12c [stack]
+ bf89a118 00000000
+ bf89a11c b55a3d3b /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::DefaultLogHandler(google::protobuf::LogLevel, char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+99)
+ bf89a120 b7439468 /system/lib/libc.so
+ bf89a124 b55ba38d /system/lib/libprotobuf-cpp-lite.so
+ bf89a128 b55ba408 /system/lib/libprotobuf-cpp-lite.so
+ bf89a12c ffffffdf
+ bf89a130 0000003d
+ bf89a134 adfedf00 [anon:libc_malloc]
+ bf89a138 bf89a158 [stack]
+ #04 bf89a13c a0cee7f0 /data/app/com.example.app-2/base.apk
+ bf89a140 b55c1cb0 /system/lib/libprotobuf-cpp-lite.so
+ bf89a144 bf89a1e4 [stack]
+'''
+
+_TEST_BACKTRACE = r'''backtrace:
+ #00 pc 00084126 /system/lib/libc.so (tgkill+22)
+ #01 pc 000815d8 /system/lib/libc.so (pthread_kill+70)
+ #02 pc 00027205 /system/lib/libc.so (raise+36)
+ #03 pc 000209e4 /system/lib/libc.so (abort+80)
+ #04 pc 0000cf73 /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogMessage::Finish()+117)
+ #05 pc 0000cf8e /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogFinisher::operator=(google::protobuf::internal::LogMessage&)+26)
+ #06 pc 0000d27f /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::VerifyVersion(int, int, char const*)+574)
+ #07 pc 007cd236 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #08 pc 000111a9 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000)
+ #09 pc 00013228 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000)
+ #10 pc 000131de /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000)
+ #11 pc 007cd2d8 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #12 pc 007cd956 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #13 pc 007c2d4a /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #14 pc 009fc9f1 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #15 pc 009fc8ea /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #16 pc 00561c63 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #17 pc 0106fbdb /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #18 pc 004d7371 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #19 pc 004d8159 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #20 pc 004d7b96 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #21 pc 004da4b6 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #22 pc 005ab66c /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
+ #23 pc 005afca2 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
+ #24 pc 0000cae8 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
+ #25 pc 00ce864f /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
+ #26 pc 00ce8dfa /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
+ #27 pc 00ce74c6 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
+ #28 pc 00004616 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x961e000)
+ #29 pc 00ce8215 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
+ #30 pc 0013d8c7 /system/lib/libart.so (art_quick_generic_jni_trampoline+71)
+ #31 pc 00137c52 /system/lib/libart.so (art_quick_invoke_static_stub+418)
+ #32 pc 00143651 /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+353)
+ #33 pc 005e06ae /system/lib/libart.so (artInterpreterToCompiledCodeBridge+190)
+ #34 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #35 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
+ #36 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
+ #37 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
+ #38 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #39 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
+ #40 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
+ #41 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
+ #42 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #43 pc 0032ebf9 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)2, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+297)
+ #44 pc 000fc955 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+30485)
+ #45 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
+ #46 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #47 pc 0033090c /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)4, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+636)
+ #48 pc 000fc67f /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29759)
+ #49 pc 00300700 /system/lib/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame*)+128)
+ #50 pc 00667c73 /system/lib/libart.so (artQuickToInterpreterBridge+808)
+ #51 pc 0013d98d /system/lib/libart.so (art_quick_to_interpreter_bridge+77)
+ #52 pc 7264bc5b /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000)
+'''
+
+_EXPECTED_BACKTRACE = r'''backtrace:
+ #00 pc 00084126 /system/lib/libc.so (tgkill+22)
+ #01 pc 000815d8 /system/lib/libc.so (pthread_kill+70)
+ #02 pc 00027205 /system/lib/libc.so (raise+36)
+ #03 pc 000209e4 /system/lib/libc.so (abort+80)
+ #04 pc 0000cf73 /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogMessage::Finish()+117)
+ #05 pc 0000cf8e /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogFinisher::operator=(google::protobuf::internal::LogMessage&)+26)
+ #06 pc 0000d27f /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::VerifyVersion(int, int, char const*)+574)
+ #07 pc 007cd236 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #08 pc 000111a9 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000)
+ #09 pc 00013228 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000)
+ #10 pc 000131de /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000)
+ #11 pc 007cd2d8 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #12 pc 007cd956 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #13 pc 007c2d4a /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #14 pc 009fc9f1 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #15 pc 009fc8ea /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #16 pc 00561c63 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #17 pc 0106fbdb /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #18 pc 004d7371 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #19 pc 004d8159 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #20 pc 004d7b96 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #21 pc 004da4b6 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #22 pc 005ab66c /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
+ #23 pc 005afca2 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
+ #24 pc 0000cae8 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
+ #25 pc 00ce864f /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
+ #26 pc 00ce8dfa /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
+ #27 pc 00ce74c6 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
+ #28 pc 00004616 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libembedder.cr.so (offset 0x28000)
+ #29 pc 00ce8215 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
+ #30 pc 0013d8c7 /system/lib/libart.so (art_quick_generic_jni_trampoline+71)
+ #31 pc 00137c52 /system/lib/libart.so (art_quick_invoke_static_stub+418)
+ #32 pc 00143651 /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+353)
+ #33 pc 005e06ae /system/lib/libart.so (artInterpreterToCompiledCodeBridge+190)
+ #34 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #35 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
+ #36 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
+ #37 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
+ #38 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #39 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
+ #40 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
+ #41 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
+ #42 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #43 pc 0032ebf9 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)2, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+297)
+ #44 pc 000fc955 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+30485)
+ #45 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
+ #46 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
+ #47 pc 0033090c /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)4, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+636)
+ #48 pc 000fc67f /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29759)
+ #49 pc 00300700 /system/lib/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame*)+128)
+ #50 pc 00667c73 /system/lib/libart.so (artQuickToInterpreterBridge+808)
+ #51 pc 0013d98d /system/lib/libart.so (art_quick_to_interpreter_bridge+77)
+ #52 pc 7264bc5b /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000)
+'''
+
+_EXPECTED_BACKTRACE_OFFSETS_MAP = {
+ '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so':
+ set([
+ 0x1c000 + 0x111a9,
+ 0x1c000 + 0x13228,
+ 0x1c000 + 0x131de,
+ ]),
+
+ '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so':
+ set([
+ 0x90e000 + 0x7cd236,
+ 0x90e000 + 0x7cd2d8,
+ 0x90e000 + 0x7cd956,
+ 0x90e000 + 0x7c2d4a,
+ 0x90e000 + 0x9fc9f1,
+ 0x90e000 + 0x9fc8ea,
+ 0x90e000 + 0x561c63,
+ 0x90e000 + 0x106fbdb,
+ 0x90e000 + 0x4d7371,
+ 0x90e000 + 0x4d8159,
+ 0x90e000 + 0x4d7b96,
+ 0x90e000 + 0x4da4b6,
+ 0x90e000 + 0xcae8,
+ ]),
+ '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so':
+ set([
+ 0xc2d000 + 0x5ab66c,
+ 0xc2d000 + 0x5afca2,
+ 0xc2d000 + 0xce864f,
+ 0xc2d000 + 0xce8dfa,
+ 0xc2d000 + 0xce74c6,
+ 0xc2d000 + 0xce8215,
+ ]),
+ '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libembedder.cr.so':
+ set([
+ 0x28000 + 0x4616,
+ ])
+}
+
+# pylint: enable=line-too-long
+
+_ONE_MB = 1024 * 1024
+_TEST_SYMBOL_DATA = {
+ # Regular symbols
+ 0: 'mock_sym_for_addr_0 [mock_src/libmock1.so.c:0]',
+ 0x1000: 'mock_sym_for_addr_4096 [mock_src/libmock1.so.c:4096]',
+
+ # Symbols without source file path.
+ _ONE_MB: 'mock_sym_for_addr_1048576 [??:0]',
+ _ONE_MB + 0x8234: 'mock_sym_for_addr_1081908 [??:0]',
+
+ # Unknown symbol.
+ 2 * _ONE_MB: '?? [??:0]',
+
+ # Inlined symbol.
+ 3 * _ONE_MB:
+ 'mock_sym_for_addr_3145728_inner [mock_src/libmock1.so.c:3145728]',
+}
+
+@contextlib.contextmanager
+def _TempDir():
+ dirname = tempfile.mkdtemp()
+ try:
+ yield dirname
+ finally:
+ shutil.rmtree(dirname)
+
+
+def _TouchFile(path):
+ # Create parent directories.
+ try:
+ os.makedirs(os.path.dirname(path))
+ except OSError:
+ pass
+ with open(path, 'a'):
+ os.utime(path, None)
+
+class MockApkTranslator(object):
+ """A mock ApkLibraryPathTranslator object used for testing."""
+
+ # Regex that matches the content of APK native library map files generated
+ # with apk_lib_dump.py.
+ _RE_MAP_FILE = re.compile(
+ r'0x(?P<file_start>[0-9a-f]+)\s+' +
+ r'0x(?P<file_end>[0-9a-f]+)\s+' +
+ r'0x(?P<file_size>[0-9a-f]+)\s+' +
+ r'0x(?P<lib_path>[0-9a-f]+)\s+')
+
+ def __init__(self, test_apk_libs=None):
+ """Initialize instance.
+
+ Args:
+ test_apk_libs: Optional list of (file_start, file_end, size, lib_path)
+ tuples, like _TEST_APK_LIBS for example. This will be used to
+ implement TranslatePath().
+ """
+ self._apk_libs = []
+ if test_apk_libs:
+ self._AddLibEntries(test_apk_libs)
+
+ def _AddLibEntries(self, entries):
+ self._apk_libs = sorted(self._apk_libs + entries,
+ lambda x, y: cmp(x[0], y[0]))
+
+ def ReadMapFile(self, file_path):
+ """Read an .apk.native-libs file that was produced with apk_lib_dump.py.
+
+ Args:
+ file_path: input path to .apk.native-libs file. Its format is
+ essentially: 0x<start> 0x<end> 0x<size> <library-path>
+ """
+ new_libs = []
+ with open(file_path) as f:
+ for line in f.readlines():
+ m = MockApkTranslator._RE_MAP_FILE.match(line)
+ if m:
+ file_start = int(m.group('file_start'), 16)
+ file_end = int(m.group('file_end'), 16)
+ file_size = int(m.group('file_size'), 16)
+ lib_path = m.group('lib_path')
+ # Sanity check
+ if file_start + file_size != file_end:
+ logging.warning('%s: Inconsistent (start, end, size) values '
+ '(0x%x, 0x%x, 0x%x)',
+ file_path, file_start, file_end, file_size)
+ else:
+ new_libs.append((file_start, file_end, file_size, lib_path))
+
+ self._AddLibEntries(new_libs)
+
+ def TranslatePath(self, lib_path, lib_offset):
+ """Translate an APK file path + offset into a library path + offset."""
+ min_pos = 0
+ max_pos = len(self._apk_libs)
+ while min_pos < max_pos:
+ mid_pos = (min_pos + max_pos) / 2
+ mid_entry = self._apk_libs[mid_pos]
+ mid_offset = mid_entry[0]
+ mid_size = mid_entry[2]
+ if lib_offset < mid_offset:
+ max_pos = mid_pos
+ elif lib_offset >= mid_offset + mid_size:
+ min_pos = mid_pos + 1
+ else:
+ # Found it
+ new_path = '%s!lib/%s' % (lib_path, mid_entry[3])
+ new_offset = lib_offset - mid_offset
+ return (new_path, new_offset)
+
+ return lib_path, lib_offset
+
+
+class HostLibraryFinderTest(unittest.TestCase):
+
+ def testEmpty(self):
+ finder = symbol_utils.HostLibraryFinder()
+ self.assertIsNone(finder.Find('/data/data/com.example.app-1/lib/libfoo.so'))
+ self.assertIsNone(
+ finder.Find('/data/data/com.example.app-1/base.apk!lib/libfoo.so'))
+
+
+ def testSimpleDirectory(self):
+ finder = symbol_utils.HostLibraryFinder()
+ with _TempDir() as tmp_dir:
+ host_libfoo_path = os.path.join(tmp_dir, 'libfoo.so')
+ host_libbar_path = os.path.join(tmp_dir, 'libbar.so')
+ _TouchFile(host_libfoo_path)
+ _TouchFile(host_libbar_path)
+
+ finder.AddSearchDir(tmp_dir)
+
+ # Regular library path (extracted at installation by the PackageManager).
+ # Note that the extraction path has changed between Android releases,
+ # i.e. it can be /data/app/, /data/data/ or /data/app-lib/ depending
+ # on the system.
+ self.assertEqual(
+ host_libfoo_path,
+ finder.Find('/data/app-lib/com.example.app-1/lib/libfoo.so'))
+
+ # Verify that the path doesn't really matter
+ self.assertEqual(
+ host_libfoo_path,
+ finder.Find('/whatever/what.apk!lib/libfoo.so'))
+
+ self.assertEqual(
+ host_libbar_path,
+ finder.Find('/data/data/com.example.app-1/lib/libbar.so'))
+
+ self.assertIsNone(
+ finder.Find('/data/data/com.example.app-1/lib/libunknown.so'))
+
+
+ def testMultipleDirectories(self):
+ with _TempDir() as tmp_dir:
+ # Create the following files:
+ # <tmp_dir>/aaa/
+ # libfoo.so
+ # <tmp_dir>/bbb/
+ # libbar.so
+ # libfoo.so (this one should never be seen because 'aaa'
+ # shall be first in the search path list).
+ #
+ aaa_dir = os.path.join(tmp_dir, 'aaa')
+ bbb_dir = os.path.join(tmp_dir, 'bbb')
+ os.makedirs(aaa_dir)
+ os.makedirs(bbb_dir)
+
+ host_libfoo_path = os.path.join(aaa_dir, 'libfoo.so')
+ host_libbar_path = os.path.join(bbb_dir, 'libbar.so')
+ host_libfoo2_path = os.path.join(bbb_dir, 'libfoo.so')
+
+ _TouchFile(host_libfoo_path)
+ _TouchFile(host_libbar_path)
+ _TouchFile(host_libfoo2_path)
+
+ finder = symbol_utils.HostLibraryFinder()
+ finder.AddSearchDir(aaa_dir)
+ finder.AddSearchDir(bbb_dir)
+
+ self.assertEqual(
+ host_libfoo_path,
+ finder.Find('/data/data/com.example.app-1/lib/libfoo.so'))
+
+ self.assertEqual(
+ host_libfoo_path,
+ finder.Find('/data/whatever/base.apk!lib/libfoo.so'))
+
+ self.assertEqual(
+ host_libbar_path,
+ finder.Find('/data/data/com.example.app-1/lib/libbar.so'))
+
+ self.assertIsNone(
+ finder.Find('/data/data/com.example.app-1/lib/libunknown.so'))
+
+
+class ElfSymbolResolverTest(unittest.TestCase):
+
+ def testCreation(self):
+ resolver = symbol_utils.ElfSymbolResolver(
+ addr2line_path_for_tests=_MOCK_A2L_PATH)
+ self.assertTrue(resolver)
+
+ def testWithSimpleOffsets(self):
+ resolver = symbol_utils.ElfSymbolResolver(
+ addr2line_path_for_tests=_MOCK_A2L_PATH)
+ resolver.SetAndroidAbi('ignored-abi')
+
+ for addr, expected_sym in _TEST_SYMBOL_DATA.iteritems():
+ self.assertEqual(resolver.FindSymbolInfo('/some/path/libmock1.so', addr),
+ expected_sym)
+
+ def testWithPreResolvedSymbols(self):
+ resolver = symbol_utils.ElfSymbolResolver(
+ addr2line_path_for_tests=_MOCK_A2L_PATH)
+ resolver.SetAndroidAbi('ignored-abi')
+ resolver.AddLibraryOffsets('/some/path/libmock1.so',
+ _TEST_SYMBOL_DATA.keys())
+
+ resolver.DisallowSymbolizerForTesting()
+
+ for addr, expected_sym in _TEST_SYMBOL_DATA.iteritems():
+ sym_info = resolver.FindSymbolInfo('/some/path/libmock1.so', addr)
+ self.assertIsNotNone(sym_info, 'None symbol info for addr %x' % addr)
+ self.assertEqual(
+ sym_info, expected_sym,
+ 'Invalid symbol info for addr %x [%s] expected [%s]' % (
+ addr, sym_info, expected_sym))
+
+
+class MemoryMapTest(unittest.TestCase):
+
+ def testCreation(self):
+ mem_map = symbol_utils.MemoryMap('test-abi32')
+ self.assertIsNone(mem_map.FindSectionForAddress(0))
+
+ def testParseLines(self):
+ mem_map = symbol_utils.MemoryMap('test-abi32')
+ mem_map.ParseLines(_TEST_MEMORY_MAP.splitlines())
+ for exp_addr, exp_size, exp_path, exp_offset in _TEST_MEMORY_MAP_SECTIONS:
+ text = '(addr:%x, size:%x, path:%s, offset=%x)' % (
+ exp_addr, exp_size, exp_path, exp_offset)
+
+ t = mem_map.FindSectionForAddress(exp_addr)
+ self.assertTrue(t, 'Could not find %s' % text)
+ self.assertEqual(t.address, exp_addr)
+ self.assertEqual(t.size, exp_size)
+ self.assertEqual(t.offset, exp_offset)
+ self.assertEqual(t.path, exp_path)
+
+ def testTranslateLine(self):
+ android_abi = 'test-abi'
+ apk_translator = MockApkTranslator(_TEST_APK_LIBS)
+ mem_map = symbol_utils.MemoryMap(android_abi)
+ for line, expected_line in zip(_TEST_MEMORY_MAP.splitlines(),
+ _EXPECTED_TEST_MEMORY_MAP.splitlines()):
+ self.assertEqual(mem_map.TranslateLine(line, apk_translator),
+ expected_line)
+
+class StackTranslatorTest(unittest.TestCase):
+
+ def testSimpleStack(self):
+ android_abi = 'test-abi32'
+ mem_map = symbol_utils.MemoryMap(android_abi)
+ mem_map.ParseLines(_TEST_MEMORY_MAP)
+ apk_translator = MockApkTranslator(_TEST_APK_LIBS)
+ stack_translator = symbol_utils.StackTranslator(android_abi, mem_map,
+ apk_translator)
+ input_stack = _TEST_STACK.splitlines()
+ expected_stack = _EXPECTED_STACK.splitlines()
+ self.assertEqual(len(input_stack), len(expected_stack))
+ for stack_line, expected_line in zip(input_stack, expected_stack):
+ new_line = stack_translator.TranslateLine(stack_line)
+ self.assertEqual(new_line, expected_line)
+
+
+class MockSymbolResolver(symbol_utils.SymbolResolver):
+
+ # A regex matching a symbol definition as it appears in a test symbol file.
+ # Format is: <hex-offset> <whitespace> <symbol-string>
+ _RE_SYMBOL_DEFINITION = re.compile(
+ r'(?P<offset>[0-9a-f]+)\s+(?P<symbol>.*)')
+
+ def __init__(self):
+ super(MockSymbolResolver, self).__init__()
+ self._map = collections.defaultdict(dict)
+
+ def AddTestLibrarySymbols(self, lib_name, offsets_map):
+ """Add a new test entry for a given library name.
+
+ Args:
+ lib_name: Library name (e.g. 'libfoo.so')
+ offsets_map: A mapping from offsets to symbol info strings.
+ """
+ self._map[lib_name] = offsets_map
+
+ def ReadTestFile(self, file_path, lib_name):
+ """Read a single test symbol file, matching a given library.
+
+ Args:
+ file_path: Input file path.
+ lib_name: Library name these symbols correspond to (e.g. 'libfoo.so')
+ """
+ with open(file_path) as f:
+ for line in f.readlines():
+ line = line.rstrip()
+ m = MockSymbolResolver._RE_SYMBOL_DEFINITION.match(line)
+ if m:
+ offset = int(m.group('offset'))
+ symbol = m.group('symbol')
+ self._map[lib_name][offset] = symbol
+
+ def ReadTestFilesInDir(self, dir_path, file_suffix):
+ """Read all symbol test files in a given directory.
+
+ Args:
+ dir_path: Directory path.
+ file_suffix: File suffix used to detect test symbol files.
+ """
+ for filename in os.listdir(dir_path):
+ if filename.endswith(file_suffix):
+ lib_name = filename[:-len(file_suffix)]
+ self.ReadTestFile(os.path.join(dir_path, filename), lib_name)
+
+ def FindSymbolInfo(self, device_path, device_offset):
+ """Implement SymbolResolver.FindSymbolInfo."""
+ lib_name = os.path.basename(device_path)
+ offsets = self._map.get(lib_name)
+ if not offsets:
+ return None
+
+ return offsets.get(device_offset)
+
+
+class BacktraceTranslatorTest(unittest.TestCase):
+
+ def testEmpty(self):
+ android_abi = 'test-abi'
+ apk_translator = MockApkTranslator()
+ backtrace_translator = symbol_utils.BacktraceTranslator(android_abi,
+ apk_translator)
+ self.assertTrue(backtrace_translator)
+
+ def testFindLibraryOffsets(self):
+ android_abi = 'test-abi'
+ apk_translator = MockApkTranslator(_TEST_APK_LIBS)
+ backtrace_translator = symbol_utils.BacktraceTranslator(android_abi,
+ apk_translator)
+ input_backtrace = _EXPECTED_BACKTRACE.splitlines()
+ expected_lib_offsets_map = _EXPECTED_BACKTRACE_OFFSETS_MAP
+ offset_map = backtrace_translator.FindLibraryOffsets(input_backtrace)
+ for lib_path, offsets in offset_map.iteritems():
+ self.assertTrue(lib_path in expected_lib_offsets_map,
+ '%s is not in expected library-offsets map!' % lib_path)
+ sorted_offsets = sorted(offsets)
+ sorted_expected_offsets = sorted(expected_lib_offsets_map[lib_path])
+ self.assertEqual(sorted_offsets, sorted_expected_offsets,
+ '%s has invalid offsets %s expected %s' % (
+ lib_path, sorted_offsets, sorted_expected_offsets))
+
+ def testTranslateLine(self):
+ android_abi = 'test-abi'
+ apk_translator = MockApkTranslator(_TEST_APK_LIBS)
+ backtrace_translator = symbol_utils.BacktraceTranslator(android_abi,
+ apk_translator)
+ input_backtrace = _TEST_BACKTRACE.splitlines()
+ expected_backtrace = _EXPECTED_BACKTRACE.splitlines()
+ self.assertEqual(len(input_backtrace), len(expected_backtrace))
+ for trace_line, expected_line in zip(input_backtrace, expected_backtrace):
+ line = backtrace_translator.TranslateLine(trace_line,
+ MockSymbolResolver())
+ self.assertEqual(line, expected_line)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/build/android/pylib/utils/app_bundle_utils.py b/chromium/build/android/pylib/utils/app_bundle_utils.py
new file mode 100644
index 00000000000..946e62661ba
--- /dev/null
+++ b/chromium/build/android/pylib/utils/app_bundle_utils.py
@@ -0,0 +1,66 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'gyp'))
+
+from util import build_utils
+from util import md5_check
+import bundletool
+
+def GenerateBundleApks(bundle_path, bundle_apks_path, aapt2_path,
+ keystore_path, keystore_password, keystore_alias):
+ """Generate an .apks archive from a an app bundle if needed.
+
+ Args:
+ bundle_path: Input bundle file path.
+ bundle_apks_path: Output bundle .apks archive path. Name must end with
+ '.apks' or this operation will fail.
+ aapt2_path: Path to aapt2 build tool.
+ keystore_path: Path to keystore.
+ keystore_password: Keystore password, as a string.
+ keystore_alias: Keystore signing key alias.
+ """
+ # NOTE: BUNDLETOOL_JAR_PATH is added to input_strings, rather than
+ # input_paths, to speed up MD5 computations by about 400ms (the .jar file
+ # contains thousands of class files which are checked independently,
+ # resulting in an .md5.stamp of more than 60000 lines!).
+ input_paths = [
+ bundle_path,
+ aapt2_path,
+ keystore_path
+ ]
+ input_strings = [
+ keystore_password,
+ keystore_alias,
+ bundletool.BUNDLETOOL_JAR_PATH,
+ # NOTE: BUNDLETOOL_VERSION is already part of BUNDLETOOL_JAR_PATH, but
+ # it's simpler to assume that this may not be the case in the future.
+ bundletool.BUNDLETOOL_VERSION
+ ]
+ output_paths = [bundle_apks_path]
+
+ def rebuild():
+ logging.info('Building %s', os.path.basename(bundle_apks_path))
+ with build_utils.AtomicOutput(bundle_apks_path) as tmp_apks:
+ cmd_args = [
+ 'java', '-jar', bundletool.BUNDLETOOL_JAR_PATH, 'build-apks',
+ '--aapt2=%s' % aapt2_path,
+ '--output=%s' % tmp_apks.name,
+ '--bundle=%s' % bundle_path,
+ '--ks=%s' % keystore_path,
+ '--ks-pass=pass:%s' % keystore_password,
+ '--ks-key-alias=%s' % keystore_alias,
+ '--overwrite',
+ ]
+ build_utils.CheckOutput(cmd_args)
+
+ md5_check.CallAndRecordIfStale(
+ rebuild,
+ input_paths=input_paths,
+ input_strings=input_strings,
+ output_paths=output_paths)
diff --git a/chromium/build/android/pylib/utils/device_dependencies.py b/chromium/build/android/pylib/utils/device_dependencies.py
index 6481d43881f..bccc1c37a67 100644
--- a/chromium/build/android/pylib/utils/device_dependencies.py
+++ b/chromium/build/android/pylib/utils/device_dependencies.py
@@ -16,6 +16,7 @@ _BLACKLIST = [
re.compile(r'.*\.py'), # Some test_support targets include python deps.
re.compile(r'.*\.stamp'), # Stamp files should never be included.
re.compile(r'.*\.apk'), # Should be installed separately.
+ re.compile(r'.*lib.java/.*'), # Never need java intermediates.
# Chrome external extensions config file.
re.compile(r'.*external_extensions\.json'),
diff --git a/chromium/build/android/pylib/utils/google_storage_helper.py b/chromium/build/android/pylib/utils/google_storage_helper.py
index 3101d71439a..d184810517a 100644
--- a/chromium/build/android/pylib/utils/google_storage_helper.py
+++ b/chromium/build/android/pylib/utils/google_storage_helper.py
@@ -79,10 +79,7 @@ def exists(name, bucket):
cmd = [_GSUTIL_PATH, '-q', 'stat', gs_path]
return_code = cmd_helper.RunCmd(cmd)
- if return_code == 0:
- return True
- else:
- return False
+ return return_code == 0
# TODO(jbudorick): Delete this function. Only one user of it.
diff --git a/chromium/build/android/pylib/utils/logdog_helper.py b/chromium/build/android/pylib/utils/logdog_helper.py
index 58fe7814bea..68a7ba57ab7 100644
--- a/chromium/build/android/pylib/utils/logdog_helper.py
+++ b/chromium/build/android/pylib/utils/logdog_helper.py
@@ -92,4 +92,3 @@ def get_viewer_url(name):
def get_logdog_client():
logging.info('Getting logdog client.')
return bootstrap.ButlerBootstrap.probe().stream_client()
-
diff --git a/chromium/build/android/pylib/utils/maven_downloader.py b/chromium/build/android/pylib/utils/maven_downloader.py
index c5b0badf29c..c60b0140acc 100755
--- a/chromium/build/android/pylib/utils/maven_downloader.py
+++ b/chromium/build/android/pylib/utils/maven_downloader.py
@@ -36,7 +36,8 @@ class MavenDownloader(object):
_REMOTE_REPO = 'https://maven.google.com'
# Default Maven repository.
- _DEFAULT_REPO_PATH = os.path.join(os.getenv('HOME'), '.m2', 'repository')
+ _DEFAULT_REPO_PATH = os.path.join(
+ os.path.expanduser('~'), '.m2', 'repository')
def __init__(self, debug=False):
self._repo_path = MavenDownloader._DEFAULT_REPO_PATH
diff --git a/chromium/build/android/pylib/utils/proguard.py b/chromium/build/android/pylib/utils/proguard.py
index c243ce9c9f3..2d439a52c32 100644
--- a/chromium/build/android/pylib/utils/proguard.py
+++ b/chromium/build/android/pylib/utils/proguard.py
@@ -31,15 +31,13 @@ _PROGUARD_ELEMENT_RES = [
_PROGUARD_INDENT_WIDTH = 2
_PROGUARD_ANNOTATION_VALUE_RE = re.compile(r'^(\s*?)- \S+? \[(.*)\]$')
-_PROGUARD_PATH_SDK = os.path.join(
- constants.PROGUARD_ROOT, 'lib', 'proguard.jar')
-_PROGUARD_PATH_BUILT = (
- os.path.join(os.environ['ANDROID_BUILD_TOP'], 'external', 'proguard',
- 'lib', 'proguard.jar')
- if 'ANDROID_BUILD_TOP' in os.environ else None)
-_PROGUARD_PATH = (
- _PROGUARD_PATH_SDK if os.path.exists(_PROGUARD_PATH_SDK)
- else _PROGUARD_PATH_BUILT)
+
+def _GetProguardPath():
+ # Use the one in lib.java rather than source tree because it is the one that
+ # is added to swarming .isolate files.
+ return os.path.join(
+ constants.GetOutDirectory(), 'lib.java', 'third_party', 'proguard',
+ 'proguard603.jar')
def Dump(jar_path):
@@ -96,7 +94,7 @@ def Dump(jar_path):
with tempfile.NamedTemporaryFile() as proguard_output:
cmd_helper.GetCmdStatusAndOutput([
'java',
- '-jar', _PROGUARD_PATH,
+ '-jar', _GetProguardPath(),
'-injars', jar_path,
'-dontshrink', '-dontoptimize', '-dontobfuscate', '-dontpreverify',
'-dump', proguard_output.name])
diff --git a/chromium/build/android/pylib/valgrind_tools.py b/chromium/build/android/pylib/valgrind_tools.py
index 3dc2488b269..4c27b083b7a 100644
--- a/chromium/build/android/pylib/valgrind_tools.py
+++ b/chromium/build/android/pylib/valgrind_tools.py
@@ -127,4 +127,3 @@ def PushFilesForTool(tool_name, device):
print 'Unknown tool %s, available tools: %s' % (
tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
sys.exit(1)
-
diff --git a/chromium/build/android/pylintrc b/chromium/build/android/pylintrc
index 8005a5d276c..2a721bf2709 100644
--- a/chromium/build/android/pylintrc
+++ b/chromium/build/android/pylintrc
@@ -4,7 +4,7 @@ max-line-length=80
[MESSAGES CONTROL]
-disable=abstract-class-not-used,bad-continuation,bad-indentation,duplicate-code,fixme,invalid-name,locally-disabled,locally-enabled,missing-docstring,star-args,too-few-public-methods,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-statements,
+disable=abstract-class-not-used,bad-continuation,bad-indentation,duplicate-code,fixme,invalid-name,locally-disabled,locally-enabled,missing-docstring,star-args,too-few-public-methods,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-statements,wrong-import-position
[REPORTS]
diff --git a/chromium/build/android/resource_sizes.py b/chromium/build/android/resource_sizes.py
index 6287d9842e4..d87a8e25a62 100755
--- a/chromium/build/android/resource_sizes.py
+++ b/chromium/build/android/resource_sizes.py
@@ -99,9 +99,9 @@ _BASE_CHART = {
}
_DUMP_STATIC_INITIALIZERS_PATH = os.path.join(
host_paths.DIR_SOURCE_ROOT, 'tools', 'linux', 'dump-static-initializers.py')
-# Pragma exists when enable_resource_whitelist_generation=true.
-_RC_HEADER_RE = re.compile(
- r'^#define (?P<name>\w+) (?:_Pragma\(.*?\) )?(?P<id>\d+)$')
+# Macro definitions look like (something, 123) when
+# enable_resource_whitelist_generation=true.
+_RC_HEADER_RE = re.compile(r'^#define (?P<name>\w+).* (?P<id>\d+)\)?$')
_RE_NON_LANGUAGE_PAK = re.compile(r'^assets/.*(resources|percent)\.pak$')
_RE_COMPRESSED_LANGUAGE_PAK = re.compile(
r'\.lpak$|^assets/(?!stored-locales/).*(?!resources|percent)\.pak$')
diff --git a/chromium/build/android/stacktrace/java_deobfuscate_test.py b/chromium/build/android/stacktrace/java_deobfuscate_test.py
index 58ab1e8f48a..6ee40ce5b5d 100755
--- a/chromium/build/android/stacktrace/java_deobfuscate_test.py
+++ b/chromium/build/android/stacktrace/java_deobfuscate_test.py
@@ -6,10 +6,14 @@
"""Tests for java_deobfuscate."""
import argparse
+import os
import subprocess
import sys
import tempfile
+import unittest
+# Set by command-line argument.
+_JAVA_DEOBFUSCATE_PATH = None
LINE_PREFIXES = [
'',
@@ -59,44 +63,75 @@ this.was.Deobfuscated: Error message
""".splitlines(True)
-def _RunTest(bin_path, map_file, prefix):
- cmd = [bin_path, map_file]
- payload = TEST_DATA
- expected_output = EXPECTED_OUTPUT
+class JavaDeobfuscateTest(unittest.TestCase):
- payload = [prefix + x for x in payload]
- expected_output = [prefix + x for x in expected_output]
+ def __init__(self, *args, **kwargs):
+ super(JavaDeobfuscateTest, self).__init__(*args, **kwargs)
+ self._map_file = None
- proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- actual_output = proc.communicate(''.join(payload))[0].splitlines(True)
- any_unexpected_failures = False
- for actual, expected in zip(actual_output, expected_output):
- if actual == expected:
- sys.stdout.write('Good: ' + actual)
- elif actual.replace('bar', 'someMethod') == expected:
- # TODO(agrieve): Fix ReTrace's ability to deobfuscated methods.
- sys.stdout.write('Fine: ' + actual)
- else:
- sys.stdout.write('BAD: ' + actual)
- any_unexpected_failures = True
- return not any_unexpected_failures
+ def setUp(self):
+ self._map_file = tempfile.NamedTemporaryFile()
+ self._map_file.write(TEST_MAP)
+ self._map_file.flush()
+ def tearDown(self):
+ if self._map_file:
+ self._map_file.close()
-def main():
- parser = argparse.ArgumentParser()
- parser.add_argument('path_to_java_deobfuscate')
- args = parser.parse_args()
+ def _testImpl(self, input_lines=None, expected_output_lines=None,
+ prefix=''):
+ self.assertTrue(bool(input_lines) == bool(expected_output_lines))
+
+ if not input_lines:
+ input_lines = [prefix + x for x in TEST_DATA]
+ if not expected_output_lines:
+ expected_output_lines = [prefix + x for x in EXPECTED_OUTPUT]
+
+ cmd = [_JAVA_DEOBFUSCATE_PATH, self._map_file.name]
+ proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ proc_output, _ = proc.communicate(''.join(input_lines))
+ actual_output_lines = proc_output.splitlines(True)
+ for actual, expected in zip(actual_output_lines, expected_output_lines):
+ self.assertTrue(
+ actual == expected or actual.replace('bar', 'someMethod') == expected,
+ msg=''.join([
+ 'Deobfuscation failed.\n',
+ ' actual: %s' % actual,
+ ' expected: %s' % expected]))
+
+ def testNoPrefix(self):
+ self._testImpl(prefix='')
- with tempfile.NamedTemporaryFile() as map_file:
- map_file.write(TEST_MAP)
- map_file.flush()
- passed = True
- for prefix in LINE_PREFIXES:
- if not _RunTest(args.path_to_java_deobfuscate, map_file.name, prefix):
- passed = False
- print 'Result:', 'PASS' if passed else 'FAIL'
- sys.exit(int(not passed))
+ def testThreadtimePrefix(self):
+ self._testImpl(prefix='09-08 14:38:35.535 18029 18084 E qcom_sensors_hal: ')
+
+ def testStandardPrefix(self):
+ self._testImpl(prefix='W/GCM (15158): ')
+
+ def testStandardPrefixWithPadding(self):
+ self._testImpl(prefix='W/GCM ( 158): ')
+
+ @unittest.skip('causes java_deobfuscate to hang, see crbug.com/876539')
+ def testIndefiniteHang(self):
+ # Test for crbug.com/876539.
+ self._testImpl(
+ input_lines=[
+ 'VFY: unable to resolve virtual method 2: LFOO;'
+ + '.onDescendantInvalidated '
+ + '(Landroid/view/View;Landroid/view/View;)V',
+ ],
+ expected_output_lines=[
+ 'VFY: unable to resolve virtual method 2: Lthis.was.Deobfuscated;'
+ + '.onDescendantInvalidated '
+ + '(Landroid/view/View;Landroid/view/View;)V',
+ ])
if __name__ == '__main__':
- main()
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--java-deobfuscate-path', type=os.path.realpath,
+ required=True)
+ known_args, unittest_args = parser.parse_known_args()
+ _JAVA_DEOBFUSCATE_PATH = known_args.java_deobfuscate_path
+ unittest_args = [sys.argv[0]] + unittest_args
+ unittest.main(argv=unittest_args)
diff --git a/chromium/build/android/test_runner.py b/chromium/build/android/test_runner.py
index a83d9ab6950..8512accd1b9 100755
--- a/chromium/build/android/test_runner.py
+++ b/chromium/build/android/test_runner.py
@@ -25,6 +25,7 @@ import unittest
# See http://crbug.com/724524 and https://bugs.python.org/issue7980.
import _strptime # pylint: disable=unused-import
+# pylint: disable=ungrouped-imports
from pylib.constants import host_paths
if host_paths.DEVIL_PATH not in sys.path:
@@ -49,7 +50,6 @@ from pylib.utils import logging_utils
from py_utils import contextlib_ext
-
_DEVIL_STATIC_CONFIG_FILE = os.path.abspath(os.path.join(
host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'devil_config.json'))
@@ -212,10 +212,12 @@ def AddCommonOptions(parser):
def ProcessCommonOptions(args):
"""Processes and handles all common options."""
run_tests_helper.SetLogLevel(args.verbose_count, add_handler=False)
+ # pylint: disable=redefined-variable-type
if args.verbose_count > 0:
handler = logging_utils.ColorStreamHandler()
else:
handler = logging.StreamHandler(sys.stdout)
+ # pylint: enable=redefined-variable-type
handler.setFormatter(run_tests_helper.CustomFormatter())
logging.getLogger().addHandler(handler)
@@ -429,6 +431,7 @@ def AddInstrumentationTestOptions(parser):
split_arg = arg.split(',')
if len(split_arg) != 2:
raise argparse.ArgumentError(
+ arg,
'Expected two comma-separated strings for --replace-system-package, '
'received %d' % len(split_arg))
PackageReplacement = collections.namedtuple('PackageReplacement',