From 867f3c203cd5f33bbc8a29cd2e9f72aa6e62207c Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 15 Oct 2026 02:48:50 +0000 Subject: [PATCH 1/8] Add riscv64 build for cbcbox cbcbox ships a prebuilt binary distribution of the CBC MILP solver (COIN-OR Branch and Cut): a py3-none-manylinux*_{x86_64,aarch64} wheel built by compiling OpenBLAS, SuiteSparse AMD, and the CoinUtils/Osi/ Clp/Cgl/Cbc stack from source and bundling the resulting binaries and shared libraries. The architecture-specific code is cleanly gated behind an x86_64 check (AVX2/Haswell variants only); the generic build path already exercised on aarch64 compiles with a plain Fortran/C++ toolchain and has no riscv64-blocking assumptions. Mirror the upstream wheel.yml's Linux ARM64 jobs (compile generic + compile debug, then package with cibuildwheel, compilation skipped) on manylinux_2_39_riscv64, since manylinux2014 (upstream's own tag for x86_64/aarch64) predates riscv64 entirely. OpenBLAS's DYNAMIC_ARCH build supports riscv64 natively (RISCV64_GENERIC/ZVL128B/ZVL256B kernels, confirmed against the pinned v0.3.31 source), Rocky Linux 10's riscv64 AppStream repo carries gcc-gfortran, and PyPI's patchelf package (used directly by cbcbox's own bundling logic, not auditwheel) ships a manylinux_2_31_riscv64 wheel. cbcbox bumps its version on master without ever pushing a git tag for most releases (v2.931-v2.933 are tagged, 2.934/2.935 are not), so pin the exact commit that carries version = "2.935": its timestamp sits about 3h20m before the 2.935 wheels were uploaded to PyPI, with no further pyproject.toml change in between. setup.py's custom bdist_wheel only maps linux_x86_64/linux_aarch64 to their manylinux2014 wheel tag; patch in a linux_riscv64 mapping to manylinux_2_39_riscv64 (patches/cbcbox/2.935/), since upstream has no riscv64 CI of its own to carry this. Note for later review: cbcbox's own bundling logic does not copy a LICENSE file for the vendored OpenBLAS/SuiteSparse AMD (BSD) next to the COIN-OR (EPL-2.0) ones it does copy -- a pre-existing gap on the already-published x86_64/aarch64 wheels, not introduced by or specific to this riscv64 port. --- .github/workflows/build-cbcbox.yml | 269 ++++++++++++++++++ docs/packages/cbcbox.yaml | 5 + ...cv64-to-manylinux_2_39_riscv64-in-th.patch | 40 +++ 3 files changed, 314 insertions(+) create mode 100644 .github/workflows/build-cbcbox.yml create mode 100644 docs/packages/cbcbox.yaml create mode 100644 patches/cbcbox/2.935/0001-setup.py-map-riscv64-to-manylinux_2_39_riscv64-in-th.patch diff --git a/.github/workflows/build-cbcbox.yml b/.github/workflows/build-cbcbox.yml new file mode 100644 index 000000000000..0732dbf0f783 --- /dev/null +++ b/.github/workflows/build-cbcbox.yml @@ -0,0 +1,269 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on the "Linux ARM64" jobs of +# https://github.com/h-g-s/cbcbox/blob/cd7056156bebb4347c2273ccbc0ca5e06170970d/.github/workflows/wheel.yml +name: Build cbcbox wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/cbcbox.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-cbcbox.yml' + - 'docs/packages/cbcbox.yaml' + - 'patches/cbcbox/**' + push: + branches: [main] + paths: + - '.github/workflows/build-cbcbox.yml' + - 'docs/packages/cbcbox.yaml' + - 'patches/cbcbox/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + # cbcbox bumps pyproject.toml's version on master without ever pushing a + # git tag for it (v2.931/v2.932/v2.933 are tagged, 2.934/2.935 are not), so + # pin the exact commit that carries version = "2.935": cd70561 bumped + # 2.934 -> 2.935 on 2026-08-16T22:43:04Z and the 2.935 wheels were uploaded + # to PyPI ~3h20m later (2026-08-17T02:08Z), with no further pyproject.toml + # change in between. Update this alongside the docs/packages/cbcbox.yaml + # version entry. + CBCBOX_REF: cd7056156bebb4347c2273ccbc0ca5e06170970d + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: cbcbox + version: ${{ inputs.version }} + + compile_linux_riscv64_generic: + name: Compile cbcbox ${{ matrix.version }} Linux riscv64 generic + needs: [setup] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + runs-on: ubuntu-24.04-riscv + timeout-minutes: 300 + env: + CBCBOX_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout cbcbox ${{ env.CBCBOX_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: h-g-s/cbcbox + ref: ${{ env.CBCBOX_REF }} + persist-credentials: false + + - name: Build generic binary (manylinux_2_39_riscv64) + run: | + for i in 1 2 3; do + docker pull "${MANYLINUX_RISCV64_IMAGE}" && break + echo "Attempt $i failed, retrying in 30s..." + sleep 30 + done + docker run --rm \ + -v "$PWD:/project" \ + -e CBCBOX_BUILD_VARIANT=generic \ + -e CBCBOX_BUILD_ONLY=1 \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash -c 'export PATH=/opt/python/cp313-cp313/bin:$PATH && + dnf install -y gcc-gfortran && + pip install setuptools wheel patchelf && + cd /project && + python setup.py build_ext' + sudo chown -R "$USER:$USER" cbc_dist/ 2>/dev/null || true + tar -czf cbc_dist_generic.tar.gz cbc_dist/ + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: cbcbox-${{ env.CBCBOX_VERSION }}-dist-linux-riscv64-generic + path: cbc_dist_generic.tar.gz + if-no-files-found: error + + compile_linux_riscv64_debug: + name: Compile cbcbox ${{ matrix.version }} Linux riscv64 debug + needs: [setup] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + runs-on: ubuntu-24.04-riscv + timeout-minutes: 300 + env: + CBCBOX_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout cbcbox ${{ env.CBCBOX_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: h-g-s/cbcbox + ref: ${{ env.CBCBOX_REF }} + persist-credentials: false + + - name: Build debug binary (manylinux_2_39_riscv64) + run: | + for i in 1 2 3; do + docker pull "${MANYLINUX_RISCV64_IMAGE}" && break + echo "Attempt $i failed, retrying in 30s..." + sleep 30 + done + docker run --rm \ + -v "$PWD:/project" \ + -e CBCBOX_BUILD_VARIANT=debug \ + -e CBCBOX_BUILD_ONLY=1 \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash -c 'export PATH=/opt/python/cp313-cp313/bin:$PATH && + dnf install -y gcc-gfortran && + pip install setuptools wheel patchelf && + cd /project && + python setup.py build_ext && + bash scripts/build_mip_debug_cuts.sh cbc_dist_debug Cbc' + sudo chown -R "$USER:$USER" cbc_dist_debug/ 2>/dev/null || true + tar -czf cbc_dist_debug.tar.gz cbc_dist_debug/ + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: cbcbox-${{ env.CBCBOX_VERSION }}-dist-linux-riscv64-debug + path: cbc_dist_debug.tar.gz + if-no-files-found: error + + package_linux_riscv64: + name: Package cbcbox ${{ matrix.version }} py3-none-manylinux_riscv64 + needs: [setup, compile_linux_riscv64_generic, compile_linux_riscv64_debug] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + runs-on: ubuntu-24.04-riscv + timeout-minutes: 300 + env: + CBCBOX_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout cbcbox ${{ env.CBCBOX_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: h-g-s/cbcbox + ref: ${{ env.CBCBOX_REF }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + # manylinux2014 (upstream's own x86_64/aarch64 wheel tag) predates + # riscv64; map to manylinux_2_39, the policy this job's image is built + # against. + - name: Patch cbcbox source + run: git apply python-wheels/patches/cbcbox/${{ env.CBCBOX_VERSION }}/*.patch + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: cbcbox-${{ env.CBCBOX_VERSION }}-dist-linux-riscv64-generic + path: . + - run: tar -xzf cbc_dist_generic.tar.gz + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: cbcbox-${{ env.CBCBOX_VERSION }}-dist-linux-riscv64-debug + path: . + - run: tar -xzf cbc_dist_debug.tar.gz + + - name: Build and test wheel (compilation skipped — binaries pre-built) + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + package-dir: . + output-dir: wheelhouse + config-file: "{package}/pyproject.toml" + env: + CIBW_BUILD: "cp313-*" + CIBW_SKIP: "*musllinux*" + CIBW_ARCHS: "riscv64" + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + CIBW_BEFORE_ALL_LINUX: > + dnf install -y gcc-gfortran && + pip install patchelf + CIBW_ENVIRONMENT_LINUX: "PERF_REPORT_DIR=/output" + CIBW_REPAIR_WHEEL_COMMAND_LINUX: "" + CIBW_TEST_REQUIRES: "pytest" + CIBW_TEST_COMMAND: "mkdir -p /output && pytest {package}/tests/ -v" + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: cbcbox-${{ env.CBCBOX_VERSION }}-py3-none-manylinux_riscv64 + path: ./wheelhouse/*.whl + if-no-files-found: error + + gpl_sources: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + name: Collect GPL sources + runs-on: ubuntu-24.04-riscv + + env: + CBCBOX_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + # cbcbox bundles libgfortran (OpenBLAS is built from source with + # gfortran) into the wheel; its source comes from the image's own gcc, + # not from cbcbox. + - uses: ./actions/collect-gpl-sources + with: + image: ${{ env.MANYLINUX_RISCV64_IMAGE }} + packages: gcc + output: gpl-sources.tar + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: cbcbox-${{ env.CBCBOX_VERSION }}-gpl-sources + path: gpl-sources.tar + if-no-files-found: error + + publish: + name: Publish cbcbox ${{ matrix.version }} + needs: [setup, package_linux_riscv64, gpl_sources] + 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: cbcbox-${{ matrix.version }}-py3-none-manylinux_riscv64 + gpl-sources-artifact: cbcbox-${{ matrix.version }}-gpl-sources + gpl-sources-description: gcc (libgfortran, linked into the bundled OpenBLAS) diff --git a/docs/packages/cbcbox.yaml b/docs/packages/cbcbox.yaml new file mode 100644 index 000000000000..5d6f069222a8 --- /dev/null +++ b/docs/packages/cbcbox.yaml @@ -0,0 +1,5 @@ +package-name: cbcbox +source-code: https://github.com/h-g-s/cbcbox +license: EPL-2.0 +versions: +- version: '2.935' diff --git a/patches/cbcbox/2.935/0001-setup.py-map-riscv64-to-manylinux_2_39_riscv64-in-th.patch b/patches/cbcbox/2.935/0001-setup.py-map-riscv64-to-manylinux_2_39_riscv64-in-th.patch new file mode 100644 index 000000000000..066c2ea7fad5 --- /dev/null +++ b/patches/cbcbox/2.935/0001-setup.py-map-riscv64-to-manylinux_2_39_riscv64-in-th.patch @@ -0,0 +1,40 @@ +From 340631d1648be435e1bc354bf4aa4150aec41603 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Thu, 15 Oct 2026 12:00:00 +0000 +Subject: [PATCH] setup.py: map riscv64 to manylinux_2_39_riscv64 in the wheel + platform tag + +genericpy_bdist_wheel.get_tag() only special-cases linux_x86_64 and +linux_aarch64 (both mapped to the manylinux2014 policy this project +targets on those architectures). riscv64 falls through unmapped, so a +CIBUILDWHEEL=1 build on linux_riscv64 would otherwise ship a wheel +tagged plain 'linux_riscv64', which pip rejects as non-manylinux. + +manylinux2014 predates riscv64 entirely (PEP 599 has no riscv64 +mapping), so riscv64 is tagged against manylinux_2_39 instead -- the +oldest manylinux policy with a riscv64 image, matching the +quay.io/pypa/manylinux_2_39_riscv64 container used to build it. + +Upstream-Status: Inappropriate [cbcbox's own CI (wheel.yml) does not build for riscv64 at all -- no GitHub-hosted riscv64 runner exists -- so this mapping has no upstream job to exercise it] + +Signed-off-by: Ludovic Henry +--- + setup.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/setup.py b/setup.py +index 15d501d..a8c01e4 100644 +--- a/setup.py ++++ b/setup.py +@@ -27,6 +27,8 @@ class genericpy_bdist_wheel(_bdist_wheel): + plat = "manylinux2014_x86_64" + elif plat == "linux_aarch64": + plat = "manylinux2014_aarch64" ++ elif plat == "linux_riscv64": ++ plat = "manylinux_2_39_riscv64" + return python, abi, plat + + +-- +2.43.0 + From 10f2662595bf249b405ef707446d843e2819ec58 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 5 Nov 2026 07:57:29 -0800 Subject: [PATCH 2/8] cbcbox: Record target Python versions --- docs/packages/cbcbox.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/packages/cbcbox.yaml b/docs/packages/cbcbox.yaml index 5d6f069222a8..e324ad4b54a6 100644 --- a/docs/packages/cbcbox.yaml +++ b/docs/packages/cbcbox.yaml @@ -3,3 +3,4 @@ source-code: https://github.com/h-g-s/cbcbox license: EPL-2.0 versions: - version: '2.935' + python-versions: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.15'] From 3020426a711b02a4694ecacd0d4e80e3ff0a0983 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 5 Nov 2026 08:32:35 -0800 Subject: [PATCH 3/8] cbcbox: Use dynamic Python matrix --- .github/workflows/build-cbcbox.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-cbcbox.yml b/.github/workflows/build-cbcbox.yml index 0732dbf0f783..60c7d2d675ce 100644 --- a/.github/workflows/build-cbcbox.yml +++ b/.github/workflows/build-cbcbox.yml @@ -57,7 +57,7 @@ jobs: strategy: fail-fast: false matrix: - version: ${{ fromJSON(needs.setup.outputs.versions) }} + include: ${{ fromJSON(needs.setup.outputs.matrix) }} runs-on: ubuntu-24.04-riscv timeout-minutes: 300 env: From ad29870c4201ea48daa7b2675fc53bc76f439552 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 6 Nov 2026 09:22:31 -0800 Subject: [PATCH 4/8] cbcbox: Use target file metadata --- docs/packages/cbcbox.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/packages/cbcbox.yaml b/docs/packages/cbcbox.yaml index e324ad4b54a6..b1f139bea084 100644 --- a/docs/packages/cbcbox.yaml +++ b/docs/packages/cbcbox.yaml @@ -3,4 +3,11 @@ source-code: https://github.com/h-g-s/cbcbox license: EPL-2.0 versions: - version: '2.935' - python-versions: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.15'] + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11:manylinux + - python: 3.12:manylinux + - python: 3.13:manylinux + - python: 3.14:manylinux + - python: 3.15:manylinux From ab9c8fe8cc1a64a4341094d67a5040c681a8d8ca Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 6 Nov 2026 09:54:15 -0800 Subject: [PATCH 5/8] cbcbox: Avoid duplicate platform-independent builds --- .github/workflows/build-cbcbox.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-cbcbox.yml b/.github/workflows/build-cbcbox.yml index 60c7d2d675ce..0732dbf0f783 100644 --- a/.github/workflows/build-cbcbox.yml +++ b/.github/workflows/build-cbcbox.yml @@ -57,7 +57,7 @@ jobs: strategy: fail-fast: false matrix: - include: ${{ fromJSON(needs.setup.outputs.matrix) }} + version: ${{ fromJSON(needs.setup.outputs.versions) }} runs-on: ubuntu-24.04-riscv timeout-minutes: 300 env: From 9b0cf6ffd9ff2d5a1c8643140f6446bd28a18bc4 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 9 Nov 2026 09:51:09 +0000 Subject: [PATCH 6/8] cbcbox: Give the riscv64 package job a realistic timeout package_linux_riscv64's cibuildwheel step builds and tests the wheel as a single action, so its 61-case MILP-solve pytest suite inherits whatever is left of the job's timeout-minutes: 300. On run 42301146079 that suite was genuinely progressing (28/61 cases passed in ~4h52m, each taking minutes on this fleet's riscv64 hardware, not hung) when the job hit its 5h ceiling and GitHub Actions auto-cancelled it mid-test, losing the already-built wheel in the process (gotcha 598). Split testing into its own step, with the wheel uploaded first and its own generous-but-bounded timeout, and give the job enough headroom overall for the suite to actually finish. --- .github/workflows/build-cbcbox.yml | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-cbcbox.yml b/.github/workflows/build-cbcbox.yml index 0732dbf0f783..22eb6e57a52e 100644 --- a/.github/workflows/build-cbcbox.yml +++ b/.github/workflows/build-cbcbox.yml @@ -154,7 +154,13 @@ jobs: matrix: version: ${{ fromJSON(needs.setup.outputs.versions) }} runs-on: ubuntu-24.04-riscv - timeout-minutes: 300 + # 24h: see the "Test wheel" step below (CLAUDE.md gotcha 598) — the 61-case + # MILP-solve test suite is genuinely slow on this fleet's riscv64 hardware, + # not hung. A single build+test job otherwise gives the test step whatever + # is left of this timeout, so a merely-slow (not stuck) suite can still burn + # the whole budget and end in "cancelled" with the already-built wheel never + # uploaded (run 42301146079: cancelled at 5h00m with 28/61 cases done). + timeout-minutes: 1440 env: CBCBOX_VERSION: ${{ matrix.version }} @@ -190,7 +196,7 @@ jobs: path: . - run: tar -xzf cbc_dist_debug.tar.gz - - name: Build and test wheel (compilation skipped — binaries pre-built) + - name: Build wheel (compilation skipped — binaries pre-built) uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 with: package-dir: . @@ -204,10 +210,7 @@ jobs: CIBW_BEFORE_ALL_LINUX: > dnf install -y gcc-gfortran && pip install patchelf - CIBW_ENVIRONMENT_LINUX: "PERF_REPORT_DIR=/output" CIBW_REPAIR_WHEEL_COMMAND_LINUX: "" - CIBW_TEST_REQUIRES: "pytest" - CIBW_TEST_COMMAND: "mkdir -p /output && pytest {package}/tests/ -v" - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: @@ -215,6 +218,27 @@ jobs: path: ./wheelhouse/*.whl if-no-files-found: error + # Split from the build step above and given its own bounded timeout + # (CLAUDE.md gotcha 598), so the wheel is uploaded before this runs and a + # slow (or genuinely stuck) test run can't silently consume the rest of + # the job's 24h budget. Reproduces cibuildwheel's own test phase (pytest + # against the installed wheel, PERF_REPORT_DIR for the solver's perf + # reports) without its wrapping, since testing no longer happens inside + # the build action. + - name: Test wheel (manylinux_2_39_riscv64) + timeout-minutes: 1380 # 23h + run: | + docker run --rm \ + -v "$PWD:/project" \ + -v "$PWD/wheelhouse:/wheelhouse" \ + -e PERF_REPORT_DIR=/output \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash -c 'export PATH=/opt/python/cp313-cp313/bin:$PATH && + mkdir -p /output && + pip install /wheelhouse/*.whl pytest && + cd /project && + pytest tests/ -v' + gpl_sources: needs: [setup] if: needs.setup.outputs.versions != '[]' From b207fe7c27001e07bd1759bc7d750ce813856162 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Tue, 10 Nov 2026 02:29:29 +0000 Subject: [PATCH 7/8] cbcbox: pass matrix input to _publish-wheel.yml _publish-wheel.yml now requires a matrix input (main's "publish: Validate effective wheel targets"), but this PR branch forked before that landed and cbcbox has never been merged to main, so build-cbcbox.yml never picked up the matching one-line update every already-published package's workflow got. The merge ref combines the new required-input _publish-wheel.yml with this branch's old build-cbcbox.yml, so any/every run has failed startup with "Input matrix is required, but not provided". Pass it the same way the mass-update commit did for every other package's publish job. --- .github/workflows/build-cbcbox.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-cbcbox.yml b/.github/workflows/build-cbcbox.yml index 22eb6e57a52e..48d710bd9f14 100644 --- a/.github/workflows/build-cbcbox.yml +++ b/.github/workflows/build-cbcbox.yml @@ -288,6 +288,7 @@ jobs: secrets: app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} with: + matrix: ${{ needs.setup.outputs.matrix }} artifact-pattern: cbcbox-${{ matrix.version }}-py3-none-manylinux_riscv64 gpl-sources-artifact: cbcbox-${{ matrix.version }}-gpl-sources gpl-sources-description: gcc (libgfortran, linked into the bundled OpenBLAS) From 6dcbcfad8742eecfc8316980f5618a6e3ccbce5b Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 15 Nov 2026 03:28:47 +0000 Subject: [PATCH 8/8] cbcbox: pin OPENBLAS_NUM_THREADS=1 for the test step Real test log (run 43082576749, job 130832260080) shows a genuine SIGABRT, not just slowness: "Fatal glibc error: pthread_mutex_lock.c:94 (___pthread_mutex_lock): assertion failed: mutex->__data.__owner == 0", with the crash's own stack trace going through libopenblas.so!blas_memory_alloc -> pthread_mutex_lock while CBC was mid-solve under its own -threads=3 branch-and-bound parallelism (CoinUtils/ Clp's dense-factorization update calling into OpenBLAS's dgetrs_ from one of CBC's worker threads). This is the classic nested-parallelism hazard: CBC already parallelizes the search itself, and OpenBLAS's own pthread thread pool then additionally tries to parallelize the BLAS calls each worker thread makes, so N CBC threads each spawn M OpenBLAS threads fighting over OpenBLAS's shared memory-pool lock - evidently exposing a real bug in OpenBLAS's pthread-backend locking on this architecture. Re-examining the run's 5 reported failures with this in mind: the 2 "wrong objective value" assertions (mzzv11, neos-860300) and 2 of the 3 test_solve_parallel TimeoutExpired failures are downstream symptoms of this same corruption/abort, not independent slowness or correctness bugs in CBC's own solve logic - the previous diagnosis of "just needs a higher time limit" undersold what's actually happening here. OPENBLAS_NUM_THREADS=1 is this repo's existing, established fix for exactly this class of problem (see build-statsmodels.yml, build-pyscf.yml, build-torchft.yml, build-ligo-skymap.yml): forcing OpenBLAS's BLAS calls to stay single-threaded lets CBC's own -threads=N remain the only level of parallelism, removing the oversubscription/locking hazard rather than papering over it with a longer timeout. --- .github/workflows/build-cbcbox.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-cbcbox.yml b/.github/workflows/build-cbcbox.yml index 48d710bd9f14..f8b2db84d7ad 100644 --- a/.github/workflows/build-cbcbox.yml +++ b/.github/workflows/build-cbcbox.yml @@ -232,6 +232,7 @@ jobs: -v "$PWD:/project" \ -v "$PWD/wheelhouse:/wheelhouse" \ -e PERF_REPORT_DIR=/output \ + -e OPENBLAS_NUM_THREADS=1 \ "${MANYLINUX_RISCV64_IMAGE}" \ bash -c 'export PATH=/opt/python/cp313-cp313/bin:$PATH && mkdir -p /output &&