17db96d56Sopenharmony_ci"""distutils.util 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciMiscellaneous utility functions -- anything that doesn't fit into 47db96d56Sopenharmony_cione of the other *util.py modules. 57db96d56Sopenharmony_ci""" 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ciimport os 87db96d56Sopenharmony_ciimport re 97db96d56Sopenharmony_ciimport importlib.util 107db96d56Sopenharmony_ciimport string 117db96d56Sopenharmony_ciimport sys 127db96d56Sopenharmony_ciimport distutils 137db96d56Sopenharmony_cifrom distutils.errors import DistutilsPlatformError 147db96d56Sopenharmony_cifrom distutils.dep_util import newer 157db96d56Sopenharmony_cifrom distutils.spawn import spawn 167db96d56Sopenharmony_cifrom distutils import log 177db96d56Sopenharmony_cifrom distutils.errors import DistutilsByteCompileError 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_cidef get_host_platform(): 207db96d56Sopenharmony_ci """Return a string that identifies the current platform. This is used mainly to 217db96d56Sopenharmony_ci distinguish platform-specific build directories and platform-specific built 227db96d56Sopenharmony_ci distributions. Typically includes the OS name and version and the 237db96d56Sopenharmony_ci architecture (as supplied by 'os.uname()'), although the exact information 247db96d56Sopenharmony_ci included depends on the OS; eg. on Linux, the kernel version isn't 257db96d56Sopenharmony_ci particularly important. 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ci Examples of returned values: 287db96d56Sopenharmony_ci linux-i586 297db96d56Sopenharmony_ci linux-alpha (?) 307db96d56Sopenharmony_ci solaris-2.6-sun4u 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci Windows will return one of: 337db96d56Sopenharmony_ci win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) 347db96d56Sopenharmony_ci win32 (all others - specifically, sys.platform is returned) 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci For other non-POSIX platforms, currently just returns 'sys.platform'. 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_ci """ 397db96d56Sopenharmony_ci if os.name == 'nt': 407db96d56Sopenharmony_ci if 'amd64' in sys.version.lower(): 417db96d56Sopenharmony_ci return 'win-amd64' 427db96d56Sopenharmony_ci if '(arm)' in sys.version.lower(): 437db96d56Sopenharmony_ci return 'win-arm32' 447db96d56Sopenharmony_ci if '(arm64)' in sys.version.lower(): 457db96d56Sopenharmony_ci return 'win-arm64' 467db96d56Sopenharmony_ci return sys.platform 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_ci # Set for cross builds explicitly 497db96d56Sopenharmony_ci if "_PYTHON_HOST_PLATFORM" in os.environ: 507db96d56Sopenharmony_ci return os.environ["_PYTHON_HOST_PLATFORM"] 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_ci if os.name != "posix" or not hasattr(os, 'uname'): 537db96d56Sopenharmony_ci # XXX what about the architecture? NT is Intel or Alpha, 547db96d56Sopenharmony_ci # Mac OS is M68k or PPC, etc. 557db96d56Sopenharmony_ci return sys.platform 567db96d56Sopenharmony_ci 577db96d56Sopenharmony_ci # Try to distinguish various flavours of Unix 587db96d56Sopenharmony_ci 597db96d56Sopenharmony_ci (osname, host, release, version, machine) = os.uname() 607db96d56Sopenharmony_ci 617db96d56Sopenharmony_ci # Convert the OS name to lowercase, remove '/' characters, and translate 627db96d56Sopenharmony_ci # spaces (for "Power Macintosh") 637db96d56Sopenharmony_ci osname = osname.lower().replace('/', '') 647db96d56Sopenharmony_ci machine = machine.replace(' ', '_') 657db96d56Sopenharmony_ci machine = machine.replace('/', '-') 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci if osname[:5] == "linux": 687db96d56Sopenharmony_ci # At least on Linux/Intel, 'machine' is the processor -- 697db96d56Sopenharmony_ci # i386, etc. 707db96d56Sopenharmony_ci # XXX what about Alpha, SPARC, etc? 717db96d56Sopenharmony_ci return "%s-%s" % (osname, machine) 727db96d56Sopenharmony_ci elif osname[:5] == "sunos": 737db96d56Sopenharmony_ci if release[0] >= "5": # SunOS 5 == Solaris 2 747db96d56Sopenharmony_ci osname = "solaris" 757db96d56Sopenharmony_ci release = "%d.%s" % (int(release[0]) - 3, release[2:]) 767db96d56Sopenharmony_ci # We can't use "platform.architecture()[0]" because a 777db96d56Sopenharmony_ci # bootstrap problem. We use a dict to get an error 787db96d56Sopenharmony_ci # if some suspicious happens. 797db96d56Sopenharmony_ci bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} 807db96d56Sopenharmony_ci machine += ".%s" % bitness[sys.maxsize] 817db96d56Sopenharmony_ci # fall through to standard osname-release-machine representation 827db96d56Sopenharmony_ci elif osname[:3] == "aix": 837db96d56Sopenharmony_ci from _aix_support import aix_platform 847db96d56Sopenharmony_ci return aix_platform() 857db96d56Sopenharmony_ci elif osname[:6] == "cygwin": 867db96d56Sopenharmony_ci osname = "cygwin" 877db96d56Sopenharmony_ci rel_re = re.compile (r'[\d.]+', re.ASCII) 887db96d56Sopenharmony_ci m = rel_re.match(release) 897db96d56Sopenharmony_ci if m: 907db96d56Sopenharmony_ci release = m.group() 917db96d56Sopenharmony_ci elif osname[:6] == "darwin": 927db96d56Sopenharmony_ci import _osx_support, distutils.sysconfig 937db96d56Sopenharmony_ci osname, release, machine = _osx_support.get_platform_osx( 947db96d56Sopenharmony_ci distutils.sysconfig.get_config_vars(), 957db96d56Sopenharmony_ci osname, release, machine) 967db96d56Sopenharmony_ci 977db96d56Sopenharmony_ci return "%s-%s-%s" % (osname, release, machine) 987db96d56Sopenharmony_ci 997db96d56Sopenharmony_cidef get_platform(): 1007db96d56Sopenharmony_ci if os.name == 'nt': 1017db96d56Sopenharmony_ci TARGET_TO_PLAT = { 1027db96d56Sopenharmony_ci 'x86' : 'win32', 1037db96d56Sopenharmony_ci 'x64' : 'win-amd64', 1047db96d56Sopenharmony_ci 'arm' : 'win-arm32', 1057db96d56Sopenharmony_ci } 1067db96d56Sopenharmony_ci return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() 1077db96d56Sopenharmony_ci else: 1087db96d56Sopenharmony_ci return get_host_platform() 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_cidef convert_path (pathname): 1117db96d56Sopenharmony_ci """Return 'pathname' as a name that will work on the native filesystem, 1127db96d56Sopenharmony_ci i.e. split it on '/' and put it back together again using the current 1137db96d56Sopenharmony_ci directory separator. Needed because filenames in the setup script are 1147db96d56Sopenharmony_ci always supplied in Unix style, and have to be converted to the local 1157db96d56Sopenharmony_ci convention before we can actually use them in the filesystem. Raises 1167db96d56Sopenharmony_ci ValueError on non-Unix-ish systems if 'pathname' either starts or 1177db96d56Sopenharmony_ci ends with a slash. 1187db96d56Sopenharmony_ci """ 1197db96d56Sopenharmony_ci if os.sep == '/': 1207db96d56Sopenharmony_ci return pathname 1217db96d56Sopenharmony_ci if not pathname: 1227db96d56Sopenharmony_ci return pathname 1237db96d56Sopenharmony_ci if pathname[0] == '/': 1247db96d56Sopenharmony_ci raise ValueError("path '%s' cannot be absolute" % pathname) 1257db96d56Sopenharmony_ci if pathname[-1] == '/': 1267db96d56Sopenharmony_ci raise ValueError("path '%s' cannot end with '/'" % pathname) 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci paths = pathname.split('/') 1297db96d56Sopenharmony_ci while '.' in paths: 1307db96d56Sopenharmony_ci paths.remove('.') 1317db96d56Sopenharmony_ci if not paths: 1327db96d56Sopenharmony_ci return os.curdir 1337db96d56Sopenharmony_ci return os.path.join(*paths) 1347db96d56Sopenharmony_ci 1357db96d56Sopenharmony_ci# convert_path () 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci 1387db96d56Sopenharmony_cidef change_root (new_root, pathname): 1397db96d56Sopenharmony_ci """Return 'pathname' with 'new_root' prepended. If 'pathname' is 1407db96d56Sopenharmony_ci relative, this is equivalent to "os.path.join(new_root,pathname)". 1417db96d56Sopenharmony_ci Otherwise, it requires making 'pathname' relative and then joining the 1427db96d56Sopenharmony_ci two, which is tricky on DOS/Windows and Mac OS. 1437db96d56Sopenharmony_ci """ 1447db96d56Sopenharmony_ci if os.name == 'posix': 1457db96d56Sopenharmony_ci if not os.path.isabs(pathname): 1467db96d56Sopenharmony_ci return os.path.join(new_root, pathname) 1477db96d56Sopenharmony_ci else: 1487db96d56Sopenharmony_ci return os.path.join(new_root, pathname[1:]) 1497db96d56Sopenharmony_ci 1507db96d56Sopenharmony_ci elif os.name == 'nt': 1517db96d56Sopenharmony_ci (drive, path) = os.path.splitdrive(pathname) 1527db96d56Sopenharmony_ci if path[0] == '\\': 1537db96d56Sopenharmony_ci path = path[1:] 1547db96d56Sopenharmony_ci return os.path.join(new_root, path) 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_ci else: 1577db96d56Sopenharmony_ci raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci_environ_checked = 0 1617db96d56Sopenharmony_cidef check_environ (): 1627db96d56Sopenharmony_ci """Ensure that 'os.environ' has all the environment variables we 1637db96d56Sopenharmony_ci guarantee that users can use in config files, command-line options, 1647db96d56Sopenharmony_ci etc. Currently this includes: 1657db96d56Sopenharmony_ci HOME - user's home directory (Unix only) 1667db96d56Sopenharmony_ci PLAT - description of the current platform, including hardware 1677db96d56Sopenharmony_ci and OS (see 'get_platform()') 1687db96d56Sopenharmony_ci """ 1697db96d56Sopenharmony_ci global _environ_checked 1707db96d56Sopenharmony_ci if _environ_checked: 1717db96d56Sopenharmony_ci return 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci if os.name == 'posix' and 'HOME' not in os.environ: 1747db96d56Sopenharmony_ci try: 1757db96d56Sopenharmony_ci import pwd 1767db96d56Sopenharmony_ci os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] 1777db96d56Sopenharmony_ci except (ImportError, KeyError): 1787db96d56Sopenharmony_ci # bpo-10496: if the current user identifier doesn't exist in the 1797db96d56Sopenharmony_ci # password database, do nothing 1807db96d56Sopenharmony_ci pass 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci if 'PLAT' not in os.environ: 1837db96d56Sopenharmony_ci os.environ['PLAT'] = get_platform() 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ci _environ_checked = 1 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_cidef subst_vars (s, local_vars): 1897db96d56Sopenharmony_ci """Perform shell/Perl-style variable substitution on 'string'. Every 1907db96d56Sopenharmony_ci occurrence of '$' followed by a name is considered a variable, and 1917db96d56Sopenharmony_ci variable is substituted by the value found in the 'local_vars' 1927db96d56Sopenharmony_ci dictionary, or in 'os.environ' if it's not in 'local_vars'. 1937db96d56Sopenharmony_ci 'os.environ' is first checked/augmented to guarantee that it contains 1947db96d56Sopenharmony_ci certain values: see 'check_environ()'. Raise ValueError for any 1957db96d56Sopenharmony_ci variables not found in either 'local_vars' or 'os.environ'. 1967db96d56Sopenharmony_ci """ 1977db96d56Sopenharmony_ci check_environ() 1987db96d56Sopenharmony_ci def _subst (match, local_vars=local_vars): 1997db96d56Sopenharmony_ci var_name = match.group(1) 2007db96d56Sopenharmony_ci if var_name in local_vars: 2017db96d56Sopenharmony_ci return str(local_vars[var_name]) 2027db96d56Sopenharmony_ci else: 2037db96d56Sopenharmony_ci return os.environ[var_name] 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ci try: 2067db96d56Sopenharmony_ci return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) 2077db96d56Sopenharmony_ci except KeyError as var: 2087db96d56Sopenharmony_ci raise ValueError("invalid variable '$%s'" % var) 2097db96d56Sopenharmony_ci 2107db96d56Sopenharmony_ci# subst_vars () 2117db96d56Sopenharmony_ci 2127db96d56Sopenharmony_ci 2137db96d56Sopenharmony_cidef grok_environment_error (exc, prefix="error: "): 2147db96d56Sopenharmony_ci # Function kept for backward compatibility. 2157db96d56Sopenharmony_ci # Used to try clever things with EnvironmentErrors, 2167db96d56Sopenharmony_ci # but nowadays str(exception) produces good messages. 2177db96d56Sopenharmony_ci return prefix + str(exc) 2187db96d56Sopenharmony_ci 2197db96d56Sopenharmony_ci 2207db96d56Sopenharmony_ci# Needed by 'split_quoted()' 2217db96d56Sopenharmony_ci_wordchars_re = _squote_re = _dquote_re = None 2227db96d56Sopenharmony_cidef _init_regex(): 2237db96d56Sopenharmony_ci global _wordchars_re, _squote_re, _dquote_re 2247db96d56Sopenharmony_ci _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) 2257db96d56Sopenharmony_ci _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") 2267db96d56Sopenharmony_ci _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') 2277db96d56Sopenharmony_ci 2287db96d56Sopenharmony_cidef split_quoted (s): 2297db96d56Sopenharmony_ci """Split a string up according to Unix shell-like rules for quotes and 2307db96d56Sopenharmony_ci backslashes. In short: words are delimited by spaces, as long as those 2317db96d56Sopenharmony_ci spaces are not escaped by a backslash, or inside a quoted string. 2327db96d56Sopenharmony_ci Single and double quotes are equivalent, and the quote characters can 2337db96d56Sopenharmony_ci be backslash-escaped. The backslash is stripped from any two-character 2347db96d56Sopenharmony_ci escape sequence, leaving only the escaped character. The quote 2357db96d56Sopenharmony_ci characters are stripped from any quoted string. Returns a list of 2367db96d56Sopenharmony_ci words. 2377db96d56Sopenharmony_ci """ 2387db96d56Sopenharmony_ci 2397db96d56Sopenharmony_ci # This is a nice algorithm for splitting up a single string, since it 2407db96d56Sopenharmony_ci # doesn't require character-by-character examination. It was a little 2417db96d56Sopenharmony_ci # bit of a brain-bender to get it working right, though... 2427db96d56Sopenharmony_ci if _wordchars_re is None: _init_regex() 2437db96d56Sopenharmony_ci 2447db96d56Sopenharmony_ci s = s.strip() 2457db96d56Sopenharmony_ci words = [] 2467db96d56Sopenharmony_ci pos = 0 2477db96d56Sopenharmony_ci 2487db96d56Sopenharmony_ci while s: 2497db96d56Sopenharmony_ci m = _wordchars_re.match(s, pos) 2507db96d56Sopenharmony_ci end = m.end() 2517db96d56Sopenharmony_ci if end == len(s): 2527db96d56Sopenharmony_ci words.append(s[:end]) 2537db96d56Sopenharmony_ci break 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ci if s[end] in string.whitespace: # unescaped, unquoted whitespace: now 2567db96d56Sopenharmony_ci words.append(s[:end]) # we definitely have a word delimiter 2577db96d56Sopenharmony_ci s = s[end:].lstrip() 2587db96d56Sopenharmony_ci pos = 0 2597db96d56Sopenharmony_ci 2607db96d56Sopenharmony_ci elif s[end] == '\\': # preserve whatever is being escaped; 2617db96d56Sopenharmony_ci # will become part of the current word 2627db96d56Sopenharmony_ci s = s[:end] + s[end+1:] 2637db96d56Sopenharmony_ci pos = end+1 2647db96d56Sopenharmony_ci 2657db96d56Sopenharmony_ci else: 2667db96d56Sopenharmony_ci if s[end] == "'": # slurp singly-quoted string 2677db96d56Sopenharmony_ci m = _squote_re.match(s, end) 2687db96d56Sopenharmony_ci elif s[end] == '"': # slurp doubly-quoted string 2697db96d56Sopenharmony_ci m = _dquote_re.match(s, end) 2707db96d56Sopenharmony_ci else: 2717db96d56Sopenharmony_ci raise RuntimeError("this can't happen (bad char '%c')" % s[end]) 2727db96d56Sopenharmony_ci 2737db96d56Sopenharmony_ci if m is None: 2747db96d56Sopenharmony_ci raise ValueError("bad string (mismatched %s quotes?)" % s[end]) 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ci (beg, end) = m.span() 2777db96d56Sopenharmony_ci s = s[:beg] + s[beg+1:end-1] + s[end:] 2787db96d56Sopenharmony_ci pos = m.end() - 2 2797db96d56Sopenharmony_ci 2807db96d56Sopenharmony_ci if pos >= len(s): 2817db96d56Sopenharmony_ci words.append(s) 2827db96d56Sopenharmony_ci break 2837db96d56Sopenharmony_ci 2847db96d56Sopenharmony_ci return words 2857db96d56Sopenharmony_ci 2867db96d56Sopenharmony_ci# split_quoted () 2877db96d56Sopenharmony_ci 2887db96d56Sopenharmony_ci 2897db96d56Sopenharmony_cidef execute (func, args, msg=None, verbose=0, dry_run=0): 2907db96d56Sopenharmony_ci """Perform some action that affects the outside world (eg. by 2917db96d56Sopenharmony_ci writing to the filesystem). Such actions are special because they 2927db96d56Sopenharmony_ci are disabled by the 'dry_run' flag. This method takes care of all 2937db96d56Sopenharmony_ci that bureaucracy for you; all you have to do is supply the 2947db96d56Sopenharmony_ci function to call and an argument tuple for it (to embody the 2957db96d56Sopenharmony_ci "external action" being performed), and an optional message to 2967db96d56Sopenharmony_ci print. 2977db96d56Sopenharmony_ci """ 2987db96d56Sopenharmony_ci if msg is None: 2997db96d56Sopenharmony_ci msg = "%s%r" % (func.__name__, args) 3007db96d56Sopenharmony_ci if msg[-2:] == ',)': # correct for singleton tuple 3017db96d56Sopenharmony_ci msg = msg[0:-2] + ')' 3027db96d56Sopenharmony_ci 3037db96d56Sopenharmony_ci log.info(msg) 3047db96d56Sopenharmony_ci if not dry_run: 3057db96d56Sopenharmony_ci func(*args) 3067db96d56Sopenharmony_ci 3077db96d56Sopenharmony_ci 3087db96d56Sopenharmony_cidef strtobool (val): 3097db96d56Sopenharmony_ci """Convert a string representation of truth to true (1) or false (0). 3107db96d56Sopenharmony_ci 3117db96d56Sopenharmony_ci True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values 3127db96d56Sopenharmony_ci are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 3137db96d56Sopenharmony_ci 'val' is anything else. 3147db96d56Sopenharmony_ci """ 3157db96d56Sopenharmony_ci val = val.lower() 3167db96d56Sopenharmony_ci if val in ('y', 'yes', 't', 'true', 'on', '1'): 3177db96d56Sopenharmony_ci return 1 3187db96d56Sopenharmony_ci elif val in ('n', 'no', 'f', 'false', 'off', '0'): 3197db96d56Sopenharmony_ci return 0 3207db96d56Sopenharmony_ci else: 3217db96d56Sopenharmony_ci raise ValueError("invalid truth value %r" % (val,)) 3227db96d56Sopenharmony_ci 3237db96d56Sopenharmony_ci 3247db96d56Sopenharmony_cidef byte_compile (py_files, 3257db96d56Sopenharmony_ci optimize=0, force=0, 3267db96d56Sopenharmony_ci prefix=None, base_dir=None, 3277db96d56Sopenharmony_ci verbose=1, dry_run=0, 3287db96d56Sopenharmony_ci direct=None): 3297db96d56Sopenharmony_ci """Byte-compile a collection of Python source files to .pyc 3307db96d56Sopenharmony_ci files in a __pycache__ subdirectory. 'py_files' is a list 3317db96d56Sopenharmony_ci of files to compile; any files that don't end in ".py" are silently 3327db96d56Sopenharmony_ci skipped. 'optimize' must be one of the following: 3337db96d56Sopenharmony_ci 0 - don't optimize 3347db96d56Sopenharmony_ci 1 - normal optimization (like "python -O") 3357db96d56Sopenharmony_ci 2 - extra optimization (like "python -OO") 3367db96d56Sopenharmony_ci If 'force' is true, all files are recompiled regardless of 3377db96d56Sopenharmony_ci timestamps. 3387db96d56Sopenharmony_ci 3397db96d56Sopenharmony_ci The source filename encoded in each bytecode file defaults to the 3407db96d56Sopenharmony_ci filenames listed in 'py_files'; you can modify these with 'prefix' and 3417db96d56Sopenharmony_ci 'basedir'. 'prefix' is a string that will be stripped off of each 3427db96d56Sopenharmony_ci source filename, and 'base_dir' is a directory name that will be 3437db96d56Sopenharmony_ci prepended (after 'prefix' is stripped). You can supply either or both 3447db96d56Sopenharmony_ci (or neither) of 'prefix' and 'base_dir', as you wish. 3457db96d56Sopenharmony_ci 3467db96d56Sopenharmony_ci If 'dry_run' is true, doesn't actually do anything that would 3477db96d56Sopenharmony_ci affect the filesystem. 3487db96d56Sopenharmony_ci 3497db96d56Sopenharmony_ci Byte-compilation is either done directly in this interpreter process 3507db96d56Sopenharmony_ci with the standard py_compile module, or indirectly by writing a 3517db96d56Sopenharmony_ci temporary script and executing it. Normally, you should let 3527db96d56Sopenharmony_ci 'byte_compile()' figure out to use direct compilation or not (see 3537db96d56Sopenharmony_ci the source for details). The 'direct' flag is used by the script 3547db96d56Sopenharmony_ci generated in indirect mode; unless you know what you're doing, leave 3557db96d56Sopenharmony_ci it set to None. 3567db96d56Sopenharmony_ci """ 3577db96d56Sopenharmony_ci 3587db96d56Sopenharmony_ci # Late import to fix a bootstrap issue: _posixsubprocess is built by 3597db96d56Sopenharmony_ci # setup.py, but setup.py uses distutils. 3607db96d56Sopenharmony_ci import subprocess 3617db96d56Sopenharmony_ci 3627db96d56Sopenharmony_ci # nothing is done if sys.dont_write_bytecode is True 3637db96d56Sopenharmony_ci if sys.dont_write_bytecode: 3647db96d56Sopenharmony_ci raise DistutilsByteCompileError('byte-compiling is disabled.') 3657db96d56Sopenharmony_ci 3667db96d56Sopenharmony_ci # First, if the caller didn't force us into direct or indirect mode, 3677db96d56Sopenharmony_ci # figure out which mode we should be in. We take a conservative 3687db96d56Sopenharmony_ci # approach: choose direct mode *only* if the current interpreter is 3697db96d56Sopenharmony_ci # in debug mode and optimize is 0. If we're not in debug mode (-O 3707db96d56Sopenharmony_ci # or -OO), we don't know which level of optimization this 3717db96d56Sopenharmony_ci # interpreter is running with, so we can't do direct 3727db96d56Sopenharmony_ci # byte-compilation and be certain that it's the right thing. Thus, 3737db96d56Sopenharmony_ci # always compile indirectly if the current interpreter is in either 3747db96d56Sopenharmony_ci # optimize mode, or if either optimization level was requested by 3757db96d56Sopenharmony_ci # the caller. 3767db96d56Sopenharmony_ci if direct is None: 3777db96d56Sopenharmony_ci direct = (__debug__ and optimize == 0) 3787db96d56Sopenharmony_ci 3797db96d56Sopenharmony_ci # "Indirect" byte-compilation: write a temporary script and then 3807db96d56Sopenharmony_ci # run it with the appropriate flags. 3817db96d56Sopenharmony_ci if not direct: 3827db96d56Sopenharmony_ci try: 3837db96d56Sopenharmony_ci from tempfile import mkstemp 3847db96d56Sopenharmony_ci (script_fd, script_name) = mkstemp(".py") 3857db96d56Sopenharmony_ci except ImportError: 3867db96d56Sopenharmony_ci from tempfile import mktemp 3877db96d56Sopenharmony_ci (script_fd, script_name) = None, mktemp(".py") 3887db96d56Sopenharmony_ci log.info("writing byte-compilation script '%s'", script_name) 3897db96d56Sopenharmony_ci if not dry_run: 3907db96d56Sopenharmony_ci if script_fd is not None: 3917db96d56Sopenharmony_ci script = os.fdopen(script_fd, "w") 3927db96d56Sopenharmony_ci else: 3937db96d56Sopenharmony_ci script = open(script_name, "w") 3947db96d56Sopenharmony_ci 3957db96d56Sopenharmony_ci with script: 3967db96d56Sopenharmony_ci script.write("""\ 3977db96d56Sopenharmony_cifrom distutils.util import byte_compile 3987db96d56Sopenharmony_cifiles = [ 3997db96d56Sopenharmony_ci""") 4007db96d56Sopenharmony_ci 4017db96d56Sopenharmony_ci # XXX would be nice to write absolute filenames, just for 4027db96d56Sopenharmony_ci # safety's sake (script should be more robust in the face of 4037db96d56Sopenharmony_ci # chdir'ing before running it). But this requires abspath'ing 4047db96d56Sopenharmony_ci # 'prefix' as well, and that breaks the hack in build_lib's 4057db96d56Sopenharmony_ci # 'byte_compile()' method that carefully tacks on a trailing 4067db96d56Sopenharmony_ci # slash (os.sep really) to make sure the prefix here is "just 4077db96d56Sopenharmony_ci # right". This whole prefix business is rather delicate -- the 4087db96d56Sopenharmony_ci # problem is that it's really a directory, but I'm treating it 4097db96d56Sopenharmony_ci # as a dumb string, so trailing slashes and so forth matter. 4107db96d56Sopenharmony_ci 4117db96d56Sopenharmony_ci #py_files = map(os.path.abspath, py_files) 4127db96d56Sopenharmony_ci #if prefix: 4137db96d56Sopenharmony_ci # prefix = os.path.abspath(prefix) 4147db96d56Sopenharmony_ci 4157db96d56Sopenharmony_ci script.write(",\n".join(map(repr, py_files)) + "]\n") 4167db96d56Sopenharmony_ci script.write(""" 4177db96d56Sopenharmony_cibyte_compile(files, optimize=%r, force=%r, 4187db96d56Sopenharmony_ci prefix=%r, base_dir=%r, 4197db96d56Sopenharmony_ci verbose=%r, dry_run=0, 4207db96d56Sopenharmony_ci direct=1) 4217db96d56Sopenharmony_ci""" % (optimize, force, prefix, base_dir, verbose)) 4227db96d56Sopenharmony_ci 4237db96d56Sopenharmony_ci msg = distutils._DEPRECATION_MESSAGE 4247db96d56Sopenharmony_ci cmd = [sys.executable] 4257db96d56Sopenharmony_ci cmd.extend(subprocess._optim_args_from_interpreter_flags()) 4267db96d56Sopenharmony_ci cmd.append(f'-Wignore:{msg}:DeprecationWarning') 4277db96d56Sopenharmony_ci cmd.append(script_name) 4287db96d56Sopenharmony_ci spawn(cmd, dry_run=dry_run) 4297db96d56Sopenharmony_ci execute(os.remove, (script_name,), "removing %s" % script_name, 4307db96d56Sopenharmony_ci dry_run=dry_run) 4317db96d56Sopenharmony_ci 4327db96d56Sopenharmony_ci # "Direct" byte-compilation: use the py_compile module to compile 4337db96d56Sopenharmony_ci # right here, right now. Note that the script generated in indirect 4347db96d56Sopenharmony_ci # mode simply calls 'byte_compile()' in direct mode, a weird sort of 4357db96d56Sopenharmony_ci # cross-process recursion. Hey, it works! 4367db96d56Sopenharmony_ci else: 4377db96d56Sopenharmony_ci from py_compile import compile 4387db96d56Sopenharmony_ci 4397db96d56Sopenharmony_ci for file in py_files: 4407db96d56Sopenharmony_ci if file[-3:] != ".py": 4417db96d56Sopenharmony_ci # This lets us be lazy and not filter filenames in 4427db96d56Sopenharmony_ci # the "install_lib" command. 4437db96d56Sopenharmony_ci continue 4447db96d56Sopenharmony_ci 4457db96d56Sopenharmony_ci # Terminology from the py_compile module: 4467db96d56Sopenharmony_ci # cfile - byte-compiled file 4477db96d56Sopenharmony_ci # dfile - purported source filename (same as 'file' by default) 4487db96d56Sopenharmony_ci if optimize >= 0: 4497db96d56Sopenharmony_ci opt = '' if optimize == 0 else optimize 4507db96d56Sopenharmony_ci cfile = importlib.util.cache_from_source( 4517db96d56Sopenharmony_ci file, optimization=opt) 4527db96d56Sopenharmony_ci else: 4537db96d56Sopenharmony_ci cfile = importlib.util.cache_from_source(file) 4547db96d56Sopenharmony_ci dfile = file 4557db96d56Sopenharmony_ci if prefix: 4567db96d56Sopenharmony_ci if file[:len(prefix)] != prefix: 4577db96d56Sopenharmony_ci raise ValueError("invalid prefix: filename %r doesn't start with %r" 4587db96d56Sopenharmony_ci % (file, prefix)) 4597db96d56Sopenharmony_ci dfile = dfile[len(prefix):] 4607db96d56Sopenharmony_ci if base_dir: 4617db96d56Sopenharmony_ci dfile = os.path.join(base_dir, dfile) 4627db96d56Sopenharmony_ci 4637db96d56Sopenharmony_ci cfile_base = os.path.basename(cfile) 4647db96d56Sopenharmony_ci if direct: 4657db96d56Sopenharmony_ci if force or newer(file, cfile): 4667db96d56Sopenharmony_ci log.info("byte-compiling %s to %s", file, cfile_base) 4677db96d56Sopenharmony_ci if not dry_run: 4687db96d56Sopenharmony_ci compile(file, cfile, dfile) 4697db96d56Sopenharmony_ci else: 4707db96d56Sopenharmony_ci log.debug("skipping byte-compilation of %s to %s", 4717db96d56Sopenharmony_ci file, cfile_base) 4727db96d56Sopenharmony_ci 4737db96d56Sopenharmony_ci# byte_compile () 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_cidef rfc822_escape (header): 4767db96d56Sopenharmony_ci """Return a version of the string escaped for inclusion in an 4777db96d56Sopenharmony_ci RFC-822 header, by ensuring there are 8 spaces space after each newline. 4787db96d56Sopenharmony_ci """ 4797db96d56Sopenharmony_ci lines = header.split('\n') 4807db96d56Sopenharmony_ci sep = '\n' + 8 * ' ' 4817db96d56Sopenharmony_ci return sep.join(lines) 4827db96d56Sopenharmony_ci 4837db96d56Sopenharmony_ci# 2to3 support 4847db96d56Sopenharmony_ci 4857db96d56Sopenharmony_cidef run_2to3(files, fixer_names=None, options=None, explicit=None): 4867db96d56Sopenharmony_ci """Invoke 2to3 on a list of Python files. 4877db96d56Sopenharmony_ci The files should all come from the build area, as the 4887db96d56Sopenharmony_ci modification is done in-place. To reduce the build time, 4897db96d56Sopenharmony_ci only files modified since the last invocation of this 4907db96d56Sopenharmony_ci function should be passed in the files argument.""" 4917db96d56Sopenharmony_ci 4927db96d56Sopenharmony_ci if not files: 4937db96d56Sopenharmony_ci return 4947db96d56Sopenharmony_ci 4957db96d56Sopenharmony_ci # Make this class local, to delay import of 2to3 4967db96d56Sopenharmony_ci from lib2to3.refactor import RefactoringTool, get_fixers_from_package 4977db96d56Sopenharmony_ci class DistutilsRefactoringTool(RefactoringTool): 4987db96d56Sopenharmony_ci def log_error(self, msg, *args, **kw): 4997db96d56Sopenharmony_ci log.error(msg, *args) 5007db96d56Sopenharmony_ci 5017db96d56Sopenharmony_ci def log_message(self, msg, *args): 5027db96d56Sopenharmony_ci log.info(msg, *args) 5037db96d56Sopenharmony_ci 5047db96d56Sopenharmony_ci def log_debug(self, msg, *args): 5057db96d56Sopenharmony_ci log.debug(msg, *args) 5067db96d56Sopenharmony_ci 5077db96d56Sopenharmony_ci if fixer_names is None: 5087db96d56Sopenharmony_ci fixer_names = get_fixers_from_package('lib2to3.fixes') 5097db96d56Sopenharmony_ci r = DistutilsRefactoringTool(fixer_names, options=options) 5107db96d56Sopenharmony_ci r.refactor(files, write=True) 5117db96d56Sopenharmony_ci 5127db96d56Sopenharmony_cidef copydir_run_2to3(src, dest, template=None, fixer_names=None, 5137db96d56Sopenharmony_ci options=None, explicit=None): 5147db96d56Sopenharmony_ci """Recursively copy a directory, only copying new and changed files, 5157db96d56Sopenharmony_ci running run_2to3 over all newly copied Python modules afterward. 5167db96d56Sopenharmony_ci 5177db96d56Sopenharmony_ci If you give a template string, it's parsed like a MANIFEST.in. 5187db96d56Sopenharmony_ci """ 5197db96d56Sopenharmony_ci from distutils.dir_util import mkpath 5207db96d56Sopenharmony_ci from distutils.file_util import copy_file 5217db96d56Sopenharmony_ci from distutils.filelist import FileList 5227db96d56Sopenharmony_ci filelist = FileList() 5237db96d56Sopenharmony_ci curdir = os.getcwd() 5247db96d56Sopenharmony_ci os.chdir(src) 5257db96d56Sopenharmony_ci try: 5267db96d56Sopenharmony_ci filelist.findall() 5277db96d56Sopenharmony_ci finally: 5287db96d56Sopenharmony_ci os.chdir(curdir) 5297db96d56Sopenharmony_ci filelist.files[:] = filelist.allfiles 5307db96d56Sopenharmony_ci if template: 5317db96d56Sopenharmony_ci for line in template.splitlines(): 5327db96d56Sopenharmony_ci line = line.strip() 5337db96d56Sopenharmony_ci if not line: continue 5347db96d56Sopenharmony_ci filelist.process_template_line(line) 5357db96d56Sopenharmony_ci copied = [] 5367db96d56Sopenharmony_ci for filename in filelist.files: 5377db96d56Sopenharmony_ci outname = os.path.join(dest, filename) 5387db96d56Sopenharmony_ci mkpath(os.path.dirname(outname)) 5397db96d56Sopenharmony_ci res = copy_file(os.path.join(src, filename), outname, update=1) 5407db96d56Sopenharmony_ci if res[1]: copied.append(outname) 5417db96d56Sopenharmony_ci run_2to3([fn for fn in copied if fn.lower().endswith('.py')], 5427db96d56Sopenharmony_ci fixer_names=fixer_names, options=options, explicit=explicit) 5437db96d56Sopenharmony_ci return copied 5447db96d56Sopenharmony_ci 5457db96d56Sopenharmony_ciclass Mixin2to3: 5467db96d56Sopenharmony_ci '''Mixin class for commands that run 2to3. 5477db96d56Sopenharmony_ci To configure 2to3, setup scripts may either change 5487db96d56Sopenharmony_ci the class variables, or inherit from individual commands 5497db96d56Sopenharmony_ci to override how 2to3 is invoked.''' 5507db96d56Sopenharmony_ci 5517db96d56Sopenharmony_ci # provide list of fixers to run; 5527db96d56Sopenharmony_ci # defaults to all from lib2to3.fixers 5537db96d56Sopenharmony_ci fixer_names = None 5547db96d56Sopenharmony_ci 5557db96d56Sopenharmony_ci # options dictionary 5567db96d56Sopenharmony_ci options = None 5577db96d56Sopenharmony_ci 5587db96d56Sopenharmony_ci # list of fixers to invoke even though they are marked as explicit 5597db96d56Sopenharmony_ci explicit = None 5607db96d56Sopenharmony_ci 5617db96d56Sopenharmony_ci def run_2to3(self, files): 5627db96d56Sopenharmony_ci return run_2to3(files, self.fixer_names, self.options, self.explicit) 563