-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathsetup.py
More file actions
128 lines (106 loc) · 3.84 KB
/
Copy pathsetup.py
File metadata and controls
128 lines (106 loc) · 3.84 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
"""Build script for Cider.
Usage:
pip install -e . # Editable install (dev)
pip install . # Regular install
python setup.py build_ext # Build C++ extension only
The C++ extension is built via CMake and installed into cider/lib/.
On Apple M4 and below, the C++ extension is skipped (INT8 TensorOps
require M5+). The pure-Python package still installs and provides
graceful fallback via is_available() → False.
"""
import os
import platform
import re
import subprocess
import sys
from pathlib import Path
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
def _detect_apple_chip() -> int:
"""Detect Apple Silicon generation. Returns chip number (4, 5, ...) or 0 if unknown."""
if platform.system() != "Darwin":
return 0
try:
brand = subprocess.run(
["sysctl", "-n", "machdep.cpu.brand_string"],
capture_output=True, text=True, timeout=5
).stdout.strip()
m = re.match(r"Apple M(\d+)", brand)
return int(m.group(1)) if m else 0
except Exception:
return 0
def _should_build_ext() -> bool:
"""Determine if C++ extension should be built."""
# Allow forcing via env var
force = os.environ.get("CIDER_FORCE_BUILD", "").lower()
if force in ("1", "true", "yes"):
return True
if force in ("0", "false", "no"):
return False
# Auto-detect: only build on M5+
chip = _detect_apple_chip()
if chip >= 5:
return True
if chip > 0:
print(f"[cider] Detected Apple M{chip} — skipping C++ extension (requires M5+)")
return False
# Unknown chip (cross-compile, CI, etc.) — try to build
return True
class CMakeBuild(build_ext):
def build_extension(self, ext):
src_dir = Path(__file__).parent.resolve()
# Resolve paths that cmake can't find in pip's isolated build env
import sysconfig
python_include = sysconfig.get_path("include")
try:
import nanobind
nanobind_dir = nanobind.cmake_dir()
except ImportError:
nanobind_dir = ""
cmake_args = [
f"-DPython_EXECUTABLE={sys.executable}",
f"-DPython_INCLUDE_DIR={python_include}",
"-DCMAKE_BUILD_TYPE=Release",
]
if nanobind_dir:
cmake_args.append(f"-Dnanobind_DIR={nanobind_dir}")
build_args = ["--config", "Release", "-j"]
# In-source build (cmake outputs .so to src_dir)
subprocess.check_call(
["cmake", "."] + cmake_args,
cwd=src_dir,
)
subprocess.check_call(
["cmake", "--build", "."] + build_args,
cwd=src_dir,
)
import shutil
# Copy .so to build_lib root (where setuptools expects the extension)
build_lib = Path(self.build_lib)
build_lib.mkdir(parents=True, exist_ok=True)
for f in src_dir.glob("_cider_prim*.so"):
shutil.copy2(f, build_lib)
# Also copy to cider/lib/ for runtime loading
output_lib_dir = build_lib / "cider" / "lib"
output_lib_dir.mkdir(parents=True, exist_ok=True)
for f in src_dir.glob("*.so"):
shutil.copy2(f, output_lib_dir)
for f in src_dir.glob("*.dylib"):
shutil.copy2(f, output_lib_dir)
# Write to source tree cider/lib/ so editable installs work
src_lib_dir = src_dir / "cider" / "lib"
src_lib_dir.mkdir(exist_ok=True)
for f in src_dir.glob("*.so"):
shutil.copy2(f, src_lib_dir)
for f in src_dir.glob("*.dylib"):
shutil.copy2(f, src_lib_dir)
if _should_build_ext():
ext_modules = [Extension("_cider_prim", sources=[])]
cmdclass = {"build_ext": CMakeBuild}
else:
ext_modules = []
cmdclass = {}
setup(
ext_modules=ext_modules,
cmdclass=cmdclass,
)