Skip to content

Commit af231a6

Browse files
authored
chore: use scikit-build-core for the build (#5598)
* chore: use scikit-build-core for the build Signed-off-by: Henry Schreiner <[email protected]> * fix: support tests job Signed-off-by: Henry Schreiner <[email protected]> * refactor: use tomlkit instead of manual parsing Signed-off-by: Henry Schreiner <[email protected]> * tests: add tests for output Signed-off-by: Henry Schreiner <[email protected]> * chore: remove more unused files Signed-off-by: Henry Schreiner <[email protected]> * fix: restore global pin Signed-off-by: Henry Schreiner <[email protected]> * fix: test and fix pinning Signed-off-by: Henry Schreiner <[email protected]> --------- Signed-off-by: Henry Schreiner <[email protected]>
1 parent 6aa3b33 commit af231a6

17 files changed

+370
-468
lines changed

.github/workflows/format.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ jobs:
3232
- name: Add matchers
3333
run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
3434
- uses: pre-commit/[email protected]
35-
with:
36-
# Slow hooks are marked with manual - slow is okay here, run them too
37-
extra_args: --hook-stage manual --all-files
3835

3936
clang-tidy:
4037
# When making changes here, please also review the "Clang-Tidy" section

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,9 @@ pybind11Targets.cmake
4545
.ipynb_checkpoints/
4646
tests/main.cpp
4747
CMakeUserPresents.json
48+
49+
/Python
50+
/tmp*
51+
.ruby-version
52+
.*cache*/
53+
*.lock

.pre-commit-config.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,6 @@ repos:
108108
- id: rst-directive-colons
109109
- id: rst-inline-touching-normal
110110

111-
# Checks the manifest for missing files (native support)
112-
- repo: https://github.com/mgedmin/check-manifest
113-
rev: "0.50"
114-
hooks:
115-
- id: check-manifest
116-
# This is a slow hook, so only run this if --hook-stage manual is passed
117-
stages: [manual]
118-
additional_dependencies: [cmake, ninja]
119-
120111
# Check for spelling
121112
# Use tools/codespell_ignore_lines_from_errors.py
122113
# to rebuild .codespell-ignore-lines

CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
6565

6666
message(STATUS "CMake ${CMAKE_VERSION}")
6767

68+
if(DEFINED SKBUILD AND DEFINED $ENV{PYBIND11_GLOBAL_PREFIX})
69+
message(
70+
FATAL_ERROR
71+
"PYBIND11_GLOBAL_PREFIX is not supported, use nox -s build_global or a pybind11-global SDist instead."
72+
)
73+
endif()
74+
6875
if(CMAKE_CXX_STANDARD)
6976
set(CMAKE_CXX_EXTENSIONS OFF)
7077
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -295,6 +302,9 @@ elseif(USE_PYTHON_INCLUDE_DIR AND DEFINED PYTHON_INCLUDE_DIR)
295302
endif()
296303

297304
if(PYBIND11_INSTALL)
305+
if(DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME STREQUAL "pybind11_global")
306+
install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION "${SKBUILD_HEADERS_DIR}")
307+
endif()
298308
install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
299309
set(PYBIND11_CMAKECONFIG_INSTALL_DIR
300310
"${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}"
@@ -361,6 +371,17 @@ if(PYBIND11_INSTALL)
361371
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc"
362372
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
363373

374+
# When building a wheel, include __init__.py's for modules
375+
# (see https://github.com/pybind/pybind11/pull/5552)
376+
if(DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME STREQUAL "pybind11")
377+
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/empty")
378+
file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py")
379+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py"
380+
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/")
381+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py"
382+
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
383+
endif()
384+
364385
# Uninstall target
365386
if(PYBIND11_MASTER_PROJECT)
366387
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in"

MANIFEST.in

Lines changed: 0 additions & 6 deletions
This file was deleted.

docs/conf.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,10 @@
6969
# built documents.
7070

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

7778
# The full version, including alpha/beta/rc tags.

noxfile.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
from __future__ import annotations
88

99
import argparse
10+
import contextlib
11+
import os
12+
from pathlib import Path
13+
from typing import TYPE_CHECKING
14+
15+
if TYPE_CHECKING:
16+
from collections.abc import Generator
1017

1118
import nox
1219

@@ -99,13 +106,46 @@ def make_changelog(session: nox.Session) -> None:
99106
@nox.session(reuse_venv=True, default=False)
100107
def build(session: nox.Session) -> None:
101108
"""
102-
Build SDists and wheels.
109+
Build SDist and wheel.
103110
"""
104111

105112
session.install("build")
106113
session.log("Building normal files")
107114
session.run("python", "-m", "build", *session.posargs)
108-
session.log("Building pybind11-global files (PYBIND11_GLOBAL_SDIST=1)")
109-
session.run(
110-
"python", "-m", "build", *session.posargs, env={"PYBIND11_GLOBAL_SDIST": "1"}
111-
)
115+
116+
117+
@contextlib.contextmanager
118+
def preserve_file(filename: Path) -> Generator[str, None, None]:
119+
"""
120+
Causes a file to be stored and preserved when the context manager exits.
121+
"""
122+
old_stat = filename.stat()
123+
old_file = filename.read_text(encoding="utf-8")
124+
try:
125+
yield old_file
126+
finally:
127+
filename.write_text(old_file, encoding="utf-8")
128+
os.utime(filename, (old_stat.st_atime, old_stat.st_mtime))
129+
130+
131+
@nox.session(reuse_venv=True)
132+
def build_global(session: nox.Session) -> None:
133+
"""
134+
Build global SDist and wheel.
135+
"""
136+
137+
installer = ["--installer=uv"] if session.venv_backend == "uv" else []
138+
session.install("build", "tomlkit")
139+
session.log("Building pybind11-global files")
140+
pyproject = Path("pyproject.toml")
141+
with preserve_file(pyproject):
142+
newer_txt = session.run("python", "tools/make_global.py", silent=True)
143+
assert isinstance(newer_txt, str)
144+
pyproject.write_text(newer_txt, encoding="utf-8")
145+
session.run(
146+
"python",
147+
"-m",
148+
"build",
149+
*installer,
150+
*session.posargs,
151+
)

pybind11/_version.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
1+
# This file will be replaced in the wheel with a hard-coded version. This only
2+
# exists to allow running directly from source without installing (not
3+
# recommended, but supported).
4+
15
from __future__ import annotations
26

7+
import re
8+
from pathlib import Path
9+
10+
DIR = Path(__file__).parent.resolve()
11+
12+
input_file = DIR.parent / "include/pybind11/detail/common.h"
13+
regex = re.compile(
14+
r"""
15+
\#define \s+ PYBIND11_VERSION_MAJOR \s+ (?P<major>\d+) .*?
16+
\#define \s+ PYBIND11_VERSION_MINOR \s+ (?P<minor>\d+) .*?
17+
\#define \s+ PYBIND11_VERSION_PATCH \s+ (?P<patch>\S+)
18+
""",
19+
re.MULTILINE | re.DOTALL | re.VERBOSE,
20+
)
21+
22+
match = regex.search(input_file.read_text(encoding="utf-8"))
23+
assert match, "Unable to find version in pybind11/detail/common.h"
24+
__version__ = "{major}.{minor}.{patch}".format(**match.groupdict())
25+
326

427
def _to_int(s: str) -> int | str:
528
try:
@@ -8,5 +31,4 @@ def _to_int(s: str) -> int | str:
831
return s
932

1033

11-
__version__ = "3.0.0.dev1"
1234
version_info = tuple(_to_int(s) for s in __version__.split("."))

pyproject.toml

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,118 @@
11
[build-system]
2-
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
3-
build-backend = "setuptools.build_meta"
2+
requires = ["scikit-build-core >=0.11.2"]
3+
build-backend = "scikit_build_core.build"
44

5+
[project]
6+
name = "pybind11"
7+
description = "Seamless operability between C++11 and Python"
8+
authors = [{name = "Wenzel Jakob", email = "[email protected]"}]
9+
license = "BSD-3-Clause"
10+
license-files = ["LICENSE"]
11+
readme = "README.rst"
12+
classifiers = [
13+
"Development Status :: 5 - Production/Stable",
14+
"Intended Audience :: Developers",
15+
"Topic :: Software Development :: Libraries :: Python Modules",
16+
"Topic :: Utilities",
17+
"Programming Language :: C++",
18+
"Programming Language :: Python :: 3 :: Only",
19+
"Programming Language :: Python :: 3.8",
20+
"Programming Language :: Python :: 3.9",
21+
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
24+
"Programming Language :: Python :: 3.13",
25+
"Programming Language :: Python :: Implementation :: PyPy",
26+
"Programming Language :: Python :: Implementation :: CPython",
27+
"Programming Language :: C++",
28+
"Topic :: Software Development :: Libraries :: Python Modules",
29+
]
30+
keywords = [
31+
"C++11",
32+
"Python bindings",
33+
]
34+
dynamic = ["version", "optional-dependencies"]
35+
requires-python = ">=3.8"
536

6-
[tool.check-manifest]
7-
ignore = [
8-
"tests/**",
9-
"docs/**",
10-
"tools/**",
11-
"include/**",
12-
".*",
13-
"pybind11/include/**",
14-
"pybind11/share/**",
15-
"CMakeLists.txt",
16-
"CMakePresets.json",
17-
"noxfile.py",
37+
[project.urls]
38+
Homepage = "https://github.com/pybind/pybind11"
39+
Documentation = "https://pybind11.readthedocs.io/"
40+
"Bug Tracker" = "https://github.com/pybind/pybind11/issues"
41+
Discussions = "https://github.com/pybind/pybind11/discussions"
42+
Changelog = "https://pybind11.readthedocs.io/en/latest/changelog.html"
43+
Chat = "https://gitter.im/pybind/Lobby"
44+
45+
[project.scripts]
46+
pybind11-config = "pybind11.__main__:main"
47+
48+
[project.entry-points."pipx.run"]
49+
pybind11 = "pybind11.__main__:main"
50+
51+
[project.entry-points.pkg_config]
52+
pybind11 = "pybind11.share.pkgconfig"
53+
54+
55+
[dependency-groups]
56+
test = [
57+
"pytest",
58+
"build",
59+
"tomlkit",
1860
]
61+
dev = [{ include-group = "test" }]
62+
63+
64+
[tool.scikit-build]
65+
minimum-version = "build-system.requires"
66+
sdist.exclude = [
67+
"/docs/**",
68+
"/.**",
69+
]
70+
wheel.install-dir = "pybind11"
71+
wheel.platlib = false
72+
73+
[tool.scikit-build.cmake.define]
74+
BUILD_TESTING = false
75+
PYBIND11_NOPYTHON = true
76+
prefix_for_pc_file = "${pcfiledir}/../../"
77+
78+
[tool.scikit-build.metadata.version]
79+
provider = "scikit_build_core.metadata.regex"
80+
input = "include/pybind11/detail/common.h"
81+
regex = '''(?sx)
82+
\#define \s+ PYBIND11_VERSION_MAJOR \s+ (?P<major>\d+) .*?
83+
\#define \s+ PYBIND11_VERSION_MINOR \s+ (?P<minor>\d+) .*?
84+
\#define \s+ PYBIND11_VERSION_PATCH \s+ (?P<patch>\S+)
85+
'''
86+
result = "{major}.{minor}.{patch}"
87+
88+
[tool.scikit-build.metadata.optional-dependencies]
89+
provider = "scikit_build_core.metadata.template"
90+
result = { global = ["pybind11-global=={project[version]}"]}
91+
92+
[[tool.scikit-build.generate]]
93+
path = "pybind11/_version.py"
94+
template = '''
95+
from __future__ import annotations
96+
97+
98+
def _to_int(s: str) -> int | str:
99+
try:
100+
return int(s)
101+
except ValueError:
102+
return s
103+
104+
105+
__version__ = "$version"
106+
version_info = tuple(_to_int(s) for s in __version__.split("."))
107+
'''
108+
19109

20-
# Can't use tool.uv.sources with requirements.txt
21110
[tool.uv]
111+
# Can't use tool.uv.sources with requirements.txt
22112
index-strategy = "unsafe-best-match"
113+
# This extra confuses uv
114+
override-dependencies = ["pybind11-global"]
115+
23116

24117
[tool.mypy]
25118
files = ["pybind11"]
@@ -29,7 +122,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
29122
warn_unreachable = true
30123

31124
[[tool.mypy.overrides]]
32-
module = ["ghapi.*"]
125+
module = ["ghapi.*", "tomlkit"] # tomlkit has types, but not very helpful
33126
ignore_missing_imports = true
34127

35128

@@ -46,10 +139,10 @@ messages_control.disable = [
46139
"protected-access",
47140
"missing-module-docstring",
48141
"unused-argument", # covered by Ruff ARG
142+
"consider-using-f-string", # triggers in _version.py incorrectly
49143
]
50144

51145
[tool.ruff]
52-
target-version = "py38"
53146
src = ["src"]
54147

55148
[tool.ruff.lint]

setup.cfg

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)