Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/pip/_internal/wheel_builder.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
| author | guerler |
|---|---|
| date | Mon, 27 Jul 2020 03:47:31 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:9e54283cc701 |
|---|---|
| 1 """Orchestrator for building wheels from InstallRequirements. | |
| 2 """ | |
| 3 | |
| 4 # The following comment should be removed at some point in the future. | |
| 5 # mypy: strict-optional=False | |
| 6 | |
| 7 import logging | |
| 8 import os.path | |
| 9 import re | |
| 10 import shutil | |
| 11 | |
| 12 from pip._internal.models.link import Link | |
| 13 from pip._internal.operations.build.wheel import build_wheel_pep517 | |
| 14 from pip._internal.operations.build.wheel_legacy import build_wheel_legacy | |
| 15 from pip._internal.utils.logging import indent_log | |
| 16 from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed | |
| 17 from pip._internal.utils.setuptools_build import make_setuptools_clean_args | |
| 18 from pip._internal.utils.subprocess import call_subprocess | |
| 19 from pip._internal.utils.temp_dir import TempDirectory | |
| 20 from pip._internal.utils.typing import MYPY_CHECK_RUNNING | |
| 21 from pip._internal.utils.urls import path_to_url | |
| 22 from pip._internal.vcs import vcs | |
| 23 | |
| 24 if MYPY_CHECK_RUNNING: | |
| 25 from typing import ( | |
| 26 Any, Callable, Iterable, List, Optional, Pattern, Tuple, | |
| 27 ) | |
| 28 | |
| 29 from pip._internal.cache import WheelCache | |
| 30 from pip._internal.req.req_install import InstallRequirement | |
| 31 | |
| 32 BinaryAllowedPredicate = Callable[[InstallRequirement], bool] | |
| 33 BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] | |
| 34 | |
| 35 logger = logging.getLogger(__name__) | |
| 36 | |
| 37 | |
| 38 def _contains_egg_info( | |
| 39 s, _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): | |
| 40 # type: (str, Pattern[str]) -> bool | |
| 41 """Determine whether the string looks like an egg_info. | |
| 42 | |
| 43 :param s: The string to parse. E.g. foo-2.1 | |
| 44 """ | |
| 45 return bool(_egg_info_re.search(s)) | |
| 46 | |
| 47 | |
| 48 def _should_build( | |
| 49 req, # type: InstallRequirement | |
| 50 need_wheel, # type: bool | |
| 51 check_binary_allowed, # type: BinaryAllowedPredicate | |
| 52 ): | |
| 53 # type: (...) -> bool | |
| 54 """Return whether an InstallRequirement should be built into a wheel.""" | |
| 55 if req.constraint: | |
| 56 # never build requirements that are merely constraints | |
| 57 return False | |
| 58 if req.is_wheel: | |
| 59 if need_wheel: | |
| 60 logger.info( | |
| 61 'Skipping %s, due to already being wheel.', req.name, | |
| 62 ) | |
| 63 return False | |
| 64 | |
| 65 if need_wheel: | |
| 66 # i.e. pip wheel, not pip install | |
| 67 return True | |
| 68 | |
| 69 # From this point, this concerns the pip install command only | |
| 70 # (need_wheel=False). | |
| 71 | |
| 72 if not req.use_pep517 and not is_wheel_installed(): | |
| 73 # we don't build legacy requirements if wheel is not installed | |
| 74 return False | |
| 75 | |
| 76 if req.editable or not req.source_dir: | |
| 77 return False | |
| 78 | |
| 79 if not check_binary_allowed(req): | |
| 80 logger.info( | |
| 81 "Skipping wheel build for %s, due to binaries " | |
| 82 "being disabled for it.", req.name, | |
| 83 ) | |
| 84 return False | |
| 85 | |
| 86 return True | |
| 87 | |
| 88 | |
| 89 def should_build_for_wheel_command( | |
| 90 req, # type: InstallRequirement | |
| 91 ): | |
| 92 # type: (...) -> bool | |
| 93 return _should_build( | |
| 94 req, need_wheel=True, check_binary_allowed=_always_true | |
| 95 ) | |
| 96 | |
| 97 | |
| 98 def should_build_for_install_command( | |
| 99 req, # type: InstallRequirement | |
| 100 check_binary_allowed, # type: BinaryAllowedPredicate | |
| 101 ): | |
| 102 # type: (...) -> bool | |
| 103 return _should_build( | |
| 104 req, need_wheel=False, check_binary_allowed=check_binary_allowed | |
| 105 ) | |
| 106 | |
| 107 | |
| 108 def _should_cache( | |
| 109 req, # type: InstallRequirement | |
| 110 ): | |
| 111 # type: (...) -> Optional[bool] | |
| 112 """ | |
| 113 Return whether a built InstallRequirement can be stored in the persistent | |
| 114 wheel cache, assuming the wheel cache is available, and _should_build() | |
| 115 has determined a wheel needs to be built. | |
| 116 """ | |
| 117 if not should_build_for_install_command( | |
| 118 req, check_binary_allowed=_always_true | |
| 119 ): | |
| 120 # never cache if pip install would not have built | |
| 121 # (editable mode, etc) | |
| 122 return False | |
| 123 | |
| 124 if req.link and req.link.is_vcs: | |
| 125 # VCS checkout. Do not cache | |
| 126 # unless it points to an immutable commit hash. | |
| 127 assert not req.editable | |
| 128 assert req.source_dir | |
| 129 vcs_backend = vcs.get_backend_for_scheme(req.link.scheme) | |
| 130 assert vcs_backend | |
| 131 if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir): | |
| 132 return True | |
| 133 return False | |
| 134 | |
| 135 base, ext = req.link.splitext() | |
| 136 if _contains_egg_info(base): | |
| 137 return True | |
| 138 | |
| 139 # Otherwise, do not cache. | |
| 140 return False | |
| 141 | |
| 142 | |
| 143 def _get_cache_dir( | |
| 144 req, # type: InstallRequirement | |
| 145 wheel_cache, # type: WheelCache | |
| 146 ): | |
| 147 # type: (...) -> str | |
| 148 """Return the persistent or temporary cache directory where the built | |
| 149 wheel need to be stored. | |
| 150 """ | |
| 151 cache_available = bool(wheel_cache.cache_dir) | |
| 152 if cache_available and _should_cache(req): | |
| 153 cache_dir = wheel_cache.get_path_for_link(req.link) | |
| 154 else: | |
| 155 cache_dir = wheel_cache.get_ephem_path_for_link(req.link) | |
| 156 return cache_dir | |
| 157 | |
| 158 | |
| 159 def _always_true(_): | |
| 160 # type: (Any) -> bool | |
| 161 return True | |
| 162 | |
| 163 | |
| 164 def _build_one( | |
| 165 req, # type: InstallRequirement | |
| 166 output_dir, # type: str | |
| 167 build_options, # type: List[str] | |
| 168 global_options, # type: List[str] | |
| 169 ): | |
| 170 # type: (...) -> Optional[str] | |
| 171 """Build one wheel. | |
| 172 | |
| 173 :return: The filename of the built wheel, or None if the build failed. | |
| 174 """ | |
| 175 try: | |
| 176 ensure_dir(output_dir) | |
| 177 except OSError as e: | |
| 178 logger.warning( | |
| 179 "Building wheel for %s failed: %s", | |
| 180 req.name, e, | |
| 181 ) | |
| 182 return None | |
| 183 | |
| 184 # Install build deps into temporary directory (PEP 518) | |
| 185 with req.build_env: | |
| 186 return _build_one_inside_env( | |
| 187 req, output_dir, build_options, global_options | |
| 188 ) | |
| 189 | |
| 190 | |
| 191 def _build_one_inside_env( | |
| 192 req, # type: InstallRequirement | |
| 193 output_dir, # type: str | |
| 194 build_options, # type: List[str] | |
| 195 global_options, # type: List[str] | |
| 196 ): | |
| 197 # type: (...) -> Optional[str] | |
| 198 with TempDirectory(kind="wheel") as temp_dir: | |
| 199 if req.use_pep517: | |
| 200 wheel_path = build_wheel_pep517( | |
| 201 name=req.name, | |
| 202 backend=req.pep517_backend, | |
| 203 metadata_directory=req.metadata_directory, | |
| 204 build_options=build_options, | |
| 205 tempd=temp_dir.path, | |
| 206 ) | |
| 207 else: | |
| 208 wheel_path = build_wheel_legacy( | |
| 209 name=req.name, | |
| 210 setup_py_path=req.setup_py_path, | |
| 211 source_dir=req.unpacked_source_directory, | |
| 212 global_options=global_options, | |
| 213 build_options=build_options, | |
| 214 tempd=temp_dir.path, | |
| 215 ) | |
| 216 | |
| 217 if wheel_path is not None: | |
| 218 wheel_name = os.path.basename(wheel_path) | |
| 219 dest_path = os.path.join(output_dir, wheel_name) | |
| 220 try: | |
| 221 wheel_hash, length = hash_file(wheel_path) | |
| 222 shutil.move(wheel_path, dest_path) | |
| 223 logger.info('Created wheel for %s: ' | |
| 224 'filename=%s size=%d sha256=%s', | |
| 225 req.name, wheel_name, length, | |
| 226 wheel_hash.hexdigest()) | |
| 227 logger.info('Stored in directory: %s', output_dir) | |
| 228 return dest_path | |
| 229 except Exception as e: | |
| 230 logger.warning( | |
| 231 "Building wheel for %s failed: %s", | |
| 232 req.name, e, | |
| 233 ) | |
| 234 # Ignore return, we can't do anything else useful. | |
| 235 if not req.use_pep517: | |
| 236 _clean_one_legacy(req, global_options) | |
| 237 return None | |
| 238 | |
| 239 | |
| 240 def _clean_one_legacy(req, global_options): | |
| 241 # type: (InstallRequirement, List[str]) -> bool | |
| 242 clean_args = make_setuptools_clean_args( | |
| 243 req.setup_py_path, | |
| 244 global_options=global_options, | |
| 245 ) | |
| 246 | |
| 247 logger.info('Running setup.py clean for %s', req.name) | |
| 248 try: | |
| 249 call_subprocess(clean_args, cwd=req.source_dir) | |
| 250 return True | |
| 251 except Exception: | |
| 252 logger.error('Failed cleaning build dir for %s', req.name) | |
| 253 return False | |
| 254 | |
| 255 | |
| 256 def build( | |
| 257 requirements, # type: Iterable[InstallRequirement] | |
| 258 wheel_cache, # type: WheelCache | |
| 259 build_options, # type: List[str] | |
| 260 global_options, # type: List[str] | |
| 261 ): | |
| 262 # type: (...) -> BuildResult | |
| 263 """Build wheels. | |
| 264 | |
| 265 :return: The list of InstallRequirement that succeeded to build and | |
| 266 the list of InstallRequirement that failed to build. | |
| 267 """ | |
| 268 if not requirements: | |
| 269 return [], [] | |
| 270 | |
| 271 # Build the wheels. | |
| 272 logger.info( | |
| 273 'Building wheels for collected packages: %s', | |
| 274 ', '.join(req.name for req in requirements), | |
| 275 ) | |
| 276 | |
| 277 with indent_log(): | |
| 278 build_successes, build_failures = [], [] | |
| 279 for req in requirements: | |
| 280 cache_dir = _get_cache_dir(req, wheel_cache) | |
| 281 wheel_file = _build_one( | |
| 282 req, cache_dir, build_options, global_options | |
| 283 ) | |
| 284 if wheel_file: | |
| 285 # Update the link for this. | |
| 286 req.link = Link(path_to_url(wheel_file)) | |
| 287 req.local_file_path = req.link.file_path | |
| 288 assert req.link.is_wheel | |
| 289 build_successes.append(req) | |
| 290 else: | |
| 291 build_failures.append(req) | |
| 292 | |
| 293 # notify success/failure | |
| 294 if build_successes: | |
| 295 logger.info( | |
| 296 'Successfully built %s', | |
| 297 ' '.join([req.name for req in build_successes]), | |
| 298 ) | |
| 299 if build_failures: | |
| 300 logger.info( | |
| 301 'Failed to build %s', | |
| 302 ' '.join([req.name for req in build_failures]), | |
| 303 ) | |
| 304 # Return a list of requirements that failed to build | |
| 305 return build_successes, build_failures |
