| #!/usr/bin/env vpython3 | |
| # Copyright 2020 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. | |
| # Lint as: python3 | |
| """Prints out available java targets. | |
| Examples: | |
| # List GN target for bundles: | |
| build/android/list_java_targets.py -C out/Default --type android_app_bundle \ | |
| --gn-labels | |
| # List all android targets with types: | |
| build/android/list_java_targets.py -C out/Default --print-types | |
| # Build all apk targets: | |
| build/android/list_java_targets.py -C out/Default --type android_apk | xargs \ | |
| autoninja -C out/Default | |
| # Show how many of each target type exist: | |
| build/android/list_java_targets.py -C out/Default --stats | |
| """ | |
| import argparse | |
| import collections | |
| import json | |
| import logging | |
| import os | |
| import subprocess | |
| import sys | |
| _SRC_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', | |
| '..')) | |
| sys.path.append(os.path.join(_SRC_ROOT, 'build', 'android')) | |
| from pylib import constants | |
| _VALID_TYPES = ( | |
| 'android_apk', | |
| 'android_app_bundle', | |
| 'android_app_bundle_module', | |
| 'android_assets', | |
| 'android_resources', | |
| 'dist_aar', | |
| 'dist_jar', | |
| 'group', | |
| 'java_annotation_processor', | |
| 'java_binary', | |
| 'java_library', | |
| 'junit_binary', | |
| 'system_java_library', | |
| ) | |
| def _run_ninja(output_dir, args): | |
| cmd = [ | |
| 'autoninja', | |
| '-C', | |
| output_dir, | |
| ] | |
| cmd.extend(args) | |
| logging.info('Running: %r', cmd) | |
| subprocess.run(cmd, check=True, stdout=sys.stderr) | |
| def _query_for_build_config_targets(output_dir): | |
| # Query ninja rather than GN since it's faster. | |
| cmd = ['ninja', '-C', output_dir, '-t', 'targets'] | |
| logging.info('Running: %r', cmd) | |
| ninja_output = subprocess.run(cmd, | |
| check=True, | |
| capture_output=True, | |
| encoding='ascii').stdout | |
| ret = [] | |
| SUFFIX = '__build_config_crbug_908819' | |
| SUFFIX_LEN = len(SUFFIX) | |
| for line in ninja_output.splitlines(): | |
| ninja_target = line.rsplit(':', 1)[0] | |
| # Ignore root aliases by ensuring a : exists. | |
| if ':' in ninja_target and ninja_target.endswith(SUFFIX): | |
| ret.append(f'//{ninja_target[:-SUFFIX_LEN]}') | |
| return ret | |
| class _TargetEntry(object): | |
| def __init__(self, gn_target): | |
| assert gn_target.startswith('//'), f'{gn_target} does not start with //' | |
| assert ':' in gn_target, f'Non-root {gn_target} required' | |
| self.gn_target = gn_target | |
| self._build_config = None | |
| @property | |
| def ninja_target(self): | |
| return self.gn_target[2:] | |
| @property | |
| def ninja_build_config_target(self): | |
| return self.ninja_target + '__build_config_crbug_908819' | |
| @property | |
| def build_config_path(self): | |
| """Returns the filepath of the project's .build_config.""" | |
| ninja_target = self.ninja_target | |
| # Support targets at the root level. e.g. //:foo | |
| if ninja_target[0] == ':': | |
| ninja_target = ninja_target[1:] | |
| subpath = ninja_target.replace(':', os.path.sep) + '.build_config' | |
| return os.path.join(constants.GetOutDirectory(), 'gen', subpath) | |
| def build_config(self): | |
| """Reads and returns the project's .build_config JSON.""" | |
| if not self._build_config: | |
| with open(self.build_config_path) as jsonfile: | |
| self._build_config = json.load(jsonfile) | |
| return self._build_config | |
| def get_type(self): | |
| """Returns the target type from its .build_config.""" | |
| return self.build_config()['deps_info']['type'] | |
| def proguard_enabled(self): | |
| """Returns whether proguard runs for this target.""" | |
| # Modules set proguard_enabled, but the proguarding happens only once at the | |
| # bundle level. | |
| if self.get_type() == 'android_app_bundle_module': | |
| return False | |
| return self.build_config()['deps_info'].get('proguard_enabled', False) | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| parser.add_argument('-C', | |
| '--output-directory', | |
| help='If outdir is not provided, will attempt to guess.') | |
| parser.add_argument('--gn-labels', | |
| action='store_true', | |
| help='Print GN labels rather than ninja targets') | |
| parser.add_argument( | |
| '--nested', | |
| action='store_true', | |
| help='Do not convert nested targets to their top-level equivalents. ' | |
| 'E.g. Without this, foo_test__apk -> foo_test') | |
| parser.add_argument('--print-types', | |
| action='store_true', | |
| help='Print type of each target') | |
| parser.add_argument('--print-build-config-paths', | |
| action='store_true', | |
| help='Print path to the .build_config of each target') | |
| parser.add_argument('--build', | |
| action='store_true', | |
| help='Build all .build_config files.') | |
| parser.add_argument('--type', | |
| action='append', | |
| help='Restrict to targets of given type', | |
| choices=_VALID_TYPES) | |
| parser.add_argument('--stats', | |
| action='store_true', | |
| help='Print counts of each target type.') | |
| parser.add_argument('--proguard-enabled', | |
| action='store_true', | |
| help='Restrict to targets that have proguard enabled') | |
| parser.add_argument('-v', '--verbose', default=0, action='count') | |
| args = parser.parse_args() | |
| args.build |= bool(args.type or args.proguard_enabled or args.print_types | |
| or args.stats) | |
| logging.basicConfig(level=logging.WARNING - (10 * args.verbose), | |
| format='%(levelname).1s %(relativeCreated)6d %(message)s') | |
| if args.output_directory: | |
| constants.SetOutputDirectory(args.output_directory) | |
| constants.CheckOutputDirectory() | |
| output_dir = constants.GetOutDirectory() | |
| # Query ninja for all __build_config_crbug_908819 targets. | |
| targets = _query_for_build_config_targets(output_dir) | |
| entries = [_TargetEntry(t) for t in targets] | |
| if args.build: | |
| logging.warning('Building %d .build_config files...', len(entries)) | |
| _run_ninja(output_dir, [e.ninja_build_config_target for e in entries]) | |
| if args.type: | |
| entries = [e for e in entries if e.get_type() in args.type] | |
| if args.proguard_enabled: | |
| entries = [e for e in entries if e.proguard_enabled()] | |
| if args.stats: | |
| counts = collections.Counter(e.get_type() for e in entries) | |
| for entry_type, count in sorted(counts.items()): | |
| print(f'{entry_type}: {count}') | |
| else: | |
| for e in entries: | |
| if args.gn_labels: | |
| to_print = e.gn_target | |
| else: | |
| to_print = e.ninja_target | |
| # Convert to top-level target | |
| if not args.nested: | |
| to_print = to_print.replace('__test_apk', '').replace('__apk', '') | |
| if args.print_types: | |
| to_print = f'{to_print}: {e.get_type()}' | |
| elif args.print_build_config_paths: | |
| to_print = f'{to_print}: {e.build_config_path}' | |
| print(to_print) | |
| if __name__ == '__main__': | |
| main() |