|
|
#!/usr/bin/env python# -*- coding: utf-8 -*-
# Setup script for PyPI; use CMakeFile.txt to build extension modules
import contextlibimport ioimport osimport reimport shutilimport stringimport subprocessimport sysimport tempfile
import setuptools.command.sdist
DIR = os.path.abspath(os.path.dirname(__file__))VERSION_REGEX = re.compile( r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE)
def build_expected_version_hex(matches): patch_level_serial = matches["PATCH"] serial = None try: major = int(matches["MAJOR"]) minor = int(matches["MINOR"]) flds = patch_level_serial.split(".") if flds: patch = int(flds[0]) level = None 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 except ValueError: pass if serial is None: msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial) raise RuntimeError(msg) return ( "0x" + "{:02x}{:02x}{:02x}{}{:x}".format( major, minor, patch, level[:1], serial ).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", False)
setup_py = "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"extra_cmd = 'cmdclass["sdist"] = SDist\n'
to_src = ( ("pyproject.toml", "tools/pyproject.toml"), ("setup.py", setup_py),)
# Read the listed versionwith open("pybind11/_version.py") as f: code = compile(f.read(), "pybind11/_version.py", "exec")loc = {}exec(code, loc)version = loc["__version__"]
# Verify that the version matches the one in C++with io.open("include/pybind11/detail/common.h", encoding="utf8") as f: matches = dict(VERSION_REGEX.findall(f.read()))cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)if version != cpp_version: msg = "Python version {} does not match C++ version {}!".format( version, cpp_version ) raise RuntimeError(msg)
version_hex = matches.get("HEX", "MISSING")expected_version_hex = build_expected_version_hex(matches)if version_hex != expected_version_hex: msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format( version_hex, expected_version_hex, ) raise RuntimeError(msg)
def get_and_replace(filename, binary=False, **opts): with open(filename, "rb" if binary else "r") as f: contents = f.read() # Replacement has to be done on text in Python 3 (both work in Python 2) if binary: return string.Template(contents.decode()).substitute(opts).encode() else: return string.Template(contents).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, files): setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files)
for to, src in to_src: txt = get_and_replace(src, binary=True, version=version, extra_cmd="")
dest = os.path.join(base_dir, to)
# This is normally linked, so unlink before writing! os.unlink(dest) with open(dest, "wb") as f: f.write(txt)
# Backport from Python 3@contextlib.contextmanagerdef TemporaryDirectory(): # noqa: N802 "Prepare a temporary directory, cleanup when done" try: tmpdir = tempfile.mkdtemp() yield tmpdir finally: shutil.rmtree(tmpdir)
# Remove the CMake install directory when done@contextlib.contextmanagerdef remove_output(*sources): 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", ] 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 cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) subprocess.check_call(cmd, **cmake_opts) subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts)
txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd) code = compile(txt, setup_py, "exec") exec(code, {"SDist": SDist})
|