16d528ed9Sopenharmony_ci#!/usr/bin/env python3
26d528ed9Sopenharmony_ci# Copyright 2020 The Chromium Authors. All rights reserved.
36d528ed9Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
46d528ed9Sopenharmony_ci# found in the LICENSE file.
56d528ed9Sopenharmony_ci
66d528ed9Sopenharmony_ci
76d528ed9Sopenharmony_ci"""
86d528ed9Sopenharmony_ciFinds unreachable gn targets by analysing --ide=json output
96d528ed9Sopenharmony_cifrom gn gen.
106d528ed9Sopenharmony_ci
116d528ed9Sopenharmony_ciUsage:
126d528ed9Sopenharmony_ci# Generate json file with targets info, will be located at out/project.json:
136d528ed9Sopenharmony_cign gen out --ide=json
146d528ed9Sopenharmony_ci# Lists all targets that are not reachable from //:all or //ci:test_all:
156d528ed9Sopenharmony_cifind_unreachable.py --from //:all --from //ci:test_all --json-file out/project.json
166d528ed9Sopenharmony_ci# Lists targets unreachable from //:all that aren't referenced by any other target:
176d528ed9Sopenharmony_cifind_unreachable.py --from //:all --json-file out/project.json --no-refs
186d528ed9Sopenharmony_ci"""
196d528ed9Sopenharmony_ci
206d528ed9Sopenharmony_ciimport argparse
216d528ed9Sopenharmony_ciimport json
226d528ed9Sopenharmony_ciimport sys
236d528ed9Sopenharmony_ci
246d528ed9Sopenharmony_ci
256d528ed9Sopenharmony_cidef find_reachable_targets(known, graph):
266d528ed9Sopenharmony_ci  reachable = set()
276d528ed9Sopenharmony_ci  to_visit = known
286d528ed9Sopenharmony_ci  while to_visit:
296d528ed9Sopenharmony_ci    next = to_visit.pop()
306d528ed9Sopenharmony_ci    if next in reachable:
316d528ed9Sopenharmony_ci      continue
326d528ed9Sopenharmony_ci    reachable.add(next)
336d528ed9Sopenharmony_ci    to_visit += graph[next]['deps']
346d528ed9Sopenharmony_ci  return reachable
356d528ed9Sopenharmony_ci
366d528ed9Sopenharmony_ci
376d528ed9Sopenharmony_cidef find_source_targets_from(targets, graph):
386d528ed9Sopenharmony_ci  source_targets = set(targets)
396d528ed9Sopenharmony_ci  for target in targets:
406d528ed9Sopenharmony_ci    source_targets -= set(graph[target]['deps'])
416d528ed9Sopenharmony_ci  return source_targets
426d528ed9Sopenharmony_ci
436d528ed9Sopenharmony_ci
446d528ed9Sopenharmony_cidef main():
456d528ed9Sopenharmony_ci  parser = argparse.ArgumentParser(description='''
466d528ed9Sopenharmony_ci    Tool to find unreachable targets.
476d528ed9Sopenharmony_ci    This can be useful to inspect forgotten targets,
486d528ed9Sopenharmony_ci    for example tests or intermediate targets in templates
496d528ed9Sopenharmony_ci    that are no longer needed.
506d528ed9Sopenharmony_ci    ''')
516d528ed9Sopenharmony_ci  parser.add_argument(
526d528ed9Sopenharmony_ci    '--json-file', required=True,
536d528ed9Sopenharmony_ci    help='JSON file from gn gen with --ide=json option')
546d528ed9Sopenharmony_ci  parser.add_argument(
556d528ed9Sopenharmony_ci    '--from', action='append', dest='roots',
566d528ed9Sopenharmony_ci    help='Known "root" targets. Can be multiple. Those targets \
576d528ed9Sopenharmony_ci        and all their recursive dependencies are considered reachable.\
586d528ed9Sopenharmony_ci        Examples: //:all, //ci:test_all')
596d528ed9Sopenharmony_ci  parser.add_argument(
606d528ed9Sopenharmony_ci    '--no-refs', action='store_true',
616d528ed9Sopenharmony_ci    help='Show only targets that aren\'t referenced by any other target')
626d528ed9Sopenharmony_ci  cmd_args = parser.parse_args()
636d528ed9Sopenharmony_ci
646d528ed9Sopenharmony_ci  with open(cmd_args.json_file) as json_file:
656d528ed9Sopenharmony_ci    targets_graph = json.load(json_file)['targets']
666d528ed9Sopenharmony_ci
676d528ed9Sopenharmony_ci  reachable = find_reachable_targets(cmd_args.roots, targets_graph)
686d528ed9Sopenharmony_ci  all = set(targets_graph.keys())
696d528ed9Sopenharmony_ci  unreachable = all - reachable
706d528ed9Sopenharmony_ci
716d528ed9Sopenharmony_ci  result = find_source_targets_from(unreachable, targets_graph) \
726d528ed9Sopenharmony_ci    if cmd_args.no_refs else unreachable
736d528ed9Sopenharmony_ci
746d528ed9Sopenharmony_ci  print('\n'.join(sorted(result)))
756d528ed9Sopenharmony_ci
766d528ed9Sopenharmony_ci
776d528ed9Sopenharmony_ciif __name__ == '__main__':
786d528ed9Sopenharmony_ci  sys.exit(main())
79