list_java_targets.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #!/usr/bin/env python3
  2. # Copyright 2020 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. # Lint as: python3
  6. """Prints out available java targets.
  7. Examples:
  8. # List GN target for bundles:
  9. build/android/list_java_targets.py -C out/Default --type android_app_bundle \
  10. --gn-labels
  11. # List all android targets with types:
  12. build/android/list_java_targets.py -C out/Default --print-types
  13. # Build all apk targets:
  14. build/android/list_java_targets.py -C out/Default --type android_apk | xargs \
  15. autoninja -C out/Default
  16. # Show how many of each target type exist:
  17. build/android/list_java_targets.py -C out/Default --stats
  18. """
  19. import argparse
  20. import collections
  21. import json
  22. import logging
  23. import os
  24. import shutil
  25. import subprocess
  26. import sys
  27. _SRC_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), '..',
  28. '..'))
  29. sys.path.append(os.path.join(_SRC_ROOT, 'build', 'android'))
  30. from pylib import constants
  31. _VALID_TYPES = (
  32. 'android_apk',
  33. 'android_app_bundle',
  34. 'android_app_bundle_module',
  35. 'android_assets',
  36. 'android_resources',
  37. 'dist_aar',
  38. 'dist_jar',
  39. 'group',
  40. 'java_annotation_processor',
  41. 'java_binary',
  42. 'java_library',
  43. 'robolectric_binary',
  44. 'system_java_library',
  45. )
  46. def _resolve_ninja(cmd):
  47. # Prefer the version on PATH, but fallback to known version if PATH doesn't
  48. # have one (e.g. on bots).
  49. if shutil.which(cmd) is None:
  50. return os.path.join(_SRC_ROOT, 'third_party', 'depot_tools', cmd)
  51. return cmd
  52. def _run_ninja(output_dir, args):
  53. cmd = [
  54. _resolve_ninja('autoninja'),
  55. '-C',
  56. output_dir,
  57. ]
  58. cmd.extend(args)
  59. logging.info('Running: %r', cmd)
  60. subprocess.run(cmd, check=True, stdout=sys.stderr)
  61. def _query_for_build_config_targets(output_dir):
  62. # Query ninja rather than GN since it's faster.
  63. # Use ninja rather than autoninja to avoid extra output if user has set the
  64. # NINJA_SUMMARIZE_BUILD environment variable.
  65. cmd = [_resolve_ninja('ninja'), '-C', output_dir, '-t', 'targets']
  66. logging.info('Running: %r', cmd)
  67. ninja_output = subprocess.run(cmd,
  68. check=True,
  69. capture_output=True,
  70. encoding='ascii').stdout
  71. ret = []
  72. SUFFIX = '__build_config_crbug_908819'
  73. SUFFIX_LEN = len(SUFFIX)
  74. for line in ninja_output.splitlines():
  75. ninja_target = line.rsplit(':', 1)[0]
  76. # Ignore root aliases by ensuring a : exists.
  77. if ':' in ninja_target and ninja_target.endswith(SUFFIX):
  78. ret.append(f'//{ninja_target[:-SUFFIX_LEN]}')
  79. return ret
  80. class _TargetEntry:
  81. def __init__(self, gn_target):
  82. assert gn_target.startswith('//'), f'{gn_target} does not start with //'
  83. assert ':' in gn_target, f'Non-root {gn_target} required'
  84. self.gn_target = gn_target
  85. self._build_config = None
  86. @property
  87. def ninja_target(self):
  88. return self.gn_target[2:]
  89. @property
  90. def ninja_build_config_target(self):
  91. return self.ninja_target + '__build_config_crbug_908819'
  92. @property
  93. def build_config_path(self):
  94. """Returns the filepath of the project's .build_config.json."""
  95. ninja_target = self.ninja_target
  96. # Support targets at the root level. e.g. //:foo
  97. if ninja_target[0] == ':':
  98. ninja_target = ninja_target[1:]
  99. subpath = ninja_target.replace(':', os.path.sep) + '.build_config.json'
  100. return os.path.join(constants.GetOutDirectory(), 'gen', subpath)
  101. def build_config(self):
  102. """Reads and returns the project's .build_config.json JSON."""
  103. if not self._build_config:
  104. with open(self.build_config_path) as jsonfile:
  105. self._build_config = json.load(jsonfile)
  106. return self._build_config
  107. def get_type(self):
  108. """Returns the target type from its .build_config.json."""
  109. return self.build_config()['deps_info']['type']
  110. def proguard_enabled(self):
  111. """Returns whether proguard runs for this target."""
  112. # Modules set proguard_enabled, but the proguarding happens only once at the
  113. # bundle level.
  114. if self.get_type() == 'android_app_bundle_module':
  115. return False
  116. return self.build_config()['deps_info'].get('proguard_enabled', False)
  117. def main():
  118. parser = argparse.ArgumentParser(
  119. description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
  120. parser.add_argument('-C',
  121. '--output-directory',
  122. help='If outdir is not provided, will attempt to guess.')
  123. parser.add_argument('--gn-labels',
  124. action='store_true',
  125. help='Print GN labels rather than ninja targets')
  126. parser.add_argument(
  127. '--nested',
  128. action='store_true',
  129. help='Do not convert nested targets to their top-level equivalents. '
  130. 'E.g. Without this, foo_test__apk -> foo_test')
  131. parser.add_argument('--print-types',
  132. action='store_true',
  133. help='Print type of each target')
  134. parser.add_argument(
  135. '--print-build-config-paths',
  136. action='store_true',
  137. help='Print path to the .build_config.json of each target')
  138. parser.add_argument('--build',
  139. action='store_true',
  140. help='Build all .build_config.json files.')
  141. parser.add_argument('--type',
  142. action='append',
  143. help='Restrict to targets of given type',
  144. choices=_VALID_TYPES)
  145. parser.add_argument('--stats',
  146. action='store_true',
  147. help='Print counts of each target type.')
  148. parser.add_argument('--proguard-enabled',
  149. action='store_true',
  150. help='Restrict to targets that have proguard enabled')
  151. parser.add_argument('-v', '--verbose', default=0, action='count')
  152. args = parser.parse_args()
  153. args.build |= bool(args.type or args.proguard_enabled or args.print_types
  154. or args.stats)
  155. logging.basicConfig(level=logging.WARNING - (10 * args.verbose),
  156. format='%(levelname).1s %(relativeCreated)6d %(message)s')
  157. if args.output_directory:
  158. constants.SetOutputDirectory(args.output_directory)
  159. constants.CheckOutputDirectory()
  160. output_dir = constants.GetOutDirectory()
  161. # Query ninja for all __build_config_crbug_908819 targets.
  162. targets = _query_for_build_config_targets(output_dir)
  163. entries = [_TargetEntry(t) for t in targets]
  164. if args.build:
  165. logging.warning('Building %d .build_config.json files...', len(entries))
  166. _run_ninja(output_dir, [e.ninja_build_config_target for e in entries])
  167. if args.type:
  168. entries = [e for e in entries if e.get_type() in args.type]
  169. if args.proguard_enabled:
  170. entries = [e for e in entries if e.proguard_enabled()]
  171. if args.stats:
  172. counts = collections.Counter(e.get_type() for e in entries)
  173. for entry_type, count in sorted(counts.items()):
  174. print(f'{entry_type}: {count}')
  175. else:
  176. for e in entries:
  177. if args.gn_labels:
  178. to_print = e.gn_target
  179. else:
  180. to_print = e.ninja_target
  181. # Convert to top-level target
  182. if not args.nested:
  183. to_print = to_print.replace('__test_apk', '').replace('__apk', '')
  184. if args.print_types:
  185. to_print = f'{to_print}: {e.get_type()}'
  186. elif args.print_build_config_paths:
  187. to_print = f'{to_print}: {e.build_config_path}'
  188. print(to_print)
  189. if __name__ == '__main__':
  190. main()