diff --git a/pip/cmdoptions.py b/pip/cmdoptions.py index 8ed3d91..01b2104 100644 --- a/pip/cmdoptions.py +++ b/pip/cmdoptions.py @@ -9,7 +9,7 @@ To be consistent, all options will follow this design. """ import copy from optparse import OptionGroup, SUPPRESS_HELP, Option -from pip.locations import build_prefix, default_log_file +from pip.locations import default_log_file def make_option_group(group, parser): @@ -297,10 +297,8 @@ build_dir = OptionMaker( '-b', '--build', '--build-dir', '--build-directory', dest='build_dir', metavar='dir', - default=build_prefix, - help='Directory to unpack packages into and build in. ' - 'The default in a virtualenv is "/build". ' - 'The default for global installs is "/pip_build_".') + help='Directory to unpack packages into and build in.', +) install_options = OptionMaker( '--install-option', diff --git a/pip/commands/install.py b/pip/commands/install.py index cbf22a0..cb7d0db 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -10,6 +10,7 @@ from pip.basecommand import Command from pip.index import PackageFinder from pip.exceptions import InstallationError, CommandError, PreviousBuildDirError from pip import cmdoptions +from pip.util import BuildDirectory class InstallCommand(Command): @@ -188,7 +189,7 @@ class InstallCommand(Command): if ( options.no_install or options.no_download or - (options.build_dir != build_prefix) or + options.build_dir or options.no_clean ): logger.deprecated('1.7', 'DEPRECATION: --no-install, --no-download, --build, ' @@ -197,7 +198,16 @@ class InstallCommand(Command): if options.download_dir: options.no_install = True options.ignore_installed = True - options.build_dir = os.path.abspath(options.build_dir) + + # If we have --no-install or --no-download and no --build we use the + # legacy static build dir + if (options.build_dir is None + and (options.no_install or options.no_download)): + options.build_dir = build_prefix + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + options.src_dir = os.path.abspath(options.src_dir) install_options = options.install_options or [] if options.use_user_site: @@ -246,73 +256,75 @@ class InstallCommand(Command): finder = self._build_package_finder(options, index_urls, session) - requirement_set = RequirementSet( - build_dir=options.build_dir, - src_dir=options.src_dir, - download_dir=options.download_dir, - download_cache=options.download_cache, - upgrade=options.upgrade, - as_egg=options.as_egg, - ignore_installed=options.ignore_installed, - ignore_dependencies=options.ignore_dependencies, - force_reinstall=options.force_reinstall, - use_user_site=options.use_user_site, - target_dir=temp_target_dir, - session=session, - pycompile=options.compile, - ) - for name in args: - requirement_set.add_requirement( - InstallRequirement.from_line(name, None)) - for name in options.editables: - requirement_set.add_requirement( - InstallRequirement.from_editable(name, default_vcs=options.default_vcs)) - for filename in options.requirements: - for req in parse_requirements(filename, finder=finder, options=options, session=session): - requirement_set.add_requirement(req) - if not requirement_set.has_requirements: - opts = {'name': self.name} - if options.find_links: - msg = ('You must give at least one requirement to %(name)s ' - '(maybe you meant "pip %(name)s %(links)s"?)' % - dict(opts, links=' '.join(options.find_links))) - else: - msg = ('You must give at least one requirement ' - 'to %(name)s (see "pip help %(name)s")' % opts) - logger.warn(msg) - return - - try: - if not options.no_download: - requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle) - else: - requirement_set.locate_files() - - if not options.no_install and not self.bundle: - requirement_set.install( - install_options, - global_options, - root=options.root_path, - strip_file_prefix=options.strip_file_prefix) - installed = ' '.join([req.name for req in - requirement_set.successfully_installed]) - if installed: - logger.notify('Successfully installed %s' % installed) - elif not self.bundle: - downloaded = ' '.join([req.name for req in - requirement_set.successfully_downloaded]) - if downloaded: - logger.notify('Successfully downloaded %s' % downloaded) - elif self.bundle: - requirement_set.create_bundle(self.bundle_filename) - logger.notify('Created bundle in %s' % self.bundle_filename) - except PreviousBuildDirError: - options.no_clean = True - raise - finally: - # Clean up - if (not options.no_clean) and ((not options.no_install) or options.download_dir): - requirement_set.cleanup_files(bundle=self.bundle) + build_delete = (not (options.no_clean or options.build_dir)) + with BuildDirectory(options.build_dir, delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + download_cache=options.download_cache, + upgrade=options.upgrade, + as_egg=options.as_egg, + ignore_installed=options.ignore_installed, + ignore_dependencies=options.ignore_dependencies, + force_reinstall=options.force_reinstall, + use_user_site=options.use_user_site, + target_dir=temp_target_dir, + session=session, + pycompile=options.compile, + ) + for name in args: + requirement_set.add_requirement( + InstallRequirement.from_line(name, None)) + for name in options.editables: + requirement_set.add_requirement( + InstallRequirement.from_editable(name, default_vcs=options.default_vcs)) + for filename in options.requirements: + for req in parse_requirements(filename, finder=finder, options=options, session=session): + requirement_set.add_requirement(req) + if not requirement_set.has_requirements: + opts = {'name': self.name} + if options.find_links: + msg = ('You must give at least one requirement to %(name)s ' + '(maybe you meant "pip %(name)s %(links)s"?)' % + dict(opts, links=' '.join(options.find_links))) + else: + msg = ('You must give at least one requirement ' + 'to %(name)s (see "pip help %(name)s")' % opts) + logger.warn(msg) + return + + try: + if not options.no_download: + requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle) + else: + requirement_set.locate_files() + + if not options.no_install and not self.bundle: + requirement_set.install( + install_options, + global_options, + root=options.root_path, + strip_file_prefix=options.strip_file_prefix) + installed = ' '.join([req.name for req in + requirement_set.successfully_installed]) + if installed: + logger.notify('Successfully installed %s' % installed) + elif not self.bundle: + downloaded = ' '.join([req.name for req in + requirement_set.successfully_downloaded]) + if downloaded: + logger.notify('Successfully downloaded %s' % downloaded) + elif self.bundle: + requirement_set.create_bundle(self.bundle_filename) + logger.notify('Created bundle in %s' % self.bundle_filename) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + # Clean up + if (not options.no_clean) and ((not options.no_install) or options.download_dir): + requirement_set.cleanup_files(bundle=self.bundle) if options.target_dir: if not os.path.exists(options.target_dir): diff --git a/pip/commands/wheel.py b/pip/commands/wheel.py index 6527063..a96631a 100644 --- a/pip/commands/wheel.py +++ b/pip/commands/wheel.py @@ -8,7 +8,7 @@ from pip.index import PackageFinder from pip.log import logger from pip.exceptions import CommandError, PreviousBuildDirError from pip.req import InstallRequirement, RequirementSet, parse_requirements -from pip.util import normalize_path +from pip.util import BuildDirectory, normalize_path from pip.wheel import WheelBuilder from pip import cmdoptions @@ -123,6 +123,9 @@ class WheelCommand(Command): "--extra-index-url is suggested.") index_urls += options.mirrors + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + session = self._build_session(options) finder = PackageFinder(find_links=options.find_links, @@ -137,59 +140,60 @@ class WheelCommand(Command): session=session, ) - options.build_dir = os.path.abspath(options.build_dir) - requirement_set = RequirementSet( - build_dir=options.build_dir, - src_dir=None, - download_dir=None, - download_cache=options.download_cache, - ignore_dependencies=options.ignore_dependencies, - ignore_installed=True, - session=session, - wheel_download_dir=options.wheel_dir - ) - - # make the wheelhouse - if not os.path.exists(options.wheel_dir): - os.makedirs(options.wheel_dir) - - #parse args and/or requirements files - for name in args: - requirement_set.add_requirement( - InstallRequirement.from_line(name, None)) - - for filename in options.requirements: - for req in parse_requirements( - filename, - finder=finder, - options=options, - session=session): - if req.editable: - logger.notify("ignoring %s" % req.url) - continue - requirement_set.add_requirement(req) - - #fail if no requirements - if not requirement_set.has_requirements: - opts = {'name': self.name} - msg = ('You must give at least one requirement ' - 'to %(name)s (see "pip help %(name)s")' % opts) - logger.error(msg) - return + build_delete = (not (options.no_clean or options.build_dir)) + with BuildDirectory(options.build_dir, delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=None, + download_dir=None, + download_cache=options.download_cache, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=True, + session=session, + wheel_download_dir=options.wheel_dir + ) - try: - #build wheels - wb = WheelBuilder( - requirement_set, - finder, - options.wheel_dir, - build_options = options.build_options or [], - global_options = options.global_options or [] - ) - wb.build() - except PreviousBuildDirError: - options.no_clean = True - raise - finally: - if not options.no_clean: - requirement_set.cleanup_files() + # make the wheelhouse + if not os.path.exists(options.wheel_dir): + os.makedirs(options.wheel_dir) + + #parse args and/or requirements files + for name in args: + requirement_set.add_requirement( + InstallRequirement.from_line(name, None)) + + for filename in options.requirements: + for req in parse_requirements( + filename, + finder=finder, + options=options, + session=session): + if req.editable: + logger.notify("ignoring %s" % req.url) + continue + requirement_set.add_requirement(req) + + #fail if no requirements + if not requirement_set.has_requirements: + opts = {'name': self.name} + msg = ('You must give at least one requirement ' + 'to %(name)s (see "pip help %(name)s")' % opts) + logger.error(msg) + return + + try: + #build wheels + wb = WheelBuilder( + requirement_set, + finder, + options.wheel_dir, + build_options = options.build_options or [], + global_options = options.global_options or [] + ) + wb.build() + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + if not options.no_clean: + requirement_set.cleanup_files() diff --git a/pip/util.py b/pip/util.py index f459bb2..f5edeeb 100644 --- a/pip/util.py +++ b/pip/util.py @@ -8,6 +8,7 @@ import zipfile import tarfile import subprocess import textwrap +import tempfile from pip.exceptions import InstallationError, BadCommand, PipError from pip.backwardcompat import(WindowsError, string_types, raw_input, @@ -718,3 +719,35 @@ def is_prerelease(vers): parsed = version._normalized_key(normalized) return any([any([y in set(["a", "b", "c", "rc", "dev"]) for y in x]) for x in parsed]) + + +class BuildDirectory(object): + + def __init__(self, name=None, delete=None): + # If we were not given an explicit directory, and we were not given an + # explicit delete option, then we'll default to deleting. + if name is None and delete is None: + delete = True + + if name is None: + name = tempfile.mkdtemp(prefix="pip-build-") + # If we were not given an explicit directory, and we were not given + # an explicit delete option, then we'll default to deleting. + if delete is None: + delete = True + + self.name = name + self.delete = delete + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.name) + + def __enter__(self): + return self.name + + def __exit__(self, exc, value, tb): + self.cleanup() + + def cleanup(self): + if self.delete: + rmtree(self.name)