1#!/usr/bin/env python3 2 3""" 4Byte compile all .py files from provided directories. This script is an 5alternative implementation of compileall.compile_dir written with 6cross-compilation in mind. 7""" 8 9import argparse 10import os 11import py_compile 12import re 13import sys 14 15 16def compile_one(host_path, strip_root=None, verbose=False): 17 """ 18 Compile a .py file into a .pyc file located next to it. 19 20 :arg host_path: 21 Absolute path to the file to compile on the host running the build. 22 :arg strip_root: 23 Prefix to remove from the original source paths encoded in compiled 24 files. 25 :arg verbose: 26 Print compiled file paths. 27 """ 28 if os.path.islink(host_path) or not os.path.isfile(host_path): 29 return # only compile real files 30 31 if not re.match(r"^[_A-Za-z][_A-Za-z0-9]*\.py$", 32 os.path.basename(host_path)): 33 return # only compile "importable" python modules 34 35 if strip_root is not None: 36 # determine the runtime path of the file (i.e.: relative path to root 37 # dir prepended with "/"). 38 runtime_path = os.path.join("/", os.path.relpath(host_path, strip_root)) 39 else: 40 runtime_path = host_path 41 42 if verbose: 43 print(" PYC {}".format(runtime_path)) 44 45 # will raise an error if the file cannot be compiled 46 py_compile.compile(host_path, cfile=host_path + "c", 47 dfile=runtime_path, doraise=True) 48 49 50def existing_dir_abs(arg): 51 """ 52 argparse type callback that checks that argument is a directory and returns 53 its absolute path. 54 """ 55 if not os.path.isdir(arg): 56 raise argparse.ArgumentTypeError('no such directory: {!r}'.format(arg)) 57 return os.path.abspath(arg) 58 59 60def main(): 61 parser = argparse.ArgumentParser(description=__doc__) 62 parser.add_argument("dirs", metavar="DIR", nargs="+", type=existing_dir_abs, 63 help="Directory to recursively scan and compile") 64 parser.add_argument("--strip-root", metavar="ROOT", type=existing_dir_abs, 65 help=""" 66 Prefix to remove from the original source paths encoded 67 in compiled files 68 """) 69 parser.add_argument("--verbose", action="store_true", 70 help="Print compiled files") 71 72 args = parser.parse_args() 73 74 try: 75 for d in args.dirs: 76 if args.strip_root and ".." in os.path.relpath(d, args.strip_root): 77 parser.error("DIR: not inside ROOT dir: {!r}".format(d)) 78 for parent, _, files in os.walk(d): 79 for f in files: 80 compile_one(os.path.join(parent, f), args.strip_root, 81 args.verbose) 82 83 except Exception as e: 84 print("error: {}".format(e)) 85 return 1 86 87 return 0 88 89 90if __name__ == "__main__": 91 sys.exit(main()) 92