blob: 5f1a106a886d3c5313de080c2f31b7607ad97788 [file] [log] [blame]
Peter Kotwicz4e476d7d2021-06-29 14:25:051#!/usr/bin/env python3
Avi Drissman73a09d12022-09-08 20:33:382# Copyright 2020 The Chromium Authors
Andrew Grievee95e8e282020-07-14 01:27:133# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6# Lint as: python3
7"""Prints out available java targets.
8
9Examples:
10# List GN target for bundles:
Peter Wena57817c2020-07-27 17:47:5211build/android/list_java_targets.py -C out/Default --type android_app_bundle \
12--gn-labels
Andrew Grievee95e8e282020-07-14 01:27:1313
14# List all android targets with types:
Peter Wena57817c2020-07-27 17:47:5215build/android/list_java_targets.py -C out/Default --print-types
Andrew Grievee95e8e282020-07-14 01:27:1316
17# Build all apk targets:
Peter Wena57817c2020-07-27 17:47:5218build/android/list_java_targets.py -C out/Default --type android_apk | xargs \
19autoninja -C out/Default
Andrew Grievee95e8e282020-07-14 01:27:1320
21# Show how many of each target type exist:
Peter Wena57817c2020-07-27 17:47:5222build/android/list_java_targets.py -C out/Default --stats
Andrew Grievee95e8e282020-07-14 01:27:1323
24"""
25
26import argparse
27import collections
28import json
29import logging
30import os
Andrew Grieve911128a2023-07-10 19:06:4231import shlex
Andrew Grievee95e8e282020-07-14 01:27:1332import subprocess
33import sys
34
35_SRC_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), '..',
36 '..'))
Andrew Grieve911128a2023-07-10 19:06:4237sys.path.append(os.path.join(_SRC_ROOT, 'build'))
38import gn_helpers
39
Andrew Grievee95e8e282020-07-14 01:27:1340sys.path.append(os.path.join(_SRC_ROOT, 'build', 'android'))
41from pylib import constants
42
43_VALID_TYPES = (
44 'android_apk',
45 'android_app_bundle',
46 'android_app_bundle_module',
47 'android_assets',
48 'android_resources',
49 'dist_aar',
50 'dist_jar',
51 'group',
52 'java_annotation_processor',
53 'java_binary',
54 'java_library',
Andrew Grieve4b7376792022-06-30 20:00:0355 'robolectric_binary',
Andrew Grievee95e8e282020-07-14 01:27:1356 'system_java_library',
57)
58
59
Andrew Grieve911128a2023-07-10 19:06:4260def _compile(output_dir, args, quiet=False):
61 cmd = gn_helpers.CreateBuildCommand(output_dir) + args
62 logging.info('Running: %s', shlex.join(cmd))
Peter Wen6754d962022-08-29 17:48:4163 if quiet:
64 subprocess.run(cmd, check=True, capture_output=True)
65 else:
66 subprocess.run(cmd, check=True, stdout=sys.stderr)
Andrew Grievee95e8e282020-07-14 01:27:1367
68
69def _query_for_build_config_targets(output_dir):
70 # Query ninja rather than GN since it's faster.
Fumitoshi Ukaifa23bad22025-04-14 13:57:5671 cmd = [
72 os.path.join(_SRC_ROOT, 'third_party', 'siso', 'cipd', 'siso'), 'query',
73 'targets', '-C', output_dir
74 ]
Andrew Grievee95e8e282020-07-14 01:27:1375 logging.info('Running: %r', cmd)
Andrew Grieve068c6f072025-04-08 19:09:3976 try:
Fumitoshi Ukaifa23bad22025-04-14 13:57:5677 query_output = subprocess.run(cmd,
Andrew Grieve068c6f072025-04-08 19:09:3978 check=True,
79 capture_output=True,
80 encoding='ascii').stdout
81 except subprocess.CalledProcessError as e:
82 sys.stderr.write('Command output:\n' + e.stdout + e.stderr)
83 raise
84
Andrew Grievee95e8e282020-07-14 01:27:1385 ret = []
86 SUFFIX = '__build_config_crbug_908819'
87 SUFFIX_LEN = len(SUFFIX)
Fumitoshi Ukaifa23bad22025-04-14 13:57:5688 for line in query_output.splitlines():
Andrew Grievee95e8e282020-07-14 01:27:1389 ninja_target = line.rsplit(':', 1)[0]
90 # Ignore root aliases by ensuring a : exists.
91 if ':' in ninja_target and ninja_target.endswith(SUFFIX):
Peter Wena57817c2020-07-27 17:47:5292 ret.append(f'//{ninja_target[:-SUFFIX_LEN]}')
Andrew Grievee95e8e282020-07-14 01:27:1393 return ret
94
95
Peter Wen73d007e2022-12-05 17:54:5996def _query_json(*, json_dict: dict, query: str, path: str):
97 """Traverses through the json dictionary according to the query.
98
99 If at any point a key does not exist, return the empty string, but raise an
100 error if a key exists but is the wrong type.
101
102 This is roughly equivalent to returning
103 json_dict[queries[0]]?[queries[1]]?...[queries[N]]? where the ? means that if
104 the key doesn't exist, the empty string is returned.
105
106 Example:
107 Given json_dict = {'a': {'b': 'c'}}
108 - If queries = ['a', 'b']
109 Return: 'c'
110 - If queries = ['a', 'd']
111 Return ''
112 - If queries = ['x']
113 Return ''
114 - If queries = ['a', 'b', 'x']
115 Raise an error since json_dict['a']['b'] is the string 'c' instead of an
116 expected dict that can be indexed into.
117
118 Returns the final result after exhausting all the queries.
119 """
120 queries = query.split('.')
121 value = json_dict
122 try:
123 for key in queries:
124 value = value.get(key)
125 if value is None:
126 return ''
127 except AttributeError as e:
128 raise Exception(
129 f'Failed when attempting to get {queries} from {path}') from e
130 return value
131
132
Yoshisato Yanagisawa9f477792021-12-09 00:00:25133class _TargetEntry:
Peter Wen73d007e2022-12-05 17:54:59134
Andrew Grievee95e8e282020-07-14 01:27:13135 def __init__(self, gn_target):
Peter Wena57817c2020-07-27 17:47:52136 assert gn_target.startswith('//'), f'{gn_target} does not start with //'
137 assert ':' in gn_target, f'Non-root {gn_target} required'
Andrew Grievee95e8e282020-07-14 01:27:13138 self.gn_target = gn_target
139 self._build_config = None
Andrew Grievee95e8e282020-07-14 01:27:13140
141 @property
142 def ninja_target(self):
143 return self.gn_target[2:]
144
145 @property
146 def ninja_build_config_target(self):
147 return self.ninja_target + '__build_config_crbug_908819'
148
Henrique Nakashima5f3062292021-03-25 21:55:45149 @property
150 def build_config_path(self):
Peter Wend9e1d002021-07-12 15:36:42151 """Returns the filepath of the project's .build_config.json."""
Henrique Nakashima5f3062292021-03-25 21:55:45152 ninja_target = self.ninja_target
153 # Support targets at the root level. e.g. //:foo
154 if ninja_target[0] == ':':
155 ninja_target = ninja_target[1:]
Peter Wend9e1d002021-07-12 15:36:42156 subpath = ninja_target.replace(':', os.path.sep) + '.build_config.json'
Andrew Grieve8def3982025-06-09 15:47:13157 return os.path.relpath(
158 os.path.join(constants.GetOutDirectory(), 'gen', subpath))
Henrique Nakashima5f3062292021-03-25 21:55:45159
Andrew Grieve7d34dfa2025-04-28 20:15:37160 @property
161 def params_path(self):
162 """Returns the filepath of the project's .params.json."""
163 return self.build_config_path.replace('.build_config.json', '.params.json')
164
Andrew Grievee95e8e282020-07-14 01:27:13165 def build_config(self):
Peter Wend9e1d002021-07-12 15:36:42166 """Reads and returns the project's .build_config.json JSON."""
Andrew Grievee95e8e282020-07-14 01:27:13167 if not self._build_config:
Andrew Grieve7d34dfa2025-04-28 20:15:37168 with open(self.params_path) as f:
169 config = json.load(f)
170 with open(self.build_config_path) as f:
171 config.update(json.load(f))
172 self._build_config = config
Andrew Grievee95e8e282020-07-14 01:27:13173 return self._build_config
174
175 def get_type(self):
Peter Wend9e1d002021-07-12 15:36:42176 """Returns the target type from its .build_config.json."""
Andrew Grieve7d34dfa2025-04-28 20:15:37177 return self.build_config()['type']
Andrew Grievee95e8e282020-07-14 01:27:13178
Andrew Grievec5e15d1b2020-12-04 18:13:02179 def proguard_enabled(self):
180 """Returns whether proguard runs for this target."""
181 # Modules set proguard_enabled, but the proguarding happens only once at the
182 # bundle level.
183 if self.get_type() == 'android_app_bundle_module':
184 return False
Andrew Grieve7d34dfa2025-04-28 20:15:37185 return self.build_config().get('proguard_enabled', False)
Andrew Grievec5e15d1b2020-12-04 18:13:02186
Andrew Grievee95e8e282020-07-14 01:27:13187
188def main():
189 parser = argparse.ArgumentParser(
190 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
Peter Wena57817c2020-07-27 17:47:52191 parser.add_argument('-C',
192 '--output-directory',
193 help='If outdir is not provided, will attempt to guess.')
Andrew Grievee95e8e282020-07-14 01:27:13194 parser.add_argument('--gn-labels',
195 action='store_true',
196 help='Print GN labels rather than ninja targets')
Andrew Grieve8def3982025-06-09 15:47:13197 parser.add_argument('--omit-targets',
198 action='store_true',
199 help='Do not print the target / gn label')
Andrew Grievee95e8e282020-07-14 01:27:13200 parser.add_argument(
201 '--nested',
202 action='store_true',
203 help='Do not convert nested targets to their top-level equivalents. '
204 'E.g. Without this, foo_test__apk -> foo_test')
205 parser.add_argument('--print-types',
206 action='store_true',
207 help='Print type of each target')
Peter Wend9e1d002021-07-12 15:36:42208 parser.add_argument(
209 '--print-build-config-paths',
210 action='store_true',
211 help='Print path to the .build_config.json of each target')
Andrew Grieve7d34dfa2025-04-28 20:15:37212 parser.add_argument('--print-params-paths',
213 action='store_true',
214 help='Print path to the .params.json of each target')
Andrew Grievec5e15d1b2020-12-04 18:13:02215 parser.add_argument('--build',
Andrew Grievee95e8e282020-07-14 01:27:13216 action='store_true',
Peter Wend9e1d002021-07-12 15:36:42217 help='Build all .build_config.json files.')
Andrew Grievee95e8e282020-07-14 01:27:13218 parser.add_argument('--type',
219 action='append',
220 help='Restrict to targets of given type',
221 choices=_VALID_TYPES)
222 parser.add_argument('--stats',
223 action='store_true',
224 help='Print counts of each target type.')
Andrew Grievec5e15d1b2020-12-04 18:13:02225 parser.add_argument('--proguard-enabled',
226 action='store_true',
Peter Wen73d007e2022-12-05 17:54:59227 help='Restrict to targets that have proguard enabled.')
228 parser.add_argument('--query',
229 help='A dot separated string specifying a query for a '
230 'build config json value of each target. Example: Use '
Andrew Grieve7d34dfa2025-04-28 20:15:37231 '--query unprocessed_jar_path to show a list '
232 'of all targets that have a non-empty '
233 '"unprocessed_jar_path" value in that dict.')
Andrew Grievee95e8e282020-07-14 01:27:13234 parser.add_argument('-v', '--verbose', default=0, action='count')
Peter Wen6754d962022-08-29 17:48:41235 parser.add_argument('-q', '--quiet', default=0, action='count')
Andrew Grievee95e8e282020-07-14 01:27:13236 args = parser.parse_args()
237
Andrew Grievec5e15d1b2020-12-04 18:13:02238 args.build |= bool(args.type or args.proguard_enabled or args.print_types
Peter Wen73d007e2022-12-05 17:54:59239 or args.stats or args.query)
Andrew Grievee95e8e282020-07-14 01:27:13240
Peter Wen6754d962022-08-29 17:48:41241 logging.basicConfig(level=logging.WARNING + 10 * (args.quiet - args.verbose),
Andrew Grievee95e8e282020-07-14 01:27:13242 format='%(levelname).1s %(relativeCreated)6d %(message)s')
243
244 if args.output_directory:
245 constants.SetOutputDirectory(args.output_directory)
246 constants.CheckOutputDirectory()
247 output_dir = constants.GetOutDirectory()
248
Andrew Grieve4281bf32025-01-14 02:29:33249 if args.build:
250 _compile(output_dir, ['build.ninja'])
251
Andrew Grievee95e8e282020-07-14 01:27:13252 # Query ninja for all __build_config_crbug_908819 targets.
253 targets = _query_for_build_config_targets(output_dir)
254 entries = [_TargetEntry(t) for t in targets]
255
Andrew Grieve4281bf32025-01-14 02:29:33256 if not entries:
257 logging.warning('No targets found. Run with --build')
258 sys.exit(1)
259
Andrew Grievec5e15d1b2020-12-04 18:13:02260 if args.build:
Peter Wend9e1d002021-07-12 15:36:42261 logging.warning('Building %d .build_config.json files...', len(entries))
Andrew Grieve911128a2023-07-10 19:06:42262 _compile(output_dir, [e.ninja_build_config_target for e in entries],
263 quiet=args.quiet)
Andrew Grievee95e8e282020-07-14 01:27:13264
265 if args.type:
266 entries = [e for e in entries if e.get_type() in args.type]
267
Andrew Grievec5e15d1b2020-12-04 18:13:02268 if args.proguard_enabled:
269 entries = [e for e in entries if e.proguard_enabled()]
270
Andrew Grievee95e8e282020-07-14 01:27:13271 if args.stats:
272 counts = collections.Counter(e.get_type() for e in entries)
273 for entry_type, count in sorted(counts.items()):
Peter Wena57817c2020-07-27 17:47:52274 print(f'{entry_type}: {count}')
Andrew Grievee95e8e282020-07-14 01:27:13275 else:
276 for e in entries:
Andrew Grieve8def3982025-06-09 15:47:13277 if args.omit_targets:
278 target_part = ''
Andrew Grievee95e8e282020-07-14 01:27:13279 else:
Andrew Grieve8def3982025-06-09 15:47:13280 if args.gn_labels:
281 target_part = e.gn_target
282 else:
283 target_part = e.ninja_target
Andrew Grievee95e8e282020-07-14 01:27:13284
Andrew Grieve8def3982025-06-09 15:47:13285 # Convert to top-level target
286 if not args.nested:
287 target_part = target_part.replace('__test_apk',
288 '').replace('__apk', '')
Andrew Grievee95e8e282020-07-14 01:27:13289
Andrew Grieve8def3982025-06-09 15:47:13290 type_part = ''
Andrew Grievee95e8e282020-07-14 01:27:13291 if args.print_types:
Andrew Grieve8def3982025-06-09 15:47:13292 type_part = e.get_type()
Henrique Nakashima5f3062292021-03-25 21:55:45293 elif args.print_build_config_paths:
Andrew Grieve8def3982025-06-09 15:47:13294 type_part = e.build_config_path
Andrew Grieve7d34dfa2025-04-28 20:15:37295 elif args.print_params_paths:
Andrew Grieve8def3982025-06-09 15:47:13296 type_part = e.params_path
Peter Wen73d007e2022-12-05 17:54:59297 elif args.query:
Andrew Grieve8def3982025-06-09 15:47:13298 type_part = _query_json(json_dict=e.build_config(),
299 query=args.query,
300 path=e.build_config_path)
301 if not type_part:
Peter Wen73d007e2022-12-05 17:54:59302 continue
Andrew Grievee95e8e282020-07-14 01:27:13303
Andrew Grieve8def3982025-06-09 15:47:13304 if target_part and type_part:
305 print(f'{target_part}: {type_part}')
306 elif target_part or type_part:
307 print(target_part or type_part)
Andrew Grievee95e8e282020-07-14 01:27:13308
309
310if __name__ == '__main__':
311 main()