diff --git a/docs/html/cli/pip_install.rst b/docs/html/cli/pip_install.rst
index 6b4d3b1a2ba..6900499a763 100644
--- a/docs/html/cli/pip_install.rst
+++ b/docs/html/cli/pip_install.rst
@@ -490,18 +490,20 @@ You can install local projects by specifying the project path to pip:
py -m pip install path/to/SomeProject
-During regular installation, pip will copy the entire project directory to a
-temporary location and install from there. The exception is that pip will
-exclude .tox and .nox directories present in the top level of the project from
-being copied. This approach is the cause of several performance and correctness
-issues, so it is planned that pip 21.3 will change to install directly from the
-local project directory. Depending on the build backend used by the project,
-this may generate secondary build artifacts in the project directory, such as
-the ``.egg-info`` and ``build`` directories in the case of the setuptools
-backend.
-
-To opt in to the future behavior, specify the ``--use-feature=in-tree-build``
-option in pip's command line.
+.. note::
+
+ Depending on the build backend used by the project, this may generate
+ secondary build artifacts in the project directory, such as the
+ ``.egg-info`` and ``build`` directories in the case of the setuptools
+ backend.
+
+ Pip has a legacy behaviour that copies the entire project directory to a
+ temporary location and installs from there. This approach was the cause of
+ several performance and correctness issues, so it is now disabled by
+ default, and it is planned that pip 22.1 will remove it.
+
+ To opt in to the legacy behavior, specify the
+ ``--use-deprecated=out-of-tree-build`` option in pip's command line.
.. _`editable-installs`:
diff --git a/news/10495.removal.rst b/news/10495.removal.rst
new file mode 100644
index 00000000000..9b5d2256408
--- /dev/null
+++ b/news/10495.removal.rst
@@ -0,0 +1,3 @@
+In-tree builds are now the default. ``--use-feature=in-tree-build`` is now
+ignored. ``--use-deprecated=out-of-tree-build`` may be used temporarily to ease
+the transition.
diff --git a/src/pip/_internal/cli/cmdoptions.py b/src/pip/_internal/cli/cmdoptions.py
index b4f0f83c679..efe209adc9c 100644
--- a/src/pip/_internal/cli/cmdoptions.py
+++ b/src/pip/_internal/cli/cmdoptions.py
@@ -961,7 +961,7 @@ def check_list_path_option(options: Values) -> None:
metavar="feature",
action="append",
default=[],
- choices=["legacy-resolver"],
+ choices=["legacy-resolver", "out-of-tree-build"],
help=("Enable deprecated functionality, that will be removed in the future."),
)
diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py
index 4129bf7e14c..c224bd276e2 100644
--- a/src/pip/_internal/cli/req_command.py
+++ b/src/pip/_internal/cli/req_command.py
@@ -34,6 +34,7 @@
from pip._internal.req.req_tracker import RequirementTracker
from pip._internal.resolution.base import BaseResolver
from pip._internal.self_outdated_check import pip_self_version_check
+from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.temp_dir import (
TempDirectory,
TempDirectoryTypeRegistry,
@@ -260,6 +261,20 @@ def make_requirement_preparer(
"fast-deps has no effect when used with the legacy resolver."
)
+ in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled
+ if "in-tree-build" in options.features_enabled:
+ deprecated(
+ reason="In-tree builds are now the default.",
+ replacement="to remove the --use-feature=in-tree-build flag",
+ gone_in="22.1",
+ )
+ if "out-of-tree-build" in options.deprecated_features_enabled:
+ deprecated(
+ reason="Out-of-tree builds are deprecated.",
+ replacement=None,
+ gone_in="22.1",
+ )
+
return RequirementPreparer(
build_dir=temp_build_dir_path,
src_dir=options.src_dir,
@@ -272,7 +287,7 @@ def make_requirement_preparer(
require_hashes=options.require_hashes,
use_user_site=use_user_site,
lazy_wheel=lazy_wheel,
- in_tree_build="in-tree-build" in options.features_enabled,
+ in_tree_build=in_tree_build,
)
@classmethod
diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py
index 67ef9dd8313..ccf034eb5a0 100644
--- a/src/pip/_internal/operations/prepare.py
+++ b/src/pip/_internal/operations/prepare.py
@@ -35,7 +35,6 @@
from pip._internal.network.session import PipSession
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_tracker import RequirementTracker
-from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.filesystem import copy2_fixed
from pip._internal.utils.hashes import Hashes, MissingHashes
from pip._internal.utils.logging import indent_log
@@ -197,19 +196,9 @@ def unpack_url(
#
# As further cleanup, _copy_source_tree and accompanying tests can
# be removed.
+ #
+ # TODO when use-deprecated=out-of-tree-build is removed
if link.is_existing_dir():
- deprecated(
- reason=(
- "pip copied the source tree into a temporary directory "
- "before building it. This is changing so that packages "
- "are built in-place "
- 'within the original source tree ("in-tree build").'
- ),
- replacement=None,
- gone_in="21.3",
- feature_flag="in-tree-build",
- issue=7555,
- )
if os.path.isdir(location):
rmtree(location)
_copy_source_tree(link.file_path, location)
diff --git a/tests/functional/test_cli.py b/tests/functional/test_cli.py
index 1df4836f9b8..e9d4157b9ba 100644
--- a/tests/functional/test_cli.py
+++ b/tests/functional/test_cli.py
@@ -36,7 +36,8 @@ def test_entrypoints_work(entrypoint, script):
)
)
- script.pip("install", "-vvv", str(fake_pkg))
+ # expect_temp because pip install will generate fake_pkg.egg-info
+ script.pip("install", "-vvv", str(fake_pkg), expect_temp=True)
result = script.pip("-V")
result2 = script.run("fake_pip", "-V", allow_stderr_warning=True)
assert result.stdout == result2.stdout
diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py
index 3af75cb5639..62f29d5bfed 100644
--- a/tests/functional/test_install.py
+++ b/tests/functional/test_install.py
@@ -587,7 +587,12 @@ def test_install_from_local_directory_with_symlinks_to_directories(script, data)
Test installing from a local directory containing symlinks to directories.
"""
to_install = data.packages.joinpath("symlinks")
- result = script.pip("install", to_install)
+ result = script.pip(
+ "install",
+ "--use-deprecated=out-of-tree-build",
+ to_install,
+ allow_stderr_warning=True, # TODO: set to False when removing out-of-tree-build
+ )
pkg_folder = script.site_packages / "symlinks"
dist_info_folder = script.site_packages / "symlinks-0.1.dev0.dist-info"
result.did_create(pkg_folder)
@@ -597,10 +602,10 @@ def test_install_from_local_directory_with_symlinks_to_directories(script, data)
@pytest.mark.usefixtures("with_wheel")
def test_install_from_local_directory_with_in_tree_build(script, data):
"""
- Test installing from a local directory with --use-feature=in-tree-build.
+ Test installing from a local directory with default in tree build.
"""
to_install = data.packages.joinpath("FSPkg")
- args = ["install", "--use-feature=in-tree-build", to_install]
+ args = ["install", to_install]
in_tree_build_dir = to_install / "build"
assert not in_tree_build_dir.exists()
@@ -618,6 +623,8 @@ def test_install_from_local_directory_with_socket_file(script, data, tmpdir):
"""
Test installing from a local directory containing a socket file.
"""
+ # TODO: remove this test when removing out-of-tree-build support,
+ # it is only meant to test the copy of socket files
dist_info_folder = script.site_packages / "FSPkg-0.1.dev0.dist-info"
package_folder = script.site_packages / "fspkg"
to_copy = data.packages.joinpath("FSPkg")
@@ -628,7 +635,13 @@ def test_install_from_local_directory_with_socket_file(script, data, tmpdir):
socket_file_path = os.path.join(to_install, "example")
make_socket_file(socket_file_path)
- result = script.pip("install", "--verbose", to_install)
+ result = script.pip(
+ "install",
+ "--use-deprecated=out-of-tree-build",
+ "--verbose",
+ to_install,
+ allow_stderr_warning=True, # because of the out-of-tree deprecation warning
+ )
result.did_create(package_folder)
result.did_create(dist_info_folder)
assert str(socket_file_path) in result.stderr
diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py
index 57bb527da1f..3adaf438b82 100644
--- a/tests/functional/test_uninstall.py
+++ b/tests/functional/test_uninstall.py
@@ -12,6 +12,7 @@
from pip._internal.utils.misc import rmtree
from tests.lib import assert_all_changes, create_test_package_with_setup, need_svn
from tests.lib.local_repos import local_checkout, local_repo
+from tests.lib.path import Path
@pytest.mark.network
@@ -279,7 +280,15 @@ def test_uninstall_console_scripts(script):
result = script.pip("install", pkg_path)
result.did_create(script.bin / "discover" + script.exe)
result2 = script.pip("uninstall", "discover", "-y")
- assert_all_changes(result, result2, [script.venv / "build", "cache"])
+ assert_all_changes(
+ result,
+ result2,
+ [
+ script.venv / "build",
+ "cache",
+ Path("scratch") / "discover" / "discover.egg-info",
+ ],
+ )
def test_uninstall_console_scripts_uppercase_name(script):