Skip to content

ray: add build-ray.yml for riscv64 wheels - #393

Merged
luhenry merged 15 commits into
mainfrom
ray
Aug 28, 2026
Merged

ray: add build-ray.yml for riscv64 wheels#393
luhenry merged 15 commits into
mainfrom
ray

Conversation

@luhenry

@luhenry luhenry commented Aug 25, 2026

Copy link
Copy Markdown
Member

Builds ray's C++ core — raylet, gcs_server and the _raylet.so extension — with bazel. Upstream publishes no riscv64 wheel.

Mirrors upstream's build-wheel-manylinux2014.sh, driving the container directly rather than through cibuildwheel.

Differs from upstream

  • bazel bootstrapped from the dist archive; bazelisk publishes no riscv64 binary.
  • rules_python and rules_java patched during that bootstrap; neither has a riscv64 entry.
  • dashboard built on x86 and carried in as an artifact; node.js has no riscv64 release.
  • //:gen_redis_pkg dropped; it needs a perl toolchain rules_perl has no riscv64 target for.
  • bazel progress rendering quieted; the default output made the job log too large to retrieve.

Matrix: cp312/cp313/cp314 — upstream publishes no free-threaded wheel.

Testing

  • smoke test only: ray.init(), a remote task, ray.shutdown(). Upstream's suite needs a cluster.

License: ✅

Patches

  • 0001-Build-with-a-manylinux-interpreter-not-a-hermetic-CPython.patch — Inappropriate [riscv64 build environment]. Upstream's hermetic CPython fetch has no riscv64 artifact. riscv64-only.
  • 0002-Build-boost.context-s-riscv64-assembly.patch — To upstream [belongs in nelhage/rules_boost, which ray pins by commit]. Without it boost.context compiles with no assembly and _raylet.so fails to load on jump_fcontext. riscv64-only.

Comment thread .github/workflows/build-ray.yml Outdated
luhenry and others added 14 commits August 27, 2026 15:16
ray's C++ core is built with bazel, and neither of the two toolchains
upstream's ci/build/build-manylinux-forge.sh downloads exists for
riscv64: bazelisk publishes no riscv64 binary (bazel itself only ships
linux-x86_64/arm64), and node.js publishes no riscv64 release at all.

So the workflow splits upstream's single manylinux script into three
jobs: the dashboard (a React app, arch-independent output) is built on
x86 with upstream's pinned node; bazel is bootstrapped from the dist
archive on the riscv64 runner, inside the same manylinux image the
wheels are built in; and the wheel job then follows upstream's
per-interpreter loop.

Bootstrapping bazel 7.5.0 (ray's .bazelversion) on riscv64 needs one
workaround: bazel 7.x pins rules_python 0.33.2, whose PLATFORMS table
has no riscv64 entry, so fetching @pythons_hub aborts the bootstrap
(bazelbuild/bazel#23018). Upstream fixed riscv64 bootstrapping in bazel
8.2.0 (bazelbuild/bazel#25745); the 7.x backport request
(bazelbuild/bazel#26986) is still open.
Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
python/setup.py vendors psutil, colorama, aiohttp and idna into the wheel
with `pip install --target`, so the build container needs the riscv64
index too, not just the test container.
ray's WORKSPACE registers a python-build-standalone CPython 3.10 for the
host platform and load()s it at the top level, so it is fetched before
anything is analysed. The rules_python version ray resolves lists no
riscv64 platform, so the load fails with "No platform declared for host
OS linux on arch riscv64" -- the same trap that stops bazel 7.x from
bootstrapping there.

Point it at the manylinux image's own /opt/python/cp310-cp310 instead.
Keeping 3.10 matters: the pip vendored by that rules_python imports
distutils, so pointing it at the interpreter each wheel is built for
fails on 3.12+.

Checked on aarch64 with a bazel bootstrapped by the same recipe: the
patched tree analyses //:gen_ray_pkg (28166 targets configured) in
quay.io/pypa/manylinux_2_39_aarch64, where the unpatched tree stops at
the @python3_10 load.
rules_java 7.x maps riscv64 (and mips64) to [":include/linux"] rather
than ["include/linux"] in the jni cc_library's `includes`, so the
bootstrap compiles libcpu_profiler.so with

    -isystem external/rules_java~/toolchains/:include/linux

and dies on `fatal error: jni_md.h: No such file or directory`. The typo
is fixed in rules_java 8.x and was never backported to the 7.x line that
bazel 7.5.0 pins, so patch it the same way as rules_python, with
--override_module.
abefc31 is not a commit in actions/download-artifact, so the wheel job died in
"Set up job" before checking anything out. Use the v8.0.1 sha the rest of the
repo pins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review feedback: `docker run ... bash <<'SCRIPT'` instead of writing the script
into the workspace first, for the test step too. The smoke test becomes a nested
heredoc, so nothing is staged on disk any more — which also means the test
container no longer needs the checkout on its working directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The three wheels share one bazel output base so the C++ core is compiled once,
but grpc's python_configure repository rule only re-resolves Python.h when one
of the vars it declares in `environ` changes — PYTHON3_BIN_PATH, PYTHON3_LIB_PATH,
BAZEL_SH. The /usr/local/bin/python3 symlink the loop flips is not one of them,
so every _raylet.so would have been compiled against cp312's headers and failed
to import on cp313/cp314. ray's own .bazelrc asks for PYTHON3_BIN_PATH by name.

Assert _raylet is the compiled module in the smoke test so a regression here is
caught as a wrong .so rather than a confusing runtime failure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
//:gen_redis_pkg builds redis with BUILD_TLS=yes against an OpenSSL 1.1.1f that
rules_foreign_cc compiles from source, and that needs two things riscv64 does not
have: a rules_perl toolchain (its BUILD registers linux arm64/x86_64/windows and
darwin only, and relocatable-perl publishes no riscv64 build), and an OpenSSL with
a riscv64 target — 1.1.1f's Configurations have none, so `config` falls through to
linux-generic32.

Analysis failed on exactly that: "No matching toolchains found for types
@@rules_perl//:toolchain_type" while resolving @@rules_perl//:current_toolchain.

RAY_BUILD_REDIS=0 is upstream's own knob for this, and the two binaries it drops —
core/src/ray/thirdparty/redis/src/{redis-server,redis-cli} — are referenced only by
ray/_private/test_utils.py's GCS fault-tolerance helpers, not by any runtime path.
Standing up a system-perl toolchain to statically link an EOL 2020 OpenSSL,
configured as 32-bit on a 64-bit target, into a wheel we distribute is the worse
trade.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The wheel job's previous failure ran 3h43m and GitHub then dropped the job
log entirely: `gh api .../jobs/98025351929/logs` answers BlobNotFound, and
the run's log zip contains only the two short jobs. The failure was
undiagnosable, so the same tree had to be rebuilt blind.

bazel's default curses progress output is the bulk of that volume — a line
per action, rewritten continuously for hours. Upstream's setup.py already
exposes BAZEL_ARGS (python/setup.py: `bazel_flags.extend(shlex.split(BAZEL_ARGS))`),
so `--curses=no --show_progress_rate_limit=60` cuts it to roughly one
progress line a minute without touching anything else about the build.

Tee the step to build.log and upload it on failure as well, so a lost job
log can't cost another multi-hour cycle. `set -o pipefail` is load-bearing:
the default `run:` shell is `bash -e {0}` without it, and tee would
otherwise swallow the build's exit status.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RocksDB (the GCS fault-tolerance backend, bazel/rocksdb.BUILD) is built
through rules_foreign_cc's cmake() rule. rules_foreign_cc 0.9.0 downloads a
prebuilt CMake for the platforms Kitware publishes and otherwise falls back
to building CMake from source, which is the path riscv64 takes — and that
bootstrap fails, 2h50m into the build:

    + CXX=/usr/bin/gcc
    Error when bootstrapping CMake:
    Cannot find a C++ compiler that supports both C++11 and the specified
    C++ flags.

rules_foreign_cc derives CXX from bazel's auto-detected cc toolchain, which
names a single driver (`gcc`) for both C and C++, so CMake 3.23.2's bootstrap
probe cannot link a C++ program. Upstream never reaches this code path: every
architecture they build has a prebuilt CMake to download.

The manylinux_2_39_riscv64 image already ships cmake 4.4.2 in /usr/local/bin,
which is on the fixed PATH bazel gives actions under
--incompatible_strict_action_env, so select the preinstalled toolchain rather
than fixing up the bootstrap's compiler. That also drops the from-source CMake
build from the critical path entirely.

Verified on riscv64 under emulation that the image's cmake configures RocksDB
10.6.2 with ray's own cache entries: "Configuring done / Generating done",
so cmake 4.x is not too new for this RocksDB.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rules_boost selects Boost.Context's per-ABI assembly with a select() that
has no riscv64 branch and ends in "//conditions:default": [], so @boost//:context
was compiled with none. jump_fcontext, make_fcontext and ontop_fcontext were
left undefined in _raylet.so and the wheel failed on its first import with

    OSError: .../ray/_raylet.so: undefined symbol: jump_fcontext

Boost 1.81.0, the version ray pins, already ships the riscv64 sources, so the
patch only has to wire them up. It goes in through ray's existing
thirdparty/patches list, next to the boost-headers.patch it already carries.

Also upload the wheels before the smoke test, so a wheel that fails to import
is still available to inspect.
@luhenry
luhenry merged commit edf7173 into main Aug 28, 2026
7 checks passed
@luhenry
luhenry deleted the ray branch August 28, 2026 05:18
@luhenry luhenry linked an issue Aug 28, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ray riscv64 support

2 participants