# Heads-up: PyTorch's build is switching from setuptools to scikit-build-core, and setup.py goes away

**URL:** <https://dev-discuss.pytorch.org/t/heads-up-pytorchs-build-is-switching-from-setuptools-to-scikit-build-core-and-setup-py-goes-away/3414>\
**Category:** release/packaging\
**Created:** [July 13, 2026, 7:29pm UTC](https://dev-discuss.pytorch.org/t/heads-up-pytorchs-build-is-switching-from-setuptools-to-scikit-build-core-and-setup-py-goes-away/3414 "2026-07-13T19:29:32Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![zklaus](https://yyz2.discourse-cdn.com/flex036/user_avatar/dev-discuss.pytorch.org/zklaus/32/2658_2.png) [@zklaus](https://dev-discuss.pytorch.org/u/zklaus)\
**Post date:** [July 13, 2026, 7:29pm UTC](https://dev-discuss.pytorch.org/t/heads-up-pytorchs-build-is-switching-from-setuptools-to-scikit-build-core-and-setup-py-goes-away/3414/1 "2026-07-13T19:29:32Z")

</div>

Next Monday Jul 20, 2026, PyTorch’s build backend switches from setuptools to  
[scikit-build-core](https://scikit-build-core.readthedocs.io/) 1.0, and `setup.py` is removed.

**TL;DR:** If you build with spin or `pip install` (regular or editable),  
nothing changes. If you invoke `python setup.py ...` anywhere  
(scripts, docs, muscle memory), switch to spin/pip. Prebuilt wheels and runtime  
behavior are unaffected.

## Why

Modern Python packaging orchestrates builds through standard interfaces  
(PEP 517/518/621); the legacy `python setup.py ...` commands they replaced  
have been deprecated for years. PyTorch is a hybrid: a substantial  
CMake-built native core alongside a large pure-Python library, and while  
CMake covers the native half down to the extension modules, wheels,  
sdists, and package metadata are outside its scope. Until now, setup.py  
bridged that gap with a decade of custom orchestration. The maintained,  
standards-based backend purpose-built for exactly this hybrid is  
scikit-build-core: it packages the Python side like any modern backend and  
drives the CMake build, all from pyproject.toml. The switch puts PyTorch’s  
build on the interfaces the rest of the ecosystem targets and makes  
standards-compliant artifacts the default.

## What you need to change

`setup.py` is removed, so any direct invocation of it stops working. The  
replacements:

| Before | After |
| --- | --- |
| `python setup.py develop` | `spin develop` (or `pip install -e . -v --no-build-isolation`) |
| `python setup.py install` | `spin install` (or `pip install . -v --no-build-isolation`) |
| `python setup.py bdist_wheel` | `python -m build --wheel --no-isolation` |
| `python setup.py clean` | `spin clean` |

Either form works. Each spin command is a thin wrapper that runs exactly  
the pip command shown beside it (using uv when available); the pip and  
`python -m build` forms are the standard Python build interfaces and have  
been the recommended way to build PyTorch for a while. The wrappers belong  
to [spin, the developer CLI we adopted last  
year](https://dev-discuss.pytorch.org/t/adopting-spin-as-developer-cli/3309).  
Build-adjacent developer tasks that used to hide behind setup.py  
subcommands are moving there; `spin` with no arguments lists what is  
available. Spin is part of requirements.txt, so a PyTorch dev environment  
already has it; for a standalone, global install use `uv tool install spin`  
and run it from the repository root.

## What stays the same

- **The spin and pip workflows are untouched** , including editable  
installs and all incremental-rebuild behavior (the CMake cache carries  
over between builds exactly as before).
- **Every build-configuration environment variable keeps working** :  
`USE_CUDA`, `USE_ROCM`, `DEBUG`, `REL_WITH_DEB_INFO`, `MAX_JOBS`,  
`CMAKE_BUILD_TYPE`, `CC`/`CXX`, and the rest. They now seed the CMake  
cache directly and are documented in `cmake/EnvVarForwarding.cmake` and  
`pyproject.toml`, instead of being interpreted by setup.py code.
- **ccache/sccache, cross-compilation, and CMake-cache-based workflows**  
carry over: the backend drives the same CMake build you know, without  
setup.py in the middle.
- **Performance is neutral, by construction and by measurement.** We  
compared same-day CI binary builds (scikit-build-core vs setuptools):  
matched build jobs show identical durations within noise, and all 52  
wheel artifacts across every platform family are byte-comparable in size  
(within 0.02%; the only content difference is license files now included  
per PEP 639, and the setuptools-specific `top_level.txt` going away).

## Editable installs

scikit-build-core’s editable mode separates the two halves of a  
development install cleanly: Python sources are imported straight from  
your checkout (edit and rerun, as always), while compiled artifacts  
(`torch._C`, the libtorch libraries) are imported from the wheel install  
location instead of being scattered through the source tree.

Day to day this is invisible (`import torch` just works), but a few  
things are worth knowing:

- Tooling that expects `.so`/`.pyd` files _inside the package directory_  
(`torch/lib`, `torch/_C*.so`) needs updating; compiled artifacts no  
longer land there.
- Rebuilding after a C++ change works exactly as it does today: rerun  
`spin develop` (or `pip install -e . -v --no-build-isolation`) for an  
incremental, CMake-cached rebuild.
- New and strictly opt-in: an editable install can also rebuild  
automatically on `import torch`. This capability is not battle-tested  
yet, so it is off by default and the rerun workflow above remains the  
recommended one. Opting in happens at install time, by setting the  
variable on the install command (`SKBUILD_EDITABLE_REBUILD=true spin develop`); that install then rebuilds on the first `import torch` in a  
process, until you reinstall without the variable. CONTRIBUTING.md has  
the details.

The full development flow is documented in [CONTRIBUTING.md](https://github.com/pytorch/pytorch?tab=contributing-ov-file).

## If you build on top of PyTorch

Nothing changes. C++/CUDA extensions built with `torch.utils.cpp_extension`  
keep working as-is: the setuptools-based helpers are not removed, and torch  
keeps its runtime setuptools dependency. The one case that needs the  
command replacements above is CI that builds PyTorch itself from source.

## If you redistribute PyTorch

For conda-forge, Spack, distro, and other from-source packagers:

- Build via the standard PEP 517 interfaces: `python -m build --wheel --no-isolation` or `pip install`. Anything that invokes setup.py  
directly needs porting.
- Build requirements now include `scikit-build-core>=1.0` (see  
`[build-system]` in pyproject.toml / requirements-build.txt).
- Configuration via environment variables is unchanged, and CMake options  
can also be passed explicitly through the standard config-settings  
mechanism, right on the build command:  
`pip install . -Ccmake.define.FOO=BAR` (long form  
`--config-settings=cmake.define.FOO=BAR`) or  
`python -m build -Ccmake.define.FOO=BAR`. See the  
[scikit-build-core configuration docs](https://scikit-build-core.readthedocs.io/en/latest/configuration/index.html)  
for the full set of options.

## Additional benefits

- Build products no longer land in the package directory: with setuptools,  
any from-source build (regular or editable) copied compiled libraries  
into `torch/` itself; now the source tree stays clean apart from the  
familiar `build/` directory.
- The switch retires over 2,000 lines of bespoke build orchestration in  
favor of declarative configuration in pyproject.toml.
- Wheel metadata (version, dependencies, license files) now flows through  
standard hooks instead of hand-rolled code; PEP 639 license inclusion,  
for example, comes for free.
- The migration lands on scikit-build-core 1.0, which gained several  
capabilities specifically to support PyTorch’s needs: for example, the  
`[tool.scikit-build.env]` table, added upstream so that PyTorch (and  
projects like it) need no custom backend wrapper at all. Our thanks to  
@henryiii and the scikit-build team. The idea has been in the air  
for a while, too:  
[scikit-build came up on this forum back in 2021](https://dev-discuss.pytorch.org/t/scikit-build-interest/405),  
before scikit-build-core was even written.

## Rollout and feedback

The change lands as two stacked PRs, [#180247](https://github.com/pytorch/pytorch/pull/180247) (backend switch) and  
[#180248](https://github.com/pytorch/pytorch/pull/180248) (setup.py removal), tracked in [RFC #157807](https://github.com/pytorch/pytorch/issues/157807), where the full  
design discussion lives. CI is validated across all platforms and wheel  
families, including content parity of the produced wheels against the  
setuptools build.

One question we would like your input on: where should the build-variable  
reference (USE\_CUDA, MAX\_JOBS, and friends) live long-term? Candidates are  
CONTRIBUTING.md, the README, the CMake file that implements the forwarding  
(cmake/EnvVarForwarding.cmake), pyproject.toml, or the rendered docs site;  
tell us which you would find most discoverable.

If you maintain tooling that scripts setup.py, redistribute PyTorch, or  
have an unusual build integration, now is the moment to check it;  
comments welcome here or on [the tracking issue #189758](https://github.com/pytorch/pytorch/issues/189758).
