import os
import sys
import warnings
from setuptools import Extension, setup
from setuptools.command.build_ext import build_ext
from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError
_OPTIONAL_EXT_ERRORS = (
CCompilerError,
DistutilsExecError,
DistutilsPlatformError,
FileNotFoundError,
PermissionError,
)
class BuildExtension(build_ext):
def build_extensions(self):
cxx = os.environ.get("CXX", "g++")
self.compiler.set_executable("compiler_so", cxx)
self.compiler.set_executable("compiler_cxx", cxx)
self.compiler.set_executable("linker_so", f"{cxx} -shared")
try:
build_ext.build_extensions(self)
except _OPTIONAL_EXT_ERRORS as e:
self._warn_optional_skip(e)
def build_extension(self, ext):
try:
super().build_extension(ext)
except _OPTIONAL_EXT_ERRORS as e:
self._warn_optional_skip(e, ext_name=ext.name)
@staticmethod
def _warn_optional_skip(exc, ext_name="modelexpress.vmm._alloc_ext"):
msg = (
f"Failed to build optional C extension {ext_name}: "
f"{type(exc).__name__}: {exc}. "
"ModelExpress will install pure-Python; MX_VMM_ARENA will be a "
"runtime no-op. Install a C++ compiler (g++ or set $CXX) and "
"reinstall to enable the arena allocator fast path."
)
warnings.warn(msg, stacklevel=2)
print(f"WARNING: {msg}", file=sys.stderr)
def _ext_modules():
if os.environ.get("MX_SKIP_EXT"):
return []
extra_compile_args = ["-std=c++17", "-O3", "-fPIC"]
return [
Extension(
name="modelexpress.vmm._alloc_ext",
sources=["modelexpress/vmm/_alloc_ext.cpp"],
extra_compile_args=extra_compile_args,
optional=True,
),
]
setup(
ext_modules=_ext_modules(),
cmdclass={"build_ext": BuildExtension},
)