Skip to content

[pre-commit.ci] pre-commit autoupdate #916

[pre-commit.ci] pre-commit autoupdate

[pre-commit.ci] pre-commit autoupdate #916

Workflow file for this run

# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
# https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml
name: Build and upload to PyPI
on:
workflow_dispatch:
pull_request:
push:
branches: [main]
release:
types: [published]
jobs:
build_wheels:
name: Build wheels for ${{ matrix.os }}-cu${{ matrix.cuda_major }}
runs-on: ${{ matrix.runs-on }}
strategy:
matrix:
include:
# CUDA 12 wheels (compiled with 12.2 for runtime 12.2+ compatibility)
- os: linux-intel
cuda_major: "12"
cuda_minor: "2"
runs-on: ubuntu-latest
cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda12_2:latest"
cuda_archs: "75-real;80-real;86-real;89-real;90"
- os: linux-arm
cuda_major: "12"
cuda_minor: "2"
runs-on: ubuntu-24.04-arm
cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda12_2:latest"
cuda_archs: "75-real;80-real;86-real;89-real;90"
# CUDA 13 wheels (native Blackwell support)
- os: linux-intel
cuda_major: "13"
cuda_minor: "0"
runs-on: ubuntu-latest
cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_0:latest"
cuda_archs: "75-real;80-real;86-real;89-real;90-real;100-real;120"
- os: linux-arm
cuda_major: "13"
cuda_minor: "0"
runs-on: ubuntu-24.04-arm
cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda13_0:latest"
cuda_archs: "75-real;80-real;86-real;89-real;90-real;100-real;120"
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Get version from hatch-vcs
id: version
run: |
pip install hatchling hatch-vcs
VERSION=$(python -m hatchling version)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Detected version: $VERSION"
- name: Set package name, extras, and CUDA archs for CUDA ${{ matrix.cuda_major }}
run: |
python3 - ${{ matrix.cuda_major }} "${{ matrix.cuda_archs }}" "${{ steps.version.outputs.version }}" <<'SCRIPT'
import re, sys, pathlib
cuda = sys.argv[1]
cuda_archs = sys.argv[2]
version = sys.argv[3]
other = "13" if cuda == "12" else "12"
path = pathlib.Path("pyproject.toml")
text = path.read_text()
def remove_toml_array(text, key):
lines = text.splitlines(keepends=True)
out = []
i = 0
while i < len(lines):
if lines[i].startswith(f"{key} = ["):
depth = lines[i].count("[") - lines[i].count("]")
i += 1
while i < len(lines) and depth > 0:
depth += lines[i].count("[") - lines[i].count("]")
i += 1
continue
out.append(lines[i])
i += 1
return "".join(out)
# Rename package
text = text.replace(
'name = "rapids-singlecell"',
f'name = "rapids-singlecell-cu{cuda}"',
)
text = text.replace(
'dynamic = [ "version" ]',
f'version = "{version}"',
)
# Rewrite self-referential extras (e.g. "rapids_singlecell[test-minimal]")
# to the renamed distribution, so `pip install rapids-singlecell-cuXX[test]`
# composes extras within this wheel instead of pulling the plain
# rapids-singlecell package from PyPI alongside it.
text, n_self = re.subn(
r'"rapids[-_]singlecell\[',
f'"rapids-singlecell-cu{cuda}[',
text,
)
print(f"rewrote {n_self} self-referential extra(s)")
if n_self == 0:
raise SystemExit("expected at least one self-referential extra to rewrite")
# Rename matching extra to "rapids", remove the other
text = text.replace(f'rapids-cu{cuda} = [', 'rapids = [')
text = remove_toml_array(text, f"rapids-cu{other}")
# Set CUDA architectures (replace "native" with CI target archs)
text = text.replace(
'CMAKE_CUDA_ARCHITECTURES = "native"',
f'CMAKE_CUDA_ARCHITECTURES = "{cuda_archs}"',
)
path.write_text(text)
# Verify
for line in path.read_text().splitlines():
if "name" in line or "rapids" in line.lower() or "CUDA_ARCH" in line:
print(line)
SCRIPT
- name: Sanity check pyproject.toml
run: |
python3 -c "import tomllib; tomllib.load(open('pyproject.toml', 'rb'))"
grep -E "name|rapids|CUDA_ARCH" pyproject.toml
- name: Build wheels
uses: pypa/cibuildwheel@v3.4.1
env:
CIBW_BUILD: 'cp312-*'
CIBW_SKIP: '*-musllinux*'
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.os == 'linux-intel' && matrix.cibw_image || '' }}
CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.os == 'linux-arm' && matrix.cibw_image || '' }}
CIBW_ENVIRONMENT: >
CUDA_PATH=/usr/local/cuda
LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
PATH=/usr/local/cuda/bin:$PATH
CIBW_BEFORE_ALL: >
dnf install -y --setopt=obsoletes=0
libcusolver-devel-${{ matrix.cuda_major }}-${{ matrix.cuda_minor }}
libcusparse-devel-${{ matrix.cuda_major }}-${{ matrix.cuda_minor }}
libnvjitlink-${{ matrix.cuda_major }}-${{ matrix.cuda_minor }}
CIBW_BEFORE_BUILD: >
python -m pip install -U pip
scikit-build-core cmake ninja nanobind
CIBW_TEST_SKIP: "*"
CIBW_TEST_COMMAND: ""
# Exclude CUDA runtime libs provided by dependency wheels.
# Use SONAME globs so CUDA 12/13 suffix changes do not bundle them.
CIBW_REPAIR_WHEEL_COMMAND: "auditwheel repair --exclude 'libcublas.so.*' --exclude 'libcublasLt.so.*' --exclude 'libcudart.so.*' --exclude 'libcusolver.so.*' --exclude 'libcusparse.so.*' --exclude 'libnvJitLink.so.*' -w {dest_dir} {wheel}"
CIBW_BUILD_VERBOSITY: "1"
- name: Check wheels link no RAPIDS C++ libs
run: |
python3 - <<'SCRIPT'
import pathlib
import shutil
import subprocess
import sys
import tempfile
import zipfile
readelf = shutil.which("readelf")
if readelf is None:
raise SystemExit("readelf is required for the RAPIDS linkage check")
# Kernels use a CuPy-backed scratch allocator and the CUDA toolkit's
# bundled CCCL, so no extension may pull in librmm/librapids_logger.
banned = ("librmm", "librapids_logger")
failures = []
for wheel in sorted(pathlib.Path("wheelhouse").glob("*.whl")):
with tempfile.TemporaryDirectory() as tmp:
tmp_path = pathlib.Path(tmp)
with zipfile.ZipFile(wheel) as zf:
for name in zf.namelist():
if name.startswith("rapids_singlecell/_cuda/") and name.endswith(".so"):
zf.extract(name, tmp_path)
for ext in sorted((tmp_path / "rapids_singlecell" / "_cuda").glob("*.so")):
result = subprocess.run(
[readelf, "-d", str(ext)],
check=True,
capture_output=True,
text=True,
)
for line in result.stdout.splitlines():
if "(NEEDED)" in line and any(b in line for b in banned):
failures.append(f"{wheel.name}:{ext.name}: {line.strip()}")
if failures:
print(
"Wheel links against RAPIDS C++ libs, which are no longer a "
"build or runtime dependency.",
file=sys.stderr,
)
print("\n".join(failures), file=sys.stderr)
raise SystemExit(1)
SCRIPT
- uses: actions/upload-artifact@v7
with:
name: cibw-wheels-${{ matrix.os }}-cu${{ matrix.cuda_major }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
# Full history + tags so hatch-vcs resolves the real version.
# Without this, non-tag runs produce a bogus 0.1.dev1 sdist.
fetch-depth: 0
- name: Build sdist
run: pipx run build --sdist
- uses: actions/upload-artifact@v7
with:
name: cibw-sdist
path: dist/*.tar.gz
upload_wheels:
needs: [build_wheels]
runs-on: ubuntu-latest
environment: publish-cu${{ matrix.cuda_major }}
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
strategy:
matrix:
cuda_major: ["12", "13"]
steps:
- uses: actions/download-artifact@v8
with:
pattern: cibw-wheels-*-cu${{ matrix.cuda_major }}-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
upload_sdist:
needs: [build_sdist]
runs-on: ubuntu-latest
environment: publish
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/download-artifact@v8
with:
name: cibw-sdist
path: dist
- uses: pypa/gh-action-pypi-publish@release/v1