Build System Interface

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the Python Reference bibliography.

When pip installs something that is not already a wheel (an sdist from an index, a local directory, a VCS checkout), it has to build a wheel first. pip does not know how to build anything itself: it is a PEP 517 frontend that drives the project’s backend. This page describes that interface for pip 26.2.2. The examples were run on CPython 3.14.6 on 2026-10-22 with the hatchling flavour of birdtally and a few deliberately broken copies of it. The standards themselves are introduced on Packaging Concepts & Standards.

The build process

pip reads [build-system] from pyproject.toml, creates an isolated environment, and calls the backend’s hooks in a subprocess:

sequenceDiagram participant P as pip (frontend) participant E as Isolated build environment participant B as Backend (hatchling.build) P->>E: create a temporary environment P->>E: install [build-system] requires (hatchling>=1.27) P->>B: get_requires_for_build_wheel(config_settings) B-->>P: extra build requirements (none for hatchling) P->>E: install them P->>B: prepare_metadata_for_build_wheel(metadata_dir) B-->>P: birdtally-0.1.0.dist-info (METADATA) Note over P: resolve dependencies from METADATA P->>B: build_wheel(wheel_dir, config_settings) B-->>P: birdtally-0.1.0-py3-none-any.whl P->>P: install the wheel, cache it

Each step shows up in pip’s output:

python -m pip wheel --no-deps --no-cache-dir -w wh ./birdtally
Processing ./birdtally
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
  Preparing metadata (pyproject.toml): started
  Preparing metadata (pyproject.toml): finished with status 'done'
Building wheels for collected packages: birdtally
  Building wheel for birdtally (pyproject.toml): started
  Building wheel for birdtally (pyproject.toml): finished with status 'done'
  Created wheel for birdtally: filename=birdtally-0.1.0-py3-none-any.whl size=3736 sha256=35a5069350968616bed7f40135c5594911438241543d3b4a03fa389984a56b5a
  Stored in directory: /tmp/pip-ephem-wheel-cache-kwzcdfg3/wheels/06/f4/44/f30b21b9ff555aba44f7388a25b539755f2cc518839b02e187
Successfully built birdtally
  • Build isolation. The temporary environment contains only the build requirements; nothing from your environment is visible. This is the PEP 518 guarantee that the build does not depend on whatever you happen to have installed.

  • Metadata generation. prepare_metadata_for_build_wheel is optional; if the backend lacks it, pip builds the wheel and reads the metadata from it. Metadata comes first because the resolver needs the dependencies before it decides to install anything.

  • Wheel generation. build_wheel writes the wheel, which pip installs and stores in its wheel cache, so the same sdist is not built twice.

  • Editable installs use the PEP 660 hook (build_editable, which pip’s documentation calls build_wheel_for_editable) and the matching get_requires_for_build_editable. Editable wheels are not cached.

  • Build output from the backend is hidden unless the build fails or you pass -v.

--config-settings

Backends may accept key=value settings, which pip passes to every hook call. --config-settings (short -C, since pip 23.1; PIP_CONFIG_SETTINGS) may be repeated. setuptools, for example, accepts editable_mode=strict, which builds an editable install from links in build/ instead of putting src on sys.path:

python -m pip install -e ./st -C editable_mode=strict     # st: birdtally with build-backend = "setuptools.build_meta"
cat .venv/lib/python3.14/site-packages/__editable__.birdtally-0.1.0.pth
Successfully installed birdtally-0.1.0
…/st/build/__editable__.birdtally-0.1.0-py3-none-any

Without the setting the same file contains …/st/src. Which settings exist is up to each backend; see Build Backends. Two scoping rules matter:

  • settings given on the command line (or in the environment or configuration) apply only to requirements named on the command line; they are not passed to the builds of dependencies, nor to requirements listed in a requirements file;

  • for a requirement in a file, write the option on its line (./st --config-settings editable_mode=strict), which is the per-requirement form described on Requirements Files.

--config-settings replaces --global-option and --build-option, which passed arguments to setup.py directly. They were deprecated in pip 23.1 and removed in pip 25.3, together with --no-use-pep517; there is no way left to run setup.py commands through pip. --install-option had already gone in 23.1.

Turning isolation off

--no-build-isolation (PIP_NO_BUILD_ISOLATION) builds in the current environment instead. Its help text says what that implies: "Build dependencies specified by PEP 518 must be already installed if this option is used." Use it when the build needs something that is already present (a locally built NumPy, a system-provided compiler toolchain wrapper) or to build offline. Without hatchling installed, the build cannot even import the backend:

python -m pip install --no-build-isolation --no-deps ./birdtally
Processing ./birdtally
  Preparing metadata (pyproject.toml): started
  Preparing metadata (pyproject.toml): finished with status 'done'
ERROR: Exception:
Traceback (most recent call last):
…
pip._vendor.pyproject_hooks._impl.BackendUnavailable: Cannot import 'hatchling.build'

--check-build-dependencies makes pip verify, in that mode, that the installed build dependencies satisfy requires. With hatchling 1.26.3 installed and hatchling>=1.27 required:

python -m pip install --no-build-isolation --check-build-dependencies --no-deps ./birdtally
Processing ./birdtally
ERROR: Some build dependencies for file://…/birdtally conflict with the backend dependencies: hatchling==1.26.3 is incompatible with hatchling>=1.27.

Projects without [build-system]

A project with a setup.py but no [build-system] table is treated as if it declared:

[build-system]
requires = ["setuptools>=40.8.0"]
build-backend = "setuptools.build_meta:__legacy__"

and a table with requires but no build-backend also uses setuptools.build_meta:__legacy__. So a bare setup.py project still installs, through the same PEP 517 path (note the (pyproject.toml) in the messages although there is no such file):

Processing ./legacy
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: finished with status 'done'
  Preparing metadata (pyproject.toml): finished with status 'done'
Building wheels for collected packages: legacy-birds
  Building wheel for legacy-birds (pyproject.toml): finished with status 'done'
…
Successfully installed legacy-birds-0.1

A [build-system] table without requires is an error, not a fallback; see below. The Packaging User Guide’s "Is setup.py deprecated?" discussion lists what to use instead of each old setup.py command (python -m build for sdist/bdist_wheel, pip install . for install, pip install -e . for develop, twine for upload).

History

pip Change

10.0

First PEP 517/518 support.

21.3

PEP 660 editable installs; in-tree builds by default.

22.1

The out-of-tree-build fallback removed.

23.1

The setup.py install code path removed; --install-option removed; --build-option and --global-option deprecated; -C short form of --config-settings.

25.3

setup.py bdist_wheel and setup.py develop code paths removed; --global-option, --build-option and --no-use-pep517 removed: PEP 517 is the only build path.

26.0

Experimental --use-feature=inprocess-build-deps.

26.2

Constraints no longer apply to builds (use --build-constraint); experimental --use-feature=venv-isolation; system packages no longer leak into isolated builds on Python 3.15.

The two experimental features change how the isolated environment is made. inprocess-build-deps (26.0) installs build dependencies within the same pip install process instead of a pip subprocess, which the changelog describes as faster, reliable with --no-clean and --no-cache-dir, and able to prompt for authentication; it will become the default in a future version. venv-isolation (26.2) creates a standard virtual environment for every build instead of pip’s own overlay; the changelog says it "will fix most (if not all) subtle isolation issues" and will become the default in a future release, and that the two flags have known edge cases when combined.

Build errors

pip wraps build failures in diagnostic errors with a stable reference name. Three are specific to builds.

subprocess-exited-with-error and metadata-generation-failed appear together when the backend fails while preparing metadata. A copy of birdtally with version = "zero point one":

Processing ./broken
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
  Preparing metadata (pyproject.toml): started
  Preparing metadata (pyproject.toml): finished with status 'error'
  error: subprocess-exited-with-error
  × Preparing metadata (pyproject.toml) did not run successfully.
  │ exit code: 1
…
      ValueError: Invalid version `zero point one` from field `project.version`, see https://peps.python.org/pep-0440/
      [end of output]
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed
× Encountered error while generating package metadata.
╰─> from file://…/broken
note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

Read the backend’s own traceback between the two error lines: the cause is always there (here, an invalid PEP 440 version). For an sdist from an index the usual causes are a missing compiler or system library, or a backend too new or too old for your Python.

missing-pyproject-build-system-requires is a [build-system] table without requires:

Processing ./noreq
error: missing-pyproject-build-system-requires

× Can not process file://…/noreq
╰─> This package has an invalid pyproject.toml file.
    The [build-system] table is missing the mandatory `requires` key.

note: This is an issue with the package mentioned above, not pip.
hint: See PEP 518 for the detailed specification.

The other error references are collected on pip: Troubleshooting.

pip and Poetry

Poetry builds its own project with poetry-core in process (poetry build), so [build-system] matters only to other frontends. When Poetry installs a dependency from an sdist, it does a PEP 517 build in an isolated environment like pip; settings for those builds go in installer.build-config-settings.<package> (experimental, Poetry 2.1), the counterpart of --config-settings. See Poetry: Configuration.