-
-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathbuild_ext.py
More file actions
165 lines (134 loc) · 5.41 KB
/
build_ext.py
File metadata and controls
165 lines (134 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env python
import multiprocessing as mp
import os
import platform
import re
import subprocess # nosec
import sys
import sysconfig
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Optional
TOP_DIR = Path(__file__).parent
print("Platform", platform.system())
uname = platform.uname()
if uname.system == "Darwin":
os.environ["MACOSX_DEPLOYMENT_TARGET"] = "10.13"
VARS = sysconfig.get_config_vars()
def get_python_base() -> str:
# Applies in this form only to Windows.
if "base" in VARS and VARS["base"]: # noqa: RUF019
return VARS["base"]
if "installed_base" in VARS and VARS["installed_base"]: # noqa: RUF019
return VARS["installed_base"]
def alternate_libdir(pth: str):
base = Path(pth).parent
libdir = Path(base) / "libs"
if libdir.exists():
# available_libs = os.listdir(libdir)
return str(libdir)
else:
return ""
def get_py_config() -> Optional[dict]:
pynd = VARS["py_version_nodot"] # Should always be present.
include = sysconfig.get_path("include") # Seems to be cross-platform.
if uname.system == "Windows":
base = get_python_base()
library = f"python{pynd}.lib"
libdir = Path(base) / "libs"
if libdir.exists():
available_libs = os.listdir(libdir)
if library in available_libs:
libdir = str(libdir)
else:
libdir = ""
else:
libdir = alternate_libdir(include)
else:
library = VARS["LIBRARY"]
DIR_VARS = ("LIBDIR", "BINLIBDEST", "DESTLIB", "LIBDEST", "MACHDESTLIB", "DESTSHARED", "LIBPL")
arch = None
if uname.system == "Linux":
arch = VARS.get("MULTIARCH", "")
found = False
for dir_var in DIR_VARS:
if found:
break
dir_name = VARS.get(dir_var)
if not dir_name:
continue
if uname.system == "Darwin":
full_path = [Path(dir_name) / library]
elif uname.system == "Linux":
full_path = [Path(dir_name) / arch / library, Path(dir_name) / library]
else:
pass
for fp in full_path:
print(f"Trying {fp!r}")
if fp.exists():
print(f"found Python library: '{fp}'")
libdir = str(fp.parent)
found = True
break
if not found:
print("Could NOT locate Python library.")
return None
return dict(exe=sys.executable, include=include, libdir=libdir, library=library)
def banner(msg: str) -> None:
print("=" * 80)
print(str.center(msg, 80))
print("=" * 80)
def get_env_int(name: str, default: int = 0) -> int:
return int(os.environ.get(name, default))
def get_env_bool(name: str, default: int = 0) -> bool:
return get_env_int(name, default)
def build_extension(debug: bool = False, use_temp_dir: bool = False) -> None:
print("build_ext::build_extension()")
use_temp_dir = use_temp_dir or get_env_bool("BUILD_TEMP")
debug = debug or get_env_bool("BUILD_DEBUG")
cfg = "Debug" if debug else "Release"
bits, linkage = platform.architecture()
print(f"Bits: {bits!r} Linkage: {linkage!r} Build-Type: {cfg!r}")
cmake_args = []
py_cfg = get_py_config()
if py_cfg is not None:
cmake_args = [
f"-DPython3_EXECUTABLE={py_cfg['exe']}",
f"-DPython3_INCLUDE_DIR={py_cfg['include']}",
f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm
]
# Only add library path if we found one
if py_cfg["libdir"]:
cmake_args.append(f"-DPython3_LIBRARY={str(Path(py_cfg['libdir']) / Path(py_cfg['library']))}")
else:
print("INFO: No explicit Python library path - CMake will auto-detect")
build_args = ["--config Release", "--verbose"]
if sys.platform.startswith("darwin"):
# Cross-compile support for macOS - respect ARCHFLAGS if set
archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", ""))
if archs:
cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))]
if use_temp_dir:
build_temp = Path(TemporaryDirectory(suffix=".build-temp").name) / "extension_it_in"
else:
build_temp = Path(".")
if not build_temp.exists():
build_temp.mkdir(parents=True)
# Clean CMake cache to avoid conflicts when building multiple Python versions in sequence
cmake_cache = build_temp / "CMakeCache.txt"
if cmake_cache.exists():
cmake_cache.unlink()
banner("Step #1: Configure")
# cmake_args += ["--debug-output"]
subprocess.run(["cmake", "-S", str(TOP_DIR), *cmake_args], cwd=build_temp, check=True) # nosec
cmake_args += [f"--parallel {mp.cpu_count()}"]
banner("Step #2: Build")
# build_args += ["-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"]
subprocess.run(["cmake", "--build", str(build_temp), *build_args], cwd=TOP_DIR, check=True) # nosec
banner("Step #3: Install")
# subprocess.run(["cmake", "--install", "."], cwd=build_temp, check=True) # nosec
subprocess.run(["cmake", "--install", build_temp], cwd=TOP_DIR, check=True) # nosec
if __name__ == "__main__":
includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec
os.environ["pybind11_DIR"] = includes
build_extension(use_temp_dir=False)