forked from aegis/pyserveX
172 lines
5.0 KiB
Python
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)
|