15f9996aaSopenharmony_ci#!/usr/bin/env python 25f9996aaSopenharmony_ci# -*- coding: utf-8 -*- 35f9996aaSopenharmony_ci# Copyright 2016 The Chromium Authors. All rights reserved. 45f9996aaSopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 55f9996aaSopenharmony_ci# found in the LICENSE file. 65f9996aaSopenharmony_ci"""Prints all non-system dependencies for the given module. 75f9996aaSopenharmony_ci 85f9996aaSopenharmony_ciThe primary use-case for this script is to generate the list of python 95f9996aaSopenharmony_cimodules required for .isolate files. 105f9996aaSopenharmony_ci""" 115f9996aaSopenharmony_ci 125f9996aaSopenharmony_ciimport argparse 135f9996aaSopenharmony_ciimport imp 145f9996aaSopenharmony_ciimport os 155f9996aaSopenharmony_ciimport pipes 165f9996aaSopenharmony_ciimport sys 175f9996aaSopenharmony_ci 185f9996aaSopenharmony_ci# Don't use any helper modules, or else they will end up in the results. 195f9996aaSopenharmony_ci 205f9996aaSopenharmony_ci_SRC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 215f9996aaSopenharmony_ci 225f9996aaSopenharmony_ci 235f9996aaSopenharmony_cidef _compute_python_dependencies(): 245f9996aaSopenharmony_ci """Gets the paths of imported non-system python modules. 255f9996aaSopenharmony_ci 265f9996aaSopenharmony_ci A path is assumed to be a "system" import if it is outside of chromium's 275f9996aaSopenharmony_ci src/. The paths will be relative to the current directory. 285f9996aaSopenharmony_ci """ 295f9996aaSopenharmony_ci module_paths = (m.__file__ for m in sys.modules.values() 305f9996aaSopenharmony_ci if m and hasattr(m, '__file__')) 315f9996aaSopenharmony_ci 325f9996aaSopenharmony_ci src_paths = set() 335f9996aaSopenharmony_ci for path in module_paths: 345f9996aaSopenharmony_ci if path == __file__: 355f9996aaSopenharmony_ci continue 365f9996aaSopenharmony_ci path = os.path.abspath(path) 375f9996aaSopenharmony_ci if not path.startswith(_SRC_ROOT): 385f9996aaSopenharmony_ci continue 395f9996aaSopenharmony_ci 405f9996aaSopenharmony_ci if (path.endswith('.pyc') 415f9996aaSopenharmony_ci or (path.endswith('c') and not os.path.splitext(path)[1])): 425f9996aaSopenharmony_ci path = path[:-1] 435f9996aaSopenharmony_ci src_paths.add(path) 445f9996aaSopenharmony_ci 455f9996aaSopenharmony_ci return src_paths 465f9996aaSopenharmony_ci 475f9996aaSopenharmony_ci 485f9996aaSopenharmony_cidef _normalize_command_line(options): 495f9996aaSopenharmony_ci """Returns a string that when run from SRC_ROOT replicates the command.""" 505f9996aaSopenharmony_ci args = ['build/print_python_deps.py'] 515f9996aaSopenharmony_ci root = os.path.relpath(options.root, _SRC_ROOT) 525f9996aaSopenharmony_ci if root != '.': 535f9996aaSopenharmony_ci args.extend(('--root', root)) 545f9996aaSopenharmony_ci if options.output: 555f9996aaSopenharmony_ci args.extend(('--output', os.path.relpath(options.output, _SRC_ROOT))) 565f9996aaSopenharmony_ci if options.gn_paths: 575f9996aaSopenharmony_ci args.extend(('--gn-paths', )) 585f9996aaSopenharmony_ci for allowlist in sorted(options.allowlists): 595f9996aaSopenharmony_ci args.extend(('--allowlist', os.path.relpath(allowlist, _SRC_ROOT))) 605f9996aaSopenharmony_ci args.append(os.path.relpath(options.module, _SRC_ROOT)) 615f9996aaSopenharmony_ci return ' '.join(pipes.quote(x) for x in args) 625f9996aaSopenharmony_ci 635f9996aaSopenharmony_ci 645f9996aaSopenharmony_cidef _find_python_in_directory(directory: dict): 655f9996aaSopenharmony_ci """Returns an iterable of all non-test python files in the given directory.""" 665f9996aaSopenharmony_ci for root, _dirnames, filenames in os.walk(directory): 675f9996aaSopenharmony_ci for filename in filenames: 685f9996aaSopenharmony_ci if filename.endswith('.py') and not filename.endswith('_test.py'): 695f9996aaSopenharmony_ci yield os.path.join(root, filename) 705f9996aaSopenharmony_ci 715f9996aaSopenharmony_ci 725f9996aaSopenharmony_cidef main(): 735f9996aaSopenharmony_ci parser = argparse.ArgumentParser( 745f9996aaSopenharmony_ci description='Prints all non-system dependencies for the given module.') 755f9996aaSopenharmony_ci parser.add_argument('module', help='The python module to analyze.') 765f9996aaSopenharmony_ci parser.add_argument('--root', 775f9996aaSopenharmony_ci default='.', 785f9996aaSopenharmony_ci help='Directory to make paths relative to.') 795f9996aaSopenharmony_ci parser.add_argument('--output', 805f9996aaSopenharmony_ci help='Write output to a file rather than stdout.') 815f9996aaSopenharmony_ci parser.add_argument( 825f9996aaSopenharmony_ci '--inplace', 835f9996aaSopenharmony_ci action='store_true', 845f9996aaSopenharmony_ci help='Write output to a file with the same path as the ' 855f9996aaSopenharmony_ci 'module, but with a .pydeps extension. Also sets the ' 865f9996aaSopenharmony_ci 'root to the module\'s directory.') 875f9996aaSopenharmony_ci parser.add_argument('--no-header', 885f9996aaSopenharmony_ci action='store_true', 895f9996aaSopenharmony_ci help='Do not write the "# Generated by" header.') 905f9996aaSopenharmony_ci parser.add_argument('--gn-paths', 915f9996aaSopenharmony_ci action='store_true', 925f9996aaSopenharmony_ci help='Write paths as //foo/bar/baz.py') 935f9996aaSopenharmony_ci parser.add_argument('--allowlist', 945f9996aaSopenharmony_ci default=[], 955f9996aaSopenharmony_ci action='append', 965f9996aaSopenharmony_ci dest='allowlists', 975f9996aaSopenharmony_ci help='Recursively include all non-test python files ' 985f9996aaSopenharmony_ci 'within this directory. ' 995f9996aaSopenharmony_ci 'May be specified multiple times.') 1005f9996aaSopenharmony_ci options = parser.parse_args() 1015f9996aaSopenharmony_ci # Replace the path entry for print_python_deps.py with the one for the given 1025f9996aaSopenharmony_ci # module. 1035f9996aaSopenharmony_ci sys.path[0] = os.path.dirname(options.module) 1045f9996aaSopenharmony_ci imp.load_source('NAME', options.module) 1055f9996aaSopenharmony_ci 1065f9996aaSopenharmony_ci if options.inplace: 1075f9996aaSopenharmony_ci if options.output: 1085f9996aaSopenharmony_ci parser.error('Cannot use --inplace and --output at the same time!') 1095f9996aaSopenharmony_ci if not options.module.endswith('.py'): 1105f9996aaSopenharmony_ci parser.error('Input module path should end with .py suffix!') 1115f9996aaSopenharmony_ci options.output = options.module + 'deps' 1125f9996aaSopenharmony_ci options.root = os.path.dirname(options.module) 1135f9996aaSopenharmony_ci 1145f9996aaSopenharmony_ci paths_set = _compute_python_dependencies() 1155f9996aaSopenharmony_ci for path in options.allowlists: 1165f9996aaSopenharmony_ci paths_set.update( 1175f9996aaSopenharmony_ci os.path.abspath(p) for p in _find_python_in_directory(path)) 1185f9996aaSopenharmony_ci 1195f9996aaSopenharmony_ci paths = [os.path.relpath(p, options.root) for p in paths_set] 1205f9996aaSopenharmony_ci 1215f9996aaSopenharmony_ci normalized_cmdline = _normalize_command_line(options) 1225f9996aaSopenharmony_ci try: 1235f9996aaSopenharmony_ci with (open(options.output, 'w') 1245f9996aaSopenharmony_ci if options.output else sys.stdout) as out: 1255f9996aaSopenharmony_ci if not options.no_header: 1265f9996aaSopenharmony_ci out.write('# Generated by running:\n') 1275f9996aaSopenharmony_ci out.write('# %s\n' % normalized_cmdline) 1285f9996aaSopenharmony_ci prefix = '//' if options.gn_paths else '' 1295f9996aaSopenharmony_ci for path in sorted(paths): 1305f9996aaSopenharmony_ci out.write(prefix + path + '\n') 1315f9996aaSopenharmony_ci except OSError as err: 1325f9996aaSopenharmony_ci raise err 1335f9996aaSopenharmony_ci finally: 1345f9996aaSopenharmony_ci out.close() 1355f9996aaSopenharmony_ci 1365f9996aaSopenharmony_ci 1375f9996aaSopenharmony_ciif __name__ == '__main__': 1385f9996aaSopenharmony_ci sys.exit(main()) 139