1*cda5da8dSAndroid Build Coastguard Worker"""Module/script to byte-compile all .py files to .pyc files. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerWhen called as a script with arguments, this compiles the directories 4*cda5da8dSAndroid Build Coastguard Workergiven as arguments recursively; the -l option prevents it from 5*cda5da8dSAndroid Build Coastguard Workerrecursing into directories. 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard WorkerWithout arguments, it compiles all modules on sys.path, without 8*cda5da8dSAndroid Build Coastguard Workerrecursing into subdirectories. (Even though it should do so for 9*cda5da8dSAndroid Build Coastguard Workerpackages -- for now, you'll have to deal with packages separately.) 10*cda5da8dSAndroid Build Coastguard Worker 11*cda5da8dSAndroid Build Coastguard WorkerSee module py_compile for details of the actual byte-compilation. 12*cda5da8dSAndroid Build Coastguard Worker""" 13*cda5da8dSAndroid Build Coastguard Workerimport os 14*cda5da8dSAndroid Build Coastguard Workerimport sys 15*cda5da8dSAndroid Build Coastguard Workerimport importlib.util 16*cda5da8dSAndroid Build Coastguard Workerimport py_compile 17*cda5da8dSAndroid Build Coastguard Workerimport struct 18*cda5da8dSAndroid Build Coastguard Workerimport filecmp 19*cda5da8dSAndroid Build Coastguard Worker 20*cda5da8dSAndroid Build Coastguard Workerfrom functools import partial 21*cda5da8dSAndroid Build Coastguard Workerfrom pathlib import Path 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Worker__all__ = ["compile_dir","compile_file","compile_path"] 24*cda5da8dSAndroid Build Coastguard Worker 25*cda5da8dSAndroid Build Coastguard Workerdef _walk_dir(dir, maxlevels, quiet=0): 26*cda5da8dSAndroid Build Coastguard Worker if quiet < 2 and isinstance(dir, os.PathLike): 27*cda5da8dSAndroid Build Coastguard Worker dir = os.fspath(dir) 28*cda5da8dSAndroid Build Coastguard Worker if not quiet: 29*cda5da8dSAndroid Build Coastguard Worker print('Listing {!r}...'.format(dir)) 30*cda5da8dSAndroid Build Coastguard Worker try: 31*cda5da8dSAndroid Build Coastguard Worker names = os.listdir(dir) 32*cda5da8dSAndroid Build Coastguard Worker except OSError: 33*cda5da8dSAndroid Build Coastguard Worker if quiet < 2: 34*cda5da8dSAndroid Build Coastguard Worker print("Can't list {!r}".format(dir)) 35*cda5da8dSAndroid Build Coastguard Worker names = [] 36*cda5da8dSAndroid Build Coastguard Worker names.sort() 37*cda5da8dSAndroid Build Coastguard Worker for name in names: 38*cda5da8dSAndroid Build Coastguard Worker if name == '__pycache__': 39*cda5da8dSAndroid Build Coastguard Worker continue 40*cda5da8dSAndroid Build Coastguard Worker fullname = os.path.join(dir, name) 41*cda5da8dSAndroid Build Coastguard Worker if not os.path.isdir(fullname): 42*cda5da8dSAndroid Build Coastguard Worker yield fullname 43*cda5da8dSAndroid Build Coastguard Worker elif (maxlevels > 0 and name != os.curdir and name != os.pardir and 44*cda5da8dSAndroid Build Coastguard Worker os.path.isdir(fullname) and not os.path.islink(fullname)): 45*cda5da8dSAndroid Build Coastguard Worker yield from _walk_dir(fullname, maxlevels=maxlevels - 1, 46*cda5da8dSAndroid Build Coastguard Worker quiet=quiet) 47*cda5da8dSAndroid Build Coastguard Worker 48*cda5da8dSAndroid Build Coastguard Workerdef compile_dir(dir, maxlevels=None, ddir=None, force=False, 49*cda5da8dSAndroid Build Coastguard Worker rx=None, quiet=0, legacy=False, optimize=-1, workers=1, 50*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=None, *, stripdir=None, 51*cda5da8dSAndroid Build Coastguard Worker prependdir=None, limit_sl_dest=None, hardlink_dupes=False): 52*cda5da8dSAndroid Build Coastguard Worker """Byte-compile all modules in the given directory tree. 53*cda5da8dSAndroid Build Coastguard Worker 54*cda5da8dSAndroid Build Coastguard Worker Arguments (only dir is required): 55*cda5da8dSAndroid Build Coastguard Worker 56*cda5da8dSAndroid Build Coastguard Worker dir: the directory to byte-compile 57*cda5da8dSAndroid Build Coastguard Worker maxlevels: maximum recursion level (default `sys.getrecursionlimit()`) 58*cda5da8dSAndroid Build Coastguard Worker ddir: the directory that will be prepended to the path to the 59*cda5da8dSAndroid Build Coastguard Worker file as it is compiled into each byte-code file. 60*cda5da8dSAndroid Build Coastguard Worker force: if True, force compilation, even if timestamps are up-to-date 61*cda5da8dSAndroid Build Coastguard Worker quiet: full output with False or 0, errors only with 1, 62*cda5da8dSAndroid Build Coastguard Worker no output with 2 63*cda5da8dSAndroid Build Coastguard Worker legacy: if True, produce legacy pyc paths instead of PEP 3147 paths 64*cda5da8dSAndroid Build Coastguard Worker optimize: int or list of optimization levels or -1 for level of 65*cda5da8dSAndroid Build Coastguard Worker the interpreter. Multiple levels leads to multiple compiled 66*cda5da8dSAndroid Build Coastguard Worker files each with one optimization level. 67*cda5da8dSAndroid Build Coastguard Worker workers: maximum number of parallel workers 68*cda5da8dSAndroid Build Coastguard Worker invalidation_mode: how the up-to-dateness of the pyc will be checked 69*cda5da8dSAndroid Build Coastguard Worker stripdir: part of path to left-strip from source file path 70*cda5da8dSAndroid Build Coastguard Worker prependdir: path to prepend to beginning of original file path, applied 71*cda5da8dSAndroid Build Coastguard Worker after stripdir 72*cda5da8dSAndroid Build Coastguard Worker limit_sl_dest: ignore symlinks if they are pointing outside of 73*cda5da8dSAndroid Build Coastguard Worker the defined path 74*cda5da8dSAndroid Build Coastguard Worker hardlink_dupes: hardlink duplicated pyc files 75*cda5da8dSAndroid Build Coastguard Worker """ 76*cda5da8dSAndroid Build Coastguard Worker ProcessPoolExecutor = None 77*cda5da8dSAndroid Build Coastguard Worker if ddir is not None and (stripdir is not None or prependdir is not None): 78*cda5da8dSAndroid Build Coastguard Worker raise ValueError(("Destination dir (ddir) cannot be used " 79*cda5da8dSAndroid Build Coastguard Worker "in combination with stripdir or prependdir")) 80*cda5da8dSAndroid Build Coastguard Worker if ddir is not None: 81*cda5da8dSAndroid Build Coastguard Worker stripdir = dir 82*cda5da8dSAndroid Build Coastguard Worker prependdir = ddir 83*cda5da8dSAndroid Build Coastguard Worker ddir = None 84*cda5da8dSAndroid Build Coastguard Worker if workers < 0: 85*cda5da8dSAndroid Build Coastguard Worker raise ValueError('workers must be greater or equal to 0') 86*cda5da8dSAndroid Build Coastguard Worker if workers != 1: 87*cda5da8dSAndroid Build Coastguard Worker # Check if this is a system where ProcessPoolExecutor can function. 88*cda5da8dSAndroid Build Coastguard Worker from concurrent.futures.process import _check_system_limits 89*cda5da8dSAndroid Build Coastguard Worker try: 90*cda5da8dSAndroid Build Coastguard Worker _check_system_limits() 91*cda5da8dSAndroid Build Coastguard Worker except NotImplementedError: 92*cda5da8dSAndroid Build Coastguard Worker workers = 1 93*cda5da8dSAndroid Build Coastguard Worker else: 94*cda5da8dSAndroid Build Coastguard Worker from concurrent.futures import ProcessPoolExecutor 95*cda5da8dSAndroid Build Coastguard Worker if maxlevels is None: 96*cda5da8dSAndroid Build Coastguard Worker maxlevels = sys.getrecursionlimit() 97*cda5da8dSAndroid Build Coastguard Worker files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels) 98*cda5da8dSAndroid Build Coastguard Worker success = True 99*cda5da8dSAndroid Build Coastguard Worker if workers != 1 and ProcessPoolExecutor is not None: 100*cda5da8dSAndroid Build Coastguard Worker # If workers == 0, let ProcessPoolExecutor choose 101*cda5da8dSAndroid Build Coastguard Worker workers = workers or None 102*cda5da8dSAndroid Build Coastguard Worker with ProcessPoolExecutor(max_workers=workers) as executor: 103*cda5da8dSAndroid Build Coastguard Worker results = executor.map(partial(compile_file, 104*cda5da8dSAndroid Build Coastguard Worker ddir=ddir, force=force, 105*cda5da8dSAndroid Build Coastguard Worker rx=rx, quiet=quiet, 106*cda5da8dSAndroid Build Coastguard Worker legacy=legacy, 107*cda5da8dSAndroid Build Coastguard Worker optimize=optimize, 108*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=invalidation_mode, 109*cda5da8dSAndroid Build Coastguard Worker stripdir=stripdir, 110*cda5da8dSAndroid Build Coastguard Worker prependdir=prependdir, 111*cda5da8dSAndroid Build Coastguard Worker limit_sl_dest=limit_sl_dest, 112*cda5da8dSAndroid Build Coastguard Worker hardlink_dupes=hardlink_dupes), 113*cda5da8dSAndroid Build Coastguard Worker files) 114*cda5da8dSAndroid Build Coastguard Worker success = min(results, default=True) 115*cda5da8dSAndroid Build Coastguard Worker else: 116*cda5da8dSAndroid Build Coastguard Worker for file in files: 117*cda5da8dSAndroid Build Coastguard Worker if not compile_file(file, ddir, force, rx, quiet, 118*cda5da8dSAndroid Build Coastguard Worker legacy, optimize, invalidation_mode, 119*cda5da8dSAndroid Build Coastguard Worker stripdir=stripdir, prependdir=prependdir, 120*cda5da8dSAndroid Build Coastguard Worker limit_sl_dest=limit_sl_dest, 121*cda5da8dSAndroid Build Coastguard Worker hardlink_dupes=hardlink_dupes): 122*cda5da8dSAndroid Build Coastguard Worker success = False 123*cda5da8dSAndroid Build Coastguard Worker return success 124*cda5da8dSAndroid Build Coastguard Worker 125*cda5da8dSAndroid Build Coastguard Workerdef compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, 126*cda5da8dSAndroid Build Coastguard Worker legacy=False, optimize=-1, 127*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=None, *, stripdir=None, prependdir=None, 128*cda5da8dSAndroid Build Coastguard Worker limit_sl_dest=None, hardlink_dupes=False): 129*cda5da8dSAndroid Build Coastguard Worker """Byte-compile one file. 130*cda5da8dSAndroid Build Coastguard Worker 131*cda5da8dSAndroid Build Coastguard Worker Arguments (only fullname is required): 132*cda5da8dSAndroid Build Coastguard Worker 133*cda5da8dSAndroid Build Coastguard Worker fullname: the file to byte-compile 134*cda5da8dSAndroid Build Coastguard Worker ddir: if given, the directory name compiled in to the 135*cda5da8dSAndroid Build Coastguard Worker byte-code file. 136*cda5da8dSAndroid Build Coastguard Worker force: if True, force compilation, even if timestamps are up-to-date 137*cda5da8dSAndroid Build Coastguard Worker quiet: full output with False or 0, errors only with 1, 138*cda5da8dSAndroid Build Coastguard Worker no output with 2 139*cda5da8dSAndroid Build Coastguard Worker legacy: if True, produce legacy pyc paths instead of PEP 3147 paths 140*cda5da8dSAndroid Build Coastguard Worker optimize: int or list of optimization levels or -1 for level of 141*cda5da8dSAndroid Build Coastguard Worker the interpreter. Multiple levels leads to multiple compiled 142*cda5da8dSAndroid Build Coastguard Worker files each with one optimization level. 143*cda5da8dSAndroid Build Coastguard Worker invalidation_mode: how the up-to-dateness of the pyc will be checked 144*cda5da8dSAndroid Build Coastguard Worker stripdir: part of path to left-strip from source file path 145*cda5da8dSAndroid Build Coastguard Worker prependdir: path to prepend to beginning of original file path, applied 146*cda5da8dSAndroid Build Coastguard Worker after stripdir 147*cda5da8dSAndroid Build Coastguard Worker limit_sl_dest: ignore symlinks if they are pointing outside of 148*cda5da8dSAndroid Build Coastguard Worker the defined path. 149*cda5da8dSAndroid Build Coastguard Worker hardlink_dupes: hardlink duplicated pyc files 150*cda5da8dSAndroid Build Coastguard Worker """ 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Worker if ddir is not None and (stripdir is not None or prependdir is not None): 153*cda5da8dSAndroid Build Coastguard Worker raise ValueError(("Destination dir (ddir) cannot be used " 154*cda5da8dSAndroid Build Coastguard Worker "in combination with stripdir or prependdir")) 155*cda5da8dSAndroid Build Coastguard Worker 156*cda5da8dSAndroid Build Coastguard Worker success = True 157*cda5da8dSAndroid Build Coastguard Worker fullname = os.fspath(fullname) 158*cda5da8dSAndroid Build Coastguard Worker stripdir = os.fspath(stripdir) if stripdir is not None else None 159*cda5da8dSAndroid Build Coastguard Worker name = os.path.basename(fullname) 160*cda5da8dSAndroid Build Coastguard Worker 161*cda5da8dSAndroid Build Coastguard Worker dfile = None 162*cda5da8dSAndroid Build Coastguard Worker 163*cda5da8dSAndroid Build Coastguard Worker if ddir is not None: 164*cda5da8dSAndroid Build Coastguard Worker dfile = os.path.join(ddir, name) 165*cda5da8dSAndroid Build Coastguard Worker 166*cda5da8dSAndroid Build Coastguard Worker if stripdir is not None: 167*cda5da8dSAndroid Build Coastguard Worker fullname_parts = fullname.split(os.path.sep) 168*cda5da8dSAndroid Build Coastguard Worker stripdir_parts = stripdir.split(os.path.sep) 169*cda5da8dSAndroid Build Coastguard Worker ddir_parts = list(fullname_parts) 170*cda5da8dSAndroid Build Coastguard Worker 171*cda5da8dSAndroid Build Coastguard Worker for spart, opart in zip(stripdir_parts, fullname_parts): 172*cda5da8dSAndroid Build Coastguard Worker if spart == opart: 173*cda5da8dSAndroid Build Coastguard Worker ddir_parts.remove(spart) 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker dfile = os.path.join(*ddir_parts) 176*cda5da8dSAndroid Build Coastguard Worker 177*cda5da8dSAndroid Build Coastguard Worker if prependdir is not None: 178*cda5da8dSAndroid Build Coastguard Worker if dfile is None: 179*cda5da8dSAndroid Build Coastguard Worker dfile = os.path.join(prependdir, fullname) 180*cda5da8dSAndroid Build Coastguard Worker else: 181*cda5da8dSAndroid Build Coastguard Worker dfile = os.path.join(prependdir, dfile) 182*cda5da8dSAndroid Build Coastguard Worker 183*cda5da8dSAndroid Build Coastguard Worker if isinstance(optimize, int): 184*cda5da8dSAndroid Build Coastguard Worker optimize = [optimize] 185*cda5da8dSAndroid Build Coastguard Worker 186*cda5da8dSAndroid Build Coastguard Worker # Use set() to remove duplicates. 187*cda5da8dSAndroid Build Coastguard Worker # Use sorted() to create pyc files in a deterministic order. 188*cda5da8dSAndroid Build Coastguard Worker optimize = sorted(set(optimize)) 189*cda5da8dSAndroid Build Coastguard Worker 190*cda5da8dSAndroid Build Coastguard Worker if hardlink_dupes and len(optimize) < 2: 191*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Hardlinking of duplicated bytecode makes sense " 192*cda5da8dSAndroid Build Coastguard Worker "only for more than one optimization level") 193*cda5da8dSAndroid Build Coastguard Worker 194*cda5da8dSAndroid Build Coastguard Worker if rx is not None: 195*cda5da8dSAndroid Build Coastguard Worker mo = rx.search(fullname) 196*cda5da8dSAndroid Build Coastguard Worker if mo: 197*cda5da8dSAndroid Build Coastguard Worker return success 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker if limit_sl_dest is not None and os.path.islink(fullname): 200*cda5da8dSAndroid Build Coastguard Worker if Path(limit_sl_dest).resolve() not in Path(fullname).resolve().parents: 201*cda5da8dSAndroid Build Coastguard Worker return success 202*cda5da8dSAndroid Build Coastguard Worker 203*cda5da8dSAndroid Build Coastguard Worker opt_cfiles = {} 204*cda5da8dSAndroid Build Coastguard Worker 205*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(fullname): 206*cda5da8dSAndroid Build Coastguard Worker for opt_level in optimize: 207*cda5da8dSAndroid Build Coastguard Worker if legacy: 208*cda5da8dSAndroid Build Coastguard Worker opt_cfiles[opt_level] = fullname + 'c' 209*cda5da8dSAndroid Build Coastguard Worker else: 210*cda5da8dSAndroid Build Coastguard Worker if opt_level >= 0: 211*cda5da8dSAndroid Build Coastguard Worker opt = opt_level if opt_level >= 1 else '' 212*cda5da8dSAndroid Build Coastguard Worker cfile = (importlib.util.cache_from_source( 213*cda5da8dSAndroid Build Coastguard Worker fullname, optimization=opt)) 214*cda5da8dSAndroid Build Coastguard Worker opt_cfiles[opt_level] = cfile 215*cda5da8dSAndroid Build Coastguard Worker else: 216*cda5da8dSAndroid Build Coastguard Worker cfile = importlib.util.cache_from_source(fullname) 217*cda5da8dSAndroid Build Coastguard Worker opt_cfiles[opt_level] = cfile 218*cda5da8dSAndroid Build Coastguard Worker 219*cda5da8dSAndroid Build Coastguard Worker head, tail = name[:-3], name[-3:] 220*cda5da8dSAndroid Build Coastguard Worker if tail == '.py': 221*cda5da8dSAndroid Build Coastguard Worker if not force: 222*cda5da8dSAndroid Build Coastguard Worker try: 223*cda5da8dSAndroid Build Coastguard Worker mtime = int(os.stat(fullname).st_mtime) 224*cda5da8dSAndroid Build Coastguard Worker expect = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, 225*cda5da8dSAndroid Build Coastguard Worker 0, mtime & 0xFFFF_FFFF) 226*cda5da8dSAndroid Build Coastguard Worker for cfile in opt_cfiles.values(): 227*cda5da8dSAndroid Build Coastguard Worker with open(cfile, 'rb') as chandle: 228*cda5da8dSAndroid Build Coastguard Worker actual = chandle.read(12) 229*cda5da8dSAndroid Build Coastguard Worker if expect != actual: 230*cda5da8dSAndroid Build Coastguard Worker break 231*cda5da8dSAndroid Build Coastguard Worker else: 232*cda5da8dSAndroid Build Coastguard Worker return success 233*cda5da8dSAndroid Build Coastguard Worker except OSError: 234*cda5da8dSAndroid Build Coastguard Worker pass 235*cda5da8dSAndroid Build Coastguard Worker if not quiet: 236*cda5da8dSAndroid Build Coastguard Worker print('Compiling {!r}...'.format(fullname)) 237*cda5da8dSAndroid Build Coastguard Worker try: 238*cda5da8dSAndroid Build Coastguard Worker for index, opt_level in enumerate(optimize): 239*cda5da8dSAndroid Build Coastguard Worker cfile = opt_cfiles[opt_level] 240*cda5da8dSAndroid Build Coastguard Worker ok = py_compile.compile(fullname, cfile, dfile, True, 241*cda5da8dSAndroid Build Coastguard Worker optimize=opt_level, 242*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=invalidation_mode) 243*cda5da8dSAndroid Build Coastguard Worker if index > 0 and hardlink_dupes: 244*cda5da8dSAndroid Build Coastguard Worker previous_cfile = opt_cfiles[optimize[index - 1]] 245*cda5da8dSAndroid Build Coastguard Worker if filecmp.cmp(cfile, previous_cfile, shallow=False): 246*cda5da8dSAndroid Build Coastguard Worker os.unlink(cfile) 247*cda5da8dSAndroid Build Coastguard Worker os.link(previous_cfile, cfile) 248*cda5da8dSAndroid Build Coastguard Worker except py_compile.PyCompileError as err: 249*cda5da8dSAndroid Build Coastguard Worker success = False 250*cda5da8dSAndroid Build Coastguard Worker if quiet >= 2: 251*cda5da8dSAndroid Build Coastguard Worker return success 252*cda5da8dSAndroid Build Coastguard Worker elif quiet: 253*cda5da8dSAndroid Build Coastguard Worker print('*** Error compiling {!r}...'.format(fullname)) 254*cda5da8dSAndroid Build Coastguard Worker else: 255*cda5da8dSAndroid Build Coastguard Worker print('*** ', end='') 256*cda5da8dSAndroid Build Coastguard Worker # escape non-printable characters in msg 257*cda5da8dSAndroid Build Coastguard Worker encoding = sys.stdout.encoding or sys.getdefaultencoding() 258*cda5da8dSAndroid Build Coastguard Worker msg = err.msg.encode(encoding, errors='backslashreplace').decode(encoding) 259*cda5da8dSAndroid Build Coastguard Worker print(msg) 260*cda5da8dSAndroid Build Coastguard Worker except (SyntaxError, UnicodeError, OSError) as e: 261*cda5da8dSAndroid Build Coastguard Worker success = False 262*cda5da8dSAndroid Build Coastguard Worker if quiet >= 2: 263*cda5da8dSAndroid Build Coastguard Worker return success 264*cda5da8dSAndroid Build Coastguard Worker elif quiet: 265*cda5da8dSAndroid Build Coastguard Worker print('*** Error compiling {!r}...'.format(fullname)) 266*cda5da8dSAndroid Build Coastguard Worker else: 267*cda5da8dSAndroid Build Coastguard Worker print('*** ', end='') 268*cda5da8dSAndroid Build Coastguard Worker print(e.__class__.__name__ + ':', e) 269*cda5da8dSAndroid Build Coastguard Worker else: 270*cda5da8dSAndroid Build Coastguard Worker if ok == 0: 271*cda5da8dSAndroid Build Coastguard Worker success = False 272*cda5da8dSAndroid Build Coastguard Worker return success 273*cda5da8dSAndroid Build Coastguard Worker 274*cda5da8dSAndroid Build Coastguard Workerdef compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=0, 275*cda5da8dSAndroid Build Coastguard Worker legacy=False, optimize=-1, 276*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=None): 277*cda5da8dSAndroid Build Coastguard Worker """Byte-compile all module on sys.path. 278*cda5da8dSAndroid Build Coastguard Worker 279*cda5da8dSAndroid Build Coastguard Worker Arguments (all optional): 280*cda5da8dSAndroid Build Coastguard Worker 281*cda5da8dSAndroid Build Coastguard Worker skip_curdir: if true, skip current directory (default True) 282*cda5da8dSAndroid Build Coastguard Worker maxlevels: max recursion level (default 0) 283*cda5da8dSAndroid Build Coastguard Worker force: as for compile_dir() (default False) 284*cda5da8dSAndroid Build Coastguard Worker quiet: as for compile_dir() (default 0) 285*cda5da8dSAndroid Build Coastguard Worker legacy: as for compile_dir() (default False) 286*cda5da8dSAndroid Build Coastguard Worker optimize: as for compile_dir() (default -1) 287*cda5da8dSAndroid Build Coastguard Worker invalidation_mode: as for compiler_dir() 288*cda5da8dSAndroid Build Coastguard Worker """ 289*cda5da8dSAndroid Build Coastguard Worker success = True 290*cda5da8dSAndroid Build Coastguard Worker for dir in sys.path: 291*cda5da8dSAndroid Build Coastguard Worker if (not dir or dir == os.curdir) and skip_curdir: 292*cda5da8dSAndroid Build Coastguard Worker if quiet < 2: 293*cda5da8dSAndroid Build Coastguard Worker print('Skipping current directory') 294*cda5da8dSAndroid Build Coastguard Worker else: 295*cda5da8dSAndroid Build Coastguard Worker success = success and compile_dir( 296*cda5da8dSAndroid Build Coastguard Worker dir, 297*cda5da8dSAndroid Build Coastguard Worker maxlevels, 298*cda5da8dSAndroid Build Coastguard Worker None, 299*cda5da8dSAndroid Build Coastguard Worker force, 300*cda5da8dSAndroid Build Coastguard Worker quiet=quiet, 301*cda5da8dSAndroid Build Coastguard Worker legacy=legacy, 302*cda5da8dSAndroid Build Coastguard Worker optimize=optimize, 303*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=invalidation_mode, 304*cda5da8dSAndroid Build Coastguard Worker ) 305*cda5da8dSAndroid Build Coastguard Worker return success 306*cda5da8dSAndroid Build Coastguard Worker 307*cda5da8dSAndroid Build Coastguard Worker 308*cda5da8dSAndroid Build Coastguard Workerdef main(): 309*cda5da8dSAndroid Build Coastguard Worker """Script main program.""" 310*cda5da8dSAndroid Build Coastguard Worker import argparse 311*cda5da8dSAndroid Build Coastguard Worker 312*cda5da8dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 313*cda5da8dSAndroid Build Coastguard Worker description='Utilities to support installing Python libraries.') 314*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-l', action='store_const', const=0, 315*cda5da8dSAndroid Build Coastguard Worker default=None, dest='maxlevels', 316*cda5da8dSAndroid Build Coastguard Worker help="don't recurse into subdirectories") 317*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-r', type=int, dest='recursion', 318*cda5da8dSAndroid Build Coastguard Worker help=('control the maximum recursion level. ' 319*cda5da8dSAndroid Build Coastguard Worker 'if `-l` and `-r` options are specified, ' 320*cda5da8dSAndroid Build Coastguard Worker 'then `-r` takes precedence.')) 321*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-f', action='store_true', dest='force', 322*cda5da8dSAndroid Build Coastguard Worker help='force rebuild even if timestamps are up to date') 323*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-q', action='count', dest='quiet', default=0, 324*cda5da8dSAndroid Build Coastguard Worker help='output only error messages; -qq will suppress ' 325*cda5da8dSAndroid Build Coastguard Worker 'the error messages as well.') 326*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-b', action='store_true', dest='legacy', 327*cda5da8dSAndroid Build Coastguard Worker help='use legacy (pre-PEP3147) compiled file locations') 328*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-d', metavar='DESTDIR', dest='ddir', default=None, 329*cda5da8dSAndroid Build Coastguard Worker help=('directory to prepend to file paths for use in ' 330*cda5da8dSAndroid Build Coastguard Worker 'compile-time tracebacks and in runtime ' 331*cda5da8dSAndroid Build Coastguard Worker 'tracebacks in cases where the source file is ' 332*cda5da8dSAndroid Build Coastguard Worker 'unavailable')) 333*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-s', metavar='STRIPDIR', dest='stripdir', 334*cda5da8dSAndroid Build Coastguard Worker default=None, 335*cda5da8dSAndroid Build Coastguard Worker help=('part of path to left-strip from path ' 336*cda5da8dSAndroid Build Coastguard Worker 'to source file - for example buildroot. ' 337*cda5da8dSAndroid Build Coastguard Worker '`-d` and `-s` options cannot be ' 338*cda5da8dSAndroid Build Coastguard Worker 'specified together.')) 339*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-p', metavar='PREPENDDIR', dest='prependdir', 340*cda5da8dSAndroid Build Coastguard Worker default=None, 341*cda5da8dSAndroid Build Coastguard Worker help=('path to add as prefix to path ' 342*cda5da8dSAndroid Build Coastguard Worker 'to source file - for example / to make ' 343*cda5da8dSAndroid Build Coastguard Worker 'it absolute when some part is removed ' 344*cda5da8dSAndroid Build Coastguard Worker 'by `-s` option. ' 345*cda5da8dSAndroid Build Coastguard Worker '`-d` and `-p` options cannot be ' 346*cda5da8dSAndroid Build Coastguard Worker 'specified together.')) 347*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-x', metavar='REGEXP', dest='rx', default=None, 348*cda5da8dSAndroid Build Coastguard Worker help=('skip files matching the regular expression; ' 349*cda5da8dSAndroid Build Coastguard Worker 'the regexp is searched for in the full path ' 350*cda5da8dSAndroid Build Coastguard Worker 'of each file considered for compilation')) 351*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-i', metavar='FILE', dest='flist', 352*cda5da8dSAndroid Build Coastguard Worker help=('add all the files and directories listed in ' 353*cda5da8dSAndroid Build Coastguard Worker 'FILE to the list considered for compilation; ' 354*cda5da8dSAndroid Build Coastguard Worker 'if "-", names are read from stdin')) 355*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('compile_dest', metavar='FILE|DIR', nargs='*', 356*cda5da8dSAndroid Build Coastguard Worker help=('zero or more file and directory names ' 357*cda5da8dSAndroid Build Coastguard Worker 'to compile; if no arguments given, defaults ' 358*cda5da8dSAndroid Build Coastguard Worker 'to the equivalent of -l sys.path')) 359*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-j', '--workers', default=1, 360*cda5da8dSAndroid Build Coastguard Worker type=int, help='Run compileall concurrently') 361*cda5da8dSAndroid Build Coastguard Worker invalidation_modes = [mode.name.lower().replace('_', '-') 362*cda5da8dSAndroid Build Coastguard Worker for mode in py_compile.PycInvalidationMode] 363*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('--invalidation-mode', 364*cda5da8dSAndroid Build Coastguard Worker choices=sorted(invalidation_modes), 365*cda5da8dSAndroid Build Coastguard Worker help=('set .pyc invalidation mode; defaults to ' 366*cda5da8dSAndroid Build Coastguard Worker '"checked-hash" if the SOURCE_DATE_EPOCH ' 367*cda5da8dSAndroid Build Coastguard Worker 'environment variable is set, and ' 368*cda5da8dSAndroid Build Coastguard Worker '"timestamp" otherwise.')) 369*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-o', action='append', type=int, dest='opt_levels', 370*cda5da8dSAndroid Build Coastguard Worker help=('Optimization levels to run compilation with. ' 371*cda5da8dSAndroid Build Coastguard Worker 'Default is -1 which uses the optimization level ' 372*cda5da8dSAndroid Build Coastguard Worker 'of the Python interpreter itself (see -O).')) 373*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-e', metavar='DIR', dest='limit_sl_dest', 374*cda5da8dSAndroid Build Coastguard Worker help='Ignore symlinks pointing outsite of the DIR') 375*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('--hardlink-dupes', action='store_true', 376*cda5da8dSAndroid Build Coastguard Worker dest='hardlink_dupes', 377*cda5da8dSAndroid Build Coastguard Worker help='Hardlink duplicated pyc files') 378*cda5da8dSAndroid Build Coastguard Worker 379*cda5da8dSAndroid Build Coastguard Worker args = parser.parse_args() 380*cda5da8dSAndroid Build Coastguard Worker compile_dests = args.compile_dest 381*cda5da8dSAndroid Build Coastguard Worker 382*cda5da8dSAndroid Build Coastguard Worker if args.rx: 383*cda5da8dSAndroid Build Coastguard Worker import re 384*cda5da8dSAndroid Build Coastguard Worker args.rx = re.compile(args.rx) 385*cda5da8dSAndroid Build Coastguard Worker 386*cda5da8dSAndroid Build Coastguard Worker if args.limit_sl_dest == "": 387*cda5da8dSAndroid Build Coastguard Worker args.limit_sl_dest = None 388*cda5da8dSAndroid Build Coastguard Worker 389*cda5da8dSAndroid Build Coastguard Worker if args.recursion is not None: 390*cda5da8dSAndroid Build Coastguard Worker maxlevels = args.recursion 391*cda5da8dSAndroid Build Coastguard Worker else: 392*cda5da8dSAndroid Build Coastguard Worker maxlevels = args.maxlevels 393*cda5da8dSAndroid Build Coastguard Worker 394*cda5da8dSAndroid Build Coastguard Worker if args.opt_levels is None: 395*cda5da8dSAndroid Build Coastguard Worker args.opt_levels = [-1] 396*cda5da8dSAndroid Build Coastguard Worker 397*cda5da8dSAndroid Build Coastguard Worker if len(args.opt_levels) == 1 and args.hardlink_dupes: 398*cda5da8dSAndroid Build Coastguard Worker parser.error(("Hardlinking of duplicated bytecode makes sense " 399*cda5da8dSAndroid Build Coastguard Worker "only for more than one optimization level.")) 400*cda5da8dSAndroid Build Coastguard Worker 401*cda5da8dSAndroid Build Coastguard Worker if args.ddir is not None and ( 402*cda5da8dSAndroid Build Coastguard Worker args.stripdir is not None or args.prependdir is not None 403*cda5da8dSAndroid Build Coastguard Worker ): 404*cda5da8dSAndroid Build Coastguard Worker parser.error("-d cannot be used in combination with -s or -p") 405*cda5da8dSAndroid Build Coastguard Worker 406*cda5da8dSAndroid Build Coastguard Worker # if flist is provided then load it 407*cda5da8dSAndroid Build Coastguard Worker if args.flist: 408*cda5da8dSAndroid Build Coastguard Worker try: 409*cda5da8dSAndroid Build Coastguard Worker with (sys.stdin if args.flist=='-' else 410*cda5da8dSAndroid Build Coastguard Worker open(args.flist, encoding="utf-8")) as f: 411*cda5da8dSAndroid Build Coastguard Worker for line in f: 412*cda5da8dSAndroid Build Coastguard Worker compile_dests.append(line.strip()) 413*cda5da8dSAndroid Build Coastguard Worker except OSError: 414*cda5da8dSAndroid Build Coastguard Worker if args.quiet < 2: 415*cda5da8dSAndroid Build Coastguard Worker print("Error reading file list {}".format(args.flist)) 416*cda5da8dSAndroid Build Coastguard Worker return False 417*cda5da8dSAndroid Build Coastguard Worker 418*cda5da8dSAndroid Build Coastguard Worker if args.invalidation_mode: 419*cda5da8dSAndroid Build Coastguard Worker ivl_mode = args.invalidation_mode.replace('-', '_').upper() 420*cda5da8dSAndroid Build Coastguard Worker invalidation_mode = py_compile.PycInvalidationMode[ivl_mode] 421*cda5da8dSAndroid Build Coastguard Worker else: 422*cda5da8dSAndroid Build Coastguard Worker invalidation_mode = None 423*cda5da8dSAndroid Build Coastguard Worker 424*cda5da8dSAndroid Build Coastguard Worker success = True 425*cda5da8dSAndroid Build Coastguard Worker try: 426*cda5da8dSAndroid Build Coastguard Worker if compile_dests: 427*cda5da8dSAndroid Build Coastguard Worker for dest in compile_dests: 428*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(dest): 429*cda5da8dSAndroid Build Coastguard Worker if not compile_file(dest, args.ddir, args.force, args.rx, 430*cda5da8dSAndroid Build Coastguard Worker args.quiet, args.legacy, 431*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=invalidation_mode, 432*cda5da8dSAndroid Build Coastguard Worker stripdir=args.stripdir, 433*cda5da8dSAndroid Build Coastguard Worker prependdir=args.prependdir, 434*cda5da8dSAndroid Build Coastguard Worker optimize=args.opt_levels, 435*cda5da8dSAndroid Build Coastguard Worker limit_sl_dest=args.limit_sl_dest, 436*cda5da8dSAndroid Build Coastguard Worker hardlink_dupes=args.hardlink_dupes): 437*cda5da8dSAndroid Build Coastguard Worker success = False 438*cda5da8dSAndroid Build Coastguard Worker else: 439*cda5da8dSAndroid Build Coastguard Worker if not compile_dir(dest, maxlevels, args.ddir, 440*cda5da8dSAndroid Build Coastguard Worker args.force, args.rx, args.quiet, 441*cda5da8dSAndroid Build Coastguard Worker args.legacy, workers=args.workers, 442*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=invalidation_mode, 443*cda5da8dSAndroid Build Coastguard Worker stripdir=args.stripdir, 444*cda5da8dSAndroid Build Coastguard Worker prependdir=args.prependdir, 445*cda5da8dSAndroid Build Coastguard Worker optimize=args.opt_levels, 446*cda5da8dSAndroid Build Coastguard Worker limit_sl_dest=args.limit_sl_dest, 447*cda5da8dSAndroid Build Coastguard Worker hardlink_dupes=args.hardlink_dupes): 448*cda5da8dSAndroid Build Coastguard Worker success = False 449*cda5da8dSAndroid Build Coastguard Worker return success 450*cda5da8dSAndroid Build Coastguard Worker else: 451*cda5da8dSAndroid Build Coastguard Worker return compile_path(legacy=args.legacy, force=args.force, 452*cda5da8dSAndroid Build Coastguard Worker quiet=args.quiet, 453*cda5da8dSAndroid Build Coastguard Worker invalidation_mode=invalidation_mode) 454*cda5da8dSAndroid Build Coastguard Worker except KeyboardInterrupt: 455*cda5da8dSAndroid Build Coastguard Worker if args.quiet < 2: 456*cda5da8dSAndroid Build Coastguard Worker print("\n[interrupted]") 457*cda5da8dSAndroid Build Coastguard Worker return False 458*cda5da8dSAndroid Build Coastguard Worker return True 459*cda5da8dSAndroid Build Coastguard Worker 460*cda5da8dSAndroid Build Coastguard Worker 461*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 462*cda5da8dSAndroid Build Coastguard Worker exit_status = int(not main()) 463*cda5da8dSAndroid Build Coastguard Worker sys.exit(exit_status) 464