Skip to content

Commit 662937e

Browse files
authored
xrootd: add build-xrootd.yml for riscv64 wheels (#1811)
* **Package**: `xrootd` * **Version**: `6.1.1` * **Source**: https://github.com/xrootd/xrootd * **Docs**: https://xrootd.org Compiles the XRootD client libraries (XrdCl/XrdUtils) and their Python bindings (`pyxrootd`/`XRootD`) from the upstream C++ project's own CMake build, excluding the server daemon. Upstream publishes no riscv64 wheel. Mirrors [upstream's `python.yml`](https://github.com/xrootd/xrootd/blob/master/.github/workflows/python.yml) `manylinux` job. **Differs from upstream** - Drops `git` from upstream's `dnf install` list - VERSION is written directly from the input tag instead of relying on `git describe`. **Testing** - same as upstream **License**: Wheel bundles OpenSSL, Kerberos (krb5-libs/libcom_err/keyutils-libs), libuuid, pcre2, libselinux and libxcrypt from the build image (Apache-2.0/MIT/BSD/LGPL-2.1 mix); upstream ships no licence text for them, so the build adds it. Built on cp312; all smoke-test assertions passed locally on the manylinux_2_39 aarch64 image (same Rocky 10 family; riscv64 QEMU crashes on this host).
1 parent c601eb5 commit 662937e

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

‎.github/workflows/build-xrootd.yml‎

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# SPDX-FileCopyrightText: 2026 The RISE Project
2+
# SPDX-License-Identifier: MIT
3+
---
4+
# This workflow is based on: https://github.com/xrootd/xrootd/blob/master/.github/workflows/python.yml
5+
name: Build xrootd wheels (riscv64)
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
version:
11+
description: 'xrootd version to build (git tag without leading v, e.g. 6.1.1)'
12+
required: true
13+
default: '6.1.1'
14+
pull_request:
15+
paths:
16+
- '.github/workflows/build-xrootd.yml'
17+
18+
concurrency:
19+
group: ${{ github.workflow }}-${{ inputs.version || '6.1.1' }}-${{ github.head_ref || github.run_id }}
20+
cancel-in-progress: true
21+
22+
permissions:
23+
contents: read # to fetch code (actions/checkout)
24+
25+
env:
26+
XROOTD_VERSION: ${{ inputs.version || '6.1.1' }}
27+
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64
28+
29+
jobs:
30+
setup:
31+
uses: $/.github/workflows/_setup.yml
32+
33+
build_wheels:
34+
needs: [setup]
35+
name: Build xrootd ${{ inputs.version || '6.1.1' }} ${{ matrix.python }}-manylinux_riscv64
36+
runs-on: ubuntu-24.04-riscv
37+
timeout-minutes: 120
38+
strategy:
39+
fail-fast: false
40+
matrix:
41+
python: ["cp312", "cp313", "cp314", "cp314t"]
42+
43+
steps:
44+
- name: Checkout xrootd v${{ env.XROOTD_VERSION }}
45+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
46+
with:
47+
repository: xrootd/xrootd
48+
ref: v${{ env.XROOTD_VERSION }}
49+
persist-credentials: false
50+
51+
# The tag's VERSION file is an unexpanded `git-archive` export-subst
52+
# placeholder (`$Format:%(describe)$`); setup.py and CMake both fall
53+
# back to `git describe`, which a shallow single-ref checkout can't
54+
# answer. Writing the real version here is the officially supported
55+
# override (cmake/XRootDVersion.cmake reads this file first).
56+
- name: Set version from tag
57+
run: echo -n '${{ env.XROOTD_VERSION }}' > VERSION
58+
59+
- name: Stage the licence-collection script
60+
run: |
61+
cat > collect-licenses.sh <<'COLLECT_EOF'
62+
#!/bin/bash
63+
# SPDX-FileCopyrightText: 2026 The RISE Project
64+
# SPDX-License-Identifier: MIT
65+
#
66+
# Stage, at the project root, the licence of every shared library
67+
# auditwheel vendors out of the build image alongside the XRootD
68+
# client libraries (OpenSSL, Kerberos, libuuid and their closure).
69+
# setuptools' default LICENSE* glob copies them into the wheel.
70+
set -euo pipefail
71+
72+
project="${1:?usage: collect-licenses.sh <project-dir>}"
73+
74+
# ldd is transitive, so these roots cover their whole closure; ldd
75+
# does not list the roots themselves, so resolve those too. libcrypt
76+
# is not pulled in by any -devel package above: XrdUtils' own CMake
77+
# probes for crypt() directly and links libc-adjacent libxcrypt,
78+
# which the base image already ships.
79+
mapfile -t libs < <(
80+
{
81+
for root in libssl.so libcrypto.so libkrb5.so libuuid.so libcrypt.so; do
82+
path="/usr/lib64/${root}"
83+
[ -e "${path}" ] || continue
84+
ldd "${path}" | tr ' ' '\n' | grep '^/'
85+
readlink -f "${path}"
86+
done
87+
} | sort -u
88+
)
89+
90+
# `rpm -qf` reports unowned files on stdout, so keep only bare package names.
91+
# glibc, the gcc runtime and zlib are on auditwheel's manylinux allowlist
92+
# (confirmed against the upstream x86_64 wheel's auditwheel-vendored set)
93+
# and are never vendored into the wheel; zlib-ng-compat also ships no
94+
# licence file via any rpm metadata path, so it would break the loop below.
95+
mapfile -t pkgs < <(
96+
rpm -qf --qf '%{NAME}\n' "${libs[@]}" 2>/dev/null |
97+
grep -E '^[A-Za-z0-9._+-]+$' | sort -u |
98+
grep -vE '^(glibc|libgcc|libstdc\+\+|gcc|zlib-ng-compat)$'
99+
)
100+
101+
for pkg in "${pkgs[@]}"; do
102+
mapfile -t files < <(rpm -q --licensefiles "$pkg" 2>/dev/null || true)
103+
104+
# Some subpackages leave the licence to a sibling of the same source RPM.
105+
if [ -z "${files[0]:-}" ]; then
106+
srpm=$(rpm -q --qf '%{SOURCERPM}\n' "$pkg")
107+
mapfile -t files < <(
108+
rpm -qa --qf '%{SOURCERPM} %{NAME}\n' |
109+
awk -v s="$srpm" '$1 == s { print $2 }' |
110+
xargs -r rpm -q --licensefiles 2>/dev/null | sort -u
111+
)
112+
fi
113+
114+
# Others mark it %doc rather than %license, and the image installs no docs.
115+
if [ -z "${files[0]:-}" ]; then
116+
dnf -y --disablerepo=extras reinstall --setopt=tsflags= "$pkg" >/dev/null
117+
mapfile -t files < <(rpm -qd "$pkg" | grep -iE '/(LICEN[CS]E|COPYING|NOTICE)')
118+
fi
119+
120+
for f in "${files[@]}"; do
121+
[ -f "$f" ] || continue
122+
cp "$f" "$project/LICENSE.${pkg}.$(basename "$f")"
123+
done
124+
compgen -G "$project/LICENSE.$pkg.*" >/dev/null ||
125+
{ echo "no licence file found for $pkg" >&2; exit 1; }
126+
done
127+
128+
ls -1 "$project"/LICENSE.* | sed "s|$project/||"
129+
COLLECT_EOF
130+
131+
- name: Build wheels
132+
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
133+
with:
134+
output-dir: wheelhouse/
135+
only: ${{ matrix.python }}-manylinux_riscv64
136+
env:
137+
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
138+
CIBW_BEFORE_ALL_LINUX: >-
139+
dnf install -y krb5-devel libuuid-devel openssl-devel &&
140+
bash {project}/collect-licenses.sh {project}
141+
CIBW_ENVIRONMENT: CMAKE_ARGS=-DCMAKE_BUILD_TYPE=Release
142+
# Upstream's own manylinux job test step (python.yml): import the
143+
# extension and the pure-Python client, then build a FileSystem
144+
# object (no network I/O, so no server needed).
145+
CIBW_TEST_COMMAND: |
146+
python -m pip show xrootd &&
147+
python -c "import XRootD; print(XRootD)" &&
148+
python -c "import pyxrootd; print(pyxrootd)" &&
149+
python -c "from XRootD import client; help(client)" &&
150+
python -c "from XRootD import client; print(client.FileSystem('root://localhost'))"
151+
152+
- name: Verify the wheel ships the compiled extension and licences
153+
run: |
154+
python3 - wheelhouse/*.whl <<'EOF'
155+
import sys, zipfile
156+
names = zipfile.ZipFile(sys.argv[1]).namelist()
157+
sos = sorted(n for n in names if n.endswith(".so"))
158+
print("\n".join(sos))
159+
assert any("/client.cpython" in "/" + n for n in sos), sos
160+
161+
lic = sorted(n.split("/")[-1] for n in names if ".dist-info/licenses/" in n and not n.endswith("/"))
162+
print("\n".join(lic))
163+
expected_pkgs = {"openssl-libs", "krb5-libs", "libuuid", "libxcrypt"}
164+
have_pkgs = {f.split(".", 2)[1] for f in lic if f.startswith("LICENSE.")}
165+
assert expected_pkgs <= have_pkgs, expected_pkgs - have_pkgs
166+
EOF
167+
168+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
169+
with:
170+
name: xrootd-${{ env.XROOTD_VERSION }}-${{ matrix.python }}-manylinux_riscv64
171+
path: wheelhouse/*.whl
172+
if-no-files-found: error
173+
174+
publish:
175+
name: Publish xrootd ${{ inputs.version || '6.1.1' }}
176+
needs: [setup, build_wheels]
177+
permissions:
178+
contents: write
179+
pull-requests: write
180+
uses: $/.github/workflows/_publish-wheel.yml
181+
with:
182+
artifact-pattern: xrootd-${{ inputs.version || '6.1.1' }}-*-manylinux_riscv64

0 commit comments

Comments
 (0)