Skip to content

chore: use scikit-build-core for the build #5598

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ jobs:
- name: Add matchers
run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
- uses: pre-commit/[email protected]
with:
# Slow hooks are marked with manual - slow is okay here, run them too
extra_args: --hook-stage manual --all-files

clang-tidy:
# When making changes here, please also review the "Clang-Tidy" section
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ pybind11Targets.cmake
.ipynb_checkpoints/
tests/main.cpp
CMakeUserPresents.json

/Python
/tmp*
.ruby-version
.*cache*/
9 changes: 0 additions & 9 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,6 @@ repos:
- id: rst-directive-colons
- id: rst-inline-touching-normal

# Checks the manifest for missing files (native support)
- repo: https://github.com/mgedmin/check-manifest
rev: "0.50"
hooks:
- id: check-manifest
# This is a slow hook, so only run this if --hook-stage manual is passed
stages: [manual]
additional_dependencies: [cmake, ninja]

# Check for spelling
# Use tools/codespell_ignore_lines_from_errors.py
# to rebuild .codespell-ignore-lines
Expand Down
21 changes: 21 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)

message(STATUS "CMake ${CMAKE_VERSION}")

if(DEFINED SKBUILD AND DEFINED $ENV{PYBIND11_GLOBAL_PREFIX})
message(
FATAL_ERROR
"PYBIND11_GLOBAL_PREFIX is not supported, use nox -s build_global or a pybind11-global SDist instead."
)
endif()

if(CMAKE_CXX_STANDARD)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down Expand Up @@ -248,6 +255,9 @@ elseif(USE_PYTHON_INCLUDE_DIR AND DEFINED PYTHON_INCLUDE_DIR)
endif()

if(PYBIND11_INSTALL)
if(DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME STREQUAL "pybind11_global")
install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION "${SKBUILD_HEADERS_DIR}")
endif()
install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
set(PYBIND11_CMAKECONFIG_INSTALL_DIR
"${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}"
Expand Down Expand Up @@ -314,6 +324,17 @@ if(PYBIND11_INSTALL)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")

# When building a wheel, include __init__.py's for modules
# (see https://github.com/pybind/pybind11/pull/5552)
if(DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME STREQUAL "pybind11")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/empty")
file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
endif()

# Uninstall target
if(PYBIND11_MASTER_PROJECT)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in"
Expand Down
6 changes: 0 additions & 6 deletions MANIFEST.in

This file was deleted.

7 changes: 4 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@
# built documents.

# Read the listed version
with open("../pybind11/_version.py") as f:
code = compile(f.read(), "../pybind11/_version.py", "exec")
loc = {}
version_file = DIR.parent / "pybind11/_version.py"
with version_file.open(encoding="utf-8") as f:
code = compile(f.read(), version_file, "exec")
loc = {"__file__": str(version_file)}
exec(code, loc)

# The full version, including alpha/beta/rc tags.
Expand Down
50 changes: 45 additions & 5 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
from __future__ import annotations

import argparse
import contextlib
import os
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from collections.abc import Generator

import nox

Expand Down Expand Up @@ -99,13 +106,46 @@ def make_changelog(session: nox.Session) -> None:
@nox.session(reuse_venv=True, default=False)
def build(session: nox.Session) -> None:
"""
Build SDists and wheels.
Build SDist and wheel.
"""

session.install("build")
session.log("Building normal files")
session.run("python", "-m", "build", *session.posargs)
session.log("Building pybind11-global files (PYBIND11_GLOBAL_SDIST=1)")
session.run(
"python", "-m", "build", *session.posargs, env={"PYBIND11_GLOBAL_SDIST": "1"}
)


@contextlib.contextmanager
def preserve_file(filename: Path) -> Generator[str, None, None]:
"""
Causes a file to be stored and preserved when the context manager exits.
"""
old_stat = filename.stat()
old_file = filename.read_text(encoding="utf-8")
try:
yield old_file
finally:
filename.write_text(old_file, encoding="utf-8")
os.utime(filename, (old_stat.st_atime, old_stat.st_mtime))


@nox.session(reuse_venv=True)
def build_global(session: nox.Session) -> None:
"""
Build global SDist and wheel.
"""

installer = ["--installer=uv"] if session.venv_backend == "uv" else []
session.install("build", "tomlkit")
session.log("Building pybind11-global files")
pyproject = Path("pyproject.toml")
with preserve_file(pyproject):
newer_txt = session.run("python", "tools/make_global.py", silent=True)
assert isinstance(newer_txt, str)
pyproject.write_text(newer_txt, encoding="utf-8")
session.run(
"python",
"-m",
"build",
*installer,
*session.posargs,
)
24 changes: 23 additions & 1 deletion pybind11/_version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# This file will be replaced in the wheel with a hard-coded version. This only
# exists to allow running directly from source without installing (not
# recommended, but supported).

from __future__ import annotations

import re
from pathlib import Path

DIR = Path(__file__).parent.resolve()

input_file = DIR.parent / "include/pybind11/detail/common.h"
regex = re.compile(
r"""
\#define \s+ PYBIND11_VERSION_MAJOR \s+ (?P<major>\d+) .*?
\#define \s+ PYBIND11_VERSION_MINOR \s+ (?P<minor>\d+) .*?
\#define \s+ PYBIND11_VERSION_PATCH \s+ (?P<patch>\S+)
""",
re.MULTILINE | re.DOTALL | re.VERBOSE,
)

match = regex.search(input_file.read_text(encoding="utf-8"))
assert match, "Unable to find version in pybind11/detail/common.h"
__version__ = "{major}.{minor}.{patch}".format(**match.groupdict())


def _to_int(s: str) -> int | str:
try:
Expand All @@ -8,5 +31,4 @@ def _to_int(s: str) -> int | str:
return s


__version__ = "3.0.0.dev1"
version_info = tuple(_to_int(s) for s in __version__.split("."))
111 changes: 96 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,106 @@
[build-system]
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
build-backend = "setuptools.build_meta"
requires = ["scikit-build-core >=0.11.0"]
build-backend = "scikit_build_core.build"

[project]
name = "pybind11"
description = "Seamless operability between C++11 and Python"
authors = [{name = "Wenzel Jakob", email = "[email protected]"}]
license = "BSD-3-Clause"
license-files = ["LICENSE"]
readme = "README.rst"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Utilities",
"Programming Language :: C++",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: C++",
"Topic :: Software Development :: Libraries :: Python Modules",
]
keywords = [
"C++11",
"Python bindings",
]
dynamic = ["version"]
requires-python = ">=3.8"

