From 9a8ac86bc3d261c2a9c4a8d66ec2c77d75426b78 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 6 Sep 2026 19:14:54 +0200 Subject: [PATCH 1/5] correctionlib: add build-correctionlib.yml for riscv64 wheels Standard sdist-then-cibuildwheel port of the pybind11 correction-factor library (vendored pybind11/rapidjson/cpp-peglib/pcg-cpp/xxhash/lwtnn submodules). Drops musllinux (no riscv64 numpy wheel), narrows the test group to skip awkward/dask-awkward (their awkward-cpp dependency has no riscv64 wheel published yet), and builds but does not test cp314t (pandas and scipy publish no cp314t wheel on any platform yet). --- .github/workflows/build-correctionlib.yml | 147 ++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/build-correctionlib.yml diff --git a/.github/workflows/build-correctionlib.yml b/.github/workflows/build-correctionlib.yml new file mode 100644 index 000000000..c8882a647 --- /dev/null +++ b/.github/workflows/build-correctionlib.yml @@ -0,0 +1,147 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on upstream's own wheel build setup: +# https://github.com/cms-nanoAOD/correctionlib/blob/v2.9.0/.github/workflows/wheels.yml +name: Build correctionlib wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'correctionlib version to build (git tag without leading v, e.g. 2.9.0)' + required: true + default: '2.9.0' + pull_request: + paths: + - '.github/workflows/build-correctionlib.yml' + +concurrency: + group: ${{ github.workflow }}-${{ inputs.version || '2.9.0' }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + # `inputs.version` is empty on pull_request events; default to 2.9.0 there. + CORRECTIONLIB_VERSION: ${{ inputs.version || '2.9.0' }} + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + + build_sdist: + needs: [setup] + # The sdist is architecture-independent: it just packages the source tree + # and the vendored submodules (pybind11, rapidjson, cpp-peglib, pcg-cpp, + # xxhash, lwtnn) plus a setuptools-scm-derived version.py. Build it once on + # x86 instead of burning a scarce riscv runner (CLAUDE.md gotcha 4). + name: Build correctionlib ${{ inputs.version || '2.9.0' }} sdist + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + sdist_name: ${{ steps.sdist.outputs.sdist_name }} + steps: + - name: Checkout correctionlib v${{ env.CORRECTIONLIB_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: cms-nanoAOD/correctionlib + ref: v${{ env.CORRECTIONLIB_VERSION }} + submodules: recursive + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + + - name: Install Python + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + python-version: '3.12' + activate-environment: true + enable-cache: false + + - name: Build sdist + id: sdist + run: | + uv pip install build twine + python -m build --sdist + twine check dist/* + sdists=(dist/*.tar.gz) + echo "sdist_name=$(basename "${sdists[0]}")" >> "$GITHUB_OUTPUT" + + - name: Upload sdist artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: correctionlib-${{ env.CORRECTIONLIB_VERSION }}-sdist + path: dist/*.tar.gz + if-no-files-found: error + + build_wheels: + needs: [setup, build_sdist] + name: Build correctionlib ${{ inputs.version || '2.9.0' }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + permissions: + contents: read + strategy: + fail-fast: false + matrix: + python: ["cp312", "cp313", "cp314", "cp314t"] + include: + # pydantic (a hard runtime dependency) needs pandas+scipy's peer + # pydantic-core, which does publish a riscv64 cp314t wheel on PyPI - + # but pandas and scipy themselves (hard `test` dependency-group + # entries) publish no cp314t wheel for any platform yet, so the test + # env can't be installed. Build the wheel, skip only its tests. + - python: "cp314t" + test_skip: "*" + + steps: + - name: Download sdist + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: correctionlib-${{ env.CORRECTIONLIB_VERSION }}-sdist + path: dist/ + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + package-dir: dist/${{ needs.build_sdist.outputs.sdist_name }} + env: + CIBW_BUILD: ${{ matrix.python }}-* + CIBW_ARCHS: riscv64 + # numpy (a hard runtime dependency) ships no musllinux riscv64 wheel + # anywhere, so musllinux is dropped (workflow-anatomy.md: an accepted + # outcome). + CIBW_SKIP: '*-musllinux_*' + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + CIBW_TEST_SKIP: ${{ matrix.test_skip }} + # Mirror upstream's test-groups = ["test"] minus awkward/dask-awkward: + # both need awkward-cpp, which has no riscv64 wheel published yet + # (still in flight as its own port). Drop the two test files that + # hard-import awkward at module scope so collection doesn't fail. + CIBW_TEST_REQUIRES: >- + pandas uproot>=4.0.4 requests scipy "pytest>=6.0" pytest-run-parallel + pytest-benchmark + CIBW_TEST_COMMAND: >- + pytest {package}/tests + --ignore {package}/tests/test_highlevel.py + --ignore {package}/tests/test_issue296.py + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: correctionlib-${{ env.CORRECTIONLIB_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: ./wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish correctionlib ${{ inputs.version || '2.9.0' }} + needs: [setup, build_wheels] + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + with: + artifact-pattern: correctionlib-${{ inputs.version || '2.9.0' }}-*-manylinux_riscv64 From 98a2456e0d9031f209075287fdba20f0e8997180 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 6 Sep 2026 20:13:00 +0200 Subject: [PATCH 2/5] correctionlib: clear inherited test-groups and drop unused uproot test dep CIBW_TEST_REQUIRES adds to, not replaces, pyproject's test-groups, so the full upstream `test` group (including awkward/dask-awkward) was still installed alongside it. Clear it with CIBW_TEST_GROUPS: ''. Also drop uproot, which pulls in cramjam (no riscv64 wheel, fails cross-compiling from source) and which no test here actually imports. Restore upstream's own PIP_ONLY_BINARY so a similar gap fails fast instead of attempting a multi-hour source build. --- .github/workflows/build-correctionlib.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-correctionlib.yml b/.github/workflows/build-correctionlib.yml index c8882a647..1a6fad838 100644 --- a/.github/workflows/build-correctionlib.yml +++ b/.github/workflows/build-correctionlib.yml @@ -116,14 +116,21 @@ jobs: # outcome). CIBW_SKIP: '*-musllinux_*' CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} - CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + CIBW_ENVIRONMENT: >- + PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + PIP_ONLY_BINARY=:all: CIBW_TEST_SKIP: ${{ matrix.test_skip }} - # Mirror upstream's test-groups = ["test"] minus awkward/dask-awkward: - # both need awkward-cpp, which has no riscv64 wheel published yet - # (still in flight as its own port). Drop the two test files that - # hard-import awkward at module scope so collection doesn't fail. + # CIBW_TEST_REQUIRES adds to, not replaces, the pyproject `test-groups` + # dependency group, so it has to be cleared here (empty string wins + # over the inherited value, same as CIBW_BEFORE_BUILD: ''). + CIBW_TEST_GROUPS: '' + # Same list as upstream's `test` dependency-group minus uproot (which + # no test here actually imports) and awkward/dask-awkward (both need + # awkward-cpp, which has no riscv64 wheel published yet - still in + # flight as its own port). Drop the two test files that hard-import + # awkward at module scope so collection doesn't fail. CIBW_TEST_REQUIRES: >- - pandas uproot>=4.0.4 requests scipy "pytest>=6.0" pytest-run-parallel + pandas requests scipy "pytest>=6.0" pytest-run-parallel pytest-benchmark CIBW_TEST_COMMAND: >- pytest {package}/tests From e34d083339f0eca94812735df6142833f0d94bb2 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 6 Sep 2026 22:40:15 +0200 Subject: [PATCH 3/5] correctionlib: deselect test_lwtnn_example (arch float-rounding divergence) test_lwtnn_example asserts exact float equality on a lwtnn/Eigen neural-network evaluation and diverges from riscv64 at the 15th significant digit (~3e-16 relative, a couple of ULP) - an architecture-dependent rounding difference in the vectorized matmul, not a functional bug. Upstream hit and deselected the identical failure on manylinux_aarch64 (cms-nanoAOD/correctionlib#348). --- .github/workflows/build-correctionlib.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build-correctionlib.yml b/.github/workflows/build-correctionlib.yml index 1a6fad838..9ca937d32 100644 --- a/.github/workflows/build-correctionlib.yml +++ b/.github/workflows/build-correctionlib.yml @@ -132,10 +132,20 @@ jobs: CIBW_TEST_REQUIRES: >- pandas requests scipy "pytest>=6.0" pytest-run-parallel pytest-benchmark + # test_lwtnn_example asserts an exact float equality on the output of a + # lwtnn (Eigen-based) neural-network evaluation; it diverges from riscv64 + # starting at the 15th significant digit (0.9518682535564676 vs the + # asserted 0.95186825355646787, ~3e-16 relative, i.e. a couple of ULP) - + # an architecture float-rounding difference in the vectorized matmul, not + # a functional bug (CLAUDE.md gotcha 302). Upstream hit the identical + # failure on manylinux_aarch64 and deselected it there too (see upstream + # PR cms-nanoAOD/correctionlib#348), confirming the mechanism is + # architecture-generic, not riscv64-specific. CIBW_TEST_COMMAND: >- pytest {package}/tests --ignore {package}/tests/test_highlevel.py --ignore {package}/tests/test_issue296.py + --deselect {package}/tests/test_lwtnn.py::test_lwtnn_example - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: From 44800e74a7ea2fa5120a5e51cf50a42fc6be9b6e Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 6 Sep 2026 22:49:57 +0200 Subject: [PATCH 4/5] correctionlib: fix gotcha number in test_lwtnn_example comment 304, not 302 - renumbered to avoid a collision while landing the new gotcha entry in the skill (see gotchas/test-failures-and-flakes.md). --- .github/workflows/build-correctionlib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-correctionlib.yml b/.github/workflows/build-correctionlib.yml index 9ca937d32..7cce9b2da 100644 --- a/.github/workflows/build-correctionlib.yml +++ b/.github/workflows/build-correctionlib.yml @@ -137,7 +137,7 @@ jobs: # starting at the 15th significant digit (0.9518682535564676 vs the # asserted 0.95186825355646787, ~3e-16 relative, i.e. a couple of ULP) - # an architecture float-rounding difference in the vectorized matmul, not - # a functional bug (CLAUDE.md gotcha 302). Upstream hit the identical + # a functional bug (CLAUDE.md gotcha 304). Upstream hit the identical # failure on manylinux_aarch64 and deselected it there too (see upstream # PR cms-nanoAOD/correctionlib#348), confirming the mechanism is # architecture-generic, not riscv64-specific. From b169bfd94b2202fae7eb93737e582f6bd6ce298b Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 6 Sep 2026 23:28:31 +0200 Subject: [PATCH 5/5] correctionlib: fix path-based --deselect silently no-op'ing (gotcha 14/283), use -k instead --- .github/workflows/build-correctionlib.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-correctionlib.yml b/.github/workflows/build-correctionlib.yml index 7cce9b2da..3aa14cd6f 100644 --- a/.github/workflows/build-correctionlib.yml +++ b/.github/workflows/build-correctionlib.yml @@ -141,11 +141,14 @@ jobs: # failure on manylinux_aarch64 and deselected it there too (see upstream # PR cms-nanoAOD/correctionlib#348), confirming the mechanism is # architecture-generic, not riscv64-specific. + # A path-based --deselect here silently no-ops (gotcha 14/283): pytest is + # invoked from a staged copy, so its own rootdir-relative nodeids don't + # match the {package}-prefixed absolute-style path. Use -k instead. CIBW_TEST_COMMAND: >- pytest {package}/tests --ignore {package}/tests/test_highlevel.py --ignore {package}/tests/test_issue296.py - --deselect {package}/tests/test_lwtnn.py::test_lwtnn_example + -k "not test_lwtnn_example" - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: