summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/android/gyp/aar.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/build/android/gyp/aar.py')
-rwxr-xr-xthird_party/libwebrtc/build/android/gyp/aar.py210
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())