1#!/usr/bin/env python3 2 3"""List all source file of a installed module.""" 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( 32 'installed_filter', nargs='+', 33 help='path filter for installed files (w.r.t. device root)') 34 parser.add_argument( 35 '--out-dir', default='out', help='path to output directory') 36 37 return parser.parse_args() 38 39 40def collect_source_files(graph, start, out_dir_pattern, out_host_dir_pattern): 41 """Collect the transitive dependencies of a target.""" 42 43 source_files = [] 44 45 # Extract the file name of the target file. We need this file name to 46 # allow the strip/copy build rules while leaving other shared libraries 47 # alone. 48 start_basename = posixpath.basename(start) 49 50 # Collect all source files 51 visited = {start} 52 stack = [start] 53 while stack: 54 cur = stack.pop() 55 56 if not out_dir_pattern.match(cur): 57 source_files.append(cur) 58 59 build = graph.get(cur) 60 if build: 61 for dep in itertools.chain(build.explicit_ins, build.implicit_ins, 62 build.depfile_implicit_ins): 63 # Skip the binaries for build process 64 if dep.startswith('prebuilts/'): 65 continue 66 if out_host_dir_pattern.match(dep): 67 continue 68 69 # Skip the shared libraries 70 if dep.endswith('.toc'): 71 continue 72 if dep.endswith('.so'): 73 if posixpath.basename(dep) != start_basename: 74 continue 75 76 if dep not in visited: 77 visited.add(dep) 78 stack.append(dep) 79 80 return sorted(source_files) 81 82 83def main(): 84 args = _parse_args() 85 86 out_dir = posixpath.normpath(args.out_dir) 87 out_dir_pattern = re.compile(re.escape(out_dir) + '/') 88 out_host_dir_pattern = re.compile(re.escape(out_dir) + '/host/') 89 out_product_dir = out_dir + '/target/product/[^/]+' 90 91 def _normalize_path(path): 92 if path.startswith(out_dir + '/target'): 93 return path 94 return posixpath.join(out_product_dir, path) 95 96 installed_filter = [_normalize_path(path) for path in args.installed_filter] 97 installed_filter = re.compile( 98 '|'.join('(?:' + p + ')' for p in installed_filter)) 99 100 manifest = ninja.load_manifest_from_args(args) 101 102 # Build lookup map 103 graph = {} 104 for build in manifest.builds: 105 for path in build.explicit_outs: 106 graph[path] = build 107 for path in build.implicit_outs: 108 graph[path] = build 109 110 # Collect all matching outputs 111 matched_files = [path for path in graph if installed_filter.match(path)] 112 matched_files.sort() 113 114 for path in matched_files: 115 source_files = collect_source_files( 116 graph, path, out_dir_pattern, out_host_dir_pattern) 117 print(path) 118 for dep in source_files: 119 print('\t' + dep) 120 print() 121 122 123if __name__ == '__main__': 124 main() 125