Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:58

0001 #!/usr/bin/env python3
0002 
0003 # Setup script for PyPI; use CMakeFile.txt to build extension modules
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 # PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
0051 # files, and the sys.prefix files (CMake and headers).
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 # Read the listed version
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 # Verify that the version matches the one in C++
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 # TODO: use literals & overload (typing extensions or Python 3.8)
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 # Use our input files instead when making the SDist (and anything that depends
0098 # on it, like a wheel)
0099 class SDist(setuptools.command.sdist.sdist):  # type: ignore[misc]
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             # This is normally linked, so unlink before writing!
0109             dest.unlink()
0110             dest.write_bytes(txt)  # type: ignore[arg-type]
0111 
0112 
0113 # Remove the CMake install directory when done
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     # Generate the files if they are not present.
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})