Commit cdbfcdfd authored by Andrew Grieve's avatar Andrew Grieve Committed by Commit Bot

Android: Fix SuperSize attribution of .aar classes and resources

The source paths for these should point at the .aar file rather than
the extracted location of its contents.

Bug: 978310
Change-Id: Id3447a96589bce079b7351304a3e2098b33e8f5a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1984820
Commit-Queue: Eric Stevenson <estevenson@chromium.org>
Auto-Submit: Andrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarEric Stevenson <estevenson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#728112}
parent 34fba514
...@@ -91,6 +91,9 @@ def _PerformExtract(aar_file, output_dir, name_whitelist): ...@@ -91,6 +91,9 @@ def _PerformExtract(aar_file, output_dir, name_whitelist):
os.mkdir(tmp_dir) os.mkdir(tmp_dir)
build_utils.ExtractAll( build_utils.ExtractAll(
aar_file, path=tmp_dir, predicate=name_whitelist.__contains__) aar_file, path=tmp_dir, predicate=name_whitelist.__contains__)
# Write a breadcrumb so that SuperSize can attribute files back to the .aar.
with open(os.path.join(tmp_dir, 'source.info'), 'w') as f:
f.write('source={}\n'.format(aar_file))
shutil.rmtree(output_dir, ignore_errors=True) shutil.rmtree(output_dir, ignore_errors=True)
shutil.move(tmp_dir, output_dir) shutil.move(tmp_dir, output_dir)
...@@ -147,10 +150,12 @@ def main(): ...@@ -147,10 +150,12 @@ def main():
if args.ignore_resources: if args.ignore_resources:
names = [n for n in names if not n.startswith('res')] names = [n for n in names if not n.startswith('res')]
output_paths = [os.path.join(args.output_dir, n) for n in names]
output_paths.append(os.path.join(args.output_dir, 'source.info'))
md5_check.CallAndRecordIfStale( md5_check.CallAndRecordIfStale(
lambda: _PerformExtract(args.aar_file, args.output_dir, set(names)), lambda: _PerformExtract(args.aar_file, args.output_dir, set(names)),
input_paths=[args.aar_file], input_paths=[args.aar_file],
output_paths=[os.path.join(args.output_dir, n) for n in names]) output_paths=output_paths)
elif args.command == 'list': elif args.command == 'list':
aar_output_present = args.output != '-' and os.path.isfile(args.output) aar_output_present = args.output != '-' and os.path.isfile(args.output)
......
...@@ -60,29 +60,41 @@ def _MergeJarInfoFiles(output, inputs): ...@@ -60,29 +60,41 @@ def _MergeJarInfoFiles(output, inputs):
Args: Args:
output: output file path. output: output file path.
inputs: List of .info.jar or .jar files. inputs: List of .jar.info or .jar files.
""" """
info_data = dict() info_data = dict()
for path in inputs: for path in inputs:
# android_java_prebuilt adds jar files in the src directory (relative to # For non-prebuilts: .jar.info files are written by compile_java.py and map
# the output directory, usually ../../third_party/example.jar). # .class files to .java source paths.
# android_aar_prebuilt collects jar files in the aar file and uses the #
# java_prebuilt rule to generate gen/example/classes.jar files. # For prebuilts: No .jar.info file exists, we scan the .jar files here and
# We scan these prebuilt jars to parse each class path for the FQN. This # map .class files to the .jar.
# allows us to later map these classes back to their respective src #
# directories. # For .aar files: We look for a "source.info" file in the containing
# TODO(agrieve): This should probably also check that the mtime of the .info # directory in order to map classes back to the .aar (rather than mapping
# is newer than that of the .jar, or change prebuilts to always output # them to the extracted .jar file).
# .info files so that they always exist (and change the depfile to
# depend directly on them).
if path.endswith('.info'): if path.endswith('.info'):
info_data.update(jar_info_utils.ParseJarInfoFile(path)) info_data.update(jar_info_utils.ParseJarInfoFile(path))
else: else:
attributed_path = path
if not path.startswith('..'):
parent_path = os.path.dirname(path)
# See if it's an sub-jar within the .aar.
if os.path.basename(parent_path) == 'libs':
parent_path = os.path.dirname(parent_path)
aar_source_info_path = os.path.join(parent_path, 'source.info')
# source.info files exist only for jars from android_aar_prebuilt().
# E.g. Could have an java_prebuilt() pointing to a generated .jar.
if os.path.exists(aar_source_info_path):
attributed_path = jar_info_utils.ReadAarSourceInfo(
aar_source_info_path)
with zipfile.ZipFile(path) as zip_info: with zipfile.ZipFile(path) as zip_info:
for name in zip_info.namelist(): for name in zip_info.namelist():
fully_qualified_name = _FullJavaNameFromClassFilePath(name) fully_qualified_name = _FullJavaNameFromClassFilePath(name)
if fully_qualified_name: if fully_qualified_name:
info_data[fully_qualified_name] = '{}/{}'.format(path, name) info_data[fully_qualified_name] = '{}/{}'.format(
attributed_path, name)
# only_if_changed=False since no build rules depend on this as an input. # only_if_changed=False since no build rules depend on this as an input.
with build_utils.AtomicOutput(output, only_if_changed=False) as f: with build_utils.AtomicOutput(output, only_if_changed=False) as f:
...@@ -147,7 +159,7 @@ def main(args): ...@@ -147,7 +159,7 @@ def main(args):
# Don't bother re-running if no .info files have changed (saves ~250ms). # Don't bother re-running if no .info files have changed (saves ~250ms).
md5_check.CallAndRecordIfStale( md5_check.CallAndRecordIfStale(
lambda: _MergeJarInfoFiles(options.jar_info_path, jar_inputs), lambda: _MergeJarInfoFiles(options.jar_info_path, jar_inputs),
input_paths=jar_inputs, input_paths=jar_inputs + [__file__],
output_paths=[options.jar_info_path]) output_paths=[options.jar_info_path])
# Always recreate these (just as fast as md5 checking them). # Always recreate these (just as fast as md5 checking them).
......
...@@ -16,6 +16,7 @@ import sys ...@@ -16,6 +16,7 @@ import sys
import zipfile import zipfile
from util import build_utils from util import build_utils
from util import jar_info_utils
from util import manifest_utils from util import manifest_utils
from util import md5_check from util import md5_check
from util import resource_utils from util import resource_utils
...@@ -108,10 +109,21 @@ def _ZipResources(resource_dirs, zip_path, ignore_pattern): ...@@ -108,10 +109,21 @@ def _ZipResources(resource_dirs, zip_path, ignore_pattern):
files_to_zip = [] files_to_zip = []
path_info = resource_utils.ResourceInfoFile() path_info = resource_utils.ResourceInfoFile()
for index, resource_dir in enumerate(resource_dirs): for index, resource_dir in enumerate(resource_dirs):
attributed_aar = None
if not resource_dir.startswith('..'):
aar_source_info_path = os.path.join(
os.path.dirname(resource_dir), 'source.info')
if os.path.exists(aar_source_info_path):
attributed_aar = jar_info_utils.ReadAarSourceInfo(aar_source_info_path)
for path, archive_path in resource_utils.IterResourceFilesInDirectories( for path, archive_path in resource_utils.IterResourceFilesInDirectories(
[resource_dir], ignore_pattern): [resource_dir], ignore_pattern):
# Put the non-prefixed path in the .info file. attributed_path = path
path_info.AddMapping(archive_path, path) if attributed_aar:
attributed_path = os.path.join(attributed_aar, 'res',
path[len(resource_dir) + 1:])
# Use the non-prefixed archive_path in the .info file.
path_info.AddMapping(archive_path, attributed_path)
resource_dir_name = os.path.basename(resource_dir) resource_dir_name = os.path.basename(resource_dir)
archive_path = '{}_{}/{}'.format(index, resource_dir_name, archive_path) archive_path = '{}_{}/{}'.format(index, resource_dir_name, archive_path)
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
prepare_resources.py prepare_resources.py
util/__init__.py util/__init__.py
util/build_utils.py util/build_utils.py
util/jar_info_utils.py
util/manifest_utils.py util/manifest_utils.py
util/md5_check.py util/md5_check.py
util/resource_utils.py util/resource_utils.py
...@@ -13,6 +13,13 @@ import os ...@@ -13,6 +13,13 @@ import os
# contains its .class definition instead. # contains its .class definition instead.
def ReadAarSourceInfo(info_path):
"""Returns the source= path from an .aar's source.info file."""
# The .info looks like: "source=path/to/.aar\n".
with open(info_path) as f:
return f.read().rstrip().split('=', 1)[1]
def ParseJarInfoFile(info_path): def ParseJarInfoFile(info_path):
"""Parse a given .jar.info file as a dictionary. """Parse a given .jar.info file as a dictionary.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment