1#!/usr/bin/env vpython 2# Copyright 2016 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Prints all non-system dependencies for the given module. 7 8The primary use-case for this script is to genererate the list of python modules 9required for .isolate files. 10""" 11 12import argparse 13import imp 14import os 15import pipes 16import sys 17 18# Don't use any helper modules, or else they will end up in the results. 19 20 21_SRC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 22 23 24def _ComputePythonDependencies(): 25 """Gets the paths of imported non-system python modules. 26 27 A path is assumed to be a "system" import if it is outside of chromium's 28 src/. The paths will be relative to the current directory. 29 """ 30 module_paths = (m.__file__ for m in sys.modules.values() 31 if m and hasattr(m, '__file__')) 32 33 src_paths = set() 34 for path in module_paths: 35 if path == __file__: 36 continue 37 path = os.path.abspath(path) 38 if not path.startswith(_SRC_ROOT): 39 continue 40 41 if (path.endswith('.pyc') 42 or (path.endswith('c') and not os.path.splitext(path)[1])): 43 path = path[:-1] 44 src_paths.add(path) 45 46 return src_paths 47 48 49def _NormalizeCommandLine(options): 50 """Returns a string that when run from SRC_ROOT replicates the command.""" 51 args = ['build/print_python_deps.py'] 52 root = os.path.relpath(options.root, _SRC_ROOT) 53 if root != '.': 54 args.extend(('--root', root)) 55 if options.output: 56 args.extend(('--output', os.path.relpath(options.output, _SRC_ROOT))) 57 for whitelist in sorted(options.whitelists): 58 args.extend(('--whitelist', os.path.relpath(whitelist, _SRC_ROOT))) 59 args.append(os.path.relpath(options.module, _SRC_ROOT)) 60 return ' '.join(pipes.quote(x) for x in args) 61 62 63def _FindPythonInDirectory(directory): 64 """Returns an iterable of all non-test python files in the given directory.""" 65 files = [] 66 for root, _dirnames, filenames in os.walk(directory): 67 for filename in filenames: 68 if filename.endswith('.py') and not filename.endswith('_test.py'): 69 yield os.path.join(root, filename) 70 71 72def main(): 73 parser = argparse.ArgumentParser( 74 description='Prints all non-system dependencies for the given module.') 75 parser.add_argument('module', 76 help='The python module to analyze.') 77 parser.add_argument('--root', default='.', 78 help='Directory to make paths relative to.') 79 parser.add_argument('--output', 80 help='Write output to a file rather than stdout.') 81 parser.add_argument('--no-header', action='store_true', 82 help='Do not write the "# Generated by" header.') 83 parser.add_argument('--gn-paths', action='store_true', 84 help='Write paths as //foo/bar/baz.py') 85 parser.add_argument('--whitelist', default=[], action='append', 86 dest='whitelists', 87 help='Recursively include all non-test python files ' 88 'within this directory. May be specified multiple times.') 89 options = parser.parse_args() 90 # Replace the path entry for print_python_deps.py with the one for the given 91 # module. 92 sys.path[0] = os.path.dirname(options.module) 93 imp.load_source('NAME', options.module) 94 95 paths_set = _ComputePythonDependencies() 96 for path in options.whitelists: 97 paths_set.update(os.path.abspath(p) for p in _FindPythonInDirectory(path)) 98 99 paths = [os.path.relpath(p, options.root) for p in paths_set] 100 101 normalized_cmdline = _NormalizeCommandLine(options) 102 out = open(options.output, 'w') if options.output else sys.stdout 103 with out: 104 if not options.no_header: 105 out.write('# Generated by running:\n') 106 out.write('# %s\n' % normalized_cmdline) 107 prefix = '//' if options.gn_paths else '' 108 for path in sorted(paths): 109 out.write(prefix + path + '\n') 110 111 112if __name__ == '__main__': 113 sys.exit(main()) 114