From 539253e1d513b3c9aad1e56bfc8d625e24b3bd18 Mon Sep 17 00:00:00 2001 From: Lianmin Zheng Date: Sun, 12 Jul 2026 05:44:58 -0700 Subject: [PATCH] Gate Rust extension builds (#30927) Co-authored-by: Jialin Ouyang --- python/setup.py | 92 ++++++++++++++++++++++++++++++++ python/sglang/srt/server_args.py | 1 + 2 files changed, 93 insertions(+) create mode 100644 python/setup.py diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 000000000..6bd5ef9cc --- /dev/null +++ b/python/setup.py @@ -0,0 +1,92 @@ +"""sglang build hooks. + +SGLANG_BUILD_RUST_EXTS controls which Rust extensions are built: + - unset or "all": build every declared Rust extension (the default). + - "none": build no Rust extensions. + - comma-separated names: build only extensions whose target matches one of the + given (case-insensitive) substrings, e.g. "grpc" matches + "sglang.srt.grpc._core". + +This is a build-time environment variable, so it is read directly from +os.environ instead of sglang.srt.environ, which is not available until after the +package has been built. +""" + +import os + +from setuptools import setup + +try: + from setuptools_rust import build_rust +except ModuleNotFoundError as exc: + if exc.name != "setuptools_rust": + raise + # Alternate platform pyprojects do not declare Rust extensions. + build_rust = None + +_BUILD_RUST_EXTS_ENV = "SGLANG_BUILD_RUST_EXTS" + + +def _selected_rust_extensions(declared): + """Return the Rust extensions selected by SGLANG_BUILD_RUST_EXTS. + + `ext.name` is the fully-qualified target (e.g. "sglang.srt.grpc._core") for + the string-target declarations in pyproject.toml, so comma-separated names + are matched as case-insensitive substrings of it. + """ + declared = list(declared) + raw = os.environ.get(_BUILD_RUST_EXTS_ENV) + if raw is None: + return declared + + spec = raw.strip().lower() + # An empty or whitespace-only value is treated as unset (build everything). + if not spec or spec == "all": + return declared + if spec == "none": + return [] + + tokens = [token.strip() for token in spec.split(",")] + if not all(tokens): + raise ValueError( + f"{_BUILD_RUST_EXTS_ENV}={raw!r} has an empty item; unset it or use " + "'all', 'none', or a comma-separated list of extension names" + ) + + matched = set() + unmatched = [] + for token in tokens: + hits = {ext.name for ext in declared if token in ext.name.lower()} + if hits: + matched |= hits + else: + unmatched.append(token) + if unmatched: + declared_names = sorted(ext.name for ext in declared) + raise ValueError( + f"{_BUILD_RUST_EXTS_ENV} matched no declared Rust extension for: " + f"{unmatched}; declared extensions are {declared_names}" + ) + + return [ext for ext in declared if ext.name in matched] + + +if build_rust is not None: + + class BuildRust(build_rust): + """Build only the Rust extensions selected by SGLANG_BUILD_RUST_EXTS.""" + + def run(self) -> None: + rust_extensions = _selected_rust_extensions(self.extensions or []) + self.extensions = rust_extensions + self.distribution.rust_extensions = rust_extensions + if not rust_extensions: + return + super().run() + + _cmdclass = {"build_rust": BuildRust} +else: + _cmdclass = {} + + +setup(cmdclass=_cmdclass) diff --git a/python/sglang/srt/server_args.py b/python/sglang/srt/server_args.py index b2f8c03f3..89b03f53a 100644 --- a/python/sglang/srt/server_args.py +++ b/python/sglang/srt/server_args.py @@ -7480,6 +7480,7 @@ class ServerArgs: return None if not host or not (0 < port < 65536): return None + return { "publisher": cfg.publisher, "endpoint_host": host,