1# -*- coding: utf-8 -*- 2 3from __future__ import absolute_import 4 5import logging 6import os 7import shutil 8 9from pip._internal.cache import WheelCache 10from pip._internal.cli import cmdoptions 11from pip._internal.cli.req_command import RequirementCommand, with_cleanup 12from pip._internal.cli.status_codes import SUCCESS 13from pip._internal.exceptions import CommandError 14from pip._internal.req.req_tracker import get_requirement_tracker 15from pip._internal.utils.misc import ensure_dir, normalize_path 16from pip._internal.utils.temp_dir import TempDirectory 17from pip._internal.utils.typing import MYPY_CHECK_RUNNING 18from pip._internal.wheel_builder import build, should_build_for_wheel_command 19 20if MYPY_CHECK_RUNNING: 21 from optparse import Values 22 from typing import List 23 24 from pip._internal.req.req_install import InstallRequirement 25 26logger = logging.getLogger(__name__) 27 28 29class WheelCommand(RequirementCommand): 30 """ 31 Build Wheel archives for your requirements and dependencies. 32 33 Wheel is a built-package format, and offers the advantage of not 34 recompiling your software during every install. For more details, see the 35 wheel docs: https://wheel.readthedocs.io/en/latest/ 36 37 Requirements: setuptools>=0.8, and wheel. 38 39 'pip wheel' uses the bdist_wheel setuptools extension from the wheel 40 package to build individual wheels. 41 42 """ 43 44 usage = """ 45 %prog [options] <requirement specifier> ... 46 %prog [options] -r <requirements file> ... 47 %prog [options] [-e] <vcs project url> ... 48 %prog [options] [-e] <local project path> ... 49 %prog [options] <archive url/path> ...""" 50 51 def add_options(self): 52 # type: () -> None 53 54 self.cmd_opts.add_option( 55 '-w', '--wheel-dir', 56 dest='wheel_dir', 57 metavar='dir', 58 default=os.curdir, 59 help=("Build wheels into <dir>, where the default is the " 60 "current working directory."), 61 ) 62 self.cmd_opts.add_option(cmdoptions.no_binary()) 63 self.cmd_opts.add_option(cmdoptions.only_binary()) 64 self.cmd_opts.add_option(cmdoptions.prefer_binary()) 65 self.cmd_opts.add_option( 66 '--build-option', 67 dest='build_options', 68 metavar='options', 69 action='append', 70 help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", 71 ) 72 self.cmd_opts.add_option(cmdoptions.no_build_isolation()) 73 self.cmd_opts.add_option(cmdoptions.use_pep517()) 74 self.cmd_opts.add_option(cmdoptions.no_use_pep517()) 75 self.cmd_opts.add_option(cmdoptions.constraints()) 76 self.cmd_opts.add_option(cmdoptions.editable()) 77 self.cmd_opts.add_option(cmdoptions.requirements()) 78 self.cmd_opts.add_option(cmdoptions.src()) 79 self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) 80 self.cmd_opts.add_option(cmdoptions.no_deps()) 81 self.cmd_opts.add_option(cmdoptions.build_dir()) 82 self.cmd_opts.add_option(cmdoptions.progress_bar()) 83 84 self.cmd_opts.add_option( 85 '--no-verify', 86 dest='no_verify', 87 action='store_true', 88 default=False, 89 help="Don't verify if built wheel is valid.", 90 ) 91 92 self.cmd_opts.add_option( 93 '--global-option', 94 dest='global_options', 95 action='append', 96 metavar='options', 97 help="Extra global options to be supplied to the setup.py " 98 "call before the 'bdist_wheel' command.") 99 100 self.cmd_opts.add_option( 101 '--pre', 102 action='store_true', 103 default=False, 104 help=("Include pre-release and development versions. By default, " 105 "pip only finds stable versions."), 106 ) 107 108 self.cmd_opts.add_option(cmdoptions.require_hashes()) 109 110 index_opts = cmdoptions.make_option_group( 111 cmdoptions.index_group, 112 self.parser, 113 ) 114 115 self.parser.insert_option_group(0, index_opts) 116 self.parser.insert_option_group(0, self.cmd_opts) 117 118 @with_cleanup 119 def run(self, options, args): 120 # type: (Values, List[str]) -> int 121 cmdoptions.check_install_build_global(options) 122 123 session = self.get_default_session(options) 124 125 finder = self._build_package_finder(options, session) 126 wheel_cache = WheelCache(options.cache_dir, options.format_control) 127 128 options.wheel_dir = normalize_path(options.wheel_dir) 129 ensure_dir(options.wheel_dir) 130 131 req_tracker = self.enter_context(get_requirement_tracker()) 132 133 directory = TempDirectory( 134 delete=not options.no_clean, 135 kind="wheel", 136 globally_managed=True, 137 ) 138 139 reqs = self.get_requirements(args, options, finder, session) 140 141 preparer = self.make_requirement_preparer( 142 temp_build_dir=directory, 143 options=options, 144 req_tracker=req_tracker, 145 session=session, 146 finder=finder, 147 download_dir=options.wheel_dir, 148 use_user_site=False, 149 ) 150 151 resolver = self.make_resolver( 152 preparer=preparer, 153 finder=finder, 154 options=options, 155 wheel_cache=wheel_cache, 156 ignore_requires_python=options.ignore_requires_python, 157 use_pep517=options.use_pep517, 158 ) 159 160 self.trace_basic_info(finder) 161 162 requirement_set = resolver.resolve( 163 reqs, check_supported_wheels=True 164 ) 165 166 reqs_to_build = [] # type: List[InstallRequirement] 167 for req in requirement_set.requirements.values(): 168 if req.is_wheel: 169 preparer.save_linked_requirement(req) 170 elif should_build_for_wheel_command(req): 171 reqs_to_build.append(req) 172 173 # build wheels 174 build_successes, build_failures = build( 175 reqs_to_build, 176 wheel_cache=wheel_cache, 177 verify=(not options.no_verify), 178 build_options=options.build_options or [], 179 global_options=options.global_options or [], 180 ) 181 for req in build_successes: 182 assert req.link and req.link.is_wheel 183 assert req.local_file_path 184 # copy from cache to target directory 185 try: 186 shutil.copy(req.local_file_path, options.wheel_dir) 187 except OSError as e: 188 logger.warning( 189 "Building wheel for %s failed: %s", 190 req.name, e, 191 ) 192 build_failures.append(req) 193 if len(build_failures) != 0: 194 raise CommandError( 195 "Failed to build one or more wheels" 196 ) 197 198 return SUCCESS 199