diff options
Diffstat (limited to 'third_party/libwebrtc/build/android/gyp/aar.py')
-rwxr-xr-x | third_party/libwebrtc/build/android/gyp/aar.py | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/gyp/aar.py b/third_party/libwebrtc/build/android/gyp/aar.py new file mode 100755 index 0000000000..b157cd816f --- /dev/null +++ b/third_party/libwebrtc/build/android/gyp/aar.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +# +# Copyright 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. + +"""Processes an Android AAR file.""" + +import argparse +import os +import posixpath +import re +import shutil +import sys +from xml.etree import ElementTree +import zipfile + +from util import build_utils + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir))) +import gn_helpers + + +_PROGUARD_TXT = 'proguard.txt' + + +def _GetManifestPackage(doc): + """Returns the package specified in the manifest. + + Args: + doc: an XML tree parsed by ElementTree + + Returns: + String representing the package name. + """ + return doc.attrib['package'] + + +def _IsManifestEmpty(doc): + """Decides whether the given manifest has merge-worthy elements. + + E.g.: <activity>, <service>, etc. + + Args: + doc: an XML tree parsed by ElementTree + + Returns: + Whether the manifest has merge-worthy elements. + """ + for node in doc: + if node.tag == 'application': + if list(node): + return False + elif node.tag != 'uses-sdk': + return False + + return True + + +def _CreateInfo(aar_file): + """Extracts and return .info data from an .aar file. + + Args: + aar_file: Path to an input .aar file. + + Returns: + A dict containing .info data. + """ + data = {} + data['aidl'] = [] + data['assets'] = [] + data['resources'] = [] + data['subjars'] = [] + data['subjar_tuples'] = [] + data['has_classes_jar'] = False + data['has_proguard_flags'] = False + data['has_native_libraries'] = False + data['has_r_text_file'] = False + with zipfile.ZipFile(aar_file) as z: + manifest_xml = ElementTree.fromstring(z.read('AndroidManifest.xml')) + data['is_manifest_empty'] = _IsManifestEmpty(manifest_xml) + manifest_package = _GetManifestPackage(manifest_xml) + if manifest_package: + data['manifest_package'] = manifest_package + + for name in z.namelist(): + if name.endswith('/'): + continue + if name.startswith('aidl/'): + data['aidl'].append(name) + elif name.startswith('res/'): + data['resources'].append(name) + elif name.startswith('libs/') and name.endswith('.jar'): + label = posixpath.basename(name)[:-4] + label = re.sub(r'[^a-zA-Z0-9._]', '_', label) + data['subjars'].append(name) + data['subjar_tuples'].append([label, name]) + elif name.startswith('assets/'): + data['assets'].append(name) + elif name.startswith('jni/'): + data['has_native_libraries'] = True + if 'native_libraries' in data: + data['native_libraries'].append(name) + else: + data['native_libraries'] = [name] + elif name == 'classes.jar': + data['has_classes_jar'] = True + elif name == _PROGUARD_TXT: + data['has_proguard_flags'] = True + elif name == 'R.txt': + # Some AARs, e.g. gvr_controller_java, have empty R.txt. Such AARs + # have no resources as well. We treat empty R.txt as having no R.txt. + data['has_r_text_file'] = bool(z.read('R.txt').strip()) + + return data + + +def _PerformExtract(aar_file, output_dir, name_allowlist): + with build_utils.TempDir() as tmp_dir: + tmp_dir = os.path.join(tmp_dir, 'staging') + os.mkdir(tmp_dir) + build_utils.ExtractAll( + aar_file, path=tmp_dir, predicate=name_allowlist.__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.move(tmp_dir, output_dir) + + +def _AddCommonArgs(parser): + parser.add_argument( + 'aar_file', help='Path to the AAR file.', type=os.path.normpath) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + command_parsers = parser.add_subparsers(dest='command') + subp = command_parsers.add_parser( + 'list', help='Output a GN scope describing the contents of the .aar.') + _AddCommonArgs(subp) + subp.add_argument('--output', help='Output file.', default='-') + + subp = command_parsers.add_parser('extract', help='Extracts the .aar') + _AddCommonArgs(subp) + subp.add_argument( + '--output-dir', + help='Output directory for the extracted files.', + required=True, + type=os.path.normpath) + subp.add_argument( + '--assert-info-file', + help='Path to .info file. Asserts that it matches what ' + '"list" would output.', + type=argparse.FileType('r')) + subp.add_argument( + '--ignore-resources', + action='store_true', + help='Whether to skip extraction of res/') + + args = parser.parse_args() + + aar_info = _CreateInfo(args.aar_file) + formatted_info = """\ +# Generated by //build/android/gyp/aar.py +# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen". + +""" + gn_helpers.ToGNString(aar_info, pretty=True) + + if args.command == 'extract': + if args.assert_info_file: + cached_info = args.assert_info_file.read() + if formatted_info != cached_info: + raise Exception('android_aar_prebuilt() cached .info file is ' + 'out-of-date. Run gn gen with ' + 'update_android_aar_prebuilts=true to update it.') + + with zipfile.ZipFile(args.aar_file) as zf: + names = zf.namelist() + if args.ignore_resources: + names = [n for n in names if not n.startswith('res')] + + _PerformExtract(args.aar_file, args.output_dir, set(names)) + + elif args.command == 'list': + aar_output_present = args.output != '-' and os.path.isfile(args.output) + if aar_output_present: + # Some .info files are read-only, for examples the cipd-controlled ones + # under third_party/android_deps/repositoty. To deal with these, first + # that its content is correct, and if it is, exit without touching + # the file system. + file_info = open(args.output, 'r').read() + if file_info == formatted_info: + return + + # Try to write the file. This may fail for read-only ones that were + # not updated. + try: + with open(args.output, 'w') as f: + f.write(formatted_info) + except IOError as e: + if not aar_output_present: + raise e + raise Exception('Could not update output file: %s\n%s\n' % + (args.output, e)) + +if __name__ == '__main__': + sys.exit(main()) |