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