163 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
| #!/usr/bin/env python
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| # Setup script for PyPI; use CMakeFile.txt to build extension modules
 | |
| 
 | |
| import contextlib
 | |
| import io
 | |
| import os
 | |
| import re
 | |
| import shutil
 | |
| import string
 | |
| import subprocess
 | |
| import sys
 | |
| import 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].upper(), serial
 | |
|     )
 | |
| 
 | |
| 
 | |
| # 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 version
 | |
| with 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.contextmanager
 | |
| def 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.contextmanager
 | |
| def 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})
 |