[CI] Build macOS arm64 CPU wheel natively on the macmini queue (#48289)

Signed-off-by: mgoin <mgoin64@gmail.com>

Michael Goin committed Jul 14, 2026 at 15:40 UTC 0b54201a044c419e07a4bd464adf91e576640977
4 files changed +90 -6
.buildkite/release-pipeline.yaml
+43
@@ -76,6 +76,49 @@ steps:
76 env:
77 DOCKER_BUILDKIT: "1"
78
79 + - label: "Build wheel - macOS arm64 - CPU"
80 + depends_on: ~
81 + id: build-wheel-macos-arm64-cpu
82 + agents:
83 + queue: macmini
84 + timeout_in_minutes: 90
85 + commands:
86 + - "bash .buildkite/scripts/build-macos-wheel.sh"
87 + - "mkdir -p artifacts/transfer"
88 + - "basename artifacts/dist/*.whl > artifacts/transfer/wheel.name"
89 + - "shasum -a 256 artifacts/dist/*.whl > artifacts/transfer/wheel.sha256"
90 + - "split -b 1m artifacts/dist/*.whl artifacts/transfer/wheel.part."
91 + env:
92 + BUILDKITE_ARTIFACT_UPLOAD_CONCURRENCY: "1"
93 + BUILDKITE_NO_HTTP2: "true"
94 + artifact_paths:
95 + - "artifacts/transfer/*"
96 +
97 + - label: "Upload wheel - macOS arm64 - CPU"
98 + depends_on: build-wheel-macos-arm64-cpu
99 + id: upload-wheel-macos-arm64-cpu
100 + agents:
101 + queue: small_cpu_queue_release
102 + timeout_in_minutes: 15
103 + commands:
104 + - 'buildkite-agent artifact download "artifacts/transfer/*" .'
105 + - "mkdir -p artifacts/dist artifacts/reassembled"
106 + - 'wheel_name=$$(cat artifacts/transfer/wheel.name)'
107 + - '[[ "$$wheel_name" =~ ^[A-Za-z0-9][A-Za-z0-9._+-]*\.whl$$ && "$$wheel_name" != *..* ]]'
108 + - 'expected_sha=$$(awk ''{print $$1}'' artifacts/transfer/wheel.sha256)'
109 + - '[[ "$$expected_sha" =~ ^[0-9a-f]{64}$$ ]]'
110 + - 'cat artifacts/transfer/wheel.part.* > artifacts/reassembled/wheel'
111 + - 'actual_sha=$$(shasum -a 256 artifacts/reassembled/wheel | awk ''{print $$1}'')'
112 + - '[[ "$$actual_sha" == "$$expected_sha" ]]'
113 + - 'mv artifacts/reassembled/wheel "artifacts/dist/$$wheel_name"'
114 + - "aws sts get-caller-identity"
115 + - "VLLM_WHEEL_PLATFORM=macos bash .buildkite/scripts/upload-nightly-wheels.sh"
116 + - 'bash .buildkite/scripts/annotate-build-artifact.sh "$$BUILDKITE_LABEL" "s3://vllm-wheels/$$BUILDKITE_COMMIT/$(cd artifacts/dist && echo *.whl)"'
117 + plugins:
118 + - aws-assume-role-with-web-identity#v1.6.0:
119 + role-arn: arn:aws:iam::936637512419:role/vllm-release-macos-wheel-uploader
120 + region: us-west-2
121 +
122 - label: "Build wheel - x86_64 - CUDA 12.9"
123 depends_on: ~
124 id: build-wheel-x86-cuda-12-9
.buildkite/scripts/build-macos-wheel.sh new
+32
@@ -0,0 +1,32 @@
1 +#!/usr/bin/env bash
2 +# SPDX-License-Identifier: Apache-2.0
3 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
4 +#
5 +# Build the macOS arm64 CPU wheel natively on a macOS agent (the `macmini`
6 +# queue) into artifacts/dist/ for upload-nightly-wheels.sh.
7 +
8 +set -euo pipefail
9 +
10 +# The Rust frontend build needs protoc.
11 +if ! command -v protoc >/dev/null 2>&1; then
12 + brew install protobuf
13 +fi
14 +
15 +# upload-nightly-wheels.sh expects exactly one wheel.
16 +rm -rf artifacts/dist
17 +mkdir -p artifacts/dist
18 +
19 +export VLLM_TARGET_DEVICE=cpu
20 +export VLLM_REQUIRE_RUST_FRONTEND=1
21 +export MACOSX_DEPLOYMENT_TARGET=11.0
22 +# uv's CPython is universal2; force an arm64-only build and tag so the wheel
23 +# isn't mislabelled universal2 and installed on Intel Macs where import fails.
24 +export ARCHFLAGS="-arch arm64"
25 +export _PYTHON_HOST_PLATFORM="macosx-11.0-arm64"
26 +export CMAKE_BUILD_PARALLEL_LEVEL="${CMAKE_BUILD_PARALLEL_LEVEL:-4}"
27 +
28 +uv venv --python 3.12
29 +uv pip install -r requirements/build/cpu.txt --index-strategy unsafe-best-match
30 +uv build --wheel --no-build-isolation -o artifacts/dist
31 +
32 +ls -l artifacts/dist/*.whl
.buildkite/scripts/upload-nightly-wheels.sh
+12 -4
@@ -6,8 +6,14 @@ set -ex
6 # manylinux platform tag with auditwheel.
7 # Index generation is handled separately by generate-and-upload-nightly-index.sh.
8
9 -# shellcheck source=lib/manylinux.sh
10 -source .buildkite/scripts/lib/manylinux.sh
9 +# auditwheel is Linux-only; macOS wheels already carry a valid tag, so skip the
10 +# manylinux retag for them.
11 +WHEEL_PLATFORM="${VLLM_WHEEL_PLATFORM:-linux}"
12 +
13 +if [[ "$WHEEL_PLATFORM" == "linux" ]]; then
14 + # shellcheck source=lib/manylinux.sh
15 + source .buildkite/scripts/lib/manylinux.sh
16 +fi
17
18 BUCKET="vllm-wheels"
19 SUBPATH=$BUILDKITE_COMMIT
@@ -27,8 +33,10 @@ wheel="${wheel_files[0]}"
33
34 # ========= detect manylinux tag and rename ==========
35
30 -wheel="$(apply_manylinux_tag "$wheel")"
31 -echo "Renamed wheel to: $wheel"
36 +if [[ "$WHEEL_PLATFORM" == "linux" ]]; then
37 + wheel="$(apply_manylinux_tag "$wheel")"
38 + echo "Renamed wheel to: $wheel"
39 +fi
40
41 # Extract the version from the wheel
42 version=$(unzip -p "$wheel" '**/METADATA' | grep '^Version: ' | cut -d' ' -f2)
csrc/cpu/torch_bindings.cpp
+3 -2
@@ -570,7 +570,8 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
570 #endif
571
572 // fused moe
573 -#if defined(__AVX512F__) || (defined(ARM_BF16_SUPPORT))
573 +#if defined(__AVX512F__) || \
574 + (defined(__aarch64__) && !defined(__APPLE__) && defined(ARM_BF16_SUPPORT))
575 ops.def(
576 "prepack_moe_weight(Tensor weight, Tensor(a1!) packed_weight, str isa) "
577 "-> ()");
@@ -581,7 +582,7 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
582 "bool skip_weighted, "
583 "str act, str isa) -> ()");
584 ops.impl("cpu_fused_moe", torch::kCPU, &cpu_fused_moe);
584 -#endif // #if defined(__AVX512F__) || (defined(ARM_BF16_SUPPORT))
585 +#endif
586 ops.def(
587 "mla_decode_kvcache("
588 " Tensor! out, Tensor query, Tensor kv_cache,"