Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .github/workflows/build-toolchain.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Build Toolchain

on:
workflow_call:
inputs:
runner:
required: true
type: string
workflow_dispatch:
inputs:
runner:
required: true
type: string
default: "ubuntu-22.04"

jobs:
build:
name: Build
runs-on: ${{ inputs.runner }}
timeout-minutes: 180
env:
GITHUB_ACTIONS: "false"
CARGO_TARGET_RISCV64IM_UNKNOWN_OPENVM_ELF_RUSTFLAGS: "-Cpasses=lower-atomic"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: "recursive"
fetch-depth: 0

- name: Detect host triple
id: host
run: |
# Match rustc's host detection
ARCH=$(uname -m)
OS=$(uname -s)
case "$ARCH" in
x86_64) ARCH_TRIPLE="x86_64" ;;
arm64|aarch64) ARCH_TRIPLE="aarch64" ;;
*) echo "Unsupported arch: $ARCH"; exit 1 ;;
esac
case "$OS" in
Linux) HOST="${ARCH_TRIPLE}-unknown-linux-gnu" ;;
Darwin) HOST="${ARCH_TRIPLE}-apple-darwin" ;;
*) echo "Unsupported OS: $OS"; exit 1 ;;
esac
echo "triple=$HOST" >> "$GITHUB_OUTPUT"
echo "Detected host triple: $HOST"

- name: Free disk space (Linux)
if: runner.os == 'Linux'
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache
df -h

- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y build-essential python3 curl cmake ninja-build pkg-config libssl-dev

- name: Write bootstrap.toml
run: |
cat > bootstrap.toml << 'EOF'
[build]
extended = true
tools = ["cargo", "cargo-clippy", "clippy", "rustfmt"]
cargo-native-static = true

[rust]
lld = true
llvm-tools = true

[llvm]
download-ci-llvm = false
EOF

- name: Create target sanity check workaround
run: |
mkdir -p /tmp/rustc-targets
cat > /tmp/rustc-targets/riscv64im-unknown-openvm-elf.json << 'EOF'
{"arch": "riscv64", "os": "openvm", "llvm-target": "riscv64"}
EOF

- name: Build toolchain
env:
RUST_TARGET_PATH: /tmp/rustc-targets
run: |
python3 x.py build --stage 2 compiler/rustc library \
--target ${{ steps.host.outputs.triple }},riscv64im-unknown-openvm-elf

- name: Package toolchain
run: |
cd build/${{ steps.host.outputs.triple }}/stage2
# Drop both `lib/rustlib/{src,rustc-src}`: we ship prebuilt std rlibs for the
# guest target, so consumers don't need `-Z build-std` and don't need rust-src.
tar --exclude lib/rustlib/src \
--exclude lib/rustlib/rustc-src \
-hczvf ../../../rust-toolchain-${{ steps.host.outputs.triple }}.tar.gz .

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: rust-toolchain-${{ steps.host.outputs.triple }}
path: rust-toolchain-${{ steps.host.outputs.triple }}.tar.gz
retention-days: 7
92 changes: 92 additions & 0 deletions .github/workflows/release-toolchain.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Release Toolchain

on:
push:
tags:
- "openvm-*"
workflow_dispatch:
inputs:
tag:
description: "Tag name for release (e.g., openvm-2.1.0-rv64)"
required: true
type: string

permissions:
contents: write

jobs:
build-x86_64-linux:
uses: ./.github/workflows/build-toolchain.yml
with:
runner: ubuntu-22.04

build-aarch64-linux:
uses: ./.github/workflows/build-toolchain.yml
with:
runner: ubuntu-22.04-arm

build-aarch64-darwin:
uses: ./.github/workflows/build-toolchain.yml
with:
runner: macos-14

release:
name: Create Release
needs: [build-x86_64-linux, build-aarch64-linux, build-aarch64-darwin]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Determine tag
id: tag
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
tag="${{ inputs.tag }}"
else
tag="${GITHUB_REF#refs/tags/}"
fi
# The tag must already exist and point at the commit this workflow is running on.
# That keeps release provenance unambiguous (no implicit tag creation, no off-commit publish).
tag_commit=$(git rev-list -n 1 "refs/tags/$tag" 2>/dev/null || true)
if [ -z "$tag_commit" ]; then
echo "::error::tag '$tag' does not exist in the repository"
exit 1
fi
if [ "$tag_commit" != "$GITHUB_SHA" ]; then
echo "::error::tag '$tag' -> $tag_commit, workflow commit -> $GITHUB_SHA — refusing to release from a different commit"
exit 1
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"

- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Collect tarballs
run: |
mkdir tars
find artifacts -name "*.tar.gz" -exec mv {} tars/ \;
ls -la tars/
test "$(ls -A tars)" || (echo "::error::no tarballs produced"; exit 1)

- name: Create or update GitHub Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.tag }}
run: |
# Rerun-safe: create-or-upload. If the release already exists (e.g. partial prior run),
# `gh release create` fails and we fall back to `upload --clobber` to refresh assets.
if ! gh release create "$TAG" \
--repo "${{ github.repository }}" \
--title "$TAG" \
--notes 'Pre-built Rust toolchain with `riscv64im-unknown-openvm-elf` target for OpenVM.' \
--draft \
tars/*; then
echo "Release already exists; uploading assets with --clobber"
gh release upload "$TAG" --repo "${{ github.repository }}" --clobber tars/*
fi
126 changes: 126 additions & 0 deletions OPENVM_RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# OpenVM Rust Toolchain — Release Process

This fork adds the `riscv64im-unknown-openvm-elf` tier 3 target and ships prebuilt toolchain tarballs as GitHub Releases.

## Branches

- `master` — tracks upstream `rust-lang/rust`. Never modified by us.
- `feat/riscv64im-unknown-openvm-elf` — the **source diff**: a single squashed commit on top of `master` with all openvm-specific changes (target spec, std PAL, panic_abort, bootstrap workaround, docs, the two GitHub Actions workflows, this file).
- `openvm-<channel>-<version>` — one branch per release, sitting on the exact upstream commit that backs that channel/date, with the source diff cherry-picked on top as a single commit. Tagged with the same name. Independent of `master` — `master` can keep advancing without disturbing existing release branches.

## Remotes

```sh
git remote add origin git@github.com:openvm-org/rust.git
git remote add upstream https://github.com/rust-lang/rust.git
```
All `git push` below means `git push origin`.

## Naming

Toolchain name = release tag = release branch name (all the same string).

| Upstream channel | OpenVM name |
|-------------------|-------------------------------|
| Stable `X.Y.Z` | `openvm-X.Y.Z` |
| Beta `YYYY-MM-DD` | `openvm-beta-YYYY-MM-DD` |
| Nightly `YYYY-MM-DD` | `openvm-nightly-YYYY-MM-DD` |

`release-toolchain.yml` triggers on any tag matching `openvm-*`.

## Cutting a release

```sh
NEW=openvm-nightly-2026-08-01

# Find the upstream commit for that nightly:
NEW_BASE=$(curl -s https://static.rust-lang.org/dist/2026-08-01/channel-rust-nightly-git-commit-hash.txt)
# For stable, use the upstream tag (e.g. `1.91.0`). For beta, swap
# `nightly` → `beta` in the URL above.

git fetch upstream
git cat-file -e "$NEW_BASE" || { echo "commit not in local history"; exit 1; }
git checkout -b "$NEW" "$NEW_BASE"
git cherry-pick feat/riscv64im-unknown-openvm-elf
# Resolve any conflicts (most common: library/std/src/sys/io/error/mod.rs
# when upstream added a new target_os entry). `git add` + `git cherry-pick --continue`.
git tag -a "$NEW" -m "openvm rust toolchain for $NEW"
git push origin "$NEW" # branch push only — does NOT fire the workflow
# Bump DEFAULT_RUSTUP_TOOLCHAIN_NAME in the downstream consumer's
# crates/toolchain/build/src/lib.rs to match $NEW. Open a PR there.
git push origin "refs/tags/$NEW" # tag push — fires release-toolchain.yml
```

## What the workflow does

`release-toolchain.yml` (tag push or `workflow_dispatch`):
1. Three parallel build jobs via reusable `build-toolchain.yml`:
| Host triple | Runner |
|-----------------------------|--------------------|
| `x86_64-unknown-linux-gnu` | `ubuntu-22.04` |
| `aarch64-unknown-linux-gnu` | `ubuntu-22.04-arm` |
| `aarch64-apple-darwin` | `macos-14` |
2. Each runs `python3 x.py build --stage 2 compiler/rustc library --target $HOST,riscv64im-unknown-openvm-elf`, then tars `build/$HOST/stage2/` (excluding `lib/rustlib/rustc-src`) as `rust-toolchain-$HOST.tar.gz`. ~2–3 h per job on hosted runners.
3. `release` job downloads all artifacts and runs `gh release create --draft`.

Tarballs ship prebuilt rlibs for both host and guest target — `lib/rustlib/<host>/lib/*.rlib` and `lib/rustlib/riscv64im-unknown-openvm-elf/lib/*.rlib`. The guest std is built with `-Cpasses=lower-atomic` (via `CARGO_TARGET_RISCV64IM_UNKNOWN_OPENVM_ELF_RUSTFLAGS` in the build job) and `panic = abort` (from the target spec), so consumers do **not** need `-Z build-std`. Both `rust-src` and `rustc-src` are excluded.

The downstream consumer invokes `cargo +<toolchain> build --target riscv64im-unknown-openvm-elf` and adds the openvm rustflags (`-Cpasses=lower-atomic`, `-C link-arg=-Ttext=…`, `--cfg getrandom_backend="custom"`, `--cfg openvm_intrinsics`) for its own user-crate codegen; std is already correctly compiled.

## Publishing the draft

After all four builds finish, open `https://github.com/openvm-org/rust/releases`, confirm the four `rust-toolchain-*.tar.gz` assets are attached, and hit "Publish release". Drafts are invisible to GitHub's public `latest` API; until you publish, the only way to consume the release is by explicit `--version <tag>`.

## Smoke-testing

```sh
cd /path/to/downstream-consumer
cargo install --path crates/cli # rebuild with the new DEFAULT_RUSTUP_TOOLCHAIN_NAME
cargo openvm toolchain install # downloads + rustup-links the latest release
cargo openvm toolchain list # confirms it's linked + marked default
# In a guest crate:
cargo openvm build # builds for riscv64im-unknown-openvm-elf
# Cleanup if needed:
cargo openvm toolchain uninstall
```
While the release is still draft, pin the version: `cargo openvm toolchain install --version <tag>`.

## Rotating to a new upstream

```sh
git fetch upstream
git push origin master # optional, keep our master synced
```
Then run "Cutting a release" with the new `NEW`. Previous release branches stay around — multiple toolchains coexist downstream under `~/.openvm/toolchains/`.

If upstream churn breaks the cherry-pick, rebase the source branch:

```sh
git checkout feat/riscv64im-unknown-openvm-elf
git rebase origin/master
git push --force-with-lease origin feat/riscv64im-unknown-openvm-elf
```

## File map (what's in the source diff)

- `compiler/rustc_target/src/spec/targets/riscv64im_unknown_openvm_elf.rs` — target spec.
- `compiler/rustc_target/src/spec/mod.rs` — target list entry + `Os::Openvm` variant.
- `library/std/src/sys/{alloc,args,env,pal/openvm,random,stdio}/openvm.rs` — std PAL stubs.
- `library/std/src/sys/{alloc,args,env,pal,random,stdio}/mod.rs`, `library/std/src/sys/io/error/mod.rs`, `library/std/src/sys/thread_local/mod.rs` — `cfg_select!` / cfg entries routing `target_os = "openvm"` to those PAL files.
- `library/std/src/sys/env_consts.rs` — `os::*` constants (FAMILY, EXE_EXTENSION, etc.) for the openvm target.
- `library/panic_abort/{Cargo.toml,src/lib.rs,src/openvm.rs}` — panic_abort PAL + alloc-dep gate.
- `library/std/{build.rs,src/random.rs}` — adds `openvm` to the no-libc target list and the random-source doc table.
- `library/test/src/{lib,console}.rs` — marks the `openvm` target as not supporting `Instant`, process spawning, or threads.
- `src/bootstrap/src/lib.rs` — enables the `compiler-builtins-mem` feature when building `std` for the `openvm` target. The bootstrap *target-sanity-check workaround* is **not** in source — it lives in the build workflow, which writes a dummy `riscv64im-unknown-openvm-elf.json` and exports `RUST_TARGET_PATH` so stage-0 rustc can validate the triple before the patched compiler is built.
- `src/doc/rustc/src/{platform-support.md,SUMMARY.md,platform-support/riscv64im-unknown-openvm-elf.md}` — docs index + platform-support page.
- `tests/assembly-llvm/targets/targets-elf.rs` — new target revision in the assembly target-coverage test.
- `tests/ui/check-cfg/{cfg-crate-features,well-known-values}.stderr` — `expected values for target_os` lists, regenerated.
- `.github/workflows/{build,release}-toolchain.yml` — release pipeline.
- `OPENVM_RELEASING.md` — this file.

## Troubleshooting

- **"no space left on device" on Linux runners.** Stage-2 rustc is tight on `ubuntu-22.04*`. The workflow already runs a "Free disk space" step; if it's still not enough, switch to a larger hosted SKU (`ubuntu-22.04-large`) or a self-hosted/RunsOn runner with `disk=large`.
- **Tag push didn't trigger the workflow.** Confirm the tag matches `openvm-*` and that the release branch contains `.github/workflows/release-toolchain.yml`. Tags pushed before the workflow file existed in the branch won't fire — delete and re-push.
- **`rust-toolchain-<host>.tar.gz` 404s for one host.** That host's build job silently failed, or the consumer's host isn't one of the four we support. Check `https://api.github.com/repos/openvm-org/rust/releases/tags/<tag>`.
- **Cherry-pick conflict.** Almost always `library/std/src/sys/io/error/mod.rs` — upstream added a new `target_os` entry between the source branch's master and this release branch's upstream commit. Manually merge the new upstream entry with our `target_os = "openvm"` line.
2 changes: 2 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,7 @@ supported_targets! {
("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu),
("riscv32gc-unknown-linux-musl", riscv32gc_unknown_linux_musl),
("riscv64im-unknown-none-elf", riscv64im_unknown_none_elf),
("riscv64im-unknown-openvm-elf", riscv64im_unknown_openvm_elf),
("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf),
("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf),
("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu),
Expand Down Expand Up @@ -2008,6 +2009,7 @@ crate::target_spec_enum! {
Nto = "nto",
NuttX = "nuttx",
OpenBsd = "openbsd",
Openvm = "openvm",
Psp = "psp",
Psx = "psx",
Qurt = "qurt",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use crate::spec::{
Arch, Cc, CodeModel, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, TargetMetadata,
TargetOptions,
};

pub(crate) fn target() -> Target {
Target {
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
llvm_target: "riscv64".into(),
metadata: TargetMetadata {
description: Some("OpenVM zero-knowledge Virtual Machine (RV64IM ISA)".into()),
tier: Some(3),
host_tools: Some(false),
std: Some(true),
},
pointer_width: 64,
arch: Arch::RiscV64,

options: TargetOptions {
os: Os::Openvm,
vendor: "unknown".into(),
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
linker: Some("rust-lld".into()),
cpu: "generic-rv64".into(),

// We set atomic_width to 64 for compatibility with crates such as crossbeam,
// but this should never be triggered since compilation should always lower
// atomics and be single-threaded.
max_atomic_width: Some(64),
atomic_cas: true,

features: "+m".into(),
llvm_abiname: "lp64".into(),
executables: true,
panic_strategy: PanicStrategy::Abort,
relocation_model: RelocModel::Static,
code_model: Some(CodeModel::Medium),
emit_debug_gdb_scripts: false,
eh_frame_header: false,
singlethread: true,
..Default::default()
},
}
}
2 changes: 1 addition & 1 deletion library/panic_abort/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ core = { path = "../rustc-std-workspace-core", package = "rustc-std-workspace-co
[target.'cfg(target_os = "android")'.dependencies]
libc = { version = "0.2", default-features = false }

[target.'cfg(any(target_os = "android", target_os = "zkvm"))'.dependencies]
[target.'cfg(any(target_os = "android", target_os = "zkvm", target_os = "openvm"))'.dependencies]
alloc = { path = "../alloc" }
Loading
Loading