| #!/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. |
| |
| """Merges .jar.info files into one for APKs.""" |
| |
| import argparse |
| import os |
| import shutil |
| import sys |
| import tempfile |
| import zipfile |
| |
| from util import build_utils |
| from util import jar_info_utils |
| |
| |
| 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 _MergeInfoFiles(output, jar_paths): |
| """Merge several .jar.info files to generate an .apk.jar.info. |
| |
| Args: |
| output: output file path. |
| jar_paths: List of .jar file paths for the target apk. |
| """ |
| info_data = dict() |
| for jar_path in jar_paths: |
| # android_java_prebuilt adds jar files in the src directory (relative to |
| # the output directory, usually ../../third_party/example.jar). |
| # android_aar_prebuilt collects jar files in the aar file and uses the |
| # java_prebuilt rule to generate gen/example/classes.jar files. |
| # We scan these prebuilt jars to parse each class path for the FQN. This |
| # allows us to later map these classes back to their respective src |
| # directories. |
| jar_info_path = jar_path + '.info' |
| # TODO(agrieve): This should probably also check that the mtime of the .info |
| # is newer than that of the .jar, or change prebuilts to always output |
| # .info files so that they always exist (and change the depfile to |
| # depend directly on them). |
| if os.path.exists(jar_info_path): |
| info_data.update(jar_info_utils.ParseJarInfoFile(jar_path + '.info')) |
| else: |
| with zipfile.ZipFile(jar_path) as zip_info: |
| for path in zip_info.namelist(): |
| fully_qualified_name = _FullJavaNameFromClassFilePath(path) |
| if fully_qualified_name: |
| info_data[fully_qualified_name] = jar_path |
| |
| jar_info_utils.WriteJarInfoFile(output, info_data) |
| |
| |
| def main(args): |
| args = build_utils.ExpandFileArgs(args) |
| parser = argparse.ArgumentParser(description=__doc__) |
| build_utils.AddDepfileOption(parser) |
| parser.add_argument('--output', required=True, |
| help='Output .apk.jar.info file') |
| parser.add_argument('--apk-jar-file', required=True, |
| help='Path to main .jar file for this APK.') |
| parser.add_argument('--dep-jar-files', required=True, |
| help='GN-list of dependent .jar file paths') |
| |
| options = parser.parse_args(args) |
| options.dep_jar_files = build_utils.ParseGnList(options.dep_jar_files) |
| jar_files = [ options.apk_jar_file ] + options.dep_jar_files |
| |
| def _OnStaleMd5(): |
| with tempfile.NamedTemporaryFile() as tmp_file: |
| _MergeInfoFiles(tmp_file.name, jar_files) |
| shutil.move(tmp_file.name, options.output) |
| tmp_file.delete = False |
| |
| build_utils.CallAndWriteDepfileIfStale( |
| _OnStaleMd5, options, |
| input_paths=jar_files, |
| output_paths=[options.output], |
| depfile_deps=jar_files, |
| add_pydeps=False) |
| |
| |
| if __name__ == '__main__': |
| main(sys.argv[1:]) |