[tool.check-manifest]
ignore = [
"tests/**",
"docs/**",
"tools/**",
"include/**",
".*",
"pybind11/include/**",
"pybind11/share/**",
"CMakeLists.txt",
"noxfile.py",
[project.urls]
Homepage = "https://github.com/pybind/pybind11"
Documentation = "https://pybind11.readthedocs.io/"
"Bug Tracker" = "https://github.com/pybind/pybind11/issues"
Discussions = "https://github.com/pybind/pybind11/discussions"
Changelog = "https://pybind11.readthedocs.io/en/latest/changelog.html"
Chat = "https://gitter.im/pybind/Lobby"

[project.optional-dependencies]
global = ["pybind11-global"] # TODO: pin

[project.scripts]
pybind11-config = "pybind11.__main__:main"

[project.entry-points."pipx.run"]
pybind11 = "pybind11.__main__:main"

[project.entry-points.pkg_config]
pybind11 = "pybind11.share.pkgconfig"


[tool.scikit-build]
minimum-version = "build-system.requires"
sdist.exclude = [
"/docs/**",
"/.**",
]
wheel.install-dir = "pybind11"
wheel.platlib = false

[tool.scikit-build.cmake.define]
BUILD_TESTING = false
PYBIND11_NOPYTHON = true
prefix_for_pc_file = "${pcfiledir}/../../"

[tool.scikit-build.metadata.version]
provider = "scikit_build_core.metadata.regex"
input = "include/pybind11/detail/common.h"
regex = '''(?sx)
\#define \s+ PYBIND11_VERSION_MAJOR \s+ (?P<major>\d+) .*?
\#define \s+ PYBIND11_VERSION_MINOR \s+ (?P<minor>\d+) .*?
\#define \s+ PYBIND11_VERSION_PATCH \s+ (?P<patch>\S+)
'''
result = "{major}.{minor}.{patch}"

# Can't use tool.uv.sources with requirements.txt
[tool.uv]
index-strategy = "unsafe-best-match"

[[tool.scikit-build.generate]]
path = "pybind11/_version.py"
template = '''
from __future__ import annotations


def _to_int(s: str) -> int | str:
try:
return int(s)
except ValueError:
return s


__version__ = "$version"
version_info = tuple(_to_int(s) for s in __version__.split("."))
'''


[tool.mypy]
files = ["pybind11"]
python_version = "3.8"
Expand All @@ -28,7 +109,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unreachable = true

[[tool.mypy.overrides]]
module = ["ghapi.*"]
module = ["ghapi.*", "tomlkit"] # tomlkit has types, but not very helpful
ignore_missing_imports = true


Expand All @@ -45,10 +126,10 @@ messages_control.disable = [
"protected-access",
"missing-module-docstring",
"unused-argument", # covered by Ruff ARG
"consider-using-f-string", # triggers in _version.py incorrectly
]

[tool.ruff]
target-version = "py38"
src = ["src"]

[tool.ruff.lint]
Expand Down
42 changes: 0 additions & 42 deletions setup.cfg

This file was deleted.

Loading
Loading