diff --git a/.github/workflows/build-zccache.yml b/.github/workflows/build-zccache.yml new file mode 100644 index 00000000000..68af43510ee --- /dev/null +++ b/.github/workflows/build-zccache.yml @@ -0,0 +1,250 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on the `build`, `build-wheels` and `test-wheels` jobs of +# https://github.com/zackees/zccache/blob/1.13.21/.github/workflows/release-auto.yml +# (with its .github/actions/build-target/action.yml) and the `native-pytest` job of +# https://github.com/zackees/zccache/blob/1.13.21/.github/workflows/python-tests.yml +name: Build zccache wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/zccache.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-zccache.yml' + - 'docs/packages/zccache.yaml' + - 'patches/zccache/**' + push: + branches: [main] + paths: + - '.github/workflows/build-zccache.yml' + - 'docs/packages/zccache.yaml' + - 'patches/zccache/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: zccache + version: ${{ inputs.version }} + + build_wheel: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + name: Build zccache ${{ matrix.version }} py3-none-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 1440 + + env: + ZCCACHE_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout zccache ${{ env.ZCCACHE_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zackees/zccache + ref: ${{ env.ZCCACHE_VERSION }} + persist-credentials: false + + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Apply patches + run: git apply --verbose python-wheels/patches/zccache/${{ env.ZCCACHE_VERSION }}/*.patch + + - name: Write container script + run: | + cat > riscv64-build.sh <<'EOF' + #!/bin/bash + set -e -x + + export PATH="/opt/python/cp312-cp312/bin:${PATH}" + TARGET=riscv64gc-unknown-linux-gnu + + # Upstream cross-compiles every target from x86_64 through soldr/zig; this builds natively instead. + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain none + . "${HOME}/.cargo/env" + rustup toolchain install + + dnf install -y protobuf-compiler + + export PYO3_PYTHON=python + cargo build --release --target "${TARGET}" -p zccache \ + --features zccache-bin,fingerprint-bin \ + --bin zccache --bin zccache-fp + cargo build --release --target "${TARGET}" -p zccache-cli --features python --lib + cargo build --release --target "${TARGET}" -p zccache-watcher-py --lib + cargo build --release --target "${TARGET}" -p zccache-fingerprint-py --lib + + release="target/${TARGET}/release" + staging=dist/linux-riscv64-gnu + mkdir -p "${staging}/python/zccache/fingerprint" "${staging}/python/zccache/watcher" + cp "${release}/zccache" "${release}/zccache-fp" "${staging}/" + cp "${release}/libzccache_cli.so" "${staging}/python/zccache/_native.so" + cp "${release}/libzccache_fingerprint.so" "${staging}/python/zccache/fingerprint/_native.so" + cp "${release}/libzccache_watcher.so" "${staging}/python/zccache/watcher/_native.so" + for f in "${staging}/zccache" "${staging}/zccache-fp" "${staging}"/python/zccache/{,fingerprint/,watcher/}_native.so; do + strip --strip-debug "${f}" + done + + now="$(date -u +%s)" + for bin in zccache zccache-fp; do + python ci/stamp_release.py \ + --binary "${staging}/${bin}" \ + --sha "${ZCCACHE_SHA}" \ + --version "${ZCCACHE_VERSION}" \ + --triple "${TARGET}" \ + --timestamp "${now}" + done + + for bin in zccache zccache-fp; do + "${staging}/${bin}" --version | grep -Eq "^${bin} [0-9]" + done + export ZCCACHE_CACHE_DIR="$(mktemp -d)/cache" + "${staging}/zccache" "$(rustup which rustc)" -vV | grep -q '^host: ' + "${staging}/zccache" start + "${staging}/zccache" status + "${staging}/zccache" stop + + python - <<'PY' + from ci.release_workflow import build_wheel, read_project_meta + + build_wheel(*read_project_meta(), "linux-riscv64-gnu", ["manylinux_2_39_riscv64"]) + PY + EOF + + - name: Build wheel + run: | + podman run -t \ + --log-driver=none \ + --network=host \ + -v "${PWD}":/zccache_src \ + --workdir /zccache_src \ + -e ZCCACHE_VERSION="${ZCCACHE_VERSION}" \ + -e ZCCACHE_SHA="$(git rev-parse HEAD)" \ + --pull=newer \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash /zccache_src/riscv64-build.sh + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: zccache-${{ env.ZCCACHE_VERSION }}-py3-none-manylinux_riscv64 + path: dist/wheels/*.whl + if-no-files-found: error + + test_wheel: + name: Test zccache ${{ matrix.version }} ${{ matrix.python }} + needs: [setup, build_wheel] + if: needs.setup.outputs.versions != '[]' + runs-on: ubuntu-24.04-riscv + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + # The pyo3 extensions are abi3, which free-threaded interpreters cannot load. + python: ["cp312", "cp313", "cp314"] + + env: + ZCCACHE_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout zccache ${{ env.ZCCACHE_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zackees/zccache + ref: ${{ env.ZCCACHE_VERSION }} + persist-credentials: false + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: zccache-${{ env.ZCCACHE_VERSION }}-py3-none-manylinux_riscv64 + path: dist/wheels + + - name: Write container script + run: | + cat > riscv64-test.sh <<'EOF' + #!/bin/bash + set -e -x + + export PATH="/opt/python/${PYTHON_TAG}-${PYTHON_TAG}/bin:${PATH}" + + python -m pip install --only-binary=:all: /zccache_src/dist/wheels/*.whl pytest + + zccache --version + zccache-fp --version + python -c 'import zccache._native, zccache.fingerprint._native, zccache.watcher._native' + python - <<'PY' + import importlib.metadata as md + + shipped = {p.name for p in md.files("zccache") if ".dist-info/" in str(p)} + assert "LICENSE" in shipped, sorted(shipped) + PY + + python ci/test_exec_cached_wheel.py + + export ZCCACHE_CACHE_DIR="$(mktemp -d)/cache" + export ZCCACHE_DAEMON_NAMESPACE=native-python-tests + zccache start + for attempt in $(seq 1 30); do + zccache status && break + sleep 1 + done + zccache status + python -m pytest python/tests -q -rs --durations=10 + python -m pytest crates/zccache-watcher/python/tests -q -rs --durations=10 + python -m pytest crates/zccache-fingerprint/python/tests -q -rs --durations=10 + zccache stop + EOF + + - name: Test wheel + run: | + podman run -t \ + --log-driver=none \ + --network=host \ + -v "${PWD}":/zccache_src \ + --workdir /zccache_src \ + -e PYTHON_TAG="${{ matrix.python }}" \ + -e PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ \ + --pull=newer \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash /zccache_src/riscv64-test.sh + + publish: + name: Publish zccache ${{ matrix.version }} + needs: [setup, build_wheel, test_wheel] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: zccache-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/zccache.yaml b/docs/packages/zccache.yaml new file mode 100644 index 00000000000..7f21499a9fb --- /dev/null +++ b/docs/packages/zccache.yaml @@ -0,0 +1,6 @@ +package-name: zccache +source-code: https://github.com/zackees/zccache +license: AGPL-3.0-only +versions: +- version: 1.13.21 + patched: true diff --git a/patches/zccache/1.13.21/0001-protocol-fall-back-to-prost-build-s-own-protoc-looku.patch b/patches/zccache/1.13.21/0001-protocol-fall-back-to-prost-build-s-own-protoc-looku.patch new file mode 100644 index 00000000000..257f3da3ddd --- /dev/null +++ b/patches/zccache/1.13.21/0001-protocol-fall-back-to-prost-build-s-own-protoc-looku.patch @@ -0,0 +1,43 @@ +From 813a874854534eb6bbb9be60184312215ccf66bd Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Wed, 14 Oct 2026 14:27:20 +0000 +Subject: [PATCH] protocol: fall back to prost-build's own protoc lookup on + riscv64 + +Upstream-Status: Inappropriate [protoc-bin-vendored ships no riscv64 binary; prost_build already defaults to its own PROTOC-env/PATH lookup, so not overriding it on unsupported platforms is the whole fix] + +protoc_bin_vendored::protoc_bin_path() returns Err on any host outside +its small set of prebuilt targets (x86, x86_64, aarch64, ppc64le, s390x, +macOS, Windows), and build.rs turned that into a panic before +prost_build ever got a chance to look for a protoc on PATH or via +$PROTOC. Only exporting PROTOC when the vendored lookup succeeds +restores that fallback, which prost-build documents in its "Sourcing +protoc" section. + +Signed-off-by: Ludovic Henry +--- + crates/zccache-protocol/build.rs | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/crates/zccache-protocol/build.rs b/crates/zccache-protocol/build.rs +index 7179f40..aba99f4 100644 +--- a/crates/zccache-protocol/build.rs ++++ b/crates/zccache-protocol/build.rs +@@ -7,9 +7,11 @@ fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=proto/zccache_v1.proto"); + +- let protoc = protoc_bin_vendored::protoc_bin_path() +- .expect("vendored protoc is available for zccache wire protobufs"); +- std::env::set_var("PROTOC", protoc); ++ // protoc-bin-vendored ships no riscv64 binary; falling through leaves ++ // prost_build's own PROTOC-env/PATH lookup in charge there. ++ if let Ok(protoc) = protoc_bin_vendored::protoc_bin_path() { ++ std::env::set_var("PROTOC", protoc); ++ } + + prost_build::Config::new() + .compile_protos(&["proto/zccache_v1.proto"], &["proto"]) +-- +2.43.0 + diff --git a/patches/zccache/1.13.21/0002-deps-bump-crash-handler-to-0.8-for-riscv64-support.patch b/patches/zccache/1.13.21/0002-deps-bump-crash-handler-to-0.8-for-riscv64-support.patch new file mode 100644 index 00000000000..cc94dd6b397 --- /dev/null +++ b/patches/zccache/1.13.21/0002-deps-bump-crash-handler-to-0.8-for-riscv64-support.patch @@ -0,0 +1,137 @@ +From 574416402af8b0467efe616739889f6045fc8c8d Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Wed, 14 Oct 2026 14:30:47 +0000 +Subject: [PATCH] deps: bump crash-handler to 0.8 for riscv64 support + +Upstream-Status: To upstream [not yet submitted; python-wheels does not open issues/PRs on third-party repos] + +crash-handler 0.6 pins crash-context 0.6, whose Linux CrashContext only +defines ucontext_t/fpregset_t for x86, x86_64, arm and aarch64, so every +crate depending on it fails to compile for riscv64gc-unknown-linux-gnu +("cannot find type `ucontext_t`"). crash-context 0.7.0 added a riscv64 +getcontext, and crash-handler 0.8 is the first release built on it. The +CrashHandler::attach / make_crash_event / CrashContext API zccache uses +is unchanged; the lockfile update is limited to crash-handler, +crash-context and mach2. + +Signed-off-by: Ludovic Henry +--- + Cargo.lock | 15 ++++++--------- + crates/zccache-cli-core/Cargo.toml | 2 +- + crates/zccache-core/Cargo.toml | 2 +- + crates/zccache-daemon-core/Cargo.toml | 2 +- + crates/zccache-platform/Cargo.toml | 2 +- + crates/zccache/Cargo.toml | 2 +- + 6 files changed, 11 insertions(+), 14 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 45b1e18..2d4ea44 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -563,9 +563,9 @@ dependencies = [ + + [[package]] + name = "crash-context" +-version = "0.6.3" ++version = "0.8.0" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "031ed29858d90cfdf27fe49fae28028a1f20466db97962fa2f4ea34809aeebf3" ++checksum = "3748e166981fa3ba9dc54ff950ad0ed5ca4aab4b73eae21d9f539659fe806e28" + dependencies = [ + "cfg-if", + "libc", +@@ -574,9 +574,9 @@ dependencies = [ + + [[package]] + name = "crash-handler" +-version = "0.6.3" ++version = "0.8.1" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "2066907075af649bcb8bcb1b9b986329b243677e6918b2d920aa64b0aac5ace3" ++checksum = "b9897480a6a7009a9ca940c1813daf2755d8a3e6857e5ac4fd0ae526ff5682d1" + dependencies = [ + "cfg-if", + "crash-context", +@@ -1707,12 +1707,9 @@ checksum = "e6effa5c575f68109664a511f378c0e170888e33a426694ee0910a673762bd47" + + [[package]] + name = "mach2" +-version = "0.4.3" ++version = "0.6.0" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +-dependencies = [ +- "libc", +-] ++checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + + [[package]] + name = "matchers" +diff --git a/crates/zccache-cli-core/Cargo.toml b/crates/zccache-cli-core/Cargo.toml +index d898fff..ae836d3 100644 +--- a/crates/zccache-cli-core/Cargo.toml ++++ b/crates/zccache-cli-core/Cargo.toml +@@ -68,7 +68,7 @@ thiserror = { workspace = true } + tracing = { workspace = true } + serde = { workspace = true } + serde_json = { workspace = true } +-crash-handler = "0.6" ++crash-handler = "0.8" + blake3 = { workspace = true } + memmap2 = { workspace = true } + bincode = { workspace = true } +diff --git a/crates/zccache-core/Cargo.toml b/crates/zccache-core/Cargo.toml +index ddcde23..37d1c39 100644 +--- a/crates/zccache-core/Cargo.toml ++++ b/crates/zccache-core/Cargo.toml +@@ -11,7 +11,7 @@ description = "Core shared types and utilities for zccache" + publish = false + + [dependencies] +-crash-handler = "0.6" ++crash-handler = "0.8" + serde = { workspace = true } + serde_json = { workspace = true } + thiserror = { workspace = true } +diff --git a/crates/zccache-daemon-core/Cargo.toml b/crates/zccache-daemon-core/Cargo.toml +index f4123b5..9732c52 100644 +--- a/crates/zccache-daemon-core/Cargo.toml ++++ b/crates/zccache-daemon-core/Cargo.toml +@@ -41,7 +41,7 @@ thiserror = { workspace = true } + tracing = { workspace = true } + serde = { workspace = true } + serde_json = { workspace = true } +-crash-handler = "0.6" ++crash-handler = "0.8" + blake3 = { workspace = true } + memmap2 = { workspace = true } + bincode = { workspace = true } +diff --git a/crates/zccache-platform/Cargo.toml b/crates/zccache-platform/Cargo.toml +index 2ebc62a..a370080 100644 +--- a/crates/zccache-platform/Cargo.toml ++++ b/crates/zccache-platform/Cargo.toml +@@ -12,7 +12,7 @@ publish = false + [dependencies] + # Native mechanics arrive per capability phase (fs, ipc, process, executable, + # host); the leaf must never depend on any zccache-* crate. +-crash-handler = "0.6" ++crash-handler = "0.8" + interprocess = "2.4.2" + tokio = { workspace = true } + +diff --git a/crates/zccache/Cargo.toml b/crates/zccache/Cargo.toml +index f6ffdd1..d41e553 100644 +--- a/crates/zccache/Cargo.toml ++++ b/crates/zccache/Cargo.toml +@@ -98,7 +98,7 @@ thiserror = { workspace = true } + tracing = { workspace = true } + serde = { workspace = true } + serde_json = { workspace = true } +-crash-handler = "0.6" ++crash-handler = "0.8" + + # hash + blake3 = { workspace = true } +-- +2.43.0 + diff --git a/patches/zccache/1.13.21/0003-release_workflow-ship-LICENSE-in-every-wheel-s-dist-.patch b/patches/zccache/1.13.21/0003-release_workflow-ship-LICENSE-in-every-wheel-s-dist-.patch new file mode 100644 index 00000000000..d2d3ca51b8a --- /dev/null +++ b/patches/zccache/1.13.21/0003-release_workflow-ship-LICENSE-in-every-wheel-s-dist-.patch @@ -0,0 +1,39 @@ +From 82939f0ed04aa9aa77ff386203439f9127d83d13 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Wed, 14 Oct 2026 14:27:48 +0000 +Subject: [PATCH] release_workflow: ship LICENSE in every wheel's dist-info + +Upstream-Status: To upstream [not yet submitted; python-wheels does not open issues/PRs on third-party repos] + +build_wheel() hand-assembles each wheel from staged binaries and writes +only METADATA, WHEEL and RECORD into dist-info, so the published wheels +carry no copy of the AGPL-3.0 licence text even though they redistribute +compiled binaries. Copy the repository LICENSE to +dist-info/licenses/LICENSE (the PEP 639 location) and record it in +RECORD. + +Signed-off-by: Ludovic Henry +--- + ci/release_workflow.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/ci/release_workflow.py b/ci/release_workflow.py +index 6a23ce8..104756a 100644 +--- a/ci/release_workflow.py ++++ b/ci/release_workflow.py +@@ -368,6 +368,12 @@ def build_wheel( + whl.writestr(rel, data) + record_rows.append((rel, record_hash(data), len(data))) + ++ license_bytes = (ROOT / "LICENSE").read_bytes() ++ whl.writestr(f"{dist_info}/licenses/LICENSE", license_bytes) ++ record_rows.append( ++ (f"{dist_info}/licenses/LICENSE", record_hash(license_bytes), len(license_bytes)) ++ ) ++ + metadata_bytes = metadata.encode() + whl.writestr(f"{dist_info}/METADATA", metadata_bytes) + record_rows.append( +-- +2.43.0 +