From 195059532f4adcd41772b50faa431d7533eafea4 Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 13 Jan 2026 17:01:03 -0800 Subject: [PATCH 1/2] ci: install crane in forge image Currently seeing issues of crane not available in the uploading environment. Installing crane on forge image. https://buildkite.com/ray-project/postmerge/builds/15375/steps/canvas?jid=019bb99d-6f9e-45fa-92e3-a5a1d9373e8d#019bb99d-6f9e-45fa-92e3-a5a1d9373e8d/L198 Topic: crane-fix Signed-off-by: andrew --- ci/docker/forge.Dockerfile | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ci/docker/forge.Dockerfile b/ci/docker/forge.Dockerfile index b15a56912864..767d18321504 100644 --- a/ci/docker/forge.Dockerfile +++ b/ci/docker/forge.Dockerfile @@ -100,6 +100,25 @@ if [[ "$(uname -i)" == "x86_64" ]]; then bash install-k8s-tools.sh fi +# Install crane (container registry tool) +CRANE_VERSION=0.19.0 +case "$(uname -m)" in + x86_64|amd64) + CRANE_ARCH="x86_64" + ;; + aarch64|arm64) + CRANE_ARCH="arm64" + ;; + *) + echo "Unsupported architecture: $(uname -m)" >&2 + exit 1 + ;; +esac + +curl -fsSL "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_Linux_${CRANE_ARCH}.tar.gz" \ + | tar -xzf - -C /usr/local/bin crane + +chmod +x /usr/local/bin/crane EOF USER forge @@ -120,4 +139,4 @@ EOF CMD ["echo", "ray forge"] -# last update: 2025-11-12 +# last update: 2026-01-13 From d5b11ffdd1b45f4950a757e51e76c7c65cfa7cc6 Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 8 Jan 2026 07:29:07 -0800 Subject: [PATCH 2/2] wanda python-agnostic ray-cpp wheel (py3-none tag) The ray-cpp wheel contains only C++ headers, libraries, and executables with no Python-specific code. Previously we built 4 identical wheels (one per Python version: cp310, cp311, cp312, cp313), wasting CI time and storage. This change produces a single wheel tagged py3-none-manylinux2014_* that works with any Python 3.x version. Changes: - Add ray-cpp-core.wanda.yaml and Dockerfile for cpp core - Add ray-cpp-wheel.wanda.yaml for cpp wheel builds - Add ci/build/build-ray-cpp-wheel.sh for Python-agnostic wheel builds - Add RayCppBdistWheel class to setup.py that forces py3-none tags (necessary because BinaryDistribution.has_ext_modules() causes bdist_wheel to use interpreter-specific ABI tags by default) - Update ray-cpp-wheel.wanda.yaml to build single wheel per architecture - Update .buildkite/build.rayci.yml to remove Python version matrix for cpp wheel build/upload steps Topic: ray-cpp-wheel Relative: crane-fix Signed-off-by: andrew --- .buildkite/build.rayci.yml | 44 +++++++++++++++ ci/build/build-ray-cpp-wheel.sh | 54 ++++++++++++++++++ ci/docker/ray-cpp-core.Dockerfile | 48 ++++++++++++++++ ci/docker/ray-cpp-core.wanda.yaml | 30 ++++++++++ ci/docker/ray-cpp-wheel.Dockerfile | 89 ++++++++++++++++++++++++++++++ ci/docker/ray-cpp-wheel.wanda.yaml | 28 ++++++++++ python/setup.py | 55 +++++++++++++++++- 7 files changed, 347 insertions(+), 1 deletion(-) create mode 100755 ci/build/build-ray-cpp-wheel.sh create mode 100644 ci/docker/ray-cpp-core.Dockerfile create mode 100644 ci/docker/ray-cpp-core.wanda.yaml create mode 100644 ci/docker/ray-cpp-wheel.Dockerfile create mode 100644 ci/docker/ray-cpp-wheel.wanda.yaml diff --git a/.buildkite/build.rayci.yml b/.buildkite/build.rayci.yml index f1bf8745ecb5..6011086daf85 100644 --- a/.buildkite/build.rayci.yml +++ b/.buildkite/build.rayci.yml @@ -76,6 +76,50 @@ steps: - skip-on-premerge - oss + - name: ray-cpp-core-build + label: "wanda: cpp core (x86_64)" + wanda: ci/docker/ray-cpp-core.wanda.yaml + env: + PYTHON_VERSION: "3.10" # Used for upstream images; output is Python-agnostic + ARCH_SUFFIX: "" + HOSTTYPE: "x86_64" + MANYLINUX_VERSION: "260103.868e54c" + tags: + - release_wheels + - oss + + - name: ray-cpp-wheel-build + label: "wanda: cpp wheel (x86_64)" + wanda: ci/docker/ray-cpp-wheel.wanda.yaml + env: + PYTHON_VERSION: "3.10" # Used for upstream images; wheel is Python-agnostic + ARCH_SUFFIX: "" + HOSTTYPE: "x86_64" + MANYLINUX_VERSION: "260103.868e54c" + tags: + - release_wheels + - linux_wheels + - oss + depends_on: + - ray-core-build + - ray-cpp-core-build + - ray-java-build + - ray-dashboard-build + + # Upload cpp wheel to S3 + - label: ":s3: upload: cpp wheel (x86_64)" + key: linux_cpp_wheels_upload + instance_type: small + commands: + - ./ci/build/extract_wanda_wheels.sh ray-cpp-wheel + - ./ci/build/copy_build_artifacts.sh wheel + depends_on: + - ray-cpp-wheel-build + tags: + - release_wheels + - skip-on-premerge + - oss + - label: ":tapioca: build: jar" key: java_wheels tags: diff --git a/ci/build/build-ray-cpp-wheel.sh b/ci/build/build-ray-cpp-wheel.sh new file mode 100755 index 000000000000..d65f557db04f --- /dev/null +++ b/ci/build/build-ray-cpp-wheel.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Build Python-agnostic ray-cpp wheel. +# +# The ray-cpp wheel contains only C++ headers, libraries, and executables +# with no Python version-specific content. This script builds a single wheel +# tagged py3-none-manylinux2014_* that works with any Python 3 version. +# +# Usage: ./ci/build/build-cpp-wheel.sh +# +# Environment variables: +# BUILDKITE_COMMIT - Git commit SHA for ray.__commit__ (required) + +set -exuo pipefail + +TRAVIS_COMMIT="${TRAVIS_COMMIT:-$BUILDKITE_COMMIT}" + +# Use any Python 3 to run setuptools (wheel content is Python-agnostic) +PYTHON="cp310-cp310" + +mkdir -p .whl +cd python + +/opt/python/"${PYTHON}"/bin/pip install -q cython==3.0.12 setuptools==80.9.0 + +# Set the commit SHA in _version.py +if [[ -n "$TRAVIS_COMMIT" ]]; then + sed -i.bak "s/{{RAY_COMMIT_SHA}}/$TRAVIS_COMMIT/g" ray/_version.py && rm ray/_version.py.bak +else + echo "TRAVIS_COMMIT variable not set - required to populate ray.__commit__." + exit 1 +fi + +# Determine platform tag based on architecture +ARCH=$(uname -m) +if [[ "$ARCH" == "x86_64" ]]; then + PLAT_NAME="manylinux2014_x86_64" +elif [[ "$ARCH" == "aarch64" ]]; then + PLAT_NAME="manylinux2014_aarch64" +else + echo "ERROR: Unsupported architecture: $ARCH" + exit 1 +fi + +echo "Building ray-cpp wheel (Python-agnostic, platform: $PLAT_NAME)..." + +# Build wheel with py3-none tag (Python version-agnostic) +# SKIP_BAZEL_BUILD=1 because artifacts are pre-built from wanda cache +PATH="/opt/python/${PYTHON}/bin:$PATH" \ +SKIP_BAZEL_BUILD=1 RAY_INSTALL_JAVA=0 RAY_INSTALL_CPP=1 \ +"/opt/python/${PYTHON}/bin/python" setup.py bdist_wheel \ + --python-tag py3 \ + --plat-name "$PLAT_NAME" + +mv dist/*.whl ../.whl/ diff --git a/ci/docker/ray-cpp-core.Dockerfile b/ci/docker/ray-cpp-core.Dockerfile new file mode 100644 index 000000000000..6f6d88e89027 --- /dev/null +++ b/ci/docker/ray-cpp-core.Dockerfile @@ -0,0 +1,48 @@ +# syntax=docker/dockerfile:1.3-labs +# +# Ray C++ Core Artifacts Builder +# ============================== +# Builds ray_cpp_pkg.zip containing C++ headers, libraries, and examples. +# +ARG ARCH_SUFFIX= +ARG HOSTTYPE=x86_64 +ARG MANYLINUX_VERSION +FROM rayproject/manylinux2014:${MANYLINUX_VERSION}-jdk-${HOSTTYPE} AS builder + +ARG PYTHON_VERSION=3.10 +ARG BUILDKITE_BAZEL_CACHE_URL +ARG BUILDKITE_CACHE_READONLY +ARG HOSTTYPE + +WORKDIR /home/forge/ray + +COPY . . + +# Mounting cache dir for faster local rebuilds (architecture-specific to avoid toolchain conflicts) +RUN --mount=type=cache,target=/home/forge/.cache,uid=2000,gid=100,id=bazel-cache-${HOSTTYPE}-${PYTHON_VERSION} \ + <<'EOF' +#!/bin/bash +set -euo pipefail + +PY_CODE="${PYTHON_VERSION//./}" +PY_BIN="cp${PY_CODE}-cp${PY_CODE}" +export RAY_BUILD_ENV="manylinux_py${PY_BIN}" + +sudo ln -sf "/opt/python/${PY_BIN}/bin/python3" /usr/local/bin/python3 +sudo ln -sf /usr/local/bin/python3 /usr/local/bin/python + +if [[ "${BUILDKITE_CACHE_READONLY:-}" == "true" ]]; then + echo "build --remote_upload_local_results=false" >> "$HOME/.bazelrc" +fi + +echo "build --repository_cache=/home/forge/.cache/bazel-repo" >> "$HOME/.bazelrc" + +bazelisk build --config=ci //cpp:ray_cpp_pkg_zip + +cp bazel-bin/cpp/ray_cpp_pkg.zip /home/forge/ray_cpp_pkg.zip + +EOF + +FROM scratch + +COPY --from=builder /home/forge/ray_cpp_pkg.zip / diff --git a/ci/docker/ray-cpp-core.wanda.yaml b/ci/docker/ray-cpp-core.wanda.yaml new file mode 100644 index 000000000000..cb398eb3cf34 --- /dev/null +++ b/ci/docker/ray-cpp-core.wanda.yaml @@ -0,0 +1,30 @@ +name: "ray-cpp-core-py$PYTHON_VERSION$ARCH_SUFFIX" +froms: ["rayproject/manylinux2014:$MANYLINUX_VERSION-jdk-$HOSTTYPE"] +dockerfile: ci/docker/ray-cpp-core.Dockerfile +srcs: + - .bazelversion + - .bazelrc + - WORKSPACE + - BUILD.bazel + - bazel/ + - thirdparty/ + - src/ + - cpp/ + - python/ray/__init__.py + - python/ray/_raylet.pxd + - python/ray/_raylet.pyi + - python/ray/_raylet.pyx + - python/ray/includes/ + - java/BUILD.bazel + - java/dependencies.bzl + - release/BUILD.bazel + - release/requirements.txt + - release/requirements_py310.txt +build_args: + - PYTHON_VERSION + - ARCH_SUFFIX + - HOSTTYPE + - MANYLINUX_VERSION + - BUILDKITE_BAZEL_CACHE_URL +build_hint_args: + - BUILDKITE_CACHE_READONLY diff --git a/ci/docker/ray-cpp-wheel.Dockerfile b/ci/docker/ray-cpp-wheel.Dockerfile new file mode 100644 index 000000000000..c963c28d668e --- /dev/null +++ b/ci/docker/ray-cpp-wheel.Dockerfile @@ -0,0 +1,89 @@ +# syntax=docker/dockerfile:1.3-labs +# +# Ray C++ Wheel Builder +# ===================== +# Builds manylinux2014-compatible ray-cpp wheel using pre-built C++ artifacts from wanda cache. +# +# This is a minimal Dockerfile for ray-cpp wheel builds only. +# It copies only the files needed for the cpp wheel, reducing build context size. + +ARG RAY_CORE_IMAGE +ARG RAY_CPP_CORE_IMAGE +ARG RAY_JAVA_IMAGE +ARG RAY_DASHBOARD_IMAGE +ARG MANYLINUX_VERSION +ARG HOSTTYPE + +FROM ${RAY_CORE_IMAGE} AS ray-core +FROM ${RAY_CPP_CORE_IMAGE} AS ray-cpp-core +FROM ${RAY_JAVA_IMAGE} AS ray-java +FROM ${RAY_DASHBOARD_IMAGE} AS ray-dashboard + +# Main build stage - manylinux2014 provides GLIBC 2.17 +FROM rayproject/manylinux2014:${MANYLINUX_VERSION}-jdk-${HOSTTYPE} AS builder + +ARG BUILDKITE_COMMIT + +WORKDIR /home/forge/ray + +# Copy artifacts from all stages +COPY --from=ray-core /ray_pkg.zip /tmp/ +COPY --from=ray-core /ray_py_proto.zip /tmp/ +COPY --from=ray-java /ray_java_pkg.zip /tmp/ +COPY --from=ray-dashboard /dashboard.tar.gz /tmp/ + +# Minimal source files needed for cpp wheel build +COPY --chown=forge ci/build/build-ray-cpp-wheel.sh ci/build/ +COPY --chown=forge README.rst pyproject.toml ./ +COPY --chown=forge python/setup.py python/ +COPY --chown=forge python/LICENSE.txt python/ +COPY --chown=forge python/MANIFEST.in python/ +COPY --chown=forge python/ray/_version.py python/ray/ + +USER forge +ENV BUILDKITE_COMMIT=${BUILDKITE_COMMIT:-unknown} +RUN --mount=from=ray-cpp-core,source=/,target=/ray-cpp-core,ro \ + <<'EOF' +#!/bin/bash +set -euo pipefail + +# Clean extraction dirs to avoid stale leftovers +rm -rf /tmp/ray_pkg /tmp/ray_java_pkg /tmp/ray_cpp_pkg +mkdir -p /tmp/ray_pkg /tmp/ray_java_pkg /tmp/ray_cpp_pkg + +# Unpack pre-built artifacts +unzip -o /tmp/ray_pkg.zip -d /tmp/ray_pkg +unzip -o /tmp/ray_py_proto.zip -d python/ +unzip -o /tmp/ray_java_pkg.zip -d /tmp/ray_java_pkg +mkdir -p python/ray/dashboard/client/build +tar -xzf /tmp/dashboard.tar.gz -C python/ray/dashboard/client/build/ + +# C++ core artifacts +cp -r /tmp/ray_pkg/ray/* python/ray/ + +# Java JARs +cp -r /tmp/ray_java_pkg/ray/* python/ray/ + +# C++ API artifacts (headers, libs, examples) +unzip -o /ray-cpp-core/ray_cpp_pkg.zip -d /tmp/ray_cpp_pkg +cp -r /tmp/ray_cpp_pkg/ray/cpp python/ray/ + +# Build Python-agnostic cpp wheel +./ci/build/build-ray-cpp-wheel.sh + +# Sanity check: ensure wheels exist +if [[ ! -d .whl ]]; then + echo "ERROR: .whl directory not created" + exit 1 +fi +wheels=($(find .whl -maxdepth 1 -name '*.whl')) +if (( ${#wheels[@]} == 0 )); then + echo "ERROR: No wheels produced in .whl/" + ls -la .whl + exit 1 +fi + +EOF + +FROM scratch +COPY --from=builder /home/forge/ray/.whl/*.whl / diff --git a/ci/docker/ray-cpp-wheel.wanda.yaml b/ci/docker/ray-cpp-wheel.wanda.yaml new file mode 100644 index 000000000000..94bfc4c2424f --- /dev/null +++ b/ci/docker/ray-cpp-wheel.wanda.yaml @@ -0,0 +1,28 @@ +# Python-agnostic ray-cpp wheel. +# Uses upstream images from one Python version but produces a py3-none wheel +# that works with any Python 3. PYTHON_VERSION is set at buildkite level. +name: "ray-cpp-wheel$ARCH_SUFFIX" +froms: + - "rayproject/manylinux2014:$MANYLINUX_VERSION-jdk-$HOSTTYPE" + - "cr.ray.io/rayproject/ray-core-py$PYTHON_VERSION$ARCH_SUFFIX" # C++ binaries (ray_pkg.zip) + - "cr.ray.io/rayproject/ray-cpp-core-py$PYTHON_VERSION$ARCH_SUFFIX" # C++ headers/libs (ray_cpp_pkg.zip) + - "cr.ray.io/rayproject/ray-java-build$ARCH_SUFFIX" # Java JARs + - "cr.ray.io/rayproject/ray-dashboard" # Dashboard +dockerfile: ci/docker/ray-cpp-wheel.Dockerfile +srcs: + - pyproject.toml + - README.rst + - ci/build/build-ray-cpp-wheel.sh + - python/setup.py + - python/LICENSE.txt + - python/MANIFEST.in + - python/ray/_version.py +build_args: + - MANYLINUX_VERSION + - HOSTTYPE + - BUILDKITE_COMMIT + - ARCH_SUFFIX + - RAY_CORE_IMAGE=cr.ray.io/rayproject/ray-core-py$PYTHON_VERSION$ARCH_SUFFIX + - RAY_CPP_CORE_IMAGE=cr.ray.io/rayproject/ray-cpp-core-py$PYTHON_VERSION$ARCH_SUFFIX + - RAY_JAVA_IMAGE=cr.ray.io/rayproject/ray-java-build$ARCH_SUFFIX + - RAY_DASHBOARD_IMAGE=cr.ray.io/rayproject/ray-dashboard diff --git a/python/setup.py b/python/setup.py index bd48e48e1533..23ace918d343 100644 --- a/python/setup.py +++ b/python/setup.py @@ -757,6 +757,13 @@ def pip_run(build_ext): import setuptools import setuptools.command.build_ext + # bdist_wheel location varies: setuptools>=70.1 has it built-in, + # older versions require the wheel package + try: + from setuptools.command.bdist_wheel import bdist_wheel + except ImportError: + from wheel.bdist_wheel import bdist_wheel + class build_ext(setuptools.command.build_ext.build_ext): def run(self): return pip_run(self) @@ -765,6 +772,46 @@ class BinaryDistribution(setuptools.Distribution): def has_ext_modules(self): return True + class RayCppBdistWheel(bdist_wheel): + """Custom bdist_wheel that produces Python-agnostic wheels for ray-cpp. + + PROBLEM + ------- + The ray-cpp wheel contains only C++ files (headers, libraries, executables) + with NO Python-specific code. It should work with any Python 3.x version. + However, by default, setuptools/bdist_wheel generates wheels tagged with + the specific Python interpreter used to build, e.g.: + + ray_cpp-3.0.0-cp310-cp310-manylinux2014_x86_64.whl + ^^^^^-^^^^^ + Python 3.10 specific! + + This class forces the wheel tag to be: + + ray_cpp-3.0.0-py3-none-manylinux2014_x86_64.whl + ^^^-^^^^ + Works with ANY Python 3! + + Tag meanings: + - "py3" = compatible with any Python 3.x + - "none" = no Python ABI dependency (no compiled .so using Python.h) + - "manylinux2014_x86_64" = platform tag (still needed for C++ binaries) + + """ + + def finalize_options(self): + super().finalize_options() + # Mark as non-pure to ensure we get a platform tag (e.g., manylinux2014). + # Pure wheels get "any" as the platform tag, which is wrong for ray-cpp + # since it contains platform-specific C++ binaries. + self.root_is_pure = False + + def get_tag(self): + # Get the platform tag from parent (e.g., "manylinux2014_x86_64") + _, _, platform_tag = super().get_tag() + # Force Python-agnostic tags: ("py3", "none", platform_tag) + return "py3", "none", platform_tag + # Ensure no remaining lib files. build_dir = os.path.join(ROOT_DIR, "build") if os.path.isdir(build_dir): @@ -781,6 +828,12 @@ def has_ext_modules(self): # If the license text has multiple lines, add an ending endline. license_text += "\n" + # Build cmdclass dict. Use RayCppBdistWheel for ray-cpp to produce + # Python-agnostic wheels. See RayCppBdistWheel docstring for details. + cmdclass = {"build_ext": build_ext} + if setup_spec.type == SetupType.RAY_CPP: + cmdclass["bdist_wheel"] = RayCppBdistWheel + setuptools.setup( name=setup_spec.name, version=setup_spec.version, @@ -801,7 +854,7 @@ def has_ext_modules(self): "Programming Language :: Python :: 3.13", ], packages=setup_spec.get_packages(), - cmdclass={"build_ext": build_ext}, + cmdclass=cmdclass, distclass=( # Avoid building extensions for deps-only builds. BinaryDistribution if setup_spec.build_type != BuildType.DEPS_ONLY else None ),