Basic Usage¶
This guide covers the most common build commands and workflows.
Building both source and wheel distributions (default)¶
By default, build creates both a source distribution (often called “sdist”) and a wheel:
$ python -m build
The process:
Builds the source distribution from your source code
Extracts the source distribution to a temporary directory
Builds the wheel from the extracted source distribution
This two-step process ensures your source distribution contains all necessary files to build your package. If files are missing, the wheel build will fail, alerting you to the problem.
This is different from python -m build --sdist --wheel, which builds both directly from source. The default behavior
is preferable because it verifies your sdist is complete.
Building only a wheel¶
$ python -m build --wheel
Or the short form:
$ python -m build -w
This builds the wheel (installable package) directly from your source directory, skipping the source distribution step. This is faster but doesn’t verify your source distribution is complete.
Building only a source distribution¶
$ python -m build --sdist
Or the short form:
$ python -m build -s
Specifying the source directory¶
Build defaults to the current directory. To build from a different location:
$ python -m build path/to/project
Or explicitly:
$ python -m build --srcdir path/to/project
Building from a source distribution¶
The positional argument also accepts a .tar.gz source distribution. Build checks the filename and the
presence of PKG-INFO, extracts the archive into a temporary directory, and runs the wheel build against the
extracted source. The wheel lands next to the input archive.
$ python -m build dist/mypackage-1.0.0.tar.gz
No flag means the same as --wheel. --metadata works the same way. --sdist errors out, since the archive
already is an sdist.
Specifying the output directory¶
By default, distributions are placed in dist/ within the source directory. To use a different location:
$ python -m build --outdir /path/to/output
Or the short form:
$ python -m build -o /path/to/output
Speeding up rebuilds of compiled packages¶
When the default build extracts the intermediate sdist, it uses a random temporary directory. Compiler caches such as ccache and sccache fold the absolute source path into their cache key, so a fresh path on every run means every C/C++ file is recompiled from scratch.
Pin the extraction path with --sdist-extract-dir so the cache can recognise unchanged files across rebuilds:
$ python -m build --sdist-extract-dir build/sdist
Build creates the directory if missing, clears and repopulates it each run so the contents stay fresh, and keeps it
after the build. The path stays stable while the project version is unchanged, as it is during iteration on the
extension sources. The same flag applies when building a wheel from a .tar.gz.
Build still runs in a fresh isolated environment whose path is not pinned, so include directories from that
environment (Python headers, dependency headers) keep changing. Caching those compile units needs toolchain-side path
remapping on top of this flag (e.g. -fdebug-prefix-map, or sccache’s base_dir).
Controlling verbosity¶
Increase verbosity to see more details:
$ python -m build -v
Or even more verbose:
$ python -m build -vv
Decrease verbosity for quieter output:
$ python -m build -q
Using a faster installer¶
By default, build uses pip to install build dependencies. For faster builds, use uv (a faster alternative to pip):
$ python -m build --installer=uv
This requires uv to be installed:
$ pip install uv
Building without isolation¶
By default, build creates an isolated environment (a clean temporary virtual environment) to ensure reproducible builds. To skip this and use your current environment:
$ python -m build --no-isolation
Or the short form:
$ python -m build -n
Warning
When using --no-isolation, you must manually install all build dependencies. This is mainly useful for:
Offline or air-gapped environments (no internet access)
Debugging build issues
Linux distribution packaging where dependencies are provided externally
Skipping dependency checks¶
To skip checking if build dependencies are installed (requires --no-isolation):
$ python -m build --no-isolation --skip-dependency-check
Or:
$ python -m build -nx
Common workflows¶
Development build¶
Quick build during development:
$ python -m build --wheel --installer=uv
Fast CI build¶
In CI where dependencies are pre-installed:
$ pip install build build-backend-here
$ python -m build --no-isolation
Release build¶
For uploading to PyPI, build both sdist and wheel:
$ python -m build
$ uv publish
uv publish is a modern option that handles uploading to PyPI with built-in support for trusted publishing.
$ python -m build
$ twine check dist/*
$ twine upload dist/*
See the twine documentation for upload options.
Tip
Use the hynek/build-and-inspect-python-package GitHub Action which handles this workflow including verification.
See CI/CD Integration.
Testing the sdist¶
Build from the sdist to ensure it’s complete:
$ python -m build --sdist
$ python -m build dist/mypackage-1.0.0.tar.gz
Or test installation:
$ python -m build
$ python -m pip install dist/mypackage-1.0.0.tar.gz
$ python -c "import mypackage; print(mypackage.__version__)"
Cleaning before build¶
There’s no built-in clean command. To ensure a fresh build, manually remove the dist directory:
$ rm -rf dist/
$ python -m build
Or to avoid stale artifacts, use a unique output directory:
$ python -m build --outdir dist/v1.0.0
Getting metadata without building¶
To extract package metadata without building the full package:
$ python -m build --metadata
This outputs the wheel metadata in JSON format to stdout.
Checking the build version¶
To see which version of build you’re using:
$ python -m build --version
Installing build dependencies only¶
For specialized workflows like static analysis or linting, you may want to install just the build dependencies without actually building:
from build import ProjectBuilder
builder = ProjectBuilder(".")
# Get all build dependencies
requires = builder.build_system_requires
for dist in ["sdist", "wheel"]:
requires.extend(builder.get_requires_for_build(dist))
# Install them
import subprocess
subprocess.run(["pip", "install", *requires])
This is useful when you need the same environment that build would create, but want to run other tools (like mypy, ruff, or custom linters) instead of building the package.
See API Documentation for more programmatic usage examples.
See also¶
Backend Configuration for passing options to your build backend
Corporate Environments for using build with private indexes
Troubleshooting for common problems
Command-Line Interface for all command-line options