Add script for finding unreachable targets Change-Id: I65cec42517b3de3e5fd88f15ff67a0609d917068 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8400 Reviewed-by: Brett Wilson <brettw@chromium.org> Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/tools/find_unreachable.py b/tools/find_unreachable.py new file mode 100755 index 0000000..406a312 --- /dev/null +++ b/tools/find_unreachable.py
@@ -0,0 +1,78 @@ +#!/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())