Repository navigation
Expand file tree
/
Copy pathsetup.py
More file actions
129 lines (109 loc) · 4.55 KB
/
Copy pathsetup.py
File metadata and controls
129 lines (109 loc) · 4.55 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
import subprocess
import sys
from pathlib import Path
from pybind11.setup_helpers import Pybind11Extension, build_ext as pybind11_build_ext
from setuptools import setup
# Optimization is asked for per compiler, not per platform: MSVC does not know
# -O3 and would fail on it, and the flag has to be chosen when the compiler is
# known, which is inside build_ext rather than here.
OPTIMIZE = {"msvc": ["/O2"], "unix": ["-O3"], "mingw32": ["-O3"]}
extra_compile_args = []
extra_link_args = []
class build_ext(pybind11_build_ext):
"""Add the compiler's own optimization flag once the compiler is known."""
def build_extensions(self):
flags = OPTIMIZE.get(self.compiler.compiler_type, [])
for extension in self.extensions:
extension.extra_compile_args = list(extension.extra_compile_args) + flags
super().build_extensions()
def _macos_libcxx_workaround():
"""Work around a Command Line Tools install with incomplete libc++ headers.
Some macOS setups have an almost empty ``CommandLineTools/usr/include/c++/v1``
while the SDK's copy is complete, so any ``#include <cstddef>`` fails. When
that is the case, point the compiler at the SDK's headers instead.
The real fix is to repair the toolchain, e.g. by pointing xcode-select at a
full Xcode (``sudo xcode-select -s /Applications/Xcode.app/Contents/Developer``)
or reinstalling the Command Line Tools.
"""
if sys.platform != "darwin":
return []
if _compiles([]):
return [] # the toolchain is fine, which is the normal case
try:
developer = subprocess.check_output(["xcode-select", "-p"], text=True).strip()
except Exception:
developer = ""
candidates = [Path("/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")]
if developer:
candidates.insert(0, Path(developer) / "SDKs/MacOSX.sdk")
for sdk in candidates:
headers = sdk / "usr/include/c++/v1"
flags = ["-nostdinc++", "-isystem", str(headers)]
if (headers / "cstddef").exists() and _compiles(flags):
print(f"note: incomplete libc++ in the active toolchain; "
f"using the headers from {headers}")
return flags
print("warning: no working C++ standard library found; the build will "
"probably fail. Try 'sudo xcode-select --install', or point "
"xcode-select at a full Xcode and run 'sudo xcodebuild -license accept'.")
return []
def _compiles(flags):
"""Whether a trivial C++ program compiles with these extra flags."""
import tempfile
with tempfile.TemporaryDirectory() as tmp:
source = Path(tmp) / "probe.cpp"
source.write_text("#include <cstddef>\n#include <thread>\nint main(){}\n")
try:
return subprocess.run(
["c++", *flags, str(source), "-o", str(Path(tmp) / "probe")],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
).returncode == 0
except Exception:
return False
extra_compile_args += _macos_libcxx_workaround()
HEADERS = sorted(str(h) for h in Path("csrc").glob("*.hpp"))
ext_modules = [
Pybind11Extension(
"smappy._fit3d",
["csrc/fit.cpp"],
depends=HEADERS, # a changed header rebuilds the module
include_dirs=["csrc"],
cxx_std=17,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
),
Pybind11Extension(
"smappy._drift",
["csrc/drift.cpp"],
depends=HEADERS,
include_dirs=["csrc"],
cxx_std=17,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
),
Pybind11Extension(
"smappy._group",
["csrc/group.cpp"],
depends=HEADERS,
include_dirs=["csrc"],
cxx_std=17,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
),
Pybind11Extension(
"smappy._render",
["csrc/render.cpp"],
depends=HEADERS,
include_dirs=["csrc"],
cxx_std=17,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
),
]
# The package lives under src/, which pyproject.toml tells `packages.find` but
# not `build_ext`: without this, `build_ext --inplace` writes the modules to a
# non-existent ./smappy/, fails on the first one and leaves every extension at
# whatever it was last built as -- a stale binary that imports fine and is
# missing the functions added since.
setup(package_dir={"": "src"}, ext_modules=ext_modules,
cmdclass={"build_ext": build_ext})