From b4041e14a1bf117dbf38f0b75899ab04f56ca195 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 1 Apr 2025 22:43:31 -0400 Subject: [PATCH 1/6] chore: use scikit-build-core for the build Signed-off-by: Henry Schreiner --- .github/workflows/format.yml | 3 - .gitignore | 5 + .pre-commit-config.yaml | 9 -- CMakeLists.txt | 10 ++ MANIFEST.in | 6 - docs/conf.py | 7 +- noxfile.py | 57 ++++++++- pybind11/_version.py | 24 +++- pyproject.toml | 107 +++++++++++++--- setup.cfg | 42 ------- setup.py | 153 ----------------------- tests/extra_python_package/test_files.py | 4 +- 12 files changed, 187 insertions(+), 240 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index e50dc0bb72..3d6d1e39fd 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -32,9 +32,6 @@ jobs: - name: Add matchers run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" - uses: pre-commit/action@v3.0.1 - 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 diff --git a/.gitignore b/.gitignore index 4daf6119c3..0efef67295 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,8 @@ pybind11Targets.cmake .ipynb_checkpoints/ tests/main.cpp CMakeUserPresents.json + +/Python +/tmp* +.ruby-version +.*cache*/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 34354898b6..b45781b65e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index d570112dd1..a71963e950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -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}" diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 7ce83c5527..0000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -prune tests -recursive-include pybind11/include/pybind11 *.h -recursive-include pybind11 *.py -recursive-include pybind11 py.typed -include pybind11/share/cmake/pybind11/*.cmake -include LICENSE README.rst SECURITY.md pyproject.toml setup.py setup.cfg diff --git a/docs/conf.py b/docs/conf.py index e5cba03826..a8758200f4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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. diff --git a/noxfile.py b/noxfile.py index 773687fb0a..79d96f5e57 100644 --- a/noxfile.py +++ b/noxfile.py @@ -7,12 +7,24 @@ from __future__ import annotations import argparse +import contextlib +import os +import re +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Generator import nox nox.needs_version = ">=2025.2.9" nox.options.default_venv_backend = "uv|virtualenv" +MARKER_PATTERN = re.compile( + r"# not-in-global-start.*?# not-in-global-end\n?", re.DOTALL +) + @nox.session(reuse_venv=True) def lint(session: nox.Session) -> None: @@ -99,13 +111,48 @@ 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]: + 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") + session.log("Building pybind11-global files") + pyproject = Path("pyproject.toml") + with preserve_file(pyproject) as txt: + new_txt = txt.replace('name = "pybind11"', 'name = "pybind11-global"') + assert txt != new_txt + newer_txt = MARKER_PATTERN.sub("", new_txt) + assert new_txt != newer_txt + + pyproject.write_text(newer_txt, encoding="utf-8") + session.run( + "python", + "-m", + "build", + *installer, + "-Cskbuild.wheel.install-dir=/data", + "-Cskbuild.experimental=true", + *session.posargs, + ) diff --git a/pybind11/_version.py b/pybind11/_version.py index 2dfe67616d..d31cadb0a6 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -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\d+) .*? +\#define \s+ PYBIND11_VERSION_MINOR \s+ (?P\d+) .*? +\#define \s+ PYBIND11_VERSION_PATCH \s+ (?P\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: @@ -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(".")) diff --git a/pyproject.toml b/pyproject.toml index 38deff474a..b706fc5285 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,97 @@ [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" - -[tool.check-manifest] -ignore = [ - "tests/**", - "docs/**", - "tools/**", - "include/**", - ".*", - "pybind11/include/**", - "pybind11/share/**", - "CMakeLists.txt", - "noxfile.py", +[project] +name = "pybind11" +description = "Seamless operability between C++11 and Python" +authors = [{name = "Wenzel Jakob", email = "wenzel.jakob@epfl.ch"}] +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" + +[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" + +# not-in-global-start +[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.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(".")) +''' +# not-in-global-end + + +[tool.scikit-build] +wheel.install-dir = "pybind11" +wheel.platlib = false +minimum-version = "build-system.requires" + +[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\d+) .*? +\#define \s+ PYBIND11_VERSION_MINOR \s+ (?P\d+) .*? +\#define \s+ PYBIND11_VERSION_PATCH \s+ (?P\S+) +''' +result = "{major}.{minor}.{patch}" # Can't use tool.uv.sources with requirements.txt [tool.uv] @@ -45,10 +122,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] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index bb5b744aae..0000000000 --- a/setup.cfg +++ /dev/null @@ -1,42 +0,0 @@ -[metadata] -long_description = file: README.rst -long_description_content_type = text/x-rst -description = Seamless operability between C++11 and Python -author = Wenzel Jakob -author_email = wenzel.jakob@epfl.ch -url = https://github.com/pybind/pybind11 -license = BSD - -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 - License :: OSI Approved :: BSD License - Programming Language :: Python :: Implementation :: PyPy - Programming Language :: Python :: Implementation :: CPython - Programming Language :: C++ - Topic :: Software Development :: Libraries :: Python Modules - -keywords = - C++11 - Python bindings - -project_urls = - 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 - -[options] -python_requires = >=3.8 -zip_safe = False diff --git a/setup.py b/setup.py deleted file mode 100644 index a5af04e18e..0000000000 --- a/setup.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python3 - -# Setup script for PyPI; use CMakeFile.txt to build extension modules -from __future__ import annotations - -import contextlib -import os -import re -import shutil -import string -import subprocess -import sys -from collections.abc import Generator -from pathlib import Path -from tempfile import TemporaryDirectory - -import setuptools.command.sdist - -DIR = Path(__file__).parent.absolute() -VERSION_REGEX = re.compile( - r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE -) -VERSION_FILE = Path("pybind11/_version.py") -COMMON_FILE = Path("include/pybind11/detail/common.h") - - -def build_expected_version_hex(matches: dict[str, str]) -> str: - patch_level_serial = matches["PATCH"] - serial = None - major = int(matches["MAJOR"]) - minor = int(matches["MINOR"]) - flds = patch_level_serial.split(".") - if flds: - patch = int(flds[0]) - if len(flds) == 1: - level = "0" - serial = 0 - elif len(flds) == 2: - level_serial = flds[1] - for level in ("a", "b", "c", "dev"): - if level_serial.startswith(level): - serial = int(level_serial[len(level) :]) - break - if serial is None: - msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"' - raise RuntimeError(msg) - version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}" - return f"0x{version_hex_str.upper()}" - - -# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers -# files, and the sys.prefix files (CMake and headers). - -global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST") - -setup_py = Path( - "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in" -) -extra_cmd = 'cmdclass["sdist"] = SDist\n' - -to_src = ( - (Path("pyproject.toml"), Path("tools/pyproject.toml")), - (Path("setup.py"), setup_py), -) - - -# Read the listed version -loc: dict[str, str] = {} -code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec") -exec(code, loc) -version = loc["__version__"] - -# Verify that the version matches the one in C++ -matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8"))) -cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches) -if version != cpp_version: - msg = f"Python version {version} does not match C++ version {cpp_version}!" - raise RuntimeError(msg) - -version_hex = matches.get("HEX", "MISSING") -exp_version_hex = build_expected_version_hex(matches) -if version_hex != exp_version_hex: - msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!" - raise RuntimeError(msg) - - -# TODO: use literals & overload (typing extensions or Python 3.8) -def get_and_replace(filename: Path, binary: bool = False, **opts: str) -> bytes | str: - if binary: - contents = filename.read_bytes() - return string.Template(contents.decode()).substitute(opts).encode() - - return string.Template(filename.read_text()).substitute(opts) - - -# Use our input files instead when making the SDist (and anything that depends -# on it, like a wheel) -class SDist(setuptools.command.sdist.sdist): - def make_release_tree(self, base_dir: str, files: list[str]) -> None: - super().make_release_tree(base_dir, files) - - for to, src in to_src: - txt = get_and_replace(src, binary=True, version=version, extra_cmd="") - - dest = Path(base_dir) / to - - # This is normally linked, so unlink before writing! - dest.unlink() - dest.write_bytes(txt) # type: ignore[arg-type] - - -# Remove the CMake install directory when done -@contextlib.contextmanager -def remove_output(*sources: str) -> Generator[None, None, None]: - try: - yield - finally: - for src in sources: - shutil.rmtree(src) - - -with remove_output("pybind11/include", "pybind11/share"): - # Generate the files if they are not present. - with TemporaryDirectory() as tmpdir: - cmd = ["cmake", "-S", ".", "-B", tmpdir] + [ - "-DCMAKE_INSTALL_PREFIX=pybind11", - "-DBUILD_TESTING=OFF", - "-DPYBIND11_NOPYTHON=ON", - "-Dprefix_for_pc_file=${pcfiledir}/../../", - ] - if "CMAKE_ARGS" in os.environ: - fcommand = [ - c - for c in os.environ["CMAKE_ARGS"].split() - if "DCMAKE_INSTALL_PREFIX" not in c - ] - cmd += fcommand - subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) - subprocess.run( - ["cmake", "--install", tmpdir], - check=True, - cwd=DIR, - stdout=sys.stdout, - stderr=sys.stderr, - ) - - # pkgconf-pypi needs pybind11/share/pkgconfig to be importable - Path("pybind11/share/__init__.py").touch() - Path("pybind11/share/pkgconfig/__init__.py").touch() - - txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd) - code = compile(txt, setup_py, "exec") - exec(code, {"SDist": SDist}) diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 017b52f6a9..c626c4cbea 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -280,12 +280,11 @@ def tests_build_wheel(monkeypatch, tmpdir): files = {f"pybind11/{n}" for n in all_files} files |= { - "dist-info/LICENSE", + "dist-info/licenses/LICENSE", "dist-info/METADATA", "dist-info/RECORD", "dist-info/WHEEL", "dist-info/entry_points.txt", - "dist-info/top_level.txt", } with zipfile.ZipFile(str(wheel)) as z: @@ -313,7 +312,6 @@ def tests_build_global_wheel(monkeypatch, tmpdir): "dist-info/LICENSE", "dist-info/METADATA", "dist-info/WHEEL", - "dist-info/top_level.txt", "dist-info/RECORD", } From 3b963322aec13d99ebca5fa7ca8e4a595e8c6061 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 3 Apr 2025 17:00:03 -0400 Subject: [PATCH 2/6] fix: support tests job Signed-off-by: Henry Schreiner --- CMakeLists.txt | 7 + pyproject.toml | 11 +- tests/extra_python_package/test_files.py | 164 +++++++++-------------- tools/pyproject.toml | 3 - 4 files changed, 81 insertions(+), 104 deletions(-) delete mode 100644 tools/pyproject.toml diff --git a/CMakeLists.txt b/CMakeLists.txt index a71963e950..e1976ded00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,6 +324,13 @@ if(PYBIND11_INSTALL) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/") + 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" diff --git a/pyproject.toml b/pyproject.toml index b706fc5285..e1da149074 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,9 +74,18 @@ version_info = tuple(_to_int(s) for s in __version__.split(".")) [tool.scikit-build] +minimum-version = "build-system.requires" +sdist.exclude = [ + "/docs/**", + "/.**", +] wheel.install-dir = "pybind11" wheel.platlib = false -minimum-version = "build-system.requires" +wheel.packages = [ +# not-in-global-start + "pybind11", +# not-in-global-end +] [tool.scikit-build.cmake.define] BUILD_TESTING = false diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index c626c4cbea..b641aae9a4 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -4,15 +4,23 @@ import os import shutil import string +import re import subprocess import sys import tarfile import zipfile +from pathlib import Path +from typing import Generator +from pprint import pprint # These tests must be run explicitly -DIR = os.path.abspath(os.path.dirname(__file__)) -MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) +DIR = Path(__file__).parent.resolve() +MAIN_DIR = DIR.parent.parent + +MARKER_PATTERN = re.compile( + r"# not-in-global-start.*?# not-in-global-end\n?", re.DOTALL +) HAS_UV = shutil.which("uv") is not None UV_ARGS = ["--installer=uv"] if HAS_UV else [] @@ -120,44 +128,48 @@ } headers = main_headers | conduit_headers | detail_headers | eigen_headers | stl_headers -src_files = headers | cmake_files | pkgconfig_files -all_files = src_files | py_files - +generated_files = cmake_files | pkgconfig_files +all_files = headers | generated_files | py_files sdist_files = { - "pybind11", - "pybind11/include", - "pybind11/include/pybind11", - "pybind11/include/pybind11/conduit", - "pybind11/include/pybind11/detail", - "pybind11/include/pybind11/eigen", - "pybind11/include/pybind11/stl", - "pybind11/share", - "pybind11/share/cmake", - "pybind11/share/cmake/pybind11", - "pybind11/share/pkgconfig", "pyproject.toml", - "setup.cfg", - "setup.py", "LICENSE", - "MANIFEST.in", "README.rst", "PKG-INFO", "SECURITY.md", } -local_sdist_files = { - ".egg-info", - ".egg-info/PKG-INFO", - ".egg-info/SOURCES.txt", - ".egg-info/dependency_links.txt", - ".egg-info/not-zip-safe", - ".egg-info/top_level.txt", -} + +@contextlib.contextmanager +def preserve_file(filename: Path) -> Generator[str, None, None]: + 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)) + + +@contextlib.contextmanager +def build_global() -> Generator[None, None, None]: + """ + Build global SDist and wheel. + """ + + pyproject = MAIN_DIR / "pyproject.toml" + with preserve_file(pyproject) as txt: + new_txt = txt.replace('name = "pybind11"', 'name = "pybind11-global"') + assert txt != new_txt + newer_txt = MARKER_PATTERN.sub("", new_txt) + assert new_txt != newer_txt + + pyproject.write_text(newer_txt, encoding="utf-8") + yield def read_tz_file(tar: tarfile.TarFile, name: str) -> bytes: - start = tar.getnames()[0] + "/" + start = tar.getnames()[0].split("/")[0] + "/" inner_file = tar.extractfile(tar.getmember(f"{start}{name}")) assert inner_file with contextlib.closing(inner_file) as f: @@ -183,49 +195,22 @@ def test_build_sdist(monkeypatch, tmpdir): version = start[9:-1] simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} - setup_py = read_tz_file(tar, "setup.py") pyproject_toml = read_tz_file(tar, "pyproject.toml") - pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc") - cmake_cfg = read_tz_file( - tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake" - ) - - assert ( - 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' - in cmake_cfg.decode("utf-8") - ) - - files = {f"pybind11/{n}" for n in all_files} - files |= sdist_files - files |= {f"pybind11{n}" for n in local_sdist_files} - files.add("pybind11.egg-info/entry_points.txt") - files.add("pybind11.egg-info/requires.txt") - assert simpler == files - - with open(os.path.join(MAIN_DIR, "tools", "setup_main.py.in"), "rb") as f: - contents = ( - string.Template(f.read().decode("utf-8")) - .substitute(version=version, extra_cmd="") - .encode("utf-8") - ) - assert setup_py == contents - with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f: - contents = f.read() - assert pyproject_toml == contents + files = headers | sdist_files + assert files <= simpler simple_version = ".".join(version.split(".")[:3]) - pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8") - assert normalize_line_endings(pkgconfig) == pkgconfig_expected + assert b'name = "pybind11"' in pyproject_toml def test_build_global_dist(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) - monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1") - subprocess.run( - [sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir), *UV_ARGS], - check=True, - ) + with build_global(): + subprocess.run( + [sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir), *UV_ARGS], + check=True, + ) (sdist,) = tmpdir.visit("*.tar.gz") @@ -234,38 +219,12 @@ def test_build_global_dist(monkeypatch, tmpdir): version = start[16:-1] simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} - setup_py = read_tz_file(tar, "setup.py") pyproject_toml = read_tz_file(tar, "pyproject.toml") - pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc") - cmake_cfg = read_tz_file( - tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake" - ) - - assert ( - 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' - in cmake_cfg.decode("utf-8") - ) - - files = {f"pybind11/{n}" for n in all_files} - files |= sdist_files - files |= {f"pybind11_global{n}" for n in local_sdist_files} - assert simpler == files - - with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f: - contents = ( - string.Template(f.read().decode()) - .substitute(version=version, extra_cmd="") - .encode("utf-8") - ) - assert setup_py == contents - with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f: - contents = f.read() - assert pyproject_toml == contents + files = headers | sdist_files + assert files <= simpler - simple_version = ".".join(version.split(".")[:3]) - pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8") - assert normalize_line_endings(pkgconfig) == pkgconfig_expected + assert b'name = "pybind11-global"' in pyproject_toml def tests_build_wheel(monkeypatch, tmpdir): @@ -297,19 +256,23 @@ def tests_build_wheel(monkeypatch, tmpdir): def tests_build_global_wheel(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) - monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1") - - subprocess.run( - [sys.executable, "-m", "build", "--wheel", "--outdir", str(tmpdir), *UV_ARGS], - check=True, - ) + with build_global(): + subprocess.run( + [sys.executable, "-m", "pip", "wheel", ".", + "-Cskbuild.wheel.install-dir=/data", + "-Cskbuild.experimental=true", + "-w", + str(tmpdir), + *UV_ARGS], check=True + ) (wheel,) = tmpdir.visit("*.whl") - files = {f"data/data/{n}" for n in src_files} + files = {f"data/data/{n}" for n in headers} files |= {f"data/headers/{n[8:]}" for n in headers} + files |= {f"data/data/{n}" for n in generated_files} files |= { - "dist-info/LICENSE", + "dist-info/licenses/LICENSE", "dist-info/METADATA", "dist-info/WHEEL", "dist-info/RECORD", @@ -319,6 +282,7 @@ def tests_build_global_wheel(monkeypatch, tmpdir): names = z.namelist() beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0] + pprint(names) trimmed = {n[len(beginning) + 1 :] for n in names} assert files == trimmed diff --git a/tools/pyproject.toml b/tools/pyproject.toml deleted file mode 100644 index 8fe2f47af9..0000000000 --- a/tools/pyproject.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build-system] -requires = ["setuptools>=42", "wheel"] -build-backend = "setuptools.build_meta" From f7c8aad1fe1cb18115204ac9ce6056cace17d2f0 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 3 Apr 2025 17:34:43 -0400 Subject: [PATCH 3/6] refactor: use tomlkit instead of manual parsing Signed-off-by: Henry Schreiner --- CMakeLists.txt | 6 ++-- noxfile.py | 18 +++------- pyproject.toml | 45 +++++++++++------------- tests/extra_python_package/test_files.py | 43 ++++++++++------------ tests/requirements.txt | 1 + tools/make_global.py | 33 +++++++++++++++++ 6 files changed, 80 insertions(+), 66 deletions(-) create mode 100755 tools/make_global.py diff --git a/CMakeLists.txt b/CMakeLists.txt index e1976ded00..8f8ba071fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,8 +327,10 @@ if(PYBIND11_INSTALL) 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/") + 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 diff --git a/noxfile.py b/noxfile.py index 79d96f5e57..b94828c8a0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -9,7 +9,6 @@ import argparse import contextlib import os -import re from pathlib import Path from typing import TYPE_CHECKING @@ -21,10 +20,6 @@ nox.needs_version = ">=2025.2.9" nox.options.default_venv_backend = "uv|virtualenv" -MARKER_PATTERN = re.compile( - r"# not-in-global-start.*?# not-in-global-end\n?", re.DOTALL -) - @nox.session(reuse_venv=True) def lint(session: nox.Session) -> None: @@ -137,22 +132,17 @@ def build_global(session: nox.Session) -> None: """ installer = ["--installer=uv"] if session.venv_backend == "uv" else [] - session.install("build") + session.install("build", "tomlkit") session.log("Building pybind11-global files") pyproject = Path("pyproject.toml") - with preserve_file(pyproject) as txt: - new_txt = txt.replace('name = "pybind11"', 'name = "pybind11-global"') - assert txt != new_txt - newer_txt = MARKER_PATTERN.sub("", new_txt) - assert new_txt != newer_txt - + 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, - "-Cskbuild.wheel.install-dir=/data", - "-Cskbuild.experimental=true", *session.posargs, ) diff --git a/pyproject.toml b/pyproject.toml index e1da149074..2d9ba0ba45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,9 +42,9 @@ Discussions = "https://github.com/pybind/pybind11/discussions" Changelog = "https://pybind11.readthedocs.io/en/latest/changelog.html" Chat = "https://gitter.im/pybind/Lobby" -# not-in-global-start [project.optional-dependencies] global = ["pybind11-global"] # TODO: pin + [project.scripts] pybind11-config = "pybind11.__main__:main" @@ -54,24 +54,6 @@ pybind11 = "pybind11.__main__:main" [project.entry-points.pkg_config] pybind11 = "pybind11.share.pkgconfig" -[[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(".")) -''' -# not-in-global-end - [tool.scikit-build] minimum-version = "build-system.requires" @@ -81,11 +63,6 @@ sdist.exclude = [ ] wheel.install-dir = "pybind11" wheel.platlib = false -wheel.packages = [ -# not-in-global-start - "pybind11", -# not-in-global-end -] [tool.scikit-build.cmake.define] BUILD_TESTING = false @@ -106,6 +83,24 @@ result = "{major}.{minor}.{patch}" [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" @@ -114,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 diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index b641aae9a4..3fa104c7c5 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -3,24 +3,23 @@ import contextlib import os import shutil -import string -import re import subprocess import sys import tarfile import zipfile from pathlib import Path from typing import Generator -from pprint import pprint # These tests must be run explicitly DIR = Path(__file__).parent.resolve() MAIN_DIR = DIR.parent.parent -MARKER_PATTERN = re.compile( - r"# not-in-global-start.*?# not-in-global-end\n?", re.DOTALL -) + +# Newer pytest has global path setting, but keeping old pytest for now +sys.path.append(str(MAIN_DIR / "tools")) + +from make_global import get_global # noqa: E402 HAS_UV = shutil.which("uv") is not None UV_ARGS = ["--installer=uv"] if HAS_UV else [] @@ -158,12 +157,8 @@ def build_global() -> Generator[None, None, None]: """ pyproject = MAIN_DIR / "pyproject.toml" - with preserve_file(pyproject) as txt: - new_txt = txt.replace('name = "pybind11"', 'name = "pybind11-global"') - assert txt != new_txt - newer_txt = MARKER_PATTERN.sub("", new_txt) - assert new_txt != newer_txt - + with preserve_file(pyproject): + newer_txt = get_global() pyproject.write_text(newer_txt, encoding="utf-8") yield @@ -191,8 +186,6 @@ def test_build_sdist(monkeypatch, tmpdir): (sdist,) = tmpdir.visit("*.tar.gz") with tarfile.open(str(sdist), "r:gz") as tar: - start = tar.getnames()[0] + "/" - version = start[9:-1] simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} pyproject_toml = read_tz_file(tar, "pyproject.toml") @@ -200,7 +193,6 @@ def test_build_sdist(monkeypatch, tmpdir): files = headers | sdist_files assert files <= simpler - simple_version = ".".join(version.split(".")[:3]) assert b'name = "pybind11"' in pyproject_toml @@ -208,15 +200,21 @@ def test_build_global_dist(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) with build_global(): subprocess.run( - [sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir), *UV_ARGS], + [ + sys.executable, + "-m", + "build", + "--sdist", + "--outdir", + str(tmpdir), + *UV_ARGS, + ], check=True, ) (sdist,) = tmpdir.visit("*.tar.gz") with tarfile.open(str(sdist), "r:gz") as tar: - start = tar.getnames()[0] + "/" - version = start[16:-1] simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} pyproject_toml = read_tz_file(tar, "pyproject.toml") @@ -258,12 +256,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) with build_global(): subprocess.run( - [sys.executable, "-m", "pip", "wheel", ".", - "-Cskbuild.wheel.install-dir=/data", - "-Cskbuild.experimental=true", - "-w", - str(tmpdir), - *UV_ARGS], check=True + [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir), *UV_ARGS], + check=True, ) (wheel,) = tmpdir.visit("*.whl") @@ -282,7 +276,6 @@ def tests_build_global_wheel(monkeypatch, tmpdir): names = z.namelist() beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0] - pprint(names) trimmed = {n[len(beginning) + 1 :] for n in names} assert files == trimmed diff --git a/tests/requirements.txt b/tests/requirements.txt index 96c0cbcee8..6e3a260b19 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -15,3 +15,4 @@ scipy~=1.5.4; platform_python_implementation=="CPython" and python_version<"3.10 scipy~=1.8.0; platform_python_implementation=="CPython" and python_version=="3.10" and sys_platform!="win32" scipy~=1.11.1; platform_python_implementation=="CPython" and python_version>="3.11" and python_version<"3.13" and sys_platform!="win32" scipy~=1.15.2; platform_python_implementation=="CPython" and python_version=="3.13" and sys_platform!="win32" +tomlkit diff --git a/tools/make_global.py b/tools/make_global.py new file mode 100755 index 0000000000..5d12df4b2c --- /dev/null +++ b/tools/make_global.py @@ -0,0 +1,33 @@ +#!/usr/bin/env -S uv run -q + +# /// script +# dependencies = ["tomlkit"] +# /// +from __future__ import annotations + +from pathlib import Path + +import tomlkit + +DIR = Path(__file__).parent.resolve() +PYPROJECT = DIR.parent / "pyproject.toml" + + +def get_global() -> str: + pyproject = tomlkit.parse(PYPROJECT.read_text()) + del pyproject["tool"]["scikit-build"]["generate"] + del pyproject["project"]["optional-dependencies"] + del pyproject["project"]["entry-points"] + del pyproject["project"]["scripts"] + pyproject["project"]["name"] = "pybind11-global" + pyproject["tool"]["scikit-build"]["experimental"] = True + pyproject["tool"]["scikit-build"]["wheel"]["install-dir"] = "/data" + pyproject["tool"]["scikit-build"]["wheel"]["packages"] = [] + + result = tomlkit.dumps(pyproject) + assert isinstance(result, str) + return result + + +if __name__ == "__main__": + print(get_global()) From 809b1a7e149040f3551a0164ae2345b9232a91a3 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sat, 5 Apr 2025 00:09:01 -0400 Subject: [PATCH 4/6] tests: add tests for output Signed-off-by: Henry Schreiner --- CMakeLists.txt | 2 ++ noxfile.py | 3 ++ tests/extra_python_package/test_files.py | 38 ++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f8ba071fc..e824383b1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,6 +324,8 @@ 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") diff --git a/noxfile.py b/noxfile.py index b94828c8a0..778025229c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -116,6 +116,9 @@ def build(session: nox.Session) -> None: @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: diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 3fa104c7c5..6ecf26ac0d 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -246,17 +246,38 @@ def tests_build_wheel(monkeypatch, tmpdir): with zipfile.ZipFile(str(wheel)) as z: names = z.namelist() + share = zipfile.Path(z, "pybind11/share") + pkgconfig = (share / "pkgconfig/pybind11.pc").read_text(encoding="utf-8") + cmakeconfig = (share / "cmake/pybind11/pybind11Config.cmake").read_text( + encoding="utf-8" + ) trimmed = {n for n in names if "dist-info" not in n} trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n} + assert files == trimmed + assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in cmakeconfig + + version = wheel.basename.split("-")[1] + simple_version = ".".join(version.split(".")[:3]) + pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version) + assert pkgconfig_expected == pkgconfig + def tests_build_global_wheel(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) with build_global(): subprocess.run( - [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir), *UV_ARGS], + [ + sys.executable, + "-m", + "build", + "--wheel", + "--outdir", + str(tmpdir), + *UV_ARGS, + ], check=True, ) @@ -274,8 +295,21 @@ def tests_build_global_wheel(monkeypatch, tmpdir): with zipfile.ZipFile(str(wheel)) as z: names = z.namelist() + beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0] + + share = zipfile.Path(z, f"{beginning}.data/data/share") + pkgconfig = (share / "pkgconfig/pybind11.pc").read_text(encoding="utf-8") + cmakeconfig = (share / "cmake/pybind11/pybind11Config.cmake").read_text( + encoding="utf-8" + ) - beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0] trimmed = {n[len(beginning) + 1 :] for n in names} assert files == trimmed + + assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in cmakeconfig + + version = wheel.basename.split("-")[1] + simple_version = ".".join(version.split(".")[:3]) + pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version) + assert pkgconfig_expected == pkgconfig From 1f204a17de64e6393af5b922efaaf97124325fc2 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 17 Apr 2025 10:02:50 -0400 Subject: [PATCH 5/6] chore: remove more unused files Signed-off-by: Henry Schreiner --- tools/setup_global.py.in | 66 ---------------------------------------- tools/setup_main.py.in | 50 ------------------------------ 2 files changed, 116 deletions(-) delete mode 100644 tools/setup_global.py.in delete mode 100644 tools/setup_main.py.in diff --git a/tools/setup_global.py.in b/tools/setup_global.py.in deleted file mode 100644 index 99b8a2b29e..0000000000 --- a/tools/setup_global.py.in +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -# Setup script for pybind11-global (in the sdist or in tools/setup_global.py in the repository) -# This package is targeted for easy use from CMake. - -import glob -import os -import re - -# Setuptools has to be before distutils -from setuptools import setup - -from distutils.command.install_headers import install_headers - -class InstallHeadersNested(install_headers): - def run(self): - headers = self.distribution.headers or [] - for header in headers: - # Remove pybind11/include/ - short_header = header.split("/", 2)[-1] - - dst = os.path.join(self.install_dir, os.path.dirname(short_header)) - self.mkpath(dst) - (out, _) = self.copy_file(header, dst) - self.outfiles.append(out) - - -main_headers = glob.glob("pybind11/include/pybind11/*.h") -conduit_headers = sum([glob.glob(f"pybind11/include/pybind11/conduit/*.{ext}") - for ext in ("h", "txt")], []) -detail_headers = glob.glob("pybind11/include/pybind11/detail/*.h") -eigen_headers = glob.glob("pybind11/include/pybind11/eigen/*.h") -stl_headers = glob.glob("pybind11/include/pybind11/stl/*.h") -cmake_files = glob.glob("pybind11/share/cmake/pybind11/*.cmake") -pkgconfig_files = glob.glob("pybind11/share/pkgconfig/*.pc") -headers = main_headers + conduit_headers + detail_headers + eigen_headers + stl_headers - -cmdclass = {"install_headers": InstallHeadersNested} -$extra_cmd - -# This will _not_ affect installing from wheels, -# only building wheels or installing from SDist. -# Primarily intended on Windows, where this is sometimes -# customized (for example, conda-forge uses Library/) -base = os.environ.get("PYBIND11_GLOBAL_PREFIX", "") - -# Must have a separator -if base and not base.endswith("/"): - base += "/" - -setup( - name="pybind11_global", - version="$version", - packages=[], - headers=headers, - data_files=[ - (base + "share/cmake/pybind11", cmake_files), - (base + "share/pkgconfig", pkgconfig_files), - (base + "include/pybind11", main_headers), - (base + "include/pybind11/conduit", conduit_headers), - (base + "include/pybind11/detail", detail_headers), - (base + "include/pybind11/eigen", eigen_headers), - (base + "include/pybind11/stl", stl_headers), - ], - cmdclass=cmdclass, -) diff --git a/tools/setup_main.py.in b/tools/setup_main.py.in deleted file mode 100644 index e04dc82049..0000000000 --- a/tools/setup_main.py.in +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 - -# Setup script (in the sdist or in tools/setup_main.py in the repository) - -from setuptools import setup - -cmdclass = {} -$extra_cmd - -setup( - name="pybind11", - version="$version", - download_url='https://github.com/pybind/pybind11/tarball/v$version', - packages=[ - "pybind11", - "pybind11.include.pybind11", - "pybind11.include.pybind11.conduit", - "pybind11.include.pybind11.detail", - "pybind11.include.pybind11.eigen", - "pybind11.include.pybind11.stl", - "pybind11.share", - "pybind11.share.cmake.pybind11", - "pybind11.share.pkgconfig", - ], - package_data={ - "pybind11": ["py.typed"], - "pybind11.include.pybind11": ["*.h"], - "pybind11.include.pybind11.conduit": ["*.h", "*.txt"], - "pybind11.include.pybind11.detail": ["*.h"], - "pybind11.include.pybind11.eigen": ["*.h"], - "pybind11.include.pybind11.stl": ["*.h"], - "pybind11.share.cmake.pybind11": ["*.cmake"], - "pybind11.share.pkgconfig": ["*.pc"], - }, - extras_require={ - "global": ["pybind11_global==$version"] - }, - entry_points={ - "console_scripts": [ - "pybind11-config = pybind11.__main__:main", - ], - "pipx.run": [ - "pybind11 = pybind11.__main__:main", - ], - "pkg_config": [ - "pybind11 = pybind11.share.pkgconfig", - ], - }, - cmdclass=cmdclass -) From ce7aec7ae8cb225b20056b9e3022e1f6e1a7e70f Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 1 May 2025 13:07:42 -0400 Subject: [PATCH 6/6] fix: restore global pin Signed-off-by: Henry Schreiner --- pyproject.toml | 13 +++++++++---- tools/make_global.py | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2d9ba0ba45..0a1c012466 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["scikit-build-core >=0.11.0"] +requires = ["scikit-build-core >=0.11.2"] build-backend = "scikit_build_core.build" [project] @@ -79,9 +79,9 @@ regex = '''(?sx) ''' result = "{major}.{minor}.{patch}" -# Can't use tool.uv.sources with requirements.txt -[tool.uv] -index-strategy = "unsafe-best-match" +[tool.scikit-build.metadata.optional-dependencies] +provider = "scikit_build_core.metadata.template" +result = { global = ["pybind11-global=={project[version]}"]} [[tool.scikit-build.generate]] path = "pybind11/_version.py" @@ -101,6 +101,11 @@ version_info = tuple(_to_int(s) for s in __version__.split(".")) ''' +# Can't use tool.uv.sources with requirements.txt +[tool.uv] +index-strategy = "unsafe-best-match" + + [tool.mypy] files = ["pybind11"] python_version = "3.8" diff --git a/tools/make_global.py b/tools/make_global.py index 5d12df4b2c..6a80498943 100755 --- a/tools/make_global.py +++ b/tools/make_global.py @@ -19,6 +19,7 @@ def get_global() -> str: del pyproject["project"]["optional-dependencies"] del pyproject["project"]["entry-points"] del pyproject["project"]["scripts"] + del pyproject["tool"]["scikit-build"]["metadata"]["optional-dependencies"] pyproject["project"]["name"] = "pybind11-global" pyproject["tool"]["scikit-build"]["experimental"] = True pyproject["tool"]["scikit-build"]["wheel"]["install-dir"] = "/data"