μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
setup.py
Go to the documentation of this file.
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Setup script for PyPI; use CMakeFile.txt to build extension modules
5
6import contextlib
7import io
8import os
9import re
10import shutil
11import string
12import subprocess
13import sys
14import tempfile
15
17
18DIR = os.path.abspath(os.path.dirname(__file__))
19VERSION_REGEX = re.compile(
20 r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
21)
22
23
25 patch_level_serial = matches["PATCH"]
26 serial = None
27 try:
28 major = int(matches["MAJOR"])
29 minor = int(matches["MINOR"])
30 flds = patch_level_serial.split(".")
31 if flds:
32 patch = int(flds[0])
33 level = None
34 if len(flds) == 1:
35 level = "0"
36 serial = 0
37 elif len(flds) == 2:
38 level_serial = flds[1]
39 for level in ("a", "b", "c", "dev"):
40 if level_serial.startswith(level):
41 serial = int(level_serial[len(level) :])
42 break
43 except ValueError:
44 pass
45 if serial is None:
46 msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial)
47 raise RuntimeError(msg)
48 return (
49 "0x"
50 + "{:02x}{:02x}{:02x}{}{:x}".format(
51 major, minor, patch, level[:1], serial
52 ).upper()
53 )
54
55
56# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
57# files, and the sys.prefix files (CMake and headers).
58
59global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False)
60
61setup_py = "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"
62extra_cmd = 'cmdclass["sdist"] = SDist\n'
63
64to_src = (
65 ("pyproject.toml", "tools/pyproject.toml"),
66 ("setup.py", setup_py),
67)
68
69# Read the listed version
70with open("pybind11/_version.py") as f:
71 code = compile(f.read(), "pybind11/_version.py", "exec")
72loc = {}
73exec(code, loc)
74version = loc["__version__"]
75
76# Verify that the version matches the one in C++
77with io.open("include/pybind11/detail/common.h", encoding="utf8") as f:
78 matches = dict(VERSION_REGEX.findall(f.read()))
79cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
80if version != cpp_version:
81 msg = "Python version {} does not match C++ version {}!".format(
82 version, cpp_version
83 )
84 raise RuntimeError(msg)
85
86version_hex = matches.get("HEX", "MISSING")
87expected_version_hex = build_expected_version_hex(matches)
88if version_hex != expected_version_hex:
89 msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
90 version_hex,
91 expected_version_hex,
92 )
93 raise RuntimeError(msg)
94
95
96def get_and_replace(filename, binary=False, **opts):
97 with open(filename, "rb" if binary else "r") as f:
98 contents = f.read()
99 # Replacement has to be done on text in Python 3 (both work in Python 2)
100 if binary:
101 return string.Template(contents.decode()).substitute(opts).encode()
102 else:
103 return string.Template(contents).substitute(opts)
104
105
106# Use our input files instead when making the SDist (and anything that depends
107# on it, like a wheel)
108class SDist(setuptools.command.sdist.sdist):
109 def make_release_tree(self, base_dir, files):
110 setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files)
111
112 for to, src in to_src:
113 txt = get_and_replace(src, binary=True, version=version, extra_cmd="")
114
115 dest = os.path.join(base_dir, to)
116
117 # This is normally linked, so unlink before writing!
118 os.unlink(dest)
119 with open(dest, "wb") as f:
120 f.write(txt)
121
122
123# Backport from Python 3
124@contextlib.contextmanager
125def TemporaryDirectory(): # noqa: N802
126 "Prepare a temporary directory, cleanup when done"
127 try:
128 tmpdir = tempfile.mkdtemp()
129 yield tmpdir
130 finally:
131 shutil.rmtree(tmpdir)
132
133
134# Remove the CMake install directory when done
135@contextlib.contextmanager
136def remove_output(*sources):
137 try:
138 yield
139 finally:
140 for src in sources:
141 shutil.rmtree(src)
142
143
144with remove_output("pybind11/include", "pybind11/share"):
145 # Generate the files if they are not present.
146 with TemporaryDirectory() as tmpdir:
147 cmd = ["cmake", "-S", ".", "-B", tmpdir] + [
148 "-DCMAKE_INSTALL_PREFIX=pybind11",
149 "-DBUILD_TESTING=OFF",
150 "-DPYBIND11_NOPYTHON=ON",
151 ]
152 if "CMAKE_ARGS" in os.environ:
153 fcommand = [
154 c
155 for c in os.environ["CMAKE_ARGS"].split()
156 if "DCMAKE_INSTALL_PREFIX" not in c
157 ]
158 cmd += fcommand
159 cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
160 subprocess.check_call(cmd, **cmake_opts)
161 subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts)
162
163 txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd)
164 code = compile(txt, setup_py, "exec")
165 exec(code, {"SDist": SDist})
Definition: pytypes.h:1694
def make_release_tree(self, base_dir, files)
Definition: setup.py:109
size_t len(handle h)
Get the length of a Python object.
Definition: pytypes.h:2002
def remove_output(*sources)
Definition: setup.py:136
def get_and_replace(filename, binary=False, **opts)
Definition: setup.py:96
def build_expected_version_hex(matches)
Definition: setup.py:24
void exec(const str &expr, object global=globals(), object local=object())
Definition: eval.h:88