forked from mooncake-track/Mooncake
172 lines
6.4 KiB
Python
172 lines
6.4 KiB
Python
import sys
|
|
import platform
|
|
from setuptools import setup, Distribution
|
|
from wheel.bdist_wheel import bdist_wheel
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Platform guard
|
|
# ---------------------------------------------------------------------------
|
|
unsupported_platforms = ["win32", "darwin"] # Still blocking non-Linux builds
|
|
if sys.platform in unsupported_platforms:
|
|
sys.exit(
|
|
f"Error: mooncake does not support {platform.system()} at this time. "
|
|
"Please use a supported Linux distribution."
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# helpers
|
|
# ---------------------------------------------------------------------------
|
|
try:
|
|
# packaging ≥20 provides a robust glibc detector used by pip/build
|
|
from packaging.tags import glibc_version_string
|
|
except ImportError:
|
|
glibc_version_string = None
|
|
|
|
|
|
def _detect_manylinux_tag() -> str:
|
|
"""
|
|
Return a PEP 600-style manylinux tag matching *this* build host (e.g. 'manylinux_2_31').
|
|
Falls back to 'manylinux_2_17' if detection fails for maximum compatibility.
|
|
"""
|
|
if glibc_version_string is not None:
|
|
ver = glibc_version_string()
|
|
else:
|
|
import subprocess
|
|
import shutil
|
|
|
|
# Try getconf first (POSIX standard, most reliable, no hardcoded libc name)
|
|
ver = None
|
|
if shutil.which("getconf"):
|
|
try:
|
|
result = subprocess.run(
|
|
["getconf", "GNU_LIBC_VERSION"],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=1,
|
|
)
|
|
if result.returncode == 0 and result.stdout.strip():
|
|
# Output format: "glibc X.Y" or "X.Y"
|
|
ver_str = result.stdout.strip()
|
|
ver = ver_str.split()[-1] if "glibc" in ver_str else ver_str
|
|
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
pass
|
|
|
|
# Fallback: use ctypes with dynamic libc detection
|
|
if ver is None:
|
|
import ctypes
|
|
|
|
try:
|
|
# Try to find libc dynamically instead of hardcoding "libc.so.6"
|
|
libc = None
|
|
# First try the standard name (works for 99.9% of Linux systems)
|
|
for name in ["libc.so.6", "libc.so"]:
|
|
try:
|
|
libc = ctypes.CDLL(name)
|
|
break
|
|
except OSError:
|
|
continue
|
|
|
|
# If standard names don't work, try to find via ldconfig
|
|
if libc is None:
|
|
try:
|
|
result = subprocess.run(
|
|
["ldconfig", "-p"],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=1,
|
|
)
|
|
if result.returncode == 0:
|
|
for line in result.stdout.split("\n"):
|
|
if "libc.so" in line:
|
|
# Extract path from ldconfig output
|
|
# Format: "libc.so.6 (libc6,x86-64) => /lib/x86_64-linux-gnu/libc.so.6"
|
|
parts = line.split("=>")
|
|
if len(parts) == 2:
|
|
libc_path = parts[1].strip()
|
|
libc = ctypes.CDLL(libc_path)
|
|
break
|
|
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
pass
|
|
|
|
if libc is not None:
|
|
# gnu_get_libc_version returns a const char* (C string)
|
|
# Set the return type to c_char_p to get the string properly
|
|
libc.gnu_get_libc_version.restype = ctypes.c_char_p
|
|
version_bytes = libc.gnu_get_libc_version()
|
|
ver = version_bytes.decode("ascii", "replace")
|
|
else:
|
|
raise OSError("libc not found")
|
|
except Exception:
|
|
ver = "2.17" # conservative baseline
|
|
|
|
major, minor, *_ = ver.split(".")
|
|
return f"manylinux_{major}_{minor}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# wheel tag builders
|
|
# ---------------------------------------------------------------------------
|
|
def get_arch() -> str:
|
|
"""
|
|
CPU architecture component of the wheel tag.
|
|
"""
|
|
if sys.platform.startswith("linux"):
|
|
if platform.machine() == "x86_64":
|
|
return "x86_64"
|
|
if platform.machine() in ("arm64", "aarch64"):
|
|
return "aarch64"
|
|
elif sys.platform == "darwin":
|
|
# Return only the arch; macOS deployment target lives in get_system()
|
|
return "aarch64" if platform.machine() in ("arm64", "aarch64") else "x86_64"
|
|
elif sys.platform == "win32":
|
|
return "win_amd64"
|
|
|
|
raise ValueError(f"Unsupported platform: {sys.platform}")
|
|
|
|
|
|
def get_system() -> str:
|
|
"""
|
|
OS component of the wheel tag (PEP 425 / PEP 600 compliant).
|
|
"""
|
|
sys_name = platform.system()
|
|
if sys_name == "Windows":
|
|
return "win"
|
|
elif sys_name == "Darwin":
|
|
# Use macosx_{major}_{minor} layout (underscored) per PEP 425
|
|
major, minor, *_ = (platform.mac_ver()[0] or "11.0").split(".")
|
|
return f"macosx_{major}_{minor}"
|
|
elif sys_name == "Linux":
|
|
return _detect_manylinux_tag()
|
|
|
|
raise ValueError(f"Unsupported system: {sys_name}")
|
|
|
|
|
|
def get_platform() -> str:
|
|
"""Full `{system}_{arch}` tag used by bdist_wheel."""
|
|
return f"{get_system()}_{get_arch()}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# dist / cmd hooks
|
|
# ---------------------------------------------------------------------------
|
|
class BinaryDistribution(Distribution):
|
|
def has_ext_modules(self):
|
|
return True
|
|
|
|
|
|
class CustomBdistWheel(bdist_wheel):
|
|
def finalize_options(self):
|
|
super().finalize_options()
|
|
self.root_is_pure = False
|
|
self.plat_name_supplied = True
|
|
self.plat_name = get_platform()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# setup()
|
|
# ---------------------------------------------------------------------------
|
|
setup(
|
|
distclass=BinaryDistribution,
|
|
cmdclass={"bdist_wheel": CustomBdistWheel},
|
|
)
|