konduktor/scripts/build_cython.py
Илья Глазунов eeeccd57da Cython routing added
2026-01-31 02:44:50 +03:00

172 lines
5.0 KiB
Python

"""
Build script for Cython extensions.
Usage:
python scripts/build_cython.py build_ext --inplace
Or via make:
make build-cython
"""
import os
import subprocess
import sys
from pathlib import Path
def get_pcre2_config():
include_dirs = []
library_dirs = []
libraries = ["pcre2-8"]
try:
cflags = subprocess.check_output(
["pkg-config", "--cflags", "libpcre2-8"],
stderr=subprocess.DEVNULL
).decode().strip()
libs = subprocess.check_output(
["pkg-config", "--libs", "libpcre2-8"],
stderr=subprocess.DEVNULL
).decode().strip()
for flag in cflags.split():
if flag.startswith("-I"):
include_dirs.append(flag[2:])
for flag in libs.split():
if flag.startswith("-L"):
library_dirs.append(flag[2:])
elif flag.startswith("-l"):
lib = flag[2:]
if lib not in libraries:
libraries.append(lib)
return include_dirs, library_dirs, libraries
except (subprocess.CalledProcessError, FileNotFoundError):
pass
try:
cflags = subprocess.check_output(
["pcre2-config", "--cflags"],
stderr=subprocess.DEVNULL
).decode().strip()
libs = subprocess.check_output(
["pcre2-config", "--libs8"],
stderr=subprocess.DEVNULL
).decode().strip()
for flag in cflags.split():
if flag.startswith("-I"):
include_dirs.append(flag[2:])
for flag in libs.split():
if flag.startswith("-L"):
library_dirs.append(flag[2:])
elif flag.startswith("-l"):
lib = flag[2:]
if lib not in libraries:
libraries.append(lib)
return include_dirs, library_dirs, libraries
except (subprocess.CalledProcessError, FileNotFoundError):
pass
# Fallback: try common paths
common_paths = [
"/opt/homebrew", # macOS ARM
"/usr/local", # macOS Intel / Linux
"/usr", # Linux
]
for base in common_paths:
include_path = Path(base) / "include"
lib_path = Path(base) / "lib"
if (include_path / "pcre2.h").exists():
include_dirs.append(str(include_path))
library_dirs.append(str(lib_path))
break
return include_dirs, library_dirs, libraries
def build_extensions():
try:
from Cython.Build import cythonize
except ImportError:
print("Cython not installed. Skipping Cython build.")
print("Install with: pip install cython")
return False
try:
from setuptools import Extension
from setuptools.dist import Distribution
from setuptools.command.build_ext import build_ext
except ImportError:
print("setuptools not installed. Skipping Cython build.")
print("Install with: pip install setuptools")
return False
pcre2_include, pcre2_libdir, pcre2_libs = get_pcre2_config()
if not pcre2_include:
print("WARNING: PCRE2 not found. Routing module may not compile.")
print("Install PCRE2: brew install pcre2 (macOS) or apt install libpcre2-dev (Linux)")
else:
print(f"Found PCRE2: includes={pcre2_include}, libs={pcre2_libdir}")
extensions = [
Extension(
"pyserve._path_matcher",
sources=["pyserve/_path_matcher.pyx"],
extra_compile_args=["-O3", "-ffast-math"],
define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")],
),
Extension(
"pyserve._routing",
sources=["pyserve/_routing.pyx"],
include_dirs=pcre2_include,
library_dirs=pcre2_libdir,
libraries=pcre2_libs,
extra_compile_args=["-O3", "-ffast-math"],
define_macros=[
("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"),
("PCRE2_CODE_UNIT_WIDTH", "8"),
],
),
]
ext_modules = cythonize(
extensions,
compiler_directives={
"language_level": "3",
"boundscheck": False,
"wraparound": False,
"cdivision": True,
"embedsignature": True,
},
annotate=True,
)
dist = Distribution({"ext_modules": ext_modules})
dist.package_dir = {"": "."}
cmd = build_ext(dist)
cmd.ensure_finalized()
cmd.inplace = True
cmd.run()
print("\nCython extensions built successfully!")
ext_suffix = ".pyd" if sys.platform == "win32" else ".so"
print(f" - pyserve/_path_matcher{ext_suffix}")
print(f" - pyserve/_routing{ext_suffix}")
return True
if __name__ == "__main__":
project_root = Path(__file__).parent.parent
os.chdir(project_root)
success = build_extensions()
sys.exit(0 if success else 1)