1#!/usr/bin/env python3
2
3"""List all transitive build rules of a target."""
4
5import argparse
6import itertools
7import posixpath
8import re
9
10try:
11    import cPickle as pickle  # Python 2
12except ImportError:
13    import pickle  # Python 3
14
15import ninja
16
17
18def _parse_args():
19    """Parse the command line arguments."""
20
21    parser = argparse.ArgumentParser()
22
23    # Ninja input file options
24    parser.add_argument('input_file', help='input ninja file')
25    parser.add_argument('--ninja-deps', help='.ninja_deps file')
26    parser.add_argument('--cwd', help='working directory for ninja')
27    parser.add_argument('--encoding', default='utf-8',
28                        help='ninja file encoding')
29
30    # Options
31    parser.add_argument('target', help='build target')
32
33    return parser.parse_args()
34
35
36def collect_build_targets(graph, target):
37    """Collect the transitive build targets."""
38
39    # Search for the first target
40    build = graph[target]
41
42    # Collect all source files
43    dfs = [build]
44
45    visited = {build}
46    stack = [build]
47    while stack:
48        build = stack.pop()
49        for dep in itertools.chain(build.explicit_ins, build.implicit_ins,
50                                   build.depfile_implicit_ins):
51            dep = graph.get(dep)
52            if not dep:
53                continue
54            if dep not in visited:
55                visited.add(dep)
56                dfs.append(dep)
57                stack.append(dep)
58
59    return dfs
60
61
62def main():
63    args = _parse_args()
64
65    # Build lookup map
66    manifest = ninja.load_manifest_from_args(args)
67    graph = {}
68    for build in manifest.builds:
69        for path in build.explicit_outs:
70            graph[path] = build
71        for path in build.implicit_outs:
72            graph[path] = build
73
74    # List all transitive targets
75    try:
76        builds = collect_build_targets(graph, args.target)
77    except KeyError:
78        print('error: Failed to find the target {}'.format(arg.target),
79              file=sys.stderr)
80        sys.exit(1)
81
82    # Print all targets
83    for build in builds:
84        print('build')
85        for path in build.explicit_outs:
86            print('  explicit_out:', path)
87        for path in build.implicit_outs:
88            print('  implicit_out:', path)
89        for path in build.explicit_ins:
90            print('  explicit_in:', path)
91        for path in build.implicit_ins:
92            print('  implicit_in:', path)
93        for path in build.prerequisites:
94            print('  prerequisites:', path)
95        for path in build.depfile_implicit_ins:
96            print('  depfile_implicit_in:', path)
97
98
99if __name__ == '__main__':
100    main()
101