@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / fdfb2566c0
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

[Rust Frontend] [CI] Unify Rust artifact builds with setuptools-rust (#44981)

Signed-off-by: Bugen Zhao <i@bugenzhao.com>

Bugen Zhao committed Jun 10, 2026 at 17:48 UTC fdfb2566c03638be4bfa34b5e75bf9f169be8869
11 files changed +117 -90
MANIFEST.in
+1
@@ -4,6 +4,7 @@ include requirements/cuda.txt
4 include requirements/rocm.txt
5 include requirements/cpu.txt
6 include CMakeLists.txt
7 +include tools/build_rust.py
8
9 recursive-include cmake *
10 recursive-include csrc *
build_rust.sh
+4 -15
@@ -1,5 +1,5 @@
1 #!/bin/bash
2 -# Build the vllm-rs Rust frontend binary and install it into the vllm package.
2 +# Build the vllm-rs Rust frontend binary.
3 # Usage: ./build_rust.sh [--debug]
4 #
5 # By default builds in release mode. Pass --debug for faster compile times
@@ -8,8 +8,6 @@
8 set -euo pipefail
9
10 REPO_ROOT="$(cd "$(dirname "$0")" && pwd)"
11 -RUST_DIR="$REPO_ROOT/rust"
12 -TARGET_PATH="${VLLM_RS_TARGET_PATH:-$REPO_ROOT/vllm/vllm-rs}"
11
12 # Read the required toolchain from rust-toolchain.toml.
13 TOOLCHAIN=$(grep '^channel' "$REPO_ROOT/rust-toolchain.toml" | sed 's/.*= *"\(.*\)"/\1/')
@@ -27,18 +25,9 @@ if ! rustup run "$TOOLCHAIN" rustc --version &>/dev/null; then
25 fi
26
27 if [[ "${1:-}" == "--debug" ]]; then
30 - PROFILE_ARGS=()
31 - PROFILE_DIR="debug"
28 + PROFILE_ARG="--debug"
29 else
33 - PROFILE_ARGS=(--release)
34 - PROFILE_DIR="release"
30 + PROFILE_ARG="--release"
31 fi
32
37 -cargo +"$TOOLCHAIN" build "${PROFILE_ARGS[@]}" \
38 - --manifest-path "$RUST_DIR/Cargo.toml" \
39 - --bin vllm-rs \
40 - --features native-tls-vendored
41 -
42 -mkdir -p "$(dirname "$TARGET_PATH")"
43 -cp "$RUST_DIR/target/$PROFILE_DIR/vllm-rs" "$TARGET_PATH"
44 -echo "Installed vllm-rs to $TARGET_PATH"
33 +python3 "$REPO_ROOT/tools/build_rust.py" "$PROFILE_ARG"
docker/Dockerfile
+17 -20
@@ -255,51 +255,47 @@ ENV TORCH_CUDA_ARCH_LIST=${torch_cuda_arch_list}
255 #################### RUST BUILD IMAGE ####################
256 # Build the Rust frontend (`vllm-rs`) in a dedicated stage so the main wheel
257 # build stage doesn't need the rust toolchain, protoc, or the rust source.
258 -# This stage runs in parallel with csrc-build/extensions-build.
259 -FROM ${BUILD_BASE_IMAGE} AS rust-build
258 +# This stage reuses the Python environment from base and runs in parallel with
259 +# csrc-build/extensions-build.
260 +FROM base AS rust-build
261 ARG BUILD_OS
262
262 -ENV DEBIAN_FRONTEND=noninteractive
263 -
264 -# Install a basic C toolchain (some rust crates compile C in their build.rs
265 -# scripts) and unzip (used to extract the pinned protoc release below).
263 +# Install native tools needed only for Rust/protoc builds.
264 RUN if [ "${BUILD_OS}" = "manylinux" ]; then \
265 dnf install -y --setopt=install_weak_deps=False \
268 - ca-certificates curl git gcc gcc-c++ make unzip \
266 + make unzip \
267 && dnf clean all && rm -rf /var/cache/dnf; \
268 else \
269 apt-get update -y \
270 && apt-get install -y --no-install-recommends \
273 - ca-certificates curl git build-essential unzip \
271 + make unzip \
272 && rm -rf /var/lib/apt/lists/*; \
273 fi
274
275 COPY tools/install_protoc.sh /tmp/install_protoc.sh
276 RUN /tmp/install_protoc.sh && rm /tmp/install_protoc.sh
277
280 -# Install rustup; the toolchain itself is pinned by rust-toolchain.toml.
281 -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
282 - sh -s -- -y --profile minimal --default-toolchain none
283 -ENV PATH="/root/.cargo/bin:${PATH}"
284 -
278 WORKDIR /workspace
279
287 -# Copy only the rust workspace — the binary is the sole artifact we need.
280 +COPY requirements/build/rust.txt requirements/build/rust.txt
281 +RUN --mount=type=cache,target=/opt/uv/cache \
282 + uv pip install --python /opt/venv/bin/python3 -r requirements/build/rust.txt
283 +
284 +# Copy only the Rust build inputs. The binary is the sole artifact we need.
285 COPY rust rust
286 COPY rust-toolchain.toml rust-toolchain.toml
287 +COPY tools/build_rust.py tools/build_rust.py
288 COPY build_rust.sh build_rust.sh
289
290 # Cap cargo parallelism to avoid exhausting the CI host's open-file limit
291 # (rustc spawns enough concurrent processes to hit RLIMIT_NOFILE otherwise).
292 ENV CARGO_BUILD_JOBS=4
293
296 -# Build the release binary. Cache cargo registry/git and target/, but copy the
297 -# binary out of the target/ cache mount so it persists into the image layer
298 -# for later COPY --from=rust-build.
294 +# Build the release binary. Cache cargo registry/git, but not target/, because
295 +# stale target metadata can outlive source updates across BuildKit cache reuse.
296 RUN --mount=type=cache,target=/root/.cargo/registry \
297 --mount=type=cache,target=/root/.cargo/git \
301 - --mount=type=cache,target=/workspace/rust/target \
302 - VLLM_RS_TARGET_PATH=/workspace/vllm-rs bash build_rust.sh
298 + bash build_rust.sh
299 #################### RUST BUILD IMAGE ####################
300
301 #################### CSRC BUILD IMAGE ####################
@@ -342,6 +338,7 @@ RUN --mount=type=cache,target=/opt/uv/cache \
338 WORKDIR /workspace
339
340 COPY pyproject.toml setup.py CMakeLists.txt ./
341 +COPY tools/build_rust.py tools/build_rust.py
342 COPY cmake cmake/
343 COPY csrc csrc/
344 COPY vllm/envs.py vllm/envs.py
@@ -508,7 +505,7 @@ COPY . .
505
506 # Drop the pre-built rust frontend binary into the source tree. setup.py
507 # detects it and ships it as-is, skipping the local cargo build.
511 -COPY --from=rust-build /workspace/vllm-rs vllm/vllm-rs
508 +COPY --from=rust-build /workspace/vllm/vllm-rs vllm/vllm-rs
509
510 ARG GIT_REPO_CHECK=0
511 RUN --mount=type=bind,source=.git,target=.git \
docker/Dockerfile.cpu
+10 -13
@@ -93,35 +93,32 @@ ENV DEBIAN_FRONTEND=noninteractive
93
94 RUN apt-get update -y \
95 && apt-get install -y --no-install-recommends \
96 - ca-certificates curl git build-essential unzip \
96 + ca-certificates curl git build-essential unzip python3 python3-pip \
97 && rm -rf /var/lib/apt/lists/*
98
99 COPY tools/install_protoc.sh /tmp/install_protoc.sh
100 RUN /tmp/install_protoc.sh && rm /tmp/install_protoc.sh
101
102 -# Install rustup; the toolchain itself is pinned by rust-toolchain.toml.
103 -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
104 - sh -s -- -y --profile minimal --default-toolchain none
105 -ENV PATH="/root/.cargo/bin:${PATH}"
106 -
102 WORKDIR /workspace
103
109 -# Copy only the rust workspace — the binary is the sole artifact we need.
104 +COPY requirements/build/rust.txt requirements/build/rust.txt
105 +RUN python3 -m pip install --no-cache-dir -r requirements/build/rust.txt
106 +
107 +# Copy only the Rust build inputs. The binary is the sole artifact we need.
108 COPY rust rust
109 COPY rust-toolchain.toml rust-toolchain.toml
110 +COPY tools/build_rust.py tools/build_rust.py
111 COPY build_rust.sh build_rust.sh
112
113 # Cap cargo parallelism to avoid exhausting the CI host's open-file limit
114 # (rustc spawns enough concurrent processes to hit RLIMIT_NOFILE otherwise).
115 ENV CARGO_BUILD_JOBS=4
116
118 -# Build the release binary. Cache cargo registry/git and target/, but copy the
119 -# binary out of the target/ cache mount so it persists into the image layer
120 -# for later COPY --from=rust-build.
117 +# Build the release binary. Cache cargo registry/git, but not target/, because
118 +# stale target metadata can outlive source updates across BuildKit cache reuse.
119 RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \
120 --mount=type=cache,target=/root/.cargo/git,sharing=locked \
123 - --mount=type=cache,target=/workspace/rust/target,sharing=locked \
124 - VLLM_RS_TARGET_PATH=/workspace/vllm-rs bash build_rust.sh
121 + bash build_rust.sh
122
123 ######################### BUILD IMAGE #########################
124 FROM base AS vllm-build
@@ -156,7 +153,7 @@ COPY . .
153
154 # Drop the pre-built rust frontend binary into the source tree. setup.py
155 # detects it and ships it as-is, skipping the local cargo build.
159 -COPY --from=rust-build /workspace/vllm-rs vllm/vllm-rs
156 +COPY --from=rust-build /workspace/vllm/vllm-rs vllm/vllm-rs
157
158 RUN if [ "$GIT_REPO_CHECK" != 0 ]; then bash tools/check_repo.sh ; fi
159
docker/Dockerfile.nightly_torch
+8 -9
@@ -102,21 +102,21 @@ ENV DEBIAN_FRONTEND=noninteractive
102
103 RUN apt-get update -y \
104 && apt-get install -y --no-install-recommends \
105 - ca-certificates curl git build-essential unzip \
105 + ca-certificates curl git build-essential unzip python3 python3-pip \
106 && rm -rf /var/lib/apt/lists/*
107
108 COPY tools/install_protoc.sh /tmp/install_protoc.sh
109 RUN /tmp/install_protoc.sh && rm /tmp/install_protoc.sh
110
111 -# Install rustup; the toolchain itself is pinned by rust-toolchain.toml.
112 -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
113 - sh -s -- -y --profile minimal --default-toolchain none
114 -ENV PATH="/root/.cargo/bin:${PATH}"
115 -
111 WORKDIR /workspace
112
113 +COPY requirements/build/rust.txt requirements/build/rust.txt
114 +RUN python3 -m pip install --no-cache-dir -r requirements/build/rust.txt
115 +
116 +# Copy only the Rust build inputs. The binary is the sole artifact we need.
117 COPY rust rust
118 COPY rust-toolchain.toml rust-toolchain.toml
119 +COPY tools/build_rust.py tools/build_rust.py
120 COPY build_rust.sh build_rust.sh
121
122 # Cap cargo parallelism to avoid exhausting the CI host's open-file limit
@@ -125,8 +125,7 @@ ENV CARGO_BUILD_JOBS=4
125
126 RUN --mount=type=cache,target=/root/.cargo/registry \
127 --mount=type=cache,target=/root/.cargo/git \
128 - --mount=type=cache,target=/workspace/rust/target \
129 - VLLM_RS_TARGET_PATH=/workspace/vllm-rs bash build_rust.sh
128 + bash build_rust.sh
129 #################### RUST BUILD IMAGE ####################
130
131 #################### WHEEL BUILD IMAGE ####################
@@ -141,7 +140,7 @@ COPY . .
140
141 # Drop the pre-built rust frontend binary into the source tree. setup.py
142 # detects it and ships it as-is, skipping the local cargo build.
144 -COPY --from=rust-build /workspace/vllm-rs vllm/vllm-rs
143 +COPY --from=rust-build /workspace/vllm/vllm-rs vllm/vllm-rs
144
145 RUN python3 use_existing_torch.py
146
docker/Dockerfile.rocm
+11 -12
@@ -138,27 +138,25 @@ RUN apt-get update -q -y && apt-get install -q -y --no-install-recommends \
138 COPY tools/install_protoc.sh /tmp/install_protoc.sh
139 RUN /tmp/install_protoc.sh && rm /tmp/install_protoc.sh
140
141 -# Install rustup; the toolchain itself is pinned by rust-toolchain.toml.
142 -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
143 - sh -s -- -y --profile minimal --default-toolchain none
144 -ENV PATH="/root/.cargo/bin:${PATH}"
145 -
141 # Cap cargo parallelism to avoid exhausting the AMD CI host's open-file limit
142 # (rustc spawns enough concurrent processes to hit RLIMIT_NOFILE otherwise).
143 ENV CARGO_BUILD_JOBS=4
144 ENV CARGO_NET_RETRY=10
145 ENV RUSTUP_MAX_RETRIES=10
146
147 +RUN --mount=type=cache,id=vllm-rocm-uv,target=/root/.cache/uv \
148 + cd ${COMMON_WORKDIR}/vllm \
149 + && uv pip install --system -r requirements/build/rust.txt
150 +
151 # Build the release binary. Cargo's registry/git caches can be written by
152 # concurrent BuildKit jobs on shared workers, so lock those cache mounts while
154 -# keeping the cache benefit. Copy the binary out so it persists into the image
155 -# layer for later COPY --from=rust-build.
153 +# keeping the cache benefit. Do not cache target/, because stale target metadata
154 +# can outlive source updates across BuildKit cache reuse.
155 RUN --mount=type=cache,id=vllm-rocm-cargo-registry,target=/root/.cargo/registry,sharing=locked \
156 --mount=type=cache,id=vllm-rocm-cargo-git,target=/root/.cargo/git,sharing=locked \
158 - --mount=type=cache,id=vllm-rocm-cargo-target,target=${COMMON_WORKDIR}/vllm/rust/target,sharing=locked \
157 cd ${COMMON_WORKDIR}/vllm \
160 - && VLLM_RS_TARGET_PATH=/tmp/vllm-rs bash build_rust.sh \
161 - && test -x /tmp/vllm-rs
158 + && bash build_rust.sh \
159 + && test -x vllm/vllm-rs
160
161 # -----------------------
162 # vLLM native build stages
@@ -178,6 +176,7 @@ RUN --mount=type=cache,id=vllm-rocm-uv,target=/root/.cache/uv \
176 # pyproject.toml is bind-mounted in the RUN step so metadata-only changes do
177 # not invalidate the expensive native build layer.
178 COPY setup.py CMakeLists.txt ./
179 +COPY tools/build_rust.py tools/build_rust.py
180 COPY cmake cmake/
181 COPY csrc csrc/
182 COPY vllm/envs.py vllm/envs.py
@@ -211,7 +210,7 @@ COPY --from=csrc-build ${COMMON_WORKDIR}/vllm/dist /precompiled-wheels
210
211 # Drop the pre-built rust frontend binary into the source tree. setup.py
212 # detects it and ships it as-is, skipping the local cargo build.
214 -COPY --from=rust-build /tmp/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs
213 +COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs
214
215 RUN --mount=type=cache,id=vllm-rocm-uv,target=/root/.cache/uv \
216 cd vllm \
@@ -420,7 +419,7 @@ ARG COMMON_WORKDIR
419
420 # Drop the pre-built rust frontend binary into the source tree. setup.py
421 # detects it and ships it as-is, skipping the local cargo build.
423 -COPY --from=rust-build /tmp/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs
422 +COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs
423
424 # Create /install directory for custom wheels
425 RUN mkdir -p /install
docker/Dockerfile.xpu
+8 -9
@@ -6,21 +6,21 @@ ENV DEBIAN_FRONTEND=noninteractive
6
7 RUN apt-get update -y \
8 && apt-get install -y --no-install-recommends \
9 - ca-certificates curl git build-essential unzip \
9 + ca-certificates curl git build-essential unzip python3 python3-pip \
10 && rm -rf /var/lib/apt/lists/*
11
12 COPY tools/install_protoc.sh /tmp/install_protoc.sh
13 RUN /tmp/install_protoc.sh && rm /tmp/install_protoc.sh
14
15 -# Install rustup; the toolchain itself is pinned by rust-toolchain.toml.
16 -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
17 - sh -s -- -y --profile minimal --default-toolchain none
18 -ENV PATH="/root/.cargo/bin:${PATH}"
19 -
15 WORKDIR /workspace
16
17 +COPY requirements/build/rust.txt requirements/build/rust.txt
18 +RUN python3 -m pip install --no-cache-dir -r requirements/build/rust.txt
19 +
20 +# Copy only the Rust build inputs. The binary is the sole artifact we need.
21 COPY rust rust
22 COPY rust-toolchain.toml rust-toolchain.toml
23 +COPY tools/build_rust.py tools/build_rust.py
24 COPY build_rust.sh build_rust.sh
25
26 # Cap cargo parallelism to avoid exhausting the CI host's open-file limit
@@ -29,8 +29,7 @@ ENV CARGO_BUILD_JOBS=4
29
30 RUN --mount=type=cache,target=/root/.cargo/registry \
31 --mount=type=cache,target=/root/.cargo/git \
32 - --mount=type=cache,target=/workspace/rust/target \
33 - VLLM_RS_TARGET_PATH=/workspace/vllm-rs bash build_rust.sh
32 + bash build_rust.sh
33
34 FROM intel/deep-learning-essentials:2025.3.2-0-devel-ubuntu24.04 AS vllm-base
35
@@ -215,7 +214,7 @@ COPY . .
214
215 # Drop the pre-built rust frontend binary into the source tree. setup.py
216 # detects it and ships it as-is, skipping the local cargo build.
218 -COPY --from=rust-build /workspace/vllm-rs vllm/vllm-rs
217 +COPY --from=rust-build /workspace/vllm/vllm-rs vllm/vllm-rs
218
219 ARG GIT_REPO_CHECK=0
220 RUN --mount=type=bind,source=.git,target=.git \
requirements/build/rust.txt new
+4
@@ -0,0 +1,4 @@
1 +# Dependencies for building Rust artifacts through setuptools-rust.
2 +setuptools>=77.0.3,<81.0.0
3 +setuptools-rust>=1.9.0
4 +wheel
rust/README.md
+1 -1
@@ -71,7 +71,7 @@ To build the `vllm-rs` in isolation:
71
72 ```bash
73 # from the local checkout
74 -cargo install --path src/cmd --bin vllm-rs
74 +./build_rust.sh
75 ```
76
77 ### Example Request
setup.py
+6 -11
@@ -18,7 +18,6 @@ import torch
18 from packaging.version import Version, parse
19 from setuptools import Extension, setup
20 from setuptools.command.build_ext import build_ext
21 -from setuptools_rust import Binding, RustExtension
21 from setuptools_rust.build import build_rust
22 from setuptools_scm import get_version
23 from torch.utils.cpp_extension import CUDA_HOME, ROCM_HOME
@@ -40,6 +39,9 @@ PRECOMPILED_RUST_FRONTEND_PATH = ROOT_DIR / "vllm" / "vllm-rs"
39 # cannot import envs directly because it depends on vllm,
40 # which is not installed yet
41 envs = load_module_from_path("envs", os.path.join(ROOT_DIR, "vllm", "envs.py"))
42 +rust_build = load_module_from_path(
43 + "rust_build", os.path.join(ROOT_DIR, "tools", "build_rust.py")
44 +)
45
46 VLLM_TARGET_DEVICE = envs.VLLM_TARGET_DEVICE
47 USE_PRECOMPILED_EXTENSIONS = envs.VLLM_USE_PRECOMPILED
@@ -1146,16 +1148,9 @@ if USE_PRECOMPILED_RUST_FRONTEND or PRECOMPILED_RUST_FRONTEND_PATH.exists():
1148 # package directory alongside the Python modules.
1149 # TODO: we may use `RustBin` to directly install it into `bin` directory, but this
1150 # requires extra work on using precompiled binaries.
1149 -rust_extensions = [
1150 - RustExtension(
1151 - target="vllm.vllm-rs",
1152 - path="rust/src/cmd/Cargo.toml",
1153 - args=["--bin", "vllm-rs"],
1154 - features=["native-tls-vendored"],
1155 - binding=Binding.Exec,
1156 - optional=not should_require_rust_frontend(),
1157 - ),
1158 -]
1151 +rust_extensions = rust_build.rust_extensions(
1152 + optional=not should_require_rust_frontend()
1153 +)
1154
1155 setup(
1156 # static metadata should rather go in pyproject.toml
tools/build_rust.py new
+47
@@ -0,0 +1,47 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +
4 +"""Shared setuptools-rust build entry for the vllm-rs binary."""
5 +
6 +from __future__ import annotations
7 +
8 +import os
9 +import sys
10 +from pathlib import Path
11 +
12 +from setuptools import setup
13 +from setuptools_rust import Binding, RustExtension
14 +
15 +ROOT_DIR = Path(__file__).resolve().parents[1]
16 +
17 +
18 +def rust_extensions(*, optional: bool) -> list[RustExtension]:
19 + return [
20 + RustExtension(
21 + target="vllm.vllm-rs",
22 + path="rust/src/cmd/Cargo.toml",
23 + args=["--bin", "vllm-rs"],
24 + features=["native-tls-vendored"],
25 + binding=Binding.Exec,
26 + optional=optional,
27 + ),
28 + ]
29 +
30 +
31 +def build_binary(build_rust_args: list[str]) -> None:
32 + os.chdir(ROOT_DIR)
33 + (ROOT_DIR / "vllm").mkdir(exist_ok=True)
34 + setup(
35 + name="vllm-rust-frontend-build",
36 + packages=[],
37 + rust_extensions=rust_extensions(optional=False),
38 + script_args=["build_rust", "--quiet", "--inplace", *build_rust_args],
39 + )
40 +
41 +
42 +def main() -> None:
43 + build_binary(sys.argv[1:])
44 +
45 +
46 +if __name__ == "__main__":
47 + main()