Blob Blame History Raw
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)