File indexing completed on 2025-01-18 10:17:58
0001
0002
0003
0004
0005 import contextlib
0006 import os
0007 import re
0008 import shutil
0009 import string
0010 import subprocess
0011 import sys
0012 from pathlib import Path
0013 from tempfile import TemporaryDirectory
0014 from typing import Dict, Iterator, List, Union
0015
0016 import setuptools.command.sdist
0017
0018 DIR = Path(__file__).parent.absolute()
0019 VERSION_REGEX = re.compile(
0020 r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
0021 )
0022 VERSION_FILE = Path("pybind11/_version.py")
0023 COMMON_FILE = Path("include/pybind11/detail/common.h")
0024
0025
0026 def build_expected_version_hex(matches: Dict[str, str]) -> str:
0027 patch_level_serial = matches["PATCH"]
0028 serial = None
0029 major = int(matches["MAJOR"])
0030 minor = int(matches["MINOR"])
0031 flds = patch_level_serial.split(".")
0032 if flds:
0033 patch = int(flds[0])
0034 if len(flds) == 1:
0035 level = "0"
0036 serial = 0
0037 elif len(flds) == 2:
0038 level_serial = flds[1]
0039 for level in ("a", "b", "c", "dev"):
0040 if level_serial.startswith(level):
0041 serial = int(level_serial[len(level) :])
0042 break
0043 if serial is None:
0044 msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"'
0045 raise RuntimeError(msg)
0046 version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
0047 return f"0x{version_hex_str.upper()}"
0048
0049
0050
0051
0052
0053 global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False)
0054
0055 setup_py = Path(
0056 "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"
0057 )
0058 extra_cmd = 'cmdclass["sdist"] = SDist\n'
0059
0060 to_src = (
0061 (Path("pyproject.toml"), Path("tools/pyproject.toml")),
0062 (Path("setup.py"), setup_py),
0063 )
0064
0065
0066
0067 loc: Dict[str, str] = {}
0068 code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
0069 exec(code, loc)
0070 version = loc["__version__"]
0071
0072
0073 matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8")))
0074 cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
0075 if version != cpp_version:
0076 msg = f"Python version {version} does not match C++ version {cpp_version}!"
0077 raise RuntimeError(msg)
0078
0079 version_hex = matches.get("HEX", "MISSING")
0080 exp_version_hex = build_expected_version_hex(matches)
0081 if version_hex != exp_version_hex:
0082 msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
0083 raise RuntimeError(msg)
0084
0085
0086
0087 def get_and_replace(
0088 filename: Path, binary: bool = False, **opts: str
0089 ) -> Union[bytes, str]:
0090 if binary:
0091 contents = filename.read_bytes()
0092 return string.Template(contents.decode()).substitute(opts).encode()
0093
0094 return string.Template(filename.read_text()).substitute(opts)
0095
0096
0097
0098
0099 class SDist(setuptools.command.sdist.sdist):
0100 def make_release_tree(self, base_dir: str, files: List[str]) -> None:
0101 super().make_release_tree(base_dir, files)
0102
0103 for to, src in to_src:
0104 txt = get_and_replace(src, binary=True, version=version, extra_cmd="")
0105
0106 dest = Path(base_dir) / to
0107
0108
0109 dest.unlink()
0110 dest.write_bytes(txt)
0111
0112
0113
0114 @contextlib.contextmanager
0115 def remove_output(*sources: str) -> Iterator[None]:
0116 try:
0117 yield
0118 finally:
0119 for src in sources:
0120 shutil.rmtree(src)
0121
0122
0123 with remove_output("pybind11/include", "pybind11/share"):
0124
0125 with TemporaryDirectory() as tmpdir:
0126 cmd = ["cmake", "-S", ".", "-B", tmpdir] + [
0127 "-DCMAKE_INSTALL_PREFIX=pybind11",
0128 "-DBUILD_TESTING=OFF",
0129 "-DPYBIND11_NOPYTHON=ON",
0130 "-Dprefix_for_pc_file=${pcfiledir}/../../",
0131 ]
0132 if "CMAKE_ARGS" in os.environ:
0133 fcommand = [
0134 c
0135 for c in os.environ["CMAKE_ARGS"].split()
0136 if "DCMAKE_INSTALL_PREFIX" not in c
0137 ]
0138 cmd += fcommand
0139 subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
0140 subprocess.run(
0141 ["cmake", "--install", tmpdir],
0142 check=True,
0143 cwd=DIR,
0144 stdout=sys.stdout,
0145 stderr=sys.stderr,
0146 )
0147
0148 txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd)
0149 code = compile(txt, setup_py, "exec")
0150 exec(code, {"SDist": SDist})