#!/usr/bin/env python # 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. """ Finds unreachable gn targets by analysing --ide=json output from gn gen. Usage: # Generate json file with targets info, will be located at out/project.json: gn gen out --ide=json # Lists all targets that are not reachable from //:all or //ci:test_all: find_unreachable.py --from //:all --from //ci:test_all --json-file out/project.json # Lists targets unreachable from //:all that aren't referenced by any other target: find_unreachable.py --from //:all --json-file out/project.json --no-refs """ import argparse import json import sys def find_reachable_targets(known, graph): reachable = set() to_visit = known while to_visit: next = to_visit.pop() if next in reachable: continue reachable.add(next) to_visit += graph[next]['deps'] return reachable def find_source_targets_from(targets, graph): source_targets = set(targets) for target in targets: source_targets -= set(graph[target]['deps']) return source_targets def main(): parser = argparse.ArgumentParser(description=''' Tool to find unreachable targets. This can be useful to inspect forgotten targets, for example tests or intermediate targets in templates that are no longer needed. ''') parser.add_argument( '--json-file', required=True, help='JSON file from gn gen with --ide=json option') parser.add_argument( '--from', action='append', dest='roots', help='Known "root" targets. Can be multiple. Those targets \ and all their recursive dependencies are considered reachable.\ Examples: //:all, //ci:test_all') parser.add_argument( '--no-refs', action='store_true', help='Show only targets that aren\'t referenced by any other target') cmd_args = parser.parse_args() with open(cmd_args.json_file) as json_file: targets_graph = json.load(json_file)['targets'] reachable = find_reachable_targets(cmd_args.roots, targets_graph) all = set(targets_graph.keys()) unreachable = all - reachable result = find_source_targets_from(unreachable, targets_graph) \ if cmd_args.no_refs else unreachable print '\n'.join(sorted(result)) if __name__ == '__main__': sys.exit(main())