# scikit-build-core: CMake Build Backend for Python

scikit-build-core is a [PEP 517](https://pydevtools.com/handbook/explanation/what-is-pep-517.md) [build backend](https://pydevtools.com/handbook/explanation/what-is-a-build-backend.md) for Python packages with compiled extensions. It drives CMake to build C, C++, and Fortran modules and reads its configuration statically from the `[tool.scikit-build]` table in [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md). It is a ground-up rewrite of the classic scikit-build, which wrapped [setuptools](https://pydevtools.com/handbook/reference/setuptools.md).

> [!NOTE]
> **Latest version:** 1.0.0 (July 2026). **License:** Apache-2.0. **Backend string:** `scikit_build_core.build`. **Requires:** Python >=3.8, plus CMake and a C/C++/Fortran toolchain at build time. Maintained under the [scikit-build](https://github.com/scikit-build/scikit-build-core) GitHub organization.

## When to use scikit-build-core

scikit-build-core fits packages whose build is already expressed in CMake, or whose C, C++, or Fortran sources are too involved for setuptools to compile cleanly. It integrates with the binding libraries these projects rely on: pybind11, nanobind, Cython, SWIG, and Fortran via f2py-cmake.

For Rust extensions, use maturin. For pure-Python packages, use a general backend such as [hatchling](https://pydevtools.com/handbook/reference/hatch.md), [flit_core](https://pydevtools.com/handbook/reference/flit.md), or uv_build. meson-python is a comparable backend for compiled projects that builds with Meson rather than CMake.

## Key Features

- **CMake-driven builds:** invokes a project's `CMakeLists.txt` to compile C, C++, and Fortran extension modules, fetching CMake (and Ninja) as build requirements when the host lacks them.
- **Binding-library support:** works with pybind11, nanobind, Cython, SWIG, and Fortran (f2py-cmake) for wrapping native code.
- **Static configuration:** all backend options live under `[tool.scikit-build]` in `pyproject.toml`; no imperative build script is required.
- **Editable installs:** implements [PEP 660](https://pydevtools.com/handbook/explanation/what-is-pep-660.md) editable installs, stabilized in 1.0 with `importlib.resources.files()` support, namespace packages, versioned shared-object names, and a manual `__loader__.rebuild()` for recompiling after source edits.
- **Dynamic metadata:** the `[[tool.dynamic-metadata]]` system extracts fields such as the version from external sources at build time, replacing the deprecated `[tool.scikit-build.metadata]` table.
- **Reproducible wheels:** normalizes timestamps with `SOURCE_DATE_EPOCH` to produce bit-for-bit identical builds.
- **Free-threaded stable ABI:** emits `abi3t` and combined `abi3.abi3t` wheel tags for [free-threaded](https://pydevtools.com/handbook/explanation/what-is-pep-703.md) builds against the stable ABI.

## Configuration

A minimal project declares the backend in `[build-system]` and its options under `[tool.scikit-build]`:

```toml {filename="pyproject.toml"}
[build-system]
requires = ["scikit-build-core"]
build-backend = "scikit_build_core.build"

[project]
name = "example"
version = "0.1.0"
requires-python = ">=3.8"

[tool.scikit-build]
minimum-version = "build-system.requires"  # pin behavior to the declared version
cmake.build-type = "Release"                # CMake build configuration
```

A `CMakeLists.txt` alongside `pyproject.toml` defines the actual build. Other commonly used tables include `env` for environment-variable defaults with platform overrides, `force-include` for mapping arbitrary files into the sdist or wheel, and `wheel.reproducible` for deterministic output.

## Scaffolding a project

`scikit-build-core init` generates a starter project for a chosen binding backend:

```bash
uvx scikit-build-core init --backend pybind11 example
```

Templates ship for pybind11, nanobind, C, Cython, SWIG, Fortran, `abi3`, and `abi3t`.

## Plugins for other backends

scikit-build-core provides plugins that add CMake builds to two other backends: a hatchling plugin (stable as of 1.0) and a setuptools plugin. Both let a project keep its existing backend while delegating the compiled portion to CMake.

## Pros

- Native CMake integration reuses an existing `CMakeLists.txt` without a setuptools shim.
- Static `pyproject.toml` configuration keeps build metadata declarative.
- First-class support for pybind11, nanobind, Cython, SWIG, and Fortran bindings.
- Editable installs, reproducible wheels, and free-threaded stable-ABI tags are built in.

## Cons

- Requires CMake and a working C/C++/Fortran toolchain at build time.
- Overkill for pure-Python packages, where a general backend is simpler.
- Does not build Rust extensions; maturin covers that case.
- Producing wheels for many platforms still needs a separate tool such as [cibuildwheel](https://pydevtools.com/handbook/reference/cibuildwheel.md).

## Learn More

- [scikit-build-core documentation](https://scikit-build-core.readthedocs.io/)
- [GitHub repository](https://github.com/scikit-build/scikit-build-core)
- [scikit-build-core 1.0 release notes](https://iscinumpy.dev/post/scikit-build-core-1-0/)
- [Build a Python library with a C++ extension](https://pydevtools.com/handbook/tutorial/build-a-python-library-with-a-cpp-extension.md): a tutorial using scikit-build-core with pybind11
- [What is a build backend?](https://pydevtools.com/handbook/explanation/what-is-a-build-backend.md)
- [What is PEP 517?](https://pydevtools.com/handbook/explanation/what-is-pep-517.md)
- [What is PEP 660?](https://pydevtools.com/handbook/explanation/what-is-pep-660.md)
- [cibuildwheel](https://pydevtools.com/handbook/reference/cibuildwheel.md): for building wheels across platforms
