1#!/usr/bin/env python3
2
3import argparse
4import itertools
5import os
6import posixpath
7import re
8
9try:
10    import cPickle as pickle  # Python 2
11except ImportError:
12    import pickle  # Python 3
13
14import ninja
15
16
17def _parse_args():
18    parser = argparse.ArgumentParser()
19
20    # Ninja input file options
21    parser.add_argument('input_file', help='input ninja file')
22    parser.add_argument('--ninja-deps', help='.ninja_deps file')
23    parser.add_argument('--cwd', help='working directory for ninja')
24    parser.add_argument('--encoding', default='utf-8',
25                        help='ninja file encoding')
26
27    # Options
28    parser.add_argument(
29            '--out-dir', default='out', help='path to output directory')
30    parser.add_argument(
31            '--installed-filter', default='system',
32            help='path filter for installed files (w.r.t. device root)')
33    parser.add_argument(
34            '--source-filter', default='vendor:device',
35            help='path filter for source files (w.r.t. source root)')
36
37    return parser.parse_args()
38
39
40def main():
41    args = _parse_args()
42
43    out_dir = posixpath.normpath(args.out_dir)
44
45    out_pattern = re.compile(re.escape(out_dir) + '/')
46
47    installed_dirs = '|'.join('(?:' + re.escape(posixpath.normpath(path)) + ')'
48                              for path in args.installed_filter.split(':'))
49
50    installed_filter = re.compile(
51        re.escape(out_dir) + '/target/product/[^/]+/' +
52        '(?:' + installed_dirs + ')')
53
54    source_filter = re.compile(
55        '|'.join('(?:' + re.escape(posixpath.normpath(path)) + ')'
56                 for path in args.source_filter.split(':')))
57
58    manifest = ninja.load_manifest_from_args(args)
59
60    # Build lookup map
61    outs = {}
62    for build in manifest.builds:
63        for path in build.explicit_outs:
64            outs[path] = build
65        for path in build.implicit_outs:
66            outs[path] = build
67
68    # Compute transitive input files
69    outs_from_vendor_cache = {}
70
71    def _are_inputs_from_vendor(build):
72        # Check whether the input files are matched by source_filter first.
73        gen_paths = []
74        paths = itertools.chain(
75            build.explicit_ins, build.implicit_ins, build.depfile_implicit_ins)
76        for path in paths:
77            if source_filter.match(path):
78                return True
79            if out_pattern.match(path):
80                gen_paths.append(path)
81
82        # Check whether the input files transitively depend on source_filter.
83        for path in gen_paths:
84            if is_from_vendor(path):
85                return True
86
87        return False
88
89    def is_from_vendor(out_path):
90        matched = outs_from_vendor_cache.get(out_path, None)
91        if matched is not None:
92            return matched
93
94        build = outs.get(out_path)
95        if build:
96            matched = _are_inputs_from_vendor(build)
97        else:
98            matched = bool(source_filter.match(path))
99        outs_from_vendor_cache[out_path] = matched
100        return matched
101
102    matched_paths = [
103        path for path in outs
104        if installed_filter.match(path) and is_from_vendor(path)]
105
106    matched_paths.sort()
107
108    for path in matched_paths:
109        print(path)
110
111
112if __name__ == '__main__':
113    main()
114