a7c7b123d7
See more at http://seclists.org/oss-sec/2014/q4/655
396 lines
16 KiB
Diff
396 lines
16 KiB
Diff
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 "<venv path>/build". '
|
|
- 'The default for global installs is "<OS temp dir>/pip_build_<username>".')
|
|
+ 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)
|