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

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

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '6.1.1' }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read # to fetch code (actions/checkout)

env:
XROOTD_VERSION: ${{ inputs.version || '6.1.1' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

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

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

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

# The tag's VERSION file is an unexpanded `git-archive` export-subst
# placeholder (`$Format:%(describe)$`); setup.py and CMake both fall
# back to `git describe`, which a shallow single-ref checkout can't
# answer. Writing the real version here is the officially supported
# override (cmake/XRootDVersion.cmake reads this file first).
- name: Set version from tag
run: echo -n '${{ env.XROOTD_VERSION }}' > VERSION

- name: Stage the licence-collection script
run: |
cat > collect-licenses.sh <<'COLLECT_EOF'
#!/bin/bash
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
#
# Stage, at the project root, the licence of every shared library
# auditwheel vendors out of the build image alongside the XRootD
# client libraries (OpenSSL, Kerberos, libuuid and their closure).
# setuptools' default LICENSE* glob copies them into the wheel.
set -euo pipefail

project="${1:?usage: collect-licenses.sh <project-dir>}"

# ldd is transitive, so these roots cover their whole closure; ldd
# does not list the roots themselves, so resolve those too. libcrypt
# is not pulled in by any -devel package above: XrdUtils' own CMake
# probes for crypt() directly and links libc-adjacent libxcrypt,
# which the base image already ships.
mapfile -t libs < <(
{
for root in libssl.so libcrypto.so libkrb5.so libuuid.so libcrypt.so; do
path="/usr/lib64/${root}"
[ -e "${path}" ] || continue
ldd "${path}" | tr ' ' '\n' | grep '^/'
readlink -f "${path}"
done
} | sort -u
)

# `rpm -qf` reports unowned files on stdout, so keep only bare package names.
# glibc, the gcc runtime and zlib are on auditwheel's manylinux allowlist
# (confirmed against the upstream x86_64 wheel's auditwheel-vendored set)
# and are never vendored into the wheel; zlib-ng-compat also ships no
# licence file via any rpm metadata path, so it would break the loop below.
mapfile -t pkgs < <(
rpm -qf --qf '%{NAME}\n' "${libs[@]}" 2>/dev/null |
grep -E '^[A-Za-z0-9._+-]+$' | sort -u |
grep -vE '^(glibc|libgcc|libstdc\+\+|gcc|zlib-ng-compat)$'
)

for pkg in "${pkgs[@]}"; do
mapfile -t files < <(rpm -q --licensefiles "$pkg" 2>/dev/null || true)

# Some subpackages leave the licence to a sibling of the same source RPM.
if [ -z "${files[0]:-}" ]; then
srpm=$(rpm -q --qf '%{SOURCERPM}\n' "$pkg")
mapfile -t files < <(
rpm -qa --qf '%{SOURCERPM} %{NAME}\n' |
awk -v s="$srpm" '$1 == s { print $2 }' |
xargs -r rpm -q --licensefiles 2>/dev/null | sort -u
)
fi

# Others mark it %doc rather than %license, and the image installs no docs.
if [ -z "${files[0]:-}" ]; then
dnf -y --disablerepo=extras reinstall --setopt=tsflags= "$pkg" >/dev/null
mapfile -t files < <(rpm -qd "$pkg" | grep -iE '/(LICEN[CS]E|COPYING|NOTICE)')
fi

for f in "${files[@]}"; do
[ -f "$f" ] || continue
cp "$f" "$project/LICENSE.${pkg}.$(basename "$f")"
done
compgen -G "$project/LICENSE.$pkg.*" >/dev/null ||
{ echo "no licence file found for $pkg" >&2; exit 1; }
done

ls -1 "$project"/LICENSE.* | sed "s|$project/||"
COLLECT_EOF

- name: Build wheels
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
output-dir: wheelhouse/
only: ${{ matrix.python }}-manylinux_riscv64
env:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
CIBW_BEFORE_ALL_LINUX: >-
dnf install -y krb5-devel libuuid-devel openssl-devel &&
bash {project}/collect-licenses.sh {project}
CIBW_ENVIRONMENT: CMAKE_ARGS=-DCMAKE_BUILD_TYPE=Release
# Upstream's own manylinux job test step (python.yml): import the
# extension and the pure-Python client, then build a FileSystem
# object (no network I/O, so no server needed).
CIBW_TEST_COMMAND: |
python -m pip show xrootd &&
python -c "import XRootD; print(XRootD)" &&
python -c "import pyxrootd; print(pyxrootd)" &&
python -c "from XRootD import client; help(client)" &&
python -c "from XRootD import client; print(client.FileSystem('root://localhost'))"

- name: Verify the wheel ships the compiled extension and licences
run: |
python3 - wheelhouse/*.whl <<'EOF'
import sys, zipfile
names = zipfile.ZipFile(sys.argv[1]).namelist()
sos = sorted(n for n in names if n.endswith(".so"))
print("\n".join(sos))
assert any("/client.cpython" in "/" + n for n in sos), sos

lic = sorted(n.split("/")[-1] for n in names if ".dist-info/licenses/" in n and not n.endswith("/"))
print("\n".join(lic))
expected_pkgs = {"openssl-libs", "krb5-libs", "libuuid", "libxcrypt"}
have_pkgs = {f.split(".", 2)[1] for f in lic if f.startswith("LICENSE.")}
assert expected_pkgs <= have_pkgs, expected_pkgs - have_pkgs
EOF

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

publish:
name: Publish xrootd ${{ inputs.version || '6.1.1' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: xrootd-${{ inputs.version || '6.1.1' }}-*-manylinux_riscv64