From aa0f66178979e782a03cdef6a40792d133aca518 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 7 Sep 2026 18:09:13 +0200 Subject: [PATCH 1/4] gpiod: add build-gpiod.yml for riscv64 wheels Vendors libgpiod's C library sources via LIBGPIOD_VERSION (upstream's own setup.py fetch_tarball mechanism), so no system libgpiod is needed. The real test suite needs a gpio-sim kernel module and a separately-built libgpiosim helper library, neither available in the build container, so CI exercises the compiled extension and the real chip-open error path instead, matching upstream's own release process which does not run tests either. --- .github/workflows/build-gpiod.yml | 102 ++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/build-gpiod.yml diff --git a/.github/workflows/build-gpiod.yml b/.github/workflows/build-gpiod.yml new file mode 100644 index 000000000..0a9c5301f --- /dev/null +++ b/.github/workflows/build-gpiod.yml @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +name: Build gpiod wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'gpiod (Python bindings) version/tag to build, e.g. 2.5.0' + required: true + default: '2.5.0' + libgpiod_version: + description: 'libgpiod C library source version to vendor (LIBGPIOD_VERSION)' + required: true + default: '2.3' + pull_request: + paths: + - '.github/workflows/build-gpiod.yml' + +concurrency: + group: ${{ github.workflow }}-${{ inputs.version || '2.5.0' }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + GPIOD_VERSION: ${{ inputs.version || '2.5.0' }} + LIBGPIOD_VERSION: ${{ inputs.libgpiod_version || '2.3' }} + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + + build_wheels: + needs: [setup] + name: Build gpiod ${{ inputs.version || '2.5.0' }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + python: ["cp312", "cp313", "cp314", "cp314t"] + + steps: + # The Python bindings live in git.kernel.org/libgpiod, tagged + # python-v; that tag isn't mirrored to github.com/brgl/libgpiod, + # so actions/checkout can't reach it. + - name: Checkout gpiod python-v${{ env.GPIOD_VERSION }} + run: | + git clone --depth 1 --branch "python-v${{ env.GPIOD_VERSION }}" \ + https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git gpiod-src + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + package-dir: gpiod-src/bindings/python + output-dir: wheelhouse/ + env: + CIBW_BUILD: ${{ matrix.python }}-manylinux_riscv64 + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + # Sets LIBGPIOD_VERSION inside the build container, where setup.py's + # fetch_tarball wrapper downloads and vendors libgpiod's lib/include + # sources before compiling gpiod._ext -- no system libgpiod needed. + CIBW_ENVIRONMENT: LIBGPIOD_VERSION=${{ env.LIBGPIOD_VERSION }} + # Upstream's own generate_pypi_artifacts.sh builds wheels with no test + # step at all: the real test suite needs a gpio-sim kernel module and + # the separately-built libgpiosim helper library (tests/gpiosim/, + # tests/system/), neither available in the build container. Exercise + # the compiled extension and the real chip-open/ioctl error path instead. + CIBW_TEST_COMMAND: | + python3 - <<'PYEOF' + import gpiod + from gpiod import _ext + assert _ext.__file__.endswith(".so"), _ext.__file__ + assert gpiod.__version__ == "${{ env.GPIOD_VERSION }}", gpiod.__version__ + assert gpiod.api_version == "${{ env.LIBGPIOD_VERSION }}", gpiod.api_version + try: + gpiod.Chip("/dev/null") + except OSError: + pass + else: + raise SystemExit("expected OSError for a non-gpiochip device") + PYEOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: gpiod-${{ env.GPIOD_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish gpiod ${{ inputs.version || '2.5.0' }} + needs: [setup, build_wheels] + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + with: + artifact-pattern: gpiod-${{ inputs.version || '2.5.0' }}-*-manylinux_riscv64 From 7cbfc47c0721b87f385719201b9b8d39f63c0847 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 7 Sep 2026 18:10:28 +0200 Subject: [PATCH 2/4] gpiod: build musllinux wheels too, matching upstream's own wheel matrix Alpine needs linux-headers for lib/uapi/gpio.h's system linux/*.h includes; manylinux already has them via glibc-devel. --- .github/workflows/build-gpiod.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-gpiod.yml b/.github/workflows/build-gpiod.yml index 0a9c5301f..1c0dbb35d 100644 --- a/.github/workflows/build-gpiod.yml +++ b/.github/workflows/build-gpiod.yml @@ -29,6 +29,7 @@ env: GPIOD_VERSION: ${{ inputs.version || '2.5.0' }} LIBGPIOD_VERSION: ${{ inputs.libgpiod_version || '2.3' }} MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + MUSLLINUX_RISCV64_IMAGE: quay.io/pypa/musllinux_1_2_riscv64 jobs: setup: @@ -36,13 +37,14 @@ jobs: build_wheels: needs: [setup] - name: Build gpiod ${{ inputs.version || '2.5.0' }} ${{ matrix.python }}-manylinux_riscv64 + name: Build gpiod ${{ inputs.version || '2.5.0' }} ${{ matrix.python }}-${{ matrix.libc }}_riscv64 runs-on: ubuntu-24.04-riscv timeout-minutes: 60 strategy: fail-fast: false matrix: python: ["cp312", "cp313", "cp314", "cp314t"] + libc: [manylinux, musllinux] steps: # The Python bindings live in git.kernel.org/libgpiod, tagged @@ -59,12 +61,17 @@ jobs: package-dir: gpiod-src/bindings/python output-dir: wheelhouse/ env: - CIBW_BUILD: ${{ matrix.python }}-manylinux_riscv64 + CIBW_BUILD: ${{ matrix.python }}-${{ matrix.libc }}_riscv64 CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + CIBW_MUSLLINUX_RISCV64_IMAGE: ${{ env.MUSLLINUX_RISCV64_IMAGE }} # Sets LIBGPIOD_VERSION inside the build container, where setup.py's # fetch_tarball wrapper downloads and vendors libgpiod's lib/include # sources before compiling gpiod._ext -- no system libgpiod needed. CIBW_ENVIRONMENT: LIBGPIOD_VERSION=${{ env.LIBGPIOD_VERSION }} + # lib/uapi/gpio.h pulls in the system linux/const.h,linux/ioctl.h, + # linux/types.h; musllinux's Alpine base needs linux-headers for + # those, manylinux already has them via glibc-devel. + CIBW_BEFORE_ALL_LINUX: (command -v apk && apk add linux-headers) || true # Upstream's own generate_pypi_artifacts.sh builds wheels with no test # step at all: the real test suite needs a gpio-sim kernel module and # the separately-built libgpiosim helper library (tests/gpiosim/, @@ -87,7 +94,7 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: gpiod-${{ env.GPIOD_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + name: gpiod-${{ env.GPIOD_VERSION }}-${{ matrix.python }}-${{ matrix.libc }}_riscv64 path: wheelhouse/*.whl if-no-files-found: error @@ -99,4 +106,4 @@ jobs: pull-requests: write uses: $/.github/workflows/_publish-wheel.yml with: - artifact-pattern: gpiod-${{ inputs.version || '2.5.0' }}-*-manylinux_riscv64 + artifact-pattern: gpiod-${{ inputs.version || '2.5.0' }}-*riscv64 From cf0f625daccf084d7ad5f8f7a04b1d1711c41126 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 7 Sep 2026 18:13:36 +0200 Subject: [PATCH 3/4] gpiod: fix CI, set CIBW_ARCHS: riscv64 Without it cibuildwheel defaulted to aarch64/x86_64 and matched zero build identifiers against the cp*-*linux_riscv64 CIBW_BUILD selector. --- .github/workflows/build-gpiod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-gpiod.yml b/.github/workflows/build-gpiod.yml index 1c0dbb35d..56ae13227 100644 --- a/.github/workflows/build-gpiod.yml +++ b/.github/workflows/build-gpiod.yml @@ -61,6 +61,7 @@ jobs: package-dir: gpiod-src/bindings/python output-dir: wheelhouse/ env: + CIBW_ARCHS: riscv64 CIBW_BUILD: ${{ matrix.python }}-${{ matrix.libc }}_riscv64 CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} CIBW_MUSLLINUX_RISCV64_IMAGE: ${{ env.MUSLLINUX_RISCV64_IMAGE }} From cbf785e0a6eb99812d2cc56fc00c6a8f809b88e9 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 7 Sep 2026 18:21:12 +0200 Subject: [PATCH 4/4] gpiod: switch to sdist->bdist shape, matching upstream's own pipeline Building wheels directly from a checkout meant setup.py's fetch_tarball decorator took the full-fetch path during build_ext, which deletes LICENSE/ lib/include right after compiling -- breaking install_egg_info's license copy. Upstream's own generate_pypi_artifacts.sh sidesteps this by building the sdist with LIBGPIOD_VERSION set first, then feeding that sdist (already carrying libgpiod-version.txt) to cibuildwheel, which takes the skip-fetch, no-cleanup path instead. --- .github/workflows/build-gpiod.yml | 69 +++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-gpiod.yml b/.github/workflows/build-gpiod.yml index 56ae13227..c2e5d7a6e 100644 --- a/.github/workflows/build-gpiod.yml +++ b/.github/workflows/build-gpiod.yml @@ -35,8 +35,56 @@ jobs: setup: uses: $/.github/workflows/_setup.yml - build_wheels: + python_sdist: needs: [setup] + # Upstream's own generate_pypi_artifacts.sh builds the sdist with + # LIBGPIOD_VERSION set, then feeds *that* sdist to cibuildwheel. Doing the + # same matters: setup.py's fetch_tarball wrapper only skips its + # post-build cleanup (which deletes LICENSE/lib/include again) when + # libgpiod-version.txt is already present and matches -- true for a wheel + # built from this sdist, false for a wheel built straight from a checkout. + runs-on: ubuntu-24.04-riscv + outputs: + sdist_artifact_name: ${{ steps.build_sdist.outputs.sdist_artifact_name }} + package_version: ${{ steps.build_sdist.outputs.package_version }} + steps: + # The Python bindings live in git.kernel.org/libgpiod, tagged + # python-v; that tag isn't mirrored to github.com/brgl/libgpiod, + # so actions/checkout can't reach it. + - name: Checkout gpiod python-v${{ env.GPIOD_VERSION }} + run: | + git clone --depth 1 --branch "python-v${{ env.GPIOD_VERSION }}" \ + https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git gpiod-src + + - name: setup uv + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + python-version: '3.12' + activate-environment: true + enable-cache: false + + - name: build sdist + id: build_sdist + working-directory: gpiod-src/bindings/python + env: + LIBGPIOD_VERSION: ${{ env.LIBGPIOD_VERSION }} + run: | + rm -rf dist/ + uv pip install build packaging + python -m build --sdist + + echo "sdist_artifact_name=$(ls ./dist)" >> "$GITHUB_OUTPUT" + echo "package_version=$(ls ./dist | sed -En 's/gpiod-(.+)\.tar\.gz/\1/p')" >> "$GITHUB_OUTPUT" + + - name: upload sdist artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ steps.build_sdist.outputs.sdist_artifact_name }} + path: gpiod-src/bindings/python/dist/${{ steps.build_sdist.outputs.sdist_artifact_name }} + if-no-files-found: error + + build_wheels: + needs: [setup, python_sdist] name: Build gpiod ${{ inputs.version || '2.5.0' }} ${{ matrix.python }}-${{ matrix.libc }}_riscv64 runs-on: ubuntu-24.04-riscv timeout-minutes: 60 @@ -47,28 +95,23 @@ jobs: libc: [manylinux, musllinux] steps: - # The Python bindings live in git.kernel.org/libgpiod, tagged - # python-v; that tag isn't mirrored to github.com/brgl/libgpiod, - # so actions/checkout can't reach it. - - name: Checkout gpiod python-v${{ env.GPIOD_VERSION }} - run: | - git clone --depth 1 --branch "python-v${{ env.GPIOD_VERSION }}" \ - https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git gpiod-src + - name: fetch sdist artifact + id: fetch_sdist + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.python_sdist.outputs.sdist_artifact_name }} - name: Build wheels uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 with: - package-dir: gpiod-src/bindings/python + # cibuildwheel accepts an sdist tarball directly and extracts it. + package-dir: ${{ steps.fetch_sdist.outputs.download-path }}/${{ needs.python_sdist.outputs.sdist_artifact_name }} output-dir: wheelhouse/ env: CIBW_ARCHS: riscv64 CIBW_BUILD: ${{ matrix.python }}-${{ matrix.libc }}_riscv64 CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} CIBW_MUSLLINUX_RISCV64_IMAGE: ${{ env.MUSLLINUX_RISCV64_IMAGE }} - # Sets LIBGPIOD_VERSION inside the build container, where setup.py's - # fetch_tarball wrapper downloads and vendors libgpiod's lib/include - # sources before compiling gpiod._ext -- no system libgpiod needed. - CIBW_ENVIRONMENT: LIBGPIOD_VERSION=${{ env.LIBGPIOD_VERSION }} # lib/uapi/gpio.h pulls in the system linux/const.h,linux/ioctl.h, # linux/types.h; musllinux's Alpine base needs linux-headers for # those, manylinux already has them via glibc-devel.