4 from __future__
import annotations
13 from collections.abc
import Generator
14 from pathlib
import Path
15 from tempfile
import TemporaryDirectory
17 import setuptools.command.sdist
19 DIR = Path(__file__).parent.absolute()
20 VERSION_REGEX = re.compile(
21 r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
23 VERSION_FILE = Path(
"pybind11/_version.py")
24 COMMON_FILE = Path(
"include/pybind11/detail/common.h")
28 patch_level_serial = matches[
"PATCH"]
30 major =
int(matches[
"MAJOR"])
31 minor =
int(matches[
"MINOR"])
32 flds = patch_level_serial.split(
".")
39 level_serial = flds[1]
40 for level
in (
"a",
"b",
"c",
"dev"):
41 if level_serial.startswith(level):
42 serial =
int(level_serial[
len(level) :])
45 msg = f
'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"'
46 raise RuntimeError(msg)
47 version_hex_str = f
"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
48 return f
"0x{version_hex_str.upper()}"
54 global_sdist = os.environ.get(
"PYBIND11_GLOBAL_SDIST",
False)
57 "tools/setup_global.py.in" if global_sdist
else "tools/setup_main.py.in"
59 extra_cmd =
'cmdclass["sdist"] = SDist\n'
62 (Path(
"pyproject.toml"), Path(
"tools/pyproject.toml")),
63 (Path(
"setup.py"), setup_py),
68 loc: dict[str, str] = {}
69 code = compile(VERSION_FILE.read_text(encoding=
"utf-8"),
"pybind11/_version.py",
"exec")
71 version = loc[
"__version__"]
74 matches =
dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding=
"utf8")))
75 cpp_version =
"{MAJOR}.{MINOR}.{PATCH}".
format(**matches)
76 if version != cpp_version:
77 msg = f
"Python version {version} does not match C++ version {cpp_version}!"
78 raise RuntimeError(msg)
80 version_hex = matches.get(
"HEX",
"MISSING")
82 if version_hex != exp_version_hex:
83 msg = f
"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
84 raise RuntimeError(msg)
88 def get_and_replace(filename: Path, binary: bool =
False, **opts: str) -> bytes | str:
90 contents = filename.read_bytes()
91 return string.Template(contents.decode()).substitute(opts).
encode()
93 return string.Template(filename.read_text()).substitute(opts)
98 class SDist(setuptools.command.sdist.sdist):
102 for to, src
in to_src:
105 dest = Path(base_dir) / to
109 dest.write_bytes(txt)
113 @contextlib.contextmanager
124 with TemporaryDirectory()
as tmpdir:
125 cmd = [
"cmake",
"-S",
".",
"-B", tmpdir] + [
126 "-DCMAKE_INSTALL_PREFIX=pybind11",
127 "-DBUILD_TESTING=OFF",
128 "-DPYBIND11_NOPYTHON=ON",
129 "-Dprefix_for_pc_file=${pcfiledir}/../../",
131 if "CMAKE_ARGS" in os.environ:
134 for c
in os.environ[
"CMAKE_ARGS"].
split()
135 if "DCMAKE_INSTALL_PREFIX" not in c
138 subprocess.run(cmd, check=
True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
140 [
"cmake",
"--install", tmpdir],
148 code = compile(txt, setup_py,
"exec")
149 exec(code, {
"SDist": SDist})