Open-source project
scikit-build/scikit-build avatar
scikit-build/scikit-build

scikit-build 1.0 becomes a thin wrapper over scikit-build-core and drops the old setuptools commands

Improved build system generator for CPython C, C++, Cython and Fortran extensions

534 stars127 forksPythonNOASSERTION

At a glance

What is it?
scikit-build has always been glue between setuptools and CMake, and version 1.0 turns it into a thin wrapper around the separate scikit-build-core backend, removing the deprecated setup.py commands and requiring CMake 3.15 or newer. For new projects the recommendation is to use scikit-build-core directly and skip setuptools entirely.
Who is it for?
Adopt scikit-build if you already have a setuptools-based project that builds C, C++, Fortran or Cython extensions through CMake and you want the 1.0 backend without rewriting your packaging, because the upgrade path is a build-system table change and the legacy helper modules still work.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What scikit-build actually is, and what 1.0 changed

scikit-build is a Python build system for CPython C, C++, Fortran and Cython extensions using CMake, and the README is blunt about its nature: the scikit-build package is fundamentally just glue between the setuptools Python module and CMake.

That sentence has always been the summary, and version 1.0 changes what the glue holds together. The next generation, scikit-build-core, provides a simple, reliable build backend for CMake that does not use setuptools and provides a lot of new features. Since scikit-build 1.0, scikit-build classic is a thin wrapper around scikit-build-core's setuptools plugin. The package now only adds the legacy CMake helper modules, which have modern equivalents, and the compatibility setup() wrapper.

So the release that looks like a major version is really a migration of the implementation. The dependency list in pyproject.toml shows it directly: the only runtime dependency is scikit-build-core with the setuptools extra at 1.0 or newer. scikit-build itself is now a compatibility layer over someone else's backend, and the project is explicit about the recommendation that follows from that: if you do not require setuptools, you should consider using scikit-build-core directly instead.

The upgrade instructions are correspondingly simple, and they are framed around what breaks. Code should build using standards-based tools like pip, uv, build or cibuildwheel as long as you are not using scikit-build's internals. setup.py commands may not work, though a few do, like setup.py build_ext --inplace, which the README says works better than before.

The two removals are named. All deprecated setuptools and distutils features are removed in scikit-build 1.0, like the test command and easy_install, and the recommendation is to use standards-based development instead, such as uv, pip and build. And older versions of CMake, those before 3.15, are no longer supported.

The primary language is Python, the licence is recorded as a custom one GitHub cannot classify although the Miscellaneous section says free software under the MIT licence, and the default branch is main. The recent releases are 1.0.0rc1 from 2026-07-23, 0.19.1 from 2026-06-23 and 0.19.0 from 2026-03-03, with the last push on 2026-10-06.

Three ways to configure the backend, and what each one commits you to

The README gives three configurations, and the differences between them are the substance of the 1.0 migration.

If you keep from skbuild import setup in your setup.py, you require scikit-build and use scikit-build-core's setuptools backend:

toml
[build-system]
requires = ["scikit-build>=1"]
build-backend = "scikit_build_core.setuptools.build_meta"

If you want scikit-build-core directly but keep setuptools:

toml
[build-system]
requires = ["scikit-build-core[setuptools]>=1"]
build-backend = "scikit_build_core.setuptools.build_meta"

And if you want the native, non-setuptools backend:

toml
[build-system]
requires = ["scikit-build-core>=1"]
build-backend = "scikit_build_core.build"

In the native case you will need to use a project table and tool.scikit-build, because setup.py, MANIFEST.in and setup.cfg have no effect without setuptools. The native backend is recommended unless you specifically need a setuptools or hatchling plugin, for example to combine with other plugins.

So the choice is between keeping setuptools as a plugin host and dropping it entirely. What you lose by dropping it is the ability to combine your extension build with other setuptools or hatchling plugins, and the legacy CMake helper modules that scikit-build still ships.

There is one subtlety in the middle option, and it is the kind of detail that catches people mid-migration. Either use setup with at least a cmake_source_dir argument:

python
from setuptools import setup

setup(cmake_source_dir=".")

or use the following in your pyproject.toml:

toml
[tool.scikit-build]
cmake.source-dir = "."

Either one of those will cause scikit-build-core's setuptools plugin to activate.

And there is a caveat about the defaults. The defaults for the native plugin are different from the skbuild.setup() wrapper, which is scikit_build_core.setuptools.wrapper.setup(). For example SKBUILD_CONFIGURE_OPTIONS and SKBUILD_BUILD_OPTIONS are not adapted without the wrapper. In other words, a project that sets those environment variables to pass CMake flags will silently stop working if it moves from the wrapper to the native plugin without rewriting them into the tool.scikit-build table.

The history, and why the project exists at all

The README's history section is short and explains the whole project in three sentences.

PyCMake was created at SciPy 2014 in response to general difficulties building C++ and Fortran based Python extensions across platforms. It was renamed to scikit-build in 2016. Scikit-build-core was started in 2022, and became the backend for this package in 2026.

That timeline is a useful lens on the current release. The problem the project was born to solve was not packaging elegance, it was that building a compiled extension for several platforms meant maintaining per-platform build recipes. The platform-tagged wheel with cibuildwheel came later and solved the distribution half, but the compilation half still needed a build system that could drive CMake across those platforms, and that is what scikit-build has been.

The publications list names the people involved and the two talks that introduced the tool. The first citation is the SciPy 2018 talk by Jean-Christophe Fillion-Robin, Matt McCormick, Omar Padron, Max Smolens, Michael Grauer and Michael Sarahan, published on Zenodo, and the README asks you to use the first citation when referencing scikit-build in scientific publications. The second is the 2022 paper on building binary extensions with pybind11, scikit-build and cibuildwheel by Schreiner, Rickerby, Grosse-Kunstleve, Jakob, Darbois, Gokaslan, Fillion-Robin and McCormick.

The fact that the paper is about pybind11, scikit-build and cibuildwheel together is the point: those three are the stack for a compiled Python extension that ships to PyPI, and scikit-build is the middle one, the part that turns your CMakeLists.txt into a wheel.

For the project itself, the packaging has moved to Hatch: the build-system table in pyproject.toml requires hatchling, hatch-fancy-pypi-readme and hatch-vcs, with the version and readme declared dynamic. That combination is the standard way to derive a version from git tags, since hatch-vcs reads the tag. The project classifiers list Development Status 5 - Production/Stable, MIT as the licence, Python 3 only from 3.10 through 3.15, and Typing :: Typed.

Support for the work was provided by an NSF grant, OAC-2209877, which is a reasonable signal for a tool that a lot of scientific Python infrastructure depends on.

Why a CMake glue layer exists at all when pip can build wheels

It is worth being explicit about the problem scikit-build exists to solve, because the answer explains why a build backend needs to exist at all.

CPython extensions are not pure Python. A module with C, C++, Fortran or Cython code has to be compiled, and compiled code is platform-specific in a way pure Python is not. The same source has to become a different binary for Linux, macOS and Windows, for different Python versions, and for different CPU architectures. A wheel is the artefact that makes that tractable: a prebuilt, tagged archive that pip can install without a compiler present.

Producing those wheels is where the complexity sits. The compilation itself is CMake's job, and CMake has been describing how to compile things for two decades. What was missing was a way to say, in Python packaging terms, this project has a CMakeLists.txt, please compile it and put the result in the wheel where it belongs. That is the glue, and it is why the package calls itself a build system generator rather than a build system.

The toolchain around it is what makes the modern picture clearer. The README's upgrade instructions name pip, uv, build and cibuildwheel as the standards-based tools, and the second citation, the paper on building binary extensions with pybind11, scikit-build and cibuildwheel, names exactly the three pieces. pybind11 is the binding layer that exposes C++ to Python, scikit-build is the part that drives CMake from a packaging front end, and cibuildwheel is the part that runs that whole thing once per platform and architecture to produce the matrix of wheels.

So the division of labour in a compiled Python project today is three tools, and the 1.0 release is a reorganisation inside the middle one rather than a change to the arrangement.

That framing also explains why the setuptools coupling has become the problem rather than the solution. The original design glued CMake to setuptools because setuptools was the only packaging front end available for compiled extensions at the time. It still works, and projects built around it continue to build. But the packaging ecosystem has since standardised on pyproject.toml with a declared build backend, and a project that can declare its backend and its CMake settings directly no longer needs a compatibility layer in the middle. scikit-build 1.0 is that recognition written down.

The practical consequence for a team evaluating the stack is that the decision is not about CMake, which you were using either way, but about how much of the old packaging world you want to keep. Each wheel you publish for each platform and each Python version is a build that has to work, and the front end you choose determines how many things can go wrong in it.

Where scikit-build is the wrong tool now

Three limits, and the first is the one the README states itself.

The first is that for a new project this is the wrong package. scikit-build exists to be glue between setuptools and CMake, and the project's own recommendation is to use scikit-build-core directly if you do not need setuptools. Adopting scikit-build classic in 2026 means adopting a wrapper around the tool you were told to use directly, which adds a dependency layer, a CMake module entry point that the pyproject still declares, and the legacy helper modules you were told have modern equivalents.

There is a related trap for anyone who reads older documentation. The setup.py commands that the old docs teach, the test command and easy_install among them, are removed in 1.0. A tutorial written before 2026 will send you to a setup.py that no longer behaves, and the failure will look like a packaging problem rather than a removed feature.

The second is the setuptools coupling itself, and it is a larger problem than it looks. Because setup.py, MANIFEST.in and setup.cfg have no effect without setuptools under the native backend, a project that currently relies on MANIFEST.in to ship data files has to convert that to the tool.scikit-build configuration. Data files in a package, which is what MANIFEST.in is for, are exactly the thing that breaks when a project moves to a native backend, and the README does not discuss the conversion.

The third is the environment variable behaviour, which I mentioned above and which deserves repeating because it fails quietly. SKBUILD_CONFIGURE_OPTIONS and SKBUILD_BUILD_OPTIONS are not adapted without the skbuild.setup() wrapper. Those variables are how a lot of existing projects pass flags like the library search path or the build type into CMake, and they are set in CI configuration rather than in the repository, so nobody notices they stopped working until a build links against the wrong library.

The alternative approach is scikit-build-core, which is where all of this is going, and the difference in approach is that it drops setuptools entirely rather than making it optional. The cost of that is the plugin ecosystem, since a project that combined scikit-build with another setuptools or hatchling plugin has to find another way to combine them, and the cost of not moving is that you are on a wrapper whose whole purpose is to be retired.

One more thing to check before choosing either: the minimum CMake. The README says older versions, before 3.15, are no longer supported, which in practice means a distribution's system CMake may be too old, and you will be installing a newer one yourself.

Editorial conclusion

Adopt scikit-build if you already have a setuptools-based project that builds C, C++, Fortran or Cython extensions through CMake and you want the 1.0 backend without rewriting your packaging, because the upgrade path is a build-system table change and the legacy helper modules still work. Do not adopt scikit-build for a new project, because the README says plainly that if you do not require setuptools you should use scikit-build-core directly, and that the native backend is recommended unless you specifically need a setuptools or hatchling plugin. Verify first that your CMake is 3.15 or newer, since older versions are no longer supported, that your build uses the standards-based tools such as pip, uv, build or cibuildwheel rather than setup.py commands, that your chosen backend is declared in pyproject.toml with a project table, and that the environment variable behaviour you relied on, such as SKBUILD_CONFIGURE_OPTIONS and SKBUILD_BUILD_OPTIONS, still applies if you keep the setup() wrapper, because those are not adapted without it.

Frequently asked questions

What is scikit-build and what does it do?

It is a Python build system for CPython C, C++, Fortran and Cython extensions using CMake, and it is fundamentally just glue between the setuptools Python module and CMake. Since version 1.0 it is a thin wrapper around scikit-build-core's setuptools plugin, adding only the legacy CMake helper modules and the compatibility setup() wrapper.

Should I use scikit-build or scikit-build-core?

The README says if you do not require setuptools you should consider using scikit-build-core directly, and that the native backend is recommended unless you specifically need a setuptools or hatchling plugin. scikit-build 1.0 is the path for projects that already rely on setuptools.

How do I migrate a project to scikit-build 1.0?

Replace the build-system table with one requiring scikit-build>=1 or scikit-build-core[setuptools]>=1 and the scikit_build_core.setuptools.build_meta backend, then keep setup.py and either pass cmake_source_dir to setup() or set cmake.source-dir under [tool.scikit-build]. Code should build with standards-based tools like pip, uv, build or cibuildwheel as long as you are not using scikit-build internals.

What was removed in scikit-build 1.0?

All deprecated setuptools and distutils features are removed, like the test command and easy_install, and the recommendation is to use standards-based development such as uv, pip and build instead. Older versions of CMake, before 3.15, are also no longer supported.

Which environment variables still work in scikit-build 1.0?

SKBUILD_CONFIGURE_OPTIONS and SKBUILD_BUILD_OPTIONS are not adapted without the skbuild.setup() wrapper, whose full name is scikit_build_core.setuptools.wrapper.setup(). The defaults for the native plugin differ from those of the wrapper, so projects that set those variables have to move them into the tool.scikit-build configuration.

Where does the scikit-build documentation live?

The full reference is at scikit-build.readthedocs.io, with the getting started example on the usage page and a separate repository of scikit-build-sample-projects holding more examples. The project also points at scikit-build-core's own documentation and hosts discussions in the scikit-build GitHub organisation.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. scikit-build/scikit-build on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/scikit-build-scikit-build.svg)](https://hysenlabs.com/projects/scikit-build-scikit-build)

Was this useful?