Skip to content
Merged
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
112 changes: 112 additions & 0 deletions .github/workflows/build-osqp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow is based on the `build_wheels` job of
# https://github.com/osqp/osqp-python/blob/v1.1.3/.github/workflows/build_default.yml
name: Build osqp wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'osqp version to build (git tag without the leading v, e.g. 1.1.3)'
required: true
default: '1.1.3'
pull_request:
paths:
- '.github/workflows/build-osqp.yml'
- 'patches/osqp/**'

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '1.1.3' }}-${{ 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 1.1.3 there.
OSQP_VERSION: ${{ inputs.version || '1.1.3' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64
# CMakeLists.txt FetchContent-pulls osqp/osqp at this tag to build osqpstatic;
# bump alongside it if upstream ever moves the pin.
OSQP_C_LIB_TAG: v1.0.0

jobs:
setup:
uses: $/.github/workflows/_setup.yml

build_wheels:
needs: [setup]
name: Build osqp ${{ inputs.version || '1.1.3' }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
python: ["cp312", "cp313", "cp314", "cp314t"]

steps:
- name: Checkout osqp-python v${{ env.OSQP_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: osqp/osqp-python
ref: v${{ env.OSQP_VERSION }}
persist-credentials: false

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false

- name: Patch osqp-python source
run: git apply python-wheels/patches/osqp/${{ env.OSQP_VERSION }}/*.patch

- name: Stage the vendored osqp C library's NOTICE and AMD licence
run: |
base="https://raw.githubusercontent.com/osqp/osqp/${{ env.OSQP_C_LIB_TAG }}"
curl -fsSL --retry 5 "${base}/NOTICE" -o NOTICE.osqp-c
curl -fsSL --retry 5 "${base}/algebra/_common/lin_sys/qdldl/amd/LICENSE" -o LICENSE.amd

- name: Build wheels
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
output-dir: wheelhouse/
config-file: cibuildwheel.toml
only: ${{ matrix.python }}-manylinux_riscv64
env:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# scikit-build-core fetches cmake/ninja from PyPI when missing on PATH,
# and pybind11's headers need pip to resolve; both need our registry.
# The patch dirties the tag checkout, which would otherwise poison
# setuptools_scm's version with a `.dev0+g<sha>` suffix (gotcha 31).
CIBW_ENVIRONMENT: >-
PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_OSQP=${{ env.OSQP_VERSION }}
# numpy/scipy have riscv64 wheels only on our registry; don't let a
# newer public-PyPI release pull pip into a source build.
CIBW_TEST_ENVIRONMENT: PIP_ONLY_BINARY=numpy,scipy
# torch has no riscv64 wheel anywhere; mirror upstream's own
# aarch64/free-threaded override that skips the osqp.nn tests.
CIBW_BEFORE_TEST: pip install scipy --prefer-binary
CIBW_TEST_GROUPS: test-no-nn
CIBW_TEST_COMMAND: >-
python -c "import importlib.metadata as m; f = sorted(str(p).split('licenses/', 1)[1] for p in m.files('osqp') if '.dist-info/licenses/' in str(p)); assert f == ['LICENSE', 'LICENSE.amd', 'NOTICE.osqp-c'], f" &&
python -m pytest -s {project}/src/osqp/tests --continue-on-collection-errors --ignore={project}/src/osqp/tests/nn_test.py

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: osqp-${{ env.OSQP_VERSION }}-${{ matrix.python }}-manylinux_riscv64
path: wheelhouse/*.whl
if-no-files-found: error

publish:
name: Publish osqp ${{ inputs.version || '1.1.3' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: osqp-${{ inputs.version || '1.1.3' }}-*-manylinux_riscv64
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
From d976eb6cd46f174c50c3046924e6dbf152ad80ed Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Fri, 4 Sep 2026 08:19:28 +0200
Subject: [PATCH] Package the vendored osqp C library's NOTICE and AMD licence

The extension statically links osqpstatic, which FetchContent pulls from
osqp/osqp at v1.0.0 and builds with the builtin algebra backend. That backend
compiles in the bundled QDLDL direct solver together with its AMD ordering
routine (BSD-3-Clause, from SuiteSparse), and osqp/osqp ships an Apache-2.0
NOTICE attributing QDLDL and AMD. The published wheel nevertheless carries
only osqp-python's own LICENSE: Apache-2.0 section 4(d) requires the NOTICE
to travel with binary redistributions, and AMD's BSD-3-Clause licence
requires its own copyright notice to do the same.

FetchContent means neither file exists in this checkout at configure time, so
the build workflow curls them from the pinned osqp/osqp tag before invoking
cibuildwheel and stages them as NOTICE.osqp-c and LICENSE.amd. Declare both
via PEP 639 license-files so scikit-build-core packages them into
dist-info/licenses/ next to LICENSE.

Upstream-Status: To upstream [not yet submitted; the same gap exists in every osqp wheel on PyPI, so it needs a maintainer discussion rather than a drive-by PR]
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index 4cf3c55..27596f3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,7 @@ name = "osqp"
dynamic = ["version"]
description = "OSQP: The Operator Splitting QP Solver"
license = "Apache-2.0"
-license-files = ["LICENSE"]
+license-files = ["LICENSE", "NOTICE.osqp-c", "LICENSE.amd"]
readme = "README.md"
requires-python = ">=3.8"
authors = [
--
2.50.1 (Apple Git-155)