| #!/usr/bin/env python3 |
| |
| # 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. |
| |
| """Creates size-info/*.info files used by SuperSize.""" |
| |
| import argparse |
| import collections |
| import os |
| import re |
| import sys |
| import zipfile |
| |
| from util import build_utils |
| from util import jar_info_utils |
| |
| |
| _AAR_VERSION_PATTERN = re.compile(r'/[^/]*?(\.aar/|\.jar/)') |
| |
| |
| def _RemoveDuplicatesFromList(source_list): |
| return collections.OrderedDict.fromkeys(source_list).keys() |
| |
| |
| def _TransformAarPaths(path): |
| # .aar files within //third_party/android_deps have a version suffix. |
| # The suffix changes each time .aar files are updated, which makes size diffs |
| # hard to compare (since the before/after have different source paths). |
| # Rather than changing how android_deps works, we employ this work-around |
| # to normalize the paths. |
| # From: .../androidx_appcompat_appcompat/appcompat-1.1.0.aar/res/... |
| # To: .../androidx_appcompat_appcompat.aar/res/... |
| # https://crbug.com/1056455 |
| if 'android_deps' not in path: |
| return path |
| return _AAR_VERSION_PATTERN.sub(r'\1', path) |
| |
| |
| def _MergeResInfoFiles(res_info_path, info_paths): |
| # Concatenate them all. |
| # only_if_changed=False since no build rules depend on this as an input. |
| with build_utils.AtomicOutput(res_info_path, only_if_changed=False, |
| mode='w+') as dst: |
| for p in info_paths: |
| with open(p) as src: |
| dst.writelines(_TransformAarPaths(l) for l in src) |
| |
| |
| def _PakInfoPathsForAssets(assets): |
| return [f.split(':')[0] + '.info' for f in assets if f.endswith('.pak')] |
| |
| |
| def _MergePakInfoFiles(merged_path, pak_infos): |
| info_lines = set() |
| for pak_info_path in pak_infos: |
| with open(pak_info_path, 'r') as src_info_file: |
| info_lines.update(_TransformAarPaths(x) for x in src_info_file) |
| # only_if_changed=False since no build rules depend on this as an input. |
| with build_utils.AtomicOutput(merged_path, only_if_changed=False, |
| mode='w+') as f: |
| f.writelines(sorted(info_lines)) |
| |
| |
| def _FullJavaNameFromClassFilePath(path): |
| # Input: base/android/java/src/org/chromium/Foo.class |
| # Output: base.android.java.src.org.chromium.Foo |
| 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 _MergeJarInfoFiles(output, inputs): |
| """Merge several .jar.info files to generate an .apk.jar.info. |
| |
| Args: |
| output: output file path. |
| inputs: List of .jar.info or .jar files. |
| """ |
| info_data = dict() |
| for path in inputs: |
| # For non-prebuilts: .jar.info files are written by compile_java.py and map |
| # .class files to .java source paths. |
| # |
| # For prebuilts: No .jar.info file exists, we scan the .jar files here and |
| # map .class files to the .jar. |
| # |
| # For .aar files: We look for a "source.info" file in the containing |
| # directory in order to map classes back to the .aar (rather than mapping |
| # them to the extracted .jar file). |
| if path.endswith('.info'): |
| info_data.update(jar_info_utils.ParseJarInfoFile(path)) |
| 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: |
| for name in zip_info.namelist(): |
| fully_qualified_name = _FullJavaNameFromClassFilePath(name) |
| if fully_qualified_name: |
| info_data[fully_qualified_name] = _TransformAarPaths('{}/{}'.format( |
| attributed_path, name)) |
| |
| # 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: |
| jar_info_utils.WriteJarInfoFile(f, info_data) |
| |
| |
| def _FindJarInputs(jar_paths): |
| ret = [] |
| for jar_path in jar_paths: |
| jar_info_path = jar_path + '.info' |
| if os.path.exists(jar_info_path): |
| ret.append(jar_info_path) |
| else: |
| ret.append(jar_path) |
| return ret |
| |
| |
| def main(args): |
| args = build_utils.ExpandFileArgs(args) |
| parser = argparse.ArgumentParser(description=__doc__) |
| build_utils.AddDepfileOption(parser) |
| parser.add_argument( |
| '--jar-info-path', required=True, help='Output .jar.info file') |
| parser.add_argument( |
| '--pak-info-path', required=True, help='Output .pak.info file') |
| parser.add_argument( |
| '--res-info-path', required=True, help='Output .res.info file') |
| parser.add_argument( |
| '--jar-files', |
| required=True, |
| action='append', |
| help='GN-list of .jar file paths') |
| parser.add_argument( |
| '--assets', |
| required=True, |
| action='append', |
| help='GN-list of files to add as assets in the form ' |
| '"srcPath:zipPath", where ":zipPath" is optional.') |
| parser.add_argument( |
| '--uncompressed-assets', |
| required=True, |
| action='append', |
| help='Same as --assets, except disables compression.') |
| parser.add_argument( |
| '--in-res-info-path', |
| required=True, |
| action='append', |
| help='Paths to .ap_.info files') |
| |
| options = parser.parse_args(args) |
| |
| options.jar_files = build_utils.ParseGnList(options.jar_files) |
| options.assets = build_utils.ParseGnList(options.assets) |
| options.uncompressed_assets = build_utils.ParseGnList( |
| options.uncompressed_assets) |
| |
| jar_inputs = _FindJarInputs(_RemoveDuplicatesFromList(options.jar_files)) |
| pak_inputs = _PakInfoPathsForAssets(options.assets + |
| options.uncompressed_assets) |
| res_inputs = options.in_res_info_path |
| |
| # Just create the info files every time. See https://crbug.com/1045024 |
| _MergeJarInfoFiles(options.jar_info_path, jar_inputs) |
| _MergePakInfoFiles(options.pak_info_path, pak_inputs) |
| _MergeResInfoFiles(options.res_info_path, res_inputs) |
| |
| all_inputs = jar_inputs + pak_inputs + res_inputs |
| build_utils.WriteDepfile(options.depfile, |
| options.jar_info_path, |
| inputs=all_inputs) |
| |
| |
| if __name__ == '__main__': |
| main(sys.argv[1:]) |