Compare commits

...
Author SHA1 Message Date
Bugen Zhao 1fbb5585a1 use rustbin for vllm-rs binary 2026-07-17 14:13:50 +08:00
10 changed files with 116 additions and 34 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ docker run --rm --network=none --entrypoint /bin/bash "${image_ref}" -ec '
if [ ! -d /vllm-workspace ]; then echo Missing directory: /vllm-workspace >&2; exit 1; fi
if [ ! -d /vllm-workspace/tests ]; then echo Missing directory: /vllm-workspace/tests >&2; exit 1; fi
if [ ! -d /vllm-workspace/src/vllm ]; then echo Missing directory: /vllm-workspace/src/vllm >&2; exit 1; fi
if [ ! -x /vllm-workspace/src/vllm/vllm-rs ]; then echo Missing executable: /vllm-workspace/src/vllm/vllm-rs >&2; exit 1; fi
if ! command -v vllm-rs >/dev/null 2>&1; then echo Missing executable: vllm-rs >&2; exit 1; fi
command -v python3
command -v uv
+1 -1
View File
@@ -1,5 +1,5 @@
#!/bin/bash
# Build vLLM Rust artifacts and install them into the vllm package.
# Build vLLM Rust artifacts into the vllm staging directory.
# Usage: ./build_rust.sh [--debug]
#
# By default builds in release mode. Pass --debug for faster compile times
+2 -2
View File
@@ -533,8 +533,8 @@ WORKDIR /workspace
COPY --from=csrc-build /workspace/dist /precompiled-wheels
COPY . .
# Drop the pre-built Rust artifacts into the source tree. setup.py detects
# them and ships them as-is, skipping the local Rust build.
# Drop the pre-built Rust artifacts into the source tree. setup.py installs
# the binary as RustBin and ships the PyO3 extensions as package modules.
COPY --from=rust-build /workspace/vllm/vllm-rs vllm/vllm-rs
COPY --from=rust-build /workspace/vllm/_rust_*.so vllm/
+2 -2
View File
@@ -150,8 +150,8 @@ RUN --mount=type=cache,target=/root/.cache/uv \
COPY . .
# Drop the pre-built Rust artifacts into the source tree. setup.py detects
# them and ships them as-is, skipping the local Rust build.
# Drop the pre-built Rust artifacts into the source tree. setup.py installs
# the binary as RustBin and ships the PyO3 extensions as package modules.
COPY --from=rust-build /workspace/vllm/vllm-rs vllm/vllm-rs
COPY --from=rust-build /workspace/vllm/_rust_*.so vllm/
+4 -4
View File
@@ -303,8 +303,8 @@ ENV VLLM_TARGET_DEVICE=rocm
COPY --from=csrc-build ${COMMON_WORKDIR}/vllm/dist /precompiled-wheels
# Drop the pre-built Rust artifacts into the source tree. setup.py detects
# them and ships them as-is, skipping the local Rust build.
# Drop the pre-built Rust artifacts into the source tree. setup.py installs
# the binary as RustBin and ships the PyO3 extensions as package modules.
COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs
COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/_rust_*.so ${COMMON_WORKDIR}/vllm/vllm/
@@ -521,8 +521,8 @@ FROM fetch_vllm AS build_vllm_wheel_release
ARG COMMON_WORKDIR
# Drop the pre-built Rust artifacts into the source tree. setup.py detects
# them and ships them as-is, skipping the local Rust build.
# Drop the pre-built Rust artifacts into the source tree. setup.py installs
# the binary as RustBin and ships the PyO3 extensions as package modules.
COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs
COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/_rust_*.so ${COMMON_WORKDIR}/vllm/vllm/
+2 -2
View File
@@ -223,8 +223,8 @@ RUN --mount=type=cache,target=/root/.cache/uv \
# don't invalidate heavy dependency and UCX/NIXL layers.
COPY . .
# Drop the pre-built Rust artifacts into the source tree. setup.py detects
# them and ships them as-is, skipping the local Rust build.
# Drop the pre-built Rust artifacts into the source tree. setup.py installs
# the binary as RustBin and ships the PyO3 extensions as package modules.
COPY --from=rust-build /workspace/vllm/vllm-rs vllm/vllm-rs
COPY --from=rust-build /workspace/vllm/_rust_*.so vllm/
+30 -14
View File
@@ -35,6 +35,7 @@ ROOT_DIR = Path(__file__).parent
logger = logging.getLogger(__name__)
PRECOMPILED_RUST_FRONTEND_PATH = ROOT_DIR / "vllm" / "vllm-rs"
PRECOMPILED_RUST_FRONTEND_MEMBER_REGEX = re.compile(r"^[^/]+\.data/scripts/vllm-rs$")
# setuptools-rust installs PyO3 artifacts as `<module>.<ext-suffix>`, where the
# suffix ends with `.so` on Linux and macOS alike (e.g. `_rust_foo.abi3.so`).
PRECOMPILED_RUST_EXTENSION_MEMBER_REGEX = re.compile(r"vllm/_rust_[^/]*\.so$")
@@ -483,8 +484,12 @@ class precompiled_build_rust(build_rust):
)
if not missing:
install_scripts = self.get_finalized_command("install_scripts")
destination = Path(install_scripts.build_dir) / "vllm-rs"
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(PRECOMPILED_RUST_FRONTEND_PATH, destination)
logger.info(
"Skipping local Rust build: using precompiled %s and %s",
"Installed precompiled Rust artifacts from %s and %s",
PRECOMPILED_RUST_FRONTEND_PATH,
get_precompiled_rust_extension_paths(),
)
@@ -793,9 +798,6 @@ class precompiled_wheel_utils:
"vllm/_rocm_C.abi3.so",
}
)
if extract_rust_frontend:
exact_members.add("vllm/vllm-rs")
flash_attn_regex = re.compile(
r"vllm/vllm_flash_attn/(?:[^/.][^/]*/)*(?!\.)[^/]*\.py"
)
@@ -817,6 +819,14 @@ class precompiled_wheel_utils:
tml_fa4_regex = re.compile(r"vllm/third_party/tml_fa4/.*")
file_members = []
for member in wheel.filelist:
if (
extract_rust_frontend
and PRECOMPILED_RUST_FRONTEND_MEMBER_REGEX.match(
member.filename
)
):
file_members.append(member)
continue
if member.filename in exact_members:
file_members.append(member)
continue
@@ -847,16 +857,26 @@ class precompiled_wheel_utils:
for file in file_members:
print(f"[extract] {file.filename}")
target_path = os.path.join(".", file.filename)
os.makedirs(os.path.dirname(target_path), exist_ok=True)
is_rust_frontend = bool(
PRECOMPILED_RUST_FRONTEND_MEMBER_REGEX.match(file.filename)
)
target_path = (
PRECOMPILED_RUST_FRONTEND_PATH
if is_rust_frontend
else Path(file.filename)
)
target_path.parent.mkdir(parents=True, exist_ok=True)
with (
wheel.open(file.filename) as src,
open(target_path, "wb") as dst,
target_path.open("wb") as dst,
):
shutil.copyfileobj(src, dst)
mode = file.external_attr >> 16
if mode:
os.chmod(target_path, mode)
target_path.chmod(mode)
if is_rust_frontend:
continue
pkg = os.path.dirname(file.filename).replace("/", ".")
package_data_patch.setdefault(pkg, []).append(
@@ -1225,10 +1245,6 @@ if USE_PRECOMPILED_RUST_FRONTEND:
for pkg, files in patch.items():
package_data.setdefault(pkg, []).extend(files)
# If the rust frontend binary is already present in the source tree (e.g.,
# pre-built in a separate Docker build stage), ship it as-is.
if PRECOMPILED_RUST_FRONTEND_PATH.exists():
add_vllm_package_data("vllm-rs")
for rust_extension_path in get_precompiled_rust_extension_paths():
add_vllm_package_data(rust_extension_path.name)
@@ -1250,8 +1266,8 @@ if (
):
cmdclass["build_rust"] = precompiled_build_rust
# Rust artifacts, built via setuptools-rust and installed into the package
# directory alongside the Python modules.
# Rust artifacts built via setuptools-rust. RustBin installs vllm-rs into the
# environment scripts directory; PyO3 extensions remain in the vllm package.
rust_extensions = rust_build.rust_extensions(
optional=not should_require_rust_frontend()
)
+54
View File
@@ -2,6 +2,7 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import os
from pathlib import Path
from unittest.mock import patch
import pytest
@@ -145,6 +146,59 @@ def test_precompiled_install_flags_are_orthogonal() -> None:
assert environment_variables["VLLM_USE_PRECOMPILED_RUST"]() is True
def test_resolve_rust_frontend_path_from_scripts_dir(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
binary = tmp_path / "vllm-rs"
binary.write_text("")
binary.chmod(0o755)
monkeypatch.setenv("VLLM_USE_RUST_FRONTEND", "1")
monkeypatch.setenv("VLLM_RUST_FRONTEND_PATH", "auto")
monkeypatch.setattr(envs.sysconfig, "get_path", lambda _: str(tmp_path))
monkeypatch.setattr(envs.shutil, "which", lambda _: None)
assert envs._resolve_rust_frontend_path() == str(binary)
def test_resolve_rust_frontend_path_from_path(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
binary = tmp_path / "vllm-rs"
monkeypatch.setenv("VLLM_USE_RUST_FRONTEND", "1")
monkeypatch.setenv("VLLM_RUST_FRONTEND_PATH", "auto")
monkeypatch.setattr(envs, "__file__", str(tmp_path / "vllm" / "envs.py"))
monkeypatch.setattr(envs.sysconfig, "get_path", lambda _: str(tmp_path / "bin"))
monkeypatch.setattr(envs.shutil, "which", lambda _: str(binary))
assert envs._resolve_rust_frontend_path() == str(binary)
def test_resolve_rust_frontend_path_from_package_staging(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
package_dir = tmp_path / "vllm"
package_dir.mkdir()
binary = package_dir / "vllm-rs"
binary.write_text("")
binary.chmod(0o755)
monkeypatch.setenv("VLLM_USE_RUST_FRONTEND", "1")
monkeypatch.setenv("VLLM_RUST_FRONTEND_PATH", "auto")
monkeypatch.setattr(envs, "__file__", str(package_dir / "envs.py"))
monkeypatch.setattr(envs.sysconfig, "get_path", lambda _: str(tmp_path / "bin"))
monkeypatch.setattr(envs.shutil, "which", lambda _: None)
assert envs._resolve_rust_frontend_path() == str(binary)
def test_resolve_rust_frontend_path_explicit(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("VLLM_USE_RUST_FRONTEND", "1")
monkeypatch.setenv("VLLM_RUST_FRONTEND_PATH", "/tmp/vllm-rs")
assert envs._resolve_rust_frontend_path() == "/tmp/vllm-rs"
class TestEnvWithChoices:
"""Test cases for env_with_choices function."""
+6 -5
View File
@@ -10,19 +10,19 @@ import sys
from pathlib import Path
from setuptools import setup
from setuptools_rust import Binding, RustExtension
from setuptools_rust import Binding, RustBin, RustExtension
ROOT_DIR = Path(__file__).resolve().parents[1]
RUST_STAGING_DIR = ROOT_DIR / "vllm"
def rust_extensions(*, optional: bool = False) -> list[RustExtension]:
return [
RustExtension(
target="vllm.vllm-rs",
RustBin(
target="vllm-rs",
path="rust/src/cmd/Cargo.toml",
args=["--bin", "vllm-rs"],
features=["native-tls-vendored"],
binding=Binding.Exec,
optional=optional,
),
RustExtension(
@@ -51,11 +51,12 @@ def rust_py_extension_module_names() -> list[str]:
def build_binary(build_rust_args: list[str]) -> None:
os.chdir(ROOT_DIR)
(ROOT_DIR / "vllm").mkdir(exist_ok=True)
RUST_STAGING_DIR.mkdir(exist_ok=True)
setup(
name="vllm-rust-frontend-build",
packages=[],
rust_extensions=rust_extensions(optional=False),
options={"install_scripts": {"build_dir": str(RUST_STAGING_DIR)}},
script_args=["build_rust", "--quiet", "--inplace", *build_rust_args],
)
+14 -3
View File
@@ -5,7 +5,9 @@ import functools
import json
import logging
import os
import shutil
import sys
import sysconfig
import tempfile
import uuid
from collections.abc import Callable
@@ -567,14 +569,23 @@ def _resolve_rust_frontend_path() -> str | None:
return None
if raw.lower() in ("auto", "1", "true"):
pkg_dir = os.path.dirname(os.path.abspath(__file__))
candidate = os.path.join(pkg_dir, "vllm-rs")
candidate = os.path.join(sysconfig.get_path("scripts"), "vllm-rs")
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
return candidate
package_dir = os.path.dirname(os.path.abspath(__file__))
candidate = os.path.join(package_dir, "vllm-rs")
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
return candidate
candidate = shutil.which("vllm-rs")
if candidate is not None:
return candidate
raise FileNotFoundError(
"VLLM_RUST_FRONTEND_PATH=auto but the vllm-rs binary was "
f"not found at {candidate}. "
"not found in the Python scripts directory, package directory, "
"or PATH. "
"Build with setuptools-rust or set the path explicitly."
)
return raw