(build_directory: str, verbose: bool, error_prefix: str)
| 2058 | |
| 2059 | |
| 2060 | def _run_ninja_build(build_directory: str, verbose: bool, error_prefix: str) -> None: |
| 2061 | command = ['ninja', '-v'] |
| 2062 | num_workers = _get_num_workers(verbose) |
| 2063 | if num_workers is not None: |
| 2064 | command.extend(['-j', str(num_workers)]) |
| 2065 | env = os.environ.copy() |
| 2066 | # Try to activate the vc env for the users |
| 2067 | if IS_WINDOWS and 'VSCMD_ARG_TGT_ARCH' not in env: |
| 2068 | from setuptools import distutils |
| 2069 | |
| 2070 | plat_name = distutils.util.get_platform() |
| 2071 | plat_spec = PLAT_TO_VCVARS[plat_name] |
| 2072 | |
| 2073 | vc_env = distutils._msvccompiler._get_vc_env(plat_spec) |
| 2074 | vc_env = {k.upper(): v for k, v in vc_env.items()} |
| 2075 | for k, v in env.items(): |
| 2076 | uk = k.upper() |
| 2077 | if uk not in vc_env: |
| 2078 | vc_env[uk] = v |
| 2079 | env = vc_env |
| 2080 | try: |
| 2081 | sys.stdout.flush() |
| 2082 | sys.stderr.flush() |
| 2083 | # Warning: don't pass stdout=None to subprocess.run to get output. |
| 2084 | # subprocess.run assumes that sys.__stdout__ has not been modified and |
| 2085 | # attempts to write to it by default. However, when we call _run_ninja_build |
| 2086 | # from ahead-of-time cpp extensions, the following happens: |
| 2087 | # 1) If the stdout encoding is not utf-8, setuptools detachs __stdout__. |
| 2088 | # https://github.com/pypa/setuptools/blob/7e97def47723303fafabe48b22168bbc11bb4821/setuptools/dist.py#L1110 |
| 2089 | # (it probably shouldn't do this) |
| 2090 | # 2) subprocess.run (on POSIX, with no stdout override) relies on |
| 2091 | # __stdout__ not being detached: |
| 2092 | # https://github.com/python/cpython/blob/c352e6c7446c894b13643f538db312092b351789/Lib/subprocess.py#L1214 |
| 2093 | # To work around this, we pass in the fileno directly and hope that |
| 2094 | # it is valid. |
| 2095 | stdout_fileno = 1 |
| 2096 | subprocess.run( |
| 2097 | command, |
| 2098 | stdout=stdout_fileno if verbose else subprocess.PIPE, |
| 2099 | stderr=subprocess.STDOUT, |
| 2100 | cwd=build_directory, |
| 2101 | check=True, |
| 2102 | env=env) |
| 2103 | except subprocess.CalledProcessError as e: |
| 2104 | # Python 2 and 3 compatible way of getting the error object. |
| 2105 | _, error, _ = sys.exc_info() |
| 2106 | # error.output contains the stdout and stderr of the build attempt. |
| 2107 | message = error_prefix |
| 2108 | # `error` is a CalledProcessError (which has an `output`) attribute, but |
| 2109 | # mypy thinks it's Optional[BaseException] and doesn't narrow |
| 2110 | if hasattr(error, 'output') and error.output: # type: ignore[union-attr] |
| 2111 | message += f": {error.output.decode(*SUBPROCESS_DECODE_ARGS)}" # type: ignore[union-attr] |
| 2112 | raise RuntimeError(message) from e |
| 2113 | |
| 2114 | |
| 2115 | def _get_exec_path(module_name, path): |
no test coverage detected
searching dependent graphs…