From fe7aa2d295a065709100ad6ca779d09187000330 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 20 Oct 2026 21:40:08 -0700 Subject: [PATCH 01/24] Test and document both bindings lines from main Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/build-docs.yml | 132 ++++++--- .github/workflows/ci-nightly.yml | 60 ++-- .github/workflows/ci-pixi-source-test.yml | 268 +++++++++++++---- .github/workflows/coverage.yml | 56 +++- .github/workflows/test-wheel-linux.yml | 213 ++++++++------ .github/workflows/test-wheel-windows.yml | 208 +++++++------ ci/tools/run-tests | 72 ++++- ci/tools/tests/test_test_sources.py | 340 ++++++++++++++++++++++ cuda_bindings_13/docs/build_docs.sh | 9 +- cuda_bindings_13/docs/source/install.rst | 6 +- cuda_python/docs/environment-docs.yml | 2 + toolshed/setup-docs-env.sh | 1 + 12 files changed, 1017 insertions(+), 350 deletions(-) create mode 100644 ci/tools/tests/test_test_sources.py diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index b734fdd8c73..c461ba2b219 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -43,6 +43,11 @@ on: required: false default: "gh-pages" type: string + bindings-package: + description: "Resolved CUDA bindings package JSON" + required: false + default: "" + type: string jobs: build: @@ -62,23 +67,39 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 - ref: ${{ inputs.git-tag }} + ref: ${{ inputs.is-release && format('refs/tags/{0}', inputs.git-tag) || inputs.git-tag }} + + # Release sources and workflow tools can come from different commits. + # Keep the shared CI helpers at this workflow revision while reading + # package paths and toolkit settings from the checked-out release tag. + - name: Checkout docs control plane + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + path: .ci-control - - name: Read build CTK version + - name: Install CI tool dependencies + run: python3 -m pip install -e ./.ci-control/ci + + - name: Resolve CUDA bindings package + env: + BINDINGS_PACKAGE: ${{ inputs.bindings-package }} + IS_RELEASE: ${{ inputs.is-release }} + RELEASE_TAG: ${{ inputs.git-tag }} run: | - if [[ -f ci/versions.yml ]]; then - BUILD_CTK_VER=$(yq '.cuda.build.version' ci/versions.yml) - elif [[ -f ci/versions.json ]]; then - BUILD_CTK_VER=$(jq -r '.cuda.build.version' ci/versions.json) + set -euo pipefail + if [[ -n "$BINDINGS_PACKAGE" ]]; then + resolved_bindings_package="$BINDINGS_PACKAGE" + elif [[ "${IS_RELEASE}" == "true" ]]; then + resolved_bindings_package=$(cd .ci-control && python3 -m ci.tools.bindings_config \ + --release-tag "$RELEASE_TAG" \ + --release-source-root ..) else - echo "error: cannot find ci/versions.yml or ci/versions.json" >&2 - exit 1 - fi - if [[ ! "${BUILD_CTK_VER}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "error: derived CTK build version ${BUILD_CTK_VER} does not match MAJOR.MINOR.MICRO" >&2 - exit 1 + resolved_bindings_package=$(cd .ci-control && python3 -m ci.tools.bindings_config \ + --release-status current) fi - echo "BUILD_CTK_VER=${BUILD_CTK_VER}" >> "$GITHUB_ENV" + (cd .ci-control && python3 -m ci.tools.bindings_config \ + write-github-env "$GITHUB_ENV" <<< "$resolved_bindings_package") # TODO: This workflow runs on GH-hosted runner and cannot use the proxy cache @@ -86,7 +107,7 @@ jobs: uses: conda-incubator/setup-miniconda@be893c923ea9cf1cf7cd510fbdde27c7e18cbdcb # v4.1.0 with: activate-environment: cuda-python-docs - environment-file: ./cuda_python/docs/environment-docs.yml + environment-file: ./.ci-control/cuda_python/docs/environment-docs.yml miniforge-version: latest conda-remove-defaults: "true" python-version: 3.12 @@ -106,24 +127,16 @@ jobs: cuda-version: ${{ env.BUILD_CTK_VER }} - name: Set environment variables + env: + IS_RELEASE: ${{ inputs.is-release }} + RELEASE_TAG: ${{ inputs.git-tag }} run: | PYTHON_VERSION_FORMATTED="312" # see above REPO_DIR=$(pwd) - # Release docs may check out tags from before the source-root rename. - if [[ -d cuda_bindings_13 ]]; then - CUDA_BINDINGS_SOURCE_DIR=cuda_bindings_13 - elif [[ -d cuda_bindings ]]; then - CUDA_BINDINGS_SOURCE_DIR=cuda_bindings - else - echo "error: cannot find the cuda.bindings source directory" >&2 - exit 1 - fi - echo "CUDA_BINDINGS_SOURCE_DIR=${CUDA_BINDINGS_SOURCE_DIR}" >> "$GITHUB_ENV" - - if [[ ${{ inputs.is-release }} == "true" ]]; then + if [[ "$IS_RELEASE" == "true" ]]; then FILE_HASH="*" - DOCS_GITHUB_REF="${{ inputs.git-tag }}" + DOCS_GITHUB_REF="$RELEASE_TAG" if [[ -z "${DOCS_GITHUB_REF}" ]]; then DOCS_GITHUB_REF="${GITHUB_REF_NAME}" fi @@ -132,6 +145,8 @@ jobs: DOCS_GITHUB_REF=$(python ci/tools/checkout_source_links.py --github-ref) fi + echo "CUDA_BINDINGS_SOURCE_DIR=${BINDINGS_PACKAGE_ROOT}" >> "$GITHUB_ENV" + # make outputs from the previous job as env vars CUDA_CORE_ARTIFACT_BASENAME="cuda-core-python${PYTHON_VERSION_FORMATTED}-linux-64" echo "CUDA_PYTHON_DOCS_GITHUB_REF=${DOCS_GITHUB_REF}" >> $GITHUB_ENV @@ -141,22 +156,27 @@ jobs: CUDA_BINDINGS_ARTIFACT_BASENAME="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda${BUILD_CTK_VER}-linux-64" echo "CUDA_BINDINGS_ARTIFACT_BASENAME=${CUDA_BINDINGS_ARTIFACT_BASENAME}" >> $GITHUB_ENV echo "CUDA_BINDINGS_ARTIFACT_NAME=${CUDA_BINDINGS_ARTIFACT_BASENAME}-${FILE_HASH}" >> $GITHUB_ENV - echo "CUDA_BINDINGS_ARTIFACTS_DIR=$(realpath "$REPO_DIR/$CUDA_BINDINGS_SOURCE_DIR/dist")" >> $GITHUB_ENV + echo "CUDA_BINDINGS_ARTIFACTS_DIR=$(realpath "$REPO_DIR/${BINDINGS_PACKAGE_ROOT}/dist")" >> $GITHUB_ENV + CUDA_PYTHON_ARTIFACT_NAME="cuda-python-wheel-cuda${BUILD_CTK_VER}" + echo "CUDA_PYTHON_ARTIFACT_NAME=${CUDA_PYTHON_ARTIFACT_NAME}" >> $GITHUB_ENV - name: Download cuda-python build artifacts + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-python' }} uses: $/.github/actions/download-artifact-retry with: - name: cuda-python-wheel + name: ${{ env.CUDA_PYTHON_ARTIFACT_NAME }} path: . run-id: ${{ inputs.run-id }} github-token: ${{ github.token }} - name: Display structure of downloaded cuda-python artifacts + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-python' }} run: | pwd ls -lahR . - name: Download cuda-pathfinder build artifacts + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-pathfinder' || inputs.component == 'cuda-core' }} uses: $/.github/actions/download-artifact-retry with: name: cuda-pathfinder-wheel @@ -165,6 +185,7 @@ jobs: github-token: ${{ github.token }} - name: Display structure of downloaded cuda-pathfinder artifacts + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-pathfinder' || inputs.component == 'cuda-core' }} run: | pwd ls -lahR cuda_pathfinder @@ -177,7 +198,7 @@ jobs: path: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }} - name: Download cuda.bindings build artifacts - if: ${{ inputs.is-release }} + if: ${{ inputs.is-release && (inputs.component == 'all' || inputs.component == 'cuda-bindings' || inputs.component == 'cuda-core') }} uses: $/.github/actions/download-artifact-retry with: pattern: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} @@ -187,6 +208,7 @@ jobs: github-token: ${{ github.token }} - name: Display structure of downloaded cuda.bindings artifacts + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-bindings' || inputs.component == 'cuda-core' }} run: | pwd ls -lahR $CUDA_BINDINGS_ARTIFACTS_DIR @@ -199,7 +221,7 @@ jobs: path: ${{ env.CUDA_CORE_ARTIFACTS_DIR }} - name: Download cuda.core build artifacts - if: ${{ inputs.is-release }} + if: ${{ inputs.is-release && (inputs.component == 'all' || inputs.component == 'cuda-core') }} uses: $/.github/actions/download-artifact-retry with: pattern: ${{ env.CUDA_CORE_ARTIFACT_NAME }} @@ -209,26 +231,37 @@ jobs: github-token: ${{ github.token }} - name: Display structure of downloaded cuda.core build artifacts + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-core' }} run: | pwd ls -lahR $CUDA_CORE_ARTIFACTS_DIR - - name: Install all packages + - name: Install cuda-pathfinder + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-pathfinder' || inputs.component == 'cuda-core' }} run: | pushd cuda_pathfinder pip install *.whl popd + - name: Install cuda.bindings + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-bindings' || inputs.component == 'cuda-core' }} + run: | pushd "${CUDA_BINDINGS_ARTIFACTS_DIR}" pip install *.whl popd + - name: Install cuda.core + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-core' }} + run: | pushd "${CUDA_CORE_ARTIFACTS_DIR}" pip install *.whl popd - # Subpackages are already installed from CI artifacts above. - # --no-deps avoids re-resolving cuda-core from PyPI during tag releases. + - name: Install cuda-python + if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-python' }} + run: | + # Documentation builds do not exercise runtime dependencies. + # --no-deps also avoids resolving artifacts absent from focused tag runs. pip install --no-deps cuda_python*.whl # PR CI uses pull-request/ refs regardless of the target branch. @@ -252,9 +285,11 @@ jobs: - name: Build all docs if: ${{ inputs.component == 'all' }} + env: + IS_RELEASE: ${{ inputs.is-release }} run: | pushd cuda_python/docs/ - if [[ "${{ inputs.is-release }}" == "false" ]]; then + if [[ "$IS_RELEASE" == "false" ]]; then ./build_all_docs.sh latest-only else ./build_all_docs.sh @@ -271,13 +306,20 @@ jobs: - name: Build component docs if: ${{ inputs.component != 'all' }} + env: + DOCS_COMPONENT: ${{ inputs.component }} + IS_RELEASE: ${{ inputs.is-release }} run: | - COMPONENT=$(echo "${{ inputs.component }}" | tr '-' '_') - if [[ "${{ inputs.component }}" == "cuda-bindings" ]]; then - COMPONENT="${CUDA_BINDINGS_SOURCE_DIR}" + case "$DOCS_COMPONENT" in + cuda-bindings|cuda-core|cuda-pathfinder|cuda-python) ;; + *) echo "error: unsupported docs component: $DOCS_COMPONENT" >&2; exit 1 ;; + esac + COMPONENT="${DOCS_COMPONENT//-/_}" + if [[ "$DOCS_COMPONENT" == "cuda-bindings" ]]; then + COMPONENT="${BINDINGS_PACKAGE_ROOT}" fi - pushd ${COMPONENT}/docs/ - if [[ "${{ inputs.is-release }}" == "false" ]]; then + pushd "${COMPONENT}/docs/" + if [[ "$IS_RELEASE" == "false" ]]; then ./build_docs.sh latest-only else ./build_docs.sh @@ -290,13 +332,13 @@ jobs: fi ls -l build popd - if [[ "${{ inputs.component }}" != "cuda-python" ]]; then - TARGET="${{ inputs.component }}" - mkdir -p artifacts/docs/${TARGET} + if [[ "$DOCS_COMPONENT" != "cuda-python" ]]; then + TARGET="$DOCS_COMPONENT" + mkdir -p "artifacts/docs/${TARGET}" else TARGET="" fi - mv ${COMPONENT}/docs/build/html/* artifacts/docs/${TARGET} + mv "${COMPONENT}"/docs/build/html/* "artifacts/docs/${TARGET}" # Check links and anchors in the rendered HTML built above. # Links in authored Markdown and reST files are checked separately by the @@ -363,7 +405,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 - ref: ${{ inputs.git-tag }} + ref: ${{ inputs.is-release && format('refs/tags/{0}', inputs.git-tag) || inputs.git-tag }} - name: Download docs deployment payload uses: $/.github/actions/download-artifact-retry diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index f2026a379e3..86317af1b8e 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -74,26 +74,36 @@ jobs: outputs: RUN_ID: ${{ steps.find.outputs.run_id }} HEAD_SHA: ${{ steps.find.outputs.head_sha }} - CUDA_BUILD_VER: ${{ steps.find.outputs.cuda_build_ver }} + BINDINGS_CONFIG: ${{ steps.find.outputs.bindings_config }} steps: - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 + - name: Install CI tool dependencies + run: python -m pip install -e ./ci + - name: Find latest successful CI run on main id: find env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_REPOSITORY: ${{ github.repository }} + REQUESTED_RUN_ID: ${{ inputs.run-id }} run: | - if [[ -n "${{ inputs.run-id }}" ]]; then - RUN_ID="${{ inputs.run-id }}" + set -euo pipefail + if [[ -n "$REQUESTED_RUN_ID" ]]; then + if [[ ! "$REQUESTED_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::run-id must be a positive integer" + exit 1 + fi + RUN_ID="$REQUESTED_RUN_ID" HEAD_SHA=$(gh run view "$RUN_ID" \ - -R "${{ github.repository }}" \ + -R "$RELEASE_REPOSITORY" \ --json headSha | jq -r '.headSha') else # lookup-run-id --branch --head-sha prints two lines: run_id then head_sha - OUTPUT=$(./ci/tools/lookup-run-id --branch main --head-sha "${{ github.repository }}" "CI") + OUTPUT=$(./ci/tools/lookup-run-id --branch main --head-sha "$RELEASE_REPOSITORY" "CI") RUN_ID=$(echo "$OUTPUT" | sed -n '1p') HEAD_SHA=$(echo "$OUTPUT" | sed -n '2p') fi @@ -103,20 +113,16 @@ jobs: exit 1 fi - CUDA_BUILD_VER=$(gh api \ - "repos/${{ github.repository }}/contents/ci/versions.yml?ref=$HEAD_SHA" \ - --jq '.content' \ - | base64 -d \ - | yq '.cuda.build.version') - - if [[ -z "$CUDA_BUILD_VER" || "$CUDA_BUILD_VER" == "null" ]]; then - echo "::error::Could not resolve CUDA build version from $HEAD_SHA" - exit 1 - fi + git fetch --depth=1 origin "$HEAD_SHA" + snapshot=$(mktemp -d) + trap 'rm -rf -- "$snapshot"' EXIT + git archive "$HEAD_SHA" | tar -x -C "$snapshot" + BINDINGS_CONFIG=$(python -m ci.tools.bindings_config \ + --config "$snapshot/ci/versions.yml") echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT echo "head_sha=$HEAD_SHA" >> $GITHUB_OUTPUT - echo "cuda_build_ver=$CUDA_BUILD_VER" >> $GITHUB_OUTPUT + echo "bindings_config=$BINDINGS_CONFIG" >> $GITHUB_OUTPUT # ── PyTorch interop tests ── @@ -132,7 +138,7 @@ jobs: with: build-type: nightly host-platform: linux-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-pytorch @@ -150,7 +156,7 @@ jobs: with: build-type: nightly host-platform: linux-aarch64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-pytorch @@ -168,7 +174,7 @@ jobs: with: build-type: nightly host-platform: win-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-pytorch @@ -188,7 +194,7 @@ jobs: with: build-type: nightly host-platform: linux-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-numba-cuda @@ -206,7 +212,7 @@ jobs: with: build-type: nightly host-platform: linux-aarch64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-numba-cuda @@ -224,7 +230,7 @@ jobs: with: build-type: nightly host-platform: win-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-numba-cuda @@ -244,7 +250,7 @@ jobs: with: build-type: nightly host-platform: linux-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-numba-cuda-mlir @@ -262,7 +268,7 @@ jobs: with: build-type: nightly host-platform: win-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-numba-cuda-mlir @@ -282,7 +288,7 @@ jobs: with: build-type: nightly host-platform: linux-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-cuda-core @@ -300,7 +306,7 @@ jobs: with: build-type: nightly host-platform: win-64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: nightly-cuda-core @@ -320,7 +326,7 @@ jobs: with: build-type: nightly host-platform: linux-aarch64 - build-ctk-ver: ${{ needs.find-wheels.outputs.CUDA_BUILD_VER }} + bindings-config: ${{ needs.find-wheels.outputs.BINDINGS_CONFIG }} run-id: ${{ needs.find-wheels.outputs.RUN_ID }} sha: ${{ needs.find-wheels.outputs.HEAD_SHA }} test-mode: standard diff --git a/.github/workflows/ci-pixi-source-test.yml b/.github/workflows/ci-pixi-source-test.yml index 1e10d2a5a42..c9f7b213dc1 100644 --- a/.github/workflows/ci-pixi-source-test.yml +++ b/.github/workflows/ci-pixi-source-test.yml @@ -11,14 +11,14 @@ # cython-test build mechanics drift (see #2182, #2183). This workflow is the # only thing that runs the pixi source build end to end. # -# Two tiers, to spend GPU minutes deliberately (GPUs are scarce): +# Three tiers, to spend GPU minutes deliberately (GPUs are scarce): # - build-smoke (PRs): CPU-only. Source-builds bindings + core, imports them, # builds the cython test extensions and checks placement. Catches the # compile / ABI / .so-placement regressions WITHOUT a GPU. -# - build-identity-roundtrip (nightly + manual): CPU-only. cu13 -> cu12 -> -# cu13 in one checkout, so build artifacts cannot be reused across CUDA -# majors. Kept off PRs to avoid adding another job to the org's shared -# concurrent-job quota; the fast unit tests in +# - build-identity-roundtrip (nightly + manual): CPU-only. The current ABI -> +# another configured ABI -> current ABI in one checkout, so build artifacts +# cannot be reused across CUDA majors. Kept off PRs to avoid adding another +# job to the org's shared concurrent-job quota; the fast unit tests in # cuda_core/tests/test_build_hooks.py cover the same intent per-PR. # - full-test (nightly + manual): GPU runner, full `pixi run test`. @@ -41,11 +41,15 @@ on: # Path-dependency metadata (including cuda_python_test_helpers) feeds the # solve, so it can break the source build without a pixi.toml edit. - "**/pyproject.toml" - - "cuda_bindings_13/build_hooks.py" + - "cuda_bindings*/build_hooks.py" - "cuda_core/build_hooks.py" - - "cuda_bindings_13/cuda/bindings/**" # generated bindings sources - - "cuda_bindings_13/tests/cython/**" + - "cuda_bindings*/cuda/bindings/**" # generated bindings sources + - "cuda_bindings*/docs/**" + - "cuda_bindings*/tests/cython/**" - "cuda_core/tests/cython/**" + - "ci/tools/bindings_config.py" + - "ci/tools/compute_ci_plan.py" + - "ci/pyproject.toml" - "ci/versions.yml" - "ci/pixi-version.env" - ".github/actions/setup-pixi/action.yml" @@ -53,9 +57,9 @@ on: workflow_dispatch: inputs: cuda-env: - description: "pixi environment to test (cu13 or cu12)." + description: "Registered pixi CUDA ABI environment to test (empty selects current)." type: string - default: "cu13" + default: "" defaults: run: @@ -73,7 +77,7 @@ env: jobs: # ── PR guard: CPU-only build + import + placement smoke ── build-smoke: - name: "build smoke (cu13, linux-64, CPU)" + name: "build smoke (selected CUDA packages, linux-64, CPU)" if: >- github.repository_owner == 'nvidia' && (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch') @@ -83,9 +87,9 @@ jobs: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - # Full history + tags so setuptools-scm derives the real (13.x) + # Full history + tags so setuptools-scm derives the real # package version; a shallow checkout yields 0.1.dev1, which trips - # cuda.core's "cuda.bindings 12.x or 13.x must be installed" guard. + # cuda.core's supported cuda.bindings-version guard. fetch-depth: 0 filter: blob:none # Fetch CCCL separately: checkout's filter does not apply to submodules. @@ -96,50 +100,168 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive + - name: Install CI tool dependencies + run: python3 -m pip install -e ./ci + - name: Setup pixi uses: ./.github/actions/setup-pixi - - name: Source-build + import + cython-placement smoke + - name: Select CUDA environments env: - CUDA_ENV: ${{ inputs.cuda-env || 'cu13' }} + BASE_SHA: ${{ github.event.pull_request.base.sha || '' }} + EVENT_NAME: ${{ github.event_name }} + REQUESTED_CUDA_ENV: ${{ inputs.cuda-env || '' }} run: | - # pathfinder: pure-Python, no GPU. - pixi run -e "${CUDA_ENV}" test-pathfinder - - # bindings + core: force the source build (catches nvrtc/driver - # compile errors like #2182) and import them (catches ABI mismatches). - pixi run --manifest-path cuda_bindings_13 -e "${CUDA_ENV}" \ - python -c "import cuda.bindings.driver, cuda.bindings.nvrtc, cuda.bindings.runtime; print('bindings import OK')" - pixi run --manifest-path cuda_core -e "${CUDA_ENV}" \ - python -c "import cuda.core; print('core import OK')" - - # cython test extensions: build them and confirm each .so landed next - # to its .pyx in tests/cython (catches the placement regression #2180). - pixi run --manifest-path cuda_bindings_13 -e "${CUDA_ENV}" build-cython-tests - pixi run --manifest-path cuda_core -e "${CUDA_ENV}" build-cython-tests - for d in cuda_bindings_13/tests/cython cuda_core/tests/cython; do - if ! compgen -G "${d}/*.cpython-*.so" > /dev/null; then - echo "::error::no compiled cython test .so in ${d} (placement regression)" - exit 1 + bindings_packages=$(python3 -m ci.tools.bindings_config --package-roots) + selected_packages='[]' + docs_packages='[]' + + merge_packages() { + jq -cn --argjson left "$1" --argjson right "$2" \ + '$left + $right | unique_by(.package_root)' + } + + if [[ "${EVENT_NAME}" == "workflow_dispatch" ]]; then + if [[ -n "${REQUESTED_CUDA_ENV}" ]]; then + selected_packages=$(jq -c --arg cuda_env "${REQUESTED_CUDA_ENV}" \ + '[.[] | select(.cuda_variant == $cuda_env)]' <<< "${bindings_packages}") + if [[ "${selected_packages}" == '[]' ]]; then + registered_envs=$(jq -r '[.[].cuda_variant] | join(", ")' <<< "${bindings_packages}") + echo "::error::cuda-env must be a registered pixi environment: ${registered_envs}" + exit 1 + fi + else + selected_packages=$(jq -c '[.[] | select(.release_status == "current")]' \ + <<< "${bindings_packages}") fi - done + else + merge_base=$(git merge-base HEAD "${BASE_SHA}") + workplan=$(python3 -m ci.tools.compute_ci_plan \ + --merge-base "${merge_base}" \ + --baseline-run-id pixi-source) + + # Keep package-specific work package-specific, while selecting the + # registered package for any ABI-level work. + selected_packages=$(jq -cn \ + --argjson registry "${bindings_packages}" \ + --argjson plan "${workplan}" ' + $registry + | map(. as $package | select( + ($plan.modules.bindings.package_roots[$package.package_root].needs_build // false) + or ($plan.modules.bindings.package_roots[$package.package_root].needs_test // false) + or ($plan.modules.python.package_roots[$package.package_root].needs_build // false) + or ($plan.modules.python.package_roots[$package.package_root].needs_test // false) + or ($plan.jobs.test_cuda_majors[$package.cuda_variant] // false) + )) + ') + + # Pixi manifests are deliberately ignored by the wheel planner. + # Add their package-local impact to its source/test decisions. The + # wheel planner also deliberately ignores documentation changes. + while IFS= read -r -d '' path; do + manifest_packages=$(jq -c --arg path "${path}" ' + [.[] as $package + | select( + $path == ($package.package_root + "/pixi.toml") + or $path == ($package.package_root + "/pixi.lock") + ) + | $package] + ' <<< "${bindings_packages}") + if [[ "${manifest_packages}" != '[]' ]]; then + selected_packages=$(merge_packages "${selected_packages}" "${manifest_packages}") + else + case "${path}" in + */pixi.toml|*/pixi.lock|pixi.toml|pixi.lock) + selected_packages=$(merge_packages "${selected_packages}" "${bindings_packages}") + ;; + esac + fi + + changed_docs_packages=$(jq -c --arg path "${path}" ' + [.[] as $package + | select($path | startswith($package.package_root + "/docs/")) + | $package] + ' <<< "${bindings_packages}") + if [[ "${changed_docs_packages}" != '[]' ]]; then + selected_packages=$(merge_packages "${selected_packages}" "${changed_docs_packages}") + docs_packages=$(merge_packages "${docs_packages}" "${changed_docs_packages}") + fi + done < <(git diff --no-renames --name-only -z "${merge_base}" HEAD) + fi + + if [[ "${selected_packages}" == '[]' ]]; then + echo "::error::no bindings package selected for source-build smoke tests" + exit 1 + fi + echo "BINDINGS_PACKAGES=${selected_packages}" | tee -a "$GITHUB_ENV" + { + echo '### Selected source builds' + jq -r '.[] | "- \(.package_root): CUDA \(.cuda_major), pixi \(.cuda_variant)"' <<< "$selected_packages" + } >> "$GITHUB_STEP_SUMMARY" + echo "BINDINGS_DOC_PACKAGES=${docs_packages}" | tee -a "$GITHUB_ENV" + + - name: Source-build + import + cython-placement smoke + run: | + while IFS= read -r bindings_package; do + BINDINGS_ROOT=$(jq -r '.package_root' <<< "${bindings_package}") + CUDA_ENV=$(jq -r '.cuda_variant' <<< "${bindings_package}") + echo "::group::Source-build ${BINDINGS_ROOT} (${CUDA_ENV})" + if [[ $(jq -r '.release_status' <<< "${bindings_package}") == maintenance ]]; then + python3 -m ci.tools.bindings_scm_version --package-root "${BINDINGS_ROOT}" --sha "$GITHUB_SHA" + fi + # Avoid carrying Cython outputs between bindings packages or CUDA ABIs. + rm -rf cuda_core/build/cython cuda_core/cython_debug + find cuda_core/cuda/core -type f -name '*.so' -delete + find "${BINDINGS_ROOT}/tests/cython" cuda_core/tests/cython \ + -maxdepth 1 -type f \( -name 'test_*.cpp' -o -name 'test_*.so' \) -delete + + # pathfinder: pure-Python, no GPU. + pixi run -e "${CUDA_ENV}" test-pathfinder + + # Force both source builds and import them to catch compile/ABI + # regressions such as #2182. cuda-core environments may use + # published bindings by design; wheel CI covers local pairing. + pixi run --manifest-path "${BINDINGS_ROOT}" -e "${CUDA_ENV}" \ + python -c "import cuda.bindings.driver, cuda.bindings.nvrtc, cuda.bindings.runtime; print('bindings import OK')" + pixi run --manifest-path cuda_core -e "${CUDA_ENV}" \ + python -c "import cuda.core; print('core import OK')" + pixi run --manifest-path "${BINDINGS_ROOT}" -e "${CUDA_ENV}" build-cython-tests + pixi run --manifest-path cuda_core -e "${CUDA_ENV}" build-cython-tests + + # Confirm each extension landed next to its .pyx (catches #2180). + for d in "${BINDINGS_ROOT}/tests/cython" cuda_core/tests/cython; do + if ! compgen -G "${d}/*.cpython-*.so" > /dev/null; then + echo "::error::no compiled cython test .so in ${d} (placement regression)" + exit 1 + fi + done + echo "::endgroup::" + done < <(jq -c '.[]' <<< "${BINDINGS_PACKAGES}") echo "cython test extensions placed correctly" + - name: Build changed bindings documentation + if: ${{ env.BINDINGS_DOC_PACKAGES != '[]' }} + run: | + while IFS= read -r bindings_package; do + bindings_root=$(jq -r '.package_root' <<< "${bindings_package}") + pixi run --manifest-path "${bindings_root}" -e docs build-docs + done < <(jq -c '.[]' <<< "${BINDINGS_DOC_PACKAGES}") + # ── Nightly guard: build artifacts must be CUDA-major aware ── # # Neither Cython nor setuptools tracks the CUDA major in its own up-to-date # check: Cython does not hash `compile_time_env`, and an editable install's # .so is named by the Python ABI tag alone. Before build_hooks keyed them, - # a cu13 build followed by a cu12 build in the same checkout failed while - # compiling cu13-generated C++ against CUDA 12 headers. + # a build for one ABI followed by another in the same checkout failed while + # compiling generated C++ against headers from the second ABI. # - # The round trip (not just cu13 -> cu12) is what catches the second half: - # coming back to cu13 must not silently reuse the cu12 extension. + # The round trip is what catches the second half: coming back to the current + # ABI must not silently reuse the other ABI's extension. # - # cuda_core only: cuda-bindings cannot be source-built in its cu12 - # environment at all, for reasons unrelated to stale artifacts. + # cuda_core only: not every bindings package can be source-built in every ABI + # environment, for reasons unrelated to stale artifacts. build-identity-roundtrip: - name: "cu13 -> cu12 -> cu13 round trip (linux-64, CPU)" + name: "current -> maintenance -> current round trip (linux-64, CPU)" if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository_owner == 'nvidia' }} runs-on: ubuntu-latest timeout-minutes: 60 @@ -147,9 +269,9 @@ jobs: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - # Full history + tags so setuptools-scm derives the real (13.x) + # Full history + tags so setuptools-scm derives the real # package version; a shallow checkout yields 0.1.dev1, which trips - # cuda.core's "cuda.bindings 12.x or 13.x must be installed" guard. + # cuda.core's supported cuda.bindings-version guard. fetch-depth: 0 filter: blob:none # Fetch CCCL separately: checkout's filter does not apply to submodules. @@ -160,18 +282,28 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive + - name: Install CI tool dependencies + run: python3 -m pip install -e ./ci + - name: Setup pixi uses: ./.github/actions/setup-pixi - - name: Build cu13, then cu12, then cu13 again in one checkout + - name: Build current, another ABI, then current again in one checkout run: | - for cuda_env in cu13 cu12 cu13; do + current_package=$(python3 -m ci.tools.bindings_config --release-status current) + bindings_packages=$(python3 -m ci.tools.bindings_config --package-roots) + current_env=$(jq -r '.cuda_variant' <<< "${current_package}") + other_env=$(jq -r --arg current_env "${current_env}" \ + '[.[] | select(.cuda_variant != $current_env) | .cuda_variant] | first // empty' \ + <<< "${bindings_packages}") + + for cuda_env in "${current_env}" "${other_env}" "${current_env}"; do echo "::group::${cuda_env}" pixi run --manifest-path cuda_core -e "${cuda_env}" \ python -c "import cuda.core; print('core import OK')" echo "::endgroup::" done - # The last build was cu13, and each major must have kept its own + # The last build was current, and each ABI must have kept its own # generated sources rather than overwriting the other's. # Stamp is ABI-scoped (.build-config.). Unset and gnu # both record as "gnu"; llvm records as "llvm". @@ -182,21 +314,21 @@ jobs: exit 1 fi stamp=$(<"${stamps[0]}") - if [[ ! "${stamp}" =~ ^cu13-(gnu|llvm)-(debug|opt)(-cov)?$ ]]; then - echo "::error::build stamp is '${stamp}', expected cu13-{gnu|llvm}-{debug|opt}" + if [[ ! "${stamp}" =~ ^${current_env}-(gnu|llvm)-(debug|opt)(-cov)?$ ]]; then + echo "::error::build stamp is '${stamp}', expected ${current_env}-{gnu|llvm}-{debug|opt}" exit 1 fi - suffix="${stamp#cu13-}" - for major in cu12 cu13; do - if [ ! -d "cuda_core/build/cython/${major}-${suffix}" ]; then - echo "::error::no ${major}-${suffix} generated-source directory" + suffix="${stamp#"${current_env}-"}" + for cuda_env in "${other_env}" "${current_env}"; do + if [ ! -d "cuda_core/build/cython/${cuda_env}-${suffix}" ]; then + echo "::error::no ${cuda_env}-${suffix} generated-source directory" exit 1 fi done # ── Nightly: full `pixi run test` on a GPU runner ── full-test: - name: "pixi run test (${{ inputs.cuda-env || 'cu13' }}, linux-64, GPU)" + name: "pixi run test (${{ inputs.cuda-env || 'current' }}, linux-64, GPU)" if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository_owner == 'nvidia' }} runs-on: "linux-amd64-gpu-l4-latest-1" # same label scheme as test-wheel-linux.yml timeout-minutes: 90 @@ -213,16 +345,17 @@ jobs: run: | apt-get update # ca-certificates + git for checkout; libgl1/libegl1 for pyglet - # (pulled in by the test env). pixi itself is installed by setup-pixi. + # (pulled in by the test env). PyYAML and packaging drive the + # bindings registry resolver; pixi itself is installed by setup-pixi. apt-get install -y --no-install-recommends \ - ca-certificates git libgl1 libegl1 + ca-certificates git jq libgl1 libegl1 python3 python3-packaging python3-yaml - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - # Full history + tags so setuptools-scm derives the real (13.x) + # Full history + tags so setuptools-scm derives the real # package version; a shallow checkout yields 0.1.dev1, which trips - # cuda.core's "cuda.bindings 12.x or 13.x must be installed" guard. + # cuda.core's supported cuda.bindings-version guard. fetch-depth: 0 filter: blob:none # Fetch CCCL separately: checkout's filter does not apply to submodules. @@ -233,6 +366,25 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive + - name: Select CUDA environment + env: + REQUESTED_CUDA_ENV: ${{ inputs.cuda-env || '' }} + run: | + if [[ -n "${REQUESTED_CUDA_ENV}" ]]; then + bindings_packages=$(python3 -m ci.tools.bindings_config --package-roots) + if ! jq -e --arg cuda_env "${REQUESTED_CUDA_ENV}" \ + 'any(.[]; .cuda_variant == $cuda_env)' <<< "${bindings_packages}" > /dev/null; then + registered_envs=$(jq -r '[.[].cuda_variant] | join(", ")' <<< "${bindings_packages}") + echo "::error::cuda-env must be a registered pixi environment: ${registered_envs}" + exit 1 + fi + cuda_env="${REQUESTED_CUDA_ENV}" + else + current_package=$(python3 -m ci.tools.bindings_config --release-status current) + cuda_env=$(jq -r '.cuda_variant' <<< "${current_package}") + fi + echo "CUDA_ENV=${cuda_env}" | tee -a "$GITHUB_ENV" + - name: Setup proxy cache uses: nv-gha-runners/setup-proxy-cache@main continue-on-error: true @@ -241,4 +393,4 @@ jobs: uses: ./.github/actions/setup-pixi - name: pixi run test - run: pixi run -e "${{ inputs.cuda-env || 'cu13' }}" test + run: pixi run -e "${CUDA_ENV}" test diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 50d168db042..87127900bcd 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -20,18 +20,25 @@ jobs: if: ${{ github.repository_owner == 'nvidia' }} runs-on: ubuntu-latest outputs: + BINDINGS_ROOT: ${{ steps.get-vars.outputs.bindings_root }} CUDA_VER: ${{ steps.get-vars.outputs.cuda_ver }} steps: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Get CUDA version + - name: Install CI tool dependencies + run: python3 -m pip install -e ./ci + - name: Resolve current CUDA bindings package id: get-vars run: | - cuda_ver=$(yq '.cuda.build.version' ci/versions.yml) - echo "cuda_ver=$cuda_ver" >> $GITHUB_OUTPUT + set -euo pipefail + current=$(python3 -m ci.tools.bindings_config --release-status current) + { + echo "bindings_root=$(jq -r '.package_root' <<< "$current")" + echo "cuda_ver=$(jq -r '.toolkit_version' <<< "$current")" + } >> "$GITHUB_OUTPUT" coverage-linux: - name: Coverage (Linux) + name: Coverage ${{ needs.coverage-vars.outputs.BINDINGS_ROOT }} + core (Linux) needs: [coverage-vars] runs-on: "linux-amd64-gpu-a100-latest-1" permissions: @@ -43,6 +50,7 @@ jobs: env: HOST_PLATFORM: "linux-64" ARCH: "x86_64" + BINDINGS_ROOT: ${{ needs.coverage-vars.outputs.BINDINGS_ROOT }} CUDA_VER: ${{ needs.coverage-vars.outputs.CUDA_VER }} # Our self-hosted runners require a container # TODO: use a different (nvidia?) container @@ -96,10 +104,13 @@ jobs: - name: Set environment variables env: - BUILD_CUDA_VER: ${{ env.CUDA_VER }} + BINDINGS_SOURCE: local + BINDINGS_SOURCE_DIR: ${{ env.BINDINGS_ROOT }} + DEFAULT_BINDINGS_SOURCE_DIR: ${{ env.BINDINGS_ROOT }} CUDA_VER: ${{ env.CUDA_VER }} HOST_PLATFORM: ${{ env.HOST_PLATFORM }} LOCAL_CTK: ${{ env.LOCAL_CTK }} + PATHFINDER_SOURCE: artifact PY_VER: ${{ env.PY_VER }} SHA: ${{ github.sha }} run: | @@ -138,8 +149,16 @@ jobs: run: | export PIP_BUILD_CONSTRAINT="$(pwd)/wheel-constraints/cuda-bindings.txt" export PIP_CONSTRAINT="${PIP_BUILD_CONSTRAINT}" - .venv/bin/pip wheel -v --no-deps ./cuda_bindings_13 -w ./wheels/ - .venv/bin/pip install -v ./wheels/cuda_bindings*.whl --group ./cuda_bindings_13/pyproject.toml:test + .venv/bin/pip wheel -v --no-deps "./${BINDINGS_ROOT}" -w ./wheels/ + bindings_wheels=(./wheels/cuda_bindings*.whl) + test "${#bindings_wheels[@]}" -eq 1 + test -f "${bindings_wheels[0]}" + bindings_pyproject="./${BINDINGS_ROOT}/pyproject.toml" + if grep -q '^\[dependency-groups\][[:space:]]*$' "${bindings_pyproject}"; then + .venv/bin/python -m pip install "${bindings_wheels[0]}" --group "${bindings_pyproject}:test" + else + .venv/bin/python -m pip install "${bindings_wheels[0]}[test]" + fi - name: Constrain cuda-core to the local cuda-bindings wheel run: | @@ -184,7 +203,7 @@ jobs: continue-on-error: true run: | cd $INSTALL_ROOT - $REPO_ROOT/.venv/bin/pytest -v --cov=./cuda --cov-append --cov-context=test --cov-config=$REPO_ROOT/.coveragerc $REPO_ROOT/cuda_bindings_13/tests + $REPO_ROOT/.venv/bin/pytest -v --cov=./cuda --cov-append --cov-context=test --cov-config=$REPO_ROOT/.coveragerc "$REPO_ROOT/${BINDINGS_ROOT}/tests" - name: Run cuda.core tests continue-on-error: true @@ -217,7 +236,7 @@ jobs: # Build Windows wheels on GitHub-hosted runner (has VS, no GPU) build-wheel-windows: - name: Build Wheels (Windows) + name: Build ${{ needs.coverage-vars.outputs.BINDINGS_ROOT }} + core for coverage (Windows) needs: [coverage-vars] runs-on: windows-2022 permissions: @@ -228,6 +247,7 @@ jobs: env: HOST_PLATFORM: "win-64" CUDA_PYTHON_COVERAGE: "1" + BINDINGS_ROOT: ${{ needs.coverage-vars.outputs.BINDINGS_ROOT }} CUDA_VER: ${{ needs.coverage-vars.outputs.CUDA_VER }} steps: - name: Harden OS certificate trust (eMudhra/emSign CA stopgap) @@ -286,8 +306,7 @@ jobs: run: | export PIP_BUILD_CONSTRAINT="$(cygpath -w "$(pwd)/wheel-constraints/cuda-bindings.txt")" export PIP_CONSTRAINT="${PIP_BUILD_CONSTRAINT}" - cd cuda_bindings_13 - ../.venv/Scripts/pip wheel -v --no-deps . -w ../wheels/ + .venv/Scripts/pip wheel -v --no-deps "./${BINDINGS_ROOT}" -w ./wheels/ - name: Constrain cuda.core to the local cuda.bindings wheel run: | @@ -342,7 +361,7 @@ jobs: # Run coverage tests on self-hosted GPU runner (no VS needed, installs pre-built wheels) coverage-windows: - name: Coverage (Windows) + name: Coverage ${{ needs.coverage-vars.outputs.BINDINGS_ROOT }} + core (Windows) needs: [coverage-vars, build-wheel-windows] runs-on: "windows-amd64-gpu-a100-latest-1" permissions: @@ -352,6 +371,7 @@ jobs: HOST_PLATFORM: "win-64" ARCH: "amd64" CUDA_PYTHON_COVERAGE: "1" + BINDINGS_ROOT: ${{ needs.coverage-vars.outputs.BINDINGS_ROOT }} CUDA_VER: ${{ needs.coverage-vars.outputs.CUDA_VER }} defaults: run: @@ -423,7 +443,15 @@ jobs: run: | .venv/Scripts/pip install coverage pytest-cov Cython .venv/Scripts/pip install --group ./cuda_pathfinder/pyproject.toml:test - .venv/Scripts/pip install --group ./cuda_bindings_13/pyproject.toml:test + bindings_wheels=(./wheels/cuda_bindings*.whl) + test "${#bindings_wheels[@]}" -eq 1 + test -f "${bindings_wheels[0]}" + bindings_pyproject="./${BINDINGS_ROOT}/pyproject.toml" + if grep -q '^\[dependency-groups\][[:space:]]*$' "${bindings_pyproject}"; then + .venv/Scripts/python -m pip install "${bindings_wheels[0]}" --group "${bindings_pyproject}:test" + else + .venv/Scripts/python -m pip install "${bindings_wheels[0]}[test]" + fi .venv/Scripts/pip install --group ./cuda_core/pyproject.toml:test - name: Get install root @@ -448,7 +476,7 @@ jobs: "$GITHUB_WORKSPACE/.venv/Scripts/python" -m pytest \ -v --cov=./cuda --cov-append --cov-context=test \ --cov-config="$GITHUB_WORKSPACE/.coveragerc" \ - "$GITHUB_WORKSPACE/cuda_bindings_13/tests" + "$GITHUB_WORKSPACE/${BINDINGS_ROOT}/tests" - name: Run cuda.core tests continue-on-error: true diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index ce529fc73ce..8ca234af1ad 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -13,7 +13,8 @@ on: host-platform: type: string required: true - build-ctk-ver: + bindings-config: + description: Normalized CUDA bindings package-root registry JSON. type: string required: true matrix_filter: @@ -52,17 +53,24 @@ defaults: jobs: compute-matrix: + if: >- + ${{ inputs.workplan == '' || + contains(fromJSON(inputs.workplan).jobs.test_cuda_majors.*, true) }} runs-on: ubuntu-latest env: + BINDINGS_CONFIG: ${{ inputs.bindings-config }} BUILD_TYPE: ${{ inputs.build-type }} + WORKPLAN: ${{ inputs.workplan }} + TEST_MODE: ${{ inputs.test-mode }} ARCH: ${{ (inputs.host-platform == 'linux-64' && 'amd64') || (inputs.host-platform == 'linux-aarch64' && 'arm64') }} outputs: MATRIX: ${{ steps.compute-matrix.outputs.MATRIX }} - OLD_BRANCH: ${{ steps.compute-matrix.outputs.OLD_BRANCH }} steps: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.sha || github.sha }} - name: Validate Test Type run: | @@ -83,27 +91,95 @@ jobs: # Read base matrix from YAML file for the specific architecture TEST_MATRIX=$(yq -o json ".linux[\"${MATRIX_TYPE}\"] | map(select(.ARCH == \"${ARCH}\"))" ci/test-matrix.yml) + BINDINGS_PACKAGES=$(jq -c '.package_roots' <<< "$BINDINGS_CONFIG") + CURRENT_BINDINGS_SOURCE_DIR=$(jq -r ' + .[] | select(.release_status == "current") | .package_root + ' <<< "$BINDINGS_PACKAGES") + + if [[ -z "$WORKPLAN" ]]; then + TEST_CUDA_MAJORS=$(jq -c ' + [.[].cuda_variant] | map({key: ., value: true}) | from_entries + ' <<< "$BINDINGS_PACKAGES") + PATHFINDER_SOURCE=artifact + else + TEST_CUDA_MAJORS=$(jq -c '.jobs.test_cuda_majors' <<< "$WORKPLAN") + PATHFINDER_SOURCE=$(jq -r '.sources.pathfinder' <<< "$WORKPLAN") + fi + # Apply matrix filter; reject custom DRIVER + FLAVOR=wsl (the # in-container driver swap doesn't work under WSL); add a # RUNNER_DRIVER field that maps any custom version back to # 'latest' (the install script swaps the driver itself, so we # need to land on the runner that ships with the most recent # pre-installed driver); wrap in include structure. - MATRIX=$(echo "$TEST_MATRIX" | jq -c '${{ inputs.matrix_filter }} | if any(.[]; .DRIVER != "latest" and .DRIVER != "earliest" and .FLAVOR == "wsl") then "Error: custom DRIVER is not supported with FLAVOR=wsl\n" | halt_error(1) else . end | map(. + {RUNNER_DRIVER: (if .DRIVER == "latest" or .DRIVER == "earliest" then .DRIVER else "latest" end)}) | if (. | length) > 0 then {include: .} else "Error: Empty matrix\n" | halt_error(1) end') + MATRIX=$(echo "$TEST_MATRIX" | jq -c \ + --argjson test_cuda_majors "$TEST_CUDA_MAJORS" \ + --argjson bindings_packages "$BINDINGS_PACKAGES" \ + --arg default_bindings_source_dir "$CURRENT_BINDINGS_SOURCE_DIR" \ + --arg pathfinder_source "$PATHFINDER_SOURCE" \ + --argjson workplan "${WORKPLAN:-null}" \ + --arg test_mode "$TEST_MODE" \ + '${{ inputs.matrix_filter }} + | map( + . as $row + | ("cu" + ($row.CUDA_VER | split(".")[0])) as $cuda_variant + | ($bindings_packages | map(select(.toolkit_version == $row.CUDA_VER)) | first) as $bindings_package + | $row + { + CUDA_VARIANT: $cuda_variant, + BINDINGS_SOURCE: (if $bindings_package == null then "floor" else "local" end), + BINDINGS_SOURCE_DIR: ($bindings_package.package_root // ""), + DEFAULT_BINDINGS_SOURCE_DIR: $default_bindings_source_dir, + PATHFINDER_SOURCE: $pathfinder_source + } + ) + | map(select($test_cuda_majors[.CUDA_VARIANT] == true)) + | map(. as $row | . + { + SUITES: (if $test_mode != "standard" then $test_mode else + [if $workplan == null or $workplan.modules.pathfinder.needs_test then "pathfinder" else empty end, + if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.bindings.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "bindings" + ($row.CUDA_VER | split(".")[0]) else empty end, + if $workplan == null or $workplan.modules.core.cuda_majors[$row.CUDA_VARIANT].needs_test then "core" + ($row.CUDA_VER | split(".")[0]) else empty end, + if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "python" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) + }) + | map(select(.SUITES != "")) + | if any(.[]; .DRIVER != "latest" and .DRIVER != "earliest" and .FLAVOR == "wsl") then "Error: custom DRIVER is not supported with FLAVOR=wsl\n" | halt_error(1) else . end + | map(. + {RUNNER_DRIVER: (if .DRIVER == "latest" or .DRIVER == "earliest" then .DRIVER else "latest" end)}) + | if (. | length) > 0 then {include: .} else "Error: Empty matrix after CUDA-major workplan filtering\n" | halt_error(1) end') + + # Compatibility nightly modes intentionally use published floor bindings. + # Standard PR/release runs must retain each selected local bindings line. + if [[ "$TEST_MODE" == "standard" && "$BUILD_TYPE" == "pull-request" ]]; then + jq -en --argjson packages "$BINDINGS_PACKAGES" \ + --argjson enabled "$TEST_CUDA_MAJORS" --argjson matrix "$MATRIX" \ + --argjson workplan "${WORKPLAN:-null}" ' + [$packages[] | . as $package + | select($enabled[.cuda_variant] == true) + | select($workplan == null or + $workplan.modules.bindings.package_roots[.package_root].needs_test or + $workplan.modules.python.package_roots[.package_root].needs_test) + | select(all($matrix.include[]; + .BINDINGS_SOURCE != "local" or .BINDINGS_SOURCE_DIR != $package.package_root)) + | .package_root] as $missing + | if $missing | length == 0 then true else + error("filtered test matrix omits required local-wheel coverage: \($missing | join(", "))") end + ' > /dev/null + fi - echo "MATRIX=${MATRIX}" | tee --append "${GITHUB_OUTPUT}" + { + echo '### Selected wheel test coverage' + echo '| Suites | Python | CTK | Toolkit source | Bindings source | GPU |' + echo '| --- | --- | --- | --- | --- | --- |' + jq -r '.include[] | "| \(.SUITES) | \(.PY_VER) | \(.CUDA_VER) | \(if .LOCAL_CTK == "1" then "local CTK" else "wheels" end) | \(if .BINDINGS_SOURCE == "local" then .BINDINGS_SOURCE_DIR else "published floor" end) | \(.GPU) |"' <<< "$MATRIX" + } >> "$GITHUB_STEP_SUMMARY" - # This job has yq already installed, so let's do it here - OLD_BRANCH=$(yq '.backport_branch' ci/versions.yml) - echo "OLD_BRANCH=${OLD_BRANCH}" >> "$GITHUB_OUTPUT" + echo "MATRIX=${MATRIX}" | tee --append "${GITHUB_OUTPUT}" test: env: TEST_PATHFINDER: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.pathfinder.needs_test }} - TEST_BINDINGS: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.bindings.needs_test }} - TEST_CORE: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.core.needs_test }} - TEST_PYTHON: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.python.needs_test }} - name: Python ${{ matrix.PY_VER }}, CUDA ${{ matrix.CUDA_VER }} (${{ (matrix.LOCAL_CTK == '1' && 'local') || 'wheels' }}), GPU ${{ matrix.GPU }}${{ matrix.GPU_COUNT != '1' && format(' (x{0})', matrix.GPU_COUNT) || '' }}${{ matrix.FLAVOR && format(', {0}', matrix.FLAVOR) || '' }}${{ matrix.ENV.TORCH_VER && format(', {0}+{1}', matrix.ENV.TORCH_VER, matrix.ENV.TORCH_CUDA) || '' }}${{ matrix.ENV.MODE == 'nightly-numba-cuda' && ', latest' || '' }} + TEST_BINDINGS: ${{ inputs.workplan == '' || (matrix.BINDINGS_SOURCE == 'local' && fromJSON(inputs.workplan).modules.bindings.package_roots[matrix.BINDINGS_SOURCE_DIR].needs_test) }} + TEST_CORE: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.core.cuda_majors[matrix.CUDA_VARIANT].needs_test }} + TEST_PYTHON: ${{ inputs.workplan == '' || (matrix.BINDINGS_SOURCE == 'local' && fromJSON(inputs.workplan).modules.python.package_roots[matrix.BINDINGS_SOURCE_DIR].needs_test) }} + name: ${{ matrix.SUITES }} / py${{ matrix.PY_VER }}, CTK${{ matrix.CUDA_VER }} (${{ (matrix.LOCAL_CTK == '1' && 'local CTK') || 'wheels' }}; bindings ${{ matrix.BINDINGS_SOURCE == 'local' && 'PR' || 'floor' }}), GPU ${{ matrix.GPU }}${{ matrix.GPU_COUNT != '1' && format(' (x{0})', matrix.GPU_COUNT) || '' }}${{ matrix.FLAVOR && format(', {0}', matrix.FLAVOR) || '' }}${{ matrix.ENV.TORCH_VER && format(', {0}+{1}', matrix.ENV.TORCH_VER, matrix.ENV.TORCH_CUDA) || '' }}${{ matrix.ENV.MODE == 'nightly-numba-cuda' && ', latest' || '' }} timeout-minutes: 60 needs: compute-matrix strategy: @@ -111,7 +187,8 @@ jobs: matrix: ${{ fromJSON(needs.compute-matrix.outputs.MATRIX) }} runs-on: "${{ matrix.FLAVOR || 'linux' }}-${{ matrix.ARCH }}-gpu-${{ matrix.GPU }}-${{ matrix.RUNNER_DRIVER }}-${{ matrix.GPU_COUNT }}" # The build stage could fail but we want the CI to keep moving. - if: ${{ github.repository_owner == 'nvidia' && !cancelled() }} + if: ${{ github.repository_owner == 'nvidia' && !cancelled() && + (inputs.workplan == '' || contains(fromJSON(inputs.workplan).jobs.test_cuda_majors.*, true)) }} # Our self-hosted runners require a container # TODO: use a different (nvidia?) container container: @@ -142,9 +219,8 @@ jobs: # for artifact fetching, graphics libs, g++ required for cffi in # example; util-linux for `nsenter` (custom-DRIVER rows re-exec # install_gpu_driver.sh onto the host through nsenter) - # yq: ci/tools/env-vars reads ci/versions.yml with it - dependencies: "jq wget libgl1 libegl1 g++ util-linux yq" - dependent_exes: "jq wget yq" + dependencies: "jq wget libgl1 libegl1 g++ util-linux" + dependent_exes: "jq wget" - name: Update container CA certificates run: | @@ -173,10 +249,13 @@ jobs: - name: Set environment variables env: - BUILD_CUDA_VER: ${{ inputs.build-ctk-ver }} + BINDINGS_SOURCE: ${{ matrix.BINDINGS_SOURCE }} + BINDINGS_SOURCE_DIR: ${{ matrix.BINDINGS_SOURCE_DIR }} + DEFAULT_BINDINGS_SOURCE_DIR: ${{ matrix.DEFAULT_BINDINGS_SOURCE_DIR }} CUDA_VER: ${{ matrix.CUDA_VER }} HOST_PLATFORM: ${{ inputs.host-platform }} LOCAL_CTK: ${{ matrix.LOCAL_CTK }} + PATHFINDER_SOURCE: ${{ matrix.PATHFINDER_SOURCE }} PY_VER: ${{ matrix.PY_VER }} SHA: ${{ inputs.sha || github.sha }} SKIP_BINDINGS_TEST_OVERRIDE: ${{ env.TEST_BINDINGS != 'true' && '1' || '0' }} @@ -189,7 +268,8 @@ jobs: run: echo "$MATRIX_ENV" | jq -r 'to_entries[] | "\(.key)=\(.value)"' >> "$GITHUB_ENV" - name: Download cuda-pathfinder build artifacts - if: ${{ env.TEST_PATHFINDER == 'true' || env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true' }} + if: ${{ (env.TEST_PATHFINDER == 'true' || env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && + env.PATHFINDER_SOURCE == 'artifact' }} uses: $/.github/actions/download-artifact-retry with: name: cuda-pathfinder-wheel @@ -198,17 +278,17 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Download cuda-python build artifacts - if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'main' }} + if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'local' }} uses: $/.github/actions/download-artifact-retry with: - name: cuda-python-wheel + name: ${{ env.CUDA_PYTHON_ARTIFACT_NAME }} path: . run-id: ${{ inputs.run-id || github.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} - name: Download cuda.bindings build artifacts if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - (env.BINDINGS_SOURCE == 'main' || (env.BINDINGS_SOURCE == 'floor' && env.TEST_CORE == 'true')) }} + (env.BINDINGS_SOURCE == 'local' || (env.BINDINGS_SOURCE == 'floor' && env.TEST_CORE == 'true')) }} uses: $/.github/actions/download-artifact-retry with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} @@ -216,77 +296,15 @@ jobs: run-id: ${{ inputs.run-id || github.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Look up prior-branch artifacts run ID - id: prior - if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE == 'backport' }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # See https://github.com/cli/cli/blob/trunk/docs/install_linux.md#debian-ubuntu-linux-raspberry-pi-os-apt. - # gh is needed for lookup-run-id, which calls gh api. - mkdir -p -m 755 /etc/apt/keyrings \ - && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ - && cat $out | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ - && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ - && apt update \ - && apt install gh -y - - OLD_BRANCH=${{ needs.compute-matrix.outputs.OLD_BRANCH }} - OLD_ARTIFACT_PATTERN="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda*-${{ inputs.host-platform }}-*[0-9a-f]" - LOOKUP_ARGS=( - --branch "${OLD_BRANCH}" - --artifact "${OLD_ARTIFACT_PATTERN}" - ) - if ${{ env.TEST_PYTHON == 'true' }}; then - LOOKUP_ARGS+=(--artifact cuda-python-wheel) - fi - LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id \ - "${LOOKUP_ARGS[@]}" NVIDIA/cuda-python "CI") - echo "run-id=${LATEST_PRIOR_RUN_ID}" >> "${GITHUB_OUTPUT}" - echo "artifact-pattern=${OLD_ARTIFACT_PATTERN}" >> "${GITHUB_OUTPUT}" - - - name: Download prior-branch cuda.bindings build artifacts - if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE == 'backport' }} - uses: $/.github/actions/download-artifact-retry - with: - pattern: ${{ steps.prior.outputs.artifact-pattern }} - merge-multiple: "true" - path: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }} - run-id: ${{ steps.prior.outputs.run-id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Download prior-branch cuda-python wheel - if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'backport' }} - uses: $/.github/actions/download-artifact-retry - with: - name: cuda-python-wheel - path: . - run-id: ${{ steps.prior.outputs.run-id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Verify prior-branch artifact layout - if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE == 'backport' }} - run: | - bindings_wheels=("${CUDA_BINDINGS_ARTIFACTS_DIR}"/cuda_bindings-*.whl) - if [[ "${#bindings_wheels[@]}" -ne 1 ]] || [[ ! -f "${bindings_wheels[0]}" ]]; then - echo "::error::Expected exactly one cuda_bindings wheel in ${CUDA_BINDINGS_ARTIFACTS_DIR}; found: ${bindings_wheels[*]}" - exit 1 - fi - ls -al "${CUDA_BINDINGS_ARTIFACTS_DIR}" - - name: Display structure of downloaded cuda-python artifacts - if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE != 'floor' }} + if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'local' }} run: | pwd ls -lah cuda_python*.whl cuda_pathfinder/ - name: Display structure of downloaded cuda.bindings artifacts if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE != 'floor' }} + env.BINDINGS_SOURCE == 'local' }} run: | pwd ls -lahR $CUDA_BINDINGS_ARTIFACTS_DIR @@ -325,7 +343,7 @@ jobs: if: ${{ env.TEST_CORE == 'true' && env.SKIP_CYTHON_TEST == '0' }} uses: $/.github/actions/download-artifact-retry with: - name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-tests + name: ${{ env.CUDA_CORE_CYTHON_TEST_ARTIFACT_NAME }} path: ${{ env.CUDA_CORE_CYTHON_TESTS_DIR }} run-id: ${{ inputs.run-id || github.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} @@ -419,22 +437,25 @@ jobs: run: run-tests core - name: Ensure cuda-python installable - if: ${{ inputs.test-mode == 'standard' && env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'main' }} + if: ${{ inputs.test-mode == 'standard' && env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'local' }} + env: + LOCAL_CTK: ${{ matrix.LOCAL_CTK }} + shell: bash --noprofile --norc -xeuo pipefail {0} run: | - # Package suites install their own dependencies. A metapackage-only - # run has no preceding suite, so install the exact local internal - # wheels in one transaction while resolving released dependencies - # such as cuda-core from the package index. - if ${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }}; then + # CUDA 13's metapackage also requires Core. A bindings-only release + # must resolve published Core while preserving the selected wheels. + if [[ "$TEST_CORE" == true || ("$TEST_BINDINGS" == true && "$TEST_CUDA_MAJOR" == 12) ]]; then dependency_args=(--no-deps) else - dependency_args=( - ./cuda_pathfinder/cuda_pathfinder-*.whl - "${CUDA_BINDINGS_ARTIFACTS_DIR}"/cuda_bindings-*.whl - ) + dependency_args=("${CUDA_BINDINGS_ARTIFACTS_DIR}"/cuda_bindings-*.whl) + if [[ "$PATHFINDER_SOURCE" == artifact ]]; then + dependency_args+=(./cuda_pathfinder/cuda_pathfinder-*.whl) + else + dependency_args+=("$(python -c 'from importlib.metadata import version; print("cuda-pathfinder==" + version("cuda-pathfinder"))')") + fi fi python_requirements=(cuda_python*.whl) - if [[ "${{ matrix.LOCAL_CTK }}" != 1 ]]; then + if [[ "$LOCAL_CTK" != 1 ]]; then python_requirements=("${python_requirements[@]/%/[all]}") fi pip install --only-binary=:all: "${dependency_args[@]}" "${python_requirements[@]}" diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index a3280b2962e..4286aa002a3 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -13,7 +13,8 @@ on: host-platform: type: string required: true - build-ctk-ver: + bindings-config: + description: Normalized CUDA bindings package-root registry JSON. type: string required: true matrix_filter: @@ -48,18 +49,26 @@ on: jobs: compute-matrix: + if: >- + ${{ inputs.workplan == '' || + contains(fromJSON(inputs.workplan).jobs.test_cuda_majors.*, true) }} runs-on: ubuntu-latest defaults: run: shell: bash --noprofile --norc -xeuo pipefail {0} env: + BINDINGS_CONFIG: ${{ inputs.bindings-config }} BUILD_TYPE: ${{ inputs.build-type }} + WORKPLAN: ${{ inputs.workplan }} + TEST_MODE: ${{ inputs.test-mode }} ARCH: ${{ (inputs.host-platform == 'win-64' && 'amd64') }} outputs: MATRIX: ${{ steps.compute-matrix.outputs.MATRIX }} steps: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.sha || github.sha }} - name: Validate Test Type run: | @@ -79,28 +88,100 @@ jobs: # Read base matrix from YAML file for the specific architecture TEST_MATRIX=$(yq -o json ".windows[\"${MATRIX_TYPE}\"] | map(select(.ARCH == \"${ARCH}\"))" ci/test-matrix.yml) + BINDINGS_PACKAGES=$(jq -c '.package_roots' <<< "$BINDINGS_CONFIG") + CURRENT_BINDINGS_SOURCE_DIR=$(jq -r ' + .[] | select(.release_status == "current") | .package_root + ' <<< "$BINDINGS_PACKAGES") + + if [[ -z "$WORKPLAN" ]]; then + TEST_CUDA_MAJORS=$(jq -c ' + [.[].cuda_variant] | map({key: ., value: true}) | from_entries + ' <<< "$BINDINGS_PACKAGES") + PATHFINDER_SOURCE=artifact + else + TEST_CUDA_MAJORS=$(jq -c '.jobs.test_cuda_majors' <<< "$WORKPLAN") + PATHFINDER_SOURCE=$(jq -r '.sources.pathfinder' <<< "$WORKPLAN") + fi + # Apply matrix filter; add a RUNNER_DRIVER field that maps any # custom DRIVER version back to 'latest' (install_gpu_driver.ps1 # swaps the driver itself, so the runner must be the one that # ships the most recent pre-installed driver); wrap in include. - MATRIX=$(echo "$TEST_MATRIX" | jq -c '${{ inputs.matrix_filter }} | map(. + {RUNNER_DRIVER: (if .DRIVER == "latest" or .DRIVER == "earliest" then .DRIVER else "latest" end)}) | if (. | length) > 0 then {include: .} else "Error: Empty matrix\n" | halt_error(1) end') + MATRIX=$(echo "$TEST_MATRIX" | jq -c \ + --argjson test_cuda_majors "$TEST_CUDA_MAJORS" \ + --argjson bindings_packages "$BINDINGS_PACKAGES" \ + --arg default_bindings_source_dir "$CURRENT_BINDINGS_SOURCE_DIR" \ + --arg pathfinder_source "$PATHFINDER_SOURCE" \ + --argjson workplan "${WORKPLAN:-null}" \ + --arg test_mode "$TEST_MODE" \ + '${{ inputs.matrix_filter }} + | map( + . as $row + | ("cu" + ($row.CUDA_VER | split(".")[0])) as $cuda_variant + | ($bindings_packages | map(select(.toolkit_version == $row.CUDA_VER)) | first) as $bindings_package + | $row + { + CUDA_VARIANT: $cuda_variant, + BINDINGS_SOURCE: (if $bindings_package == null then "floor" else "local" end), + BINDINGS_SOURCE_DIR: ($bindings_package.package_root // ""), + DEFAULT_BINDINGS_SOURCE_DIR: $default_bindings_source_dir, + PATHFINDER_SOURCE: $pathfinder_source + } + ) + | map(select($test_cuda_majors[.CUDA_VARIANT] == true)) + | map(. as $row | . + { + SUITES: (if $test_mode != "standard" then $test_mode else + [if $workplan == null or $workplan.modules.pathfinder.needs_test then "pathfinder" else empty end, + if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.bindings.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "bindings" + ($row.CUDA_VER | split(".")[0]) else empty end, + if $workplan == null or $workplan.modules.core.cuda_majors[$row.CUDA_VARIANT].needs_test then "core" + ($row.CUDA_VER | split(".")[0]) else empty end, + if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "python" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) + }) + | map(select(.SUITES != "")) + | map(. + {RUNNER_DRIVER: (if .DRIVER == "latest" or .DRIVER == "earliest" then .DRIVER else "latest" end)}) + | if (. | length) > 0 then {include: .} else "Error: Empty matrix after CUDA-major workplan filtering\n" | halt_error(1) end') + + # Compatibility nightly modes intentionally use published floor bindings. + # Standard PR/release runs must retain each selected local bindings line. + if [[ "$TEST_MODE" == "standard" && "$BUILD_TYPE" == "pull-request" ]]; then + jq -en --argjson packages "$BINDINGS_PACKAGES" \ + --argjson enabled "$TEST_CUDA_MAJORS" --argjson matrix "$MATRIX" \ + --argjson workplan "${WORKPLAN:-null}" ' + [$packages[] | . as $package + | select($enabled[.cuda_variant] == true) + | select($workplan == null or + $workplan.modules.bindings.package_roots[.package_root].needs_test or + $workplan.modules.python.package_roots[.package_root].needs_test) + | select(all($matrix.include[]; + .BINDINGS_SOURCE != "local" or .BINDINGS_SOURCE_DIR != $package.package_root)) + | .package_root] as $missing + | if $missing | length == 0 then true else + error("filtered test matrix omits required local-wheel coverage: \($missing | join(", "))") end + ' > /dev/null + fi + + { + echo '### Selected wheel test coverage' + echo '| Suites | Python | CTK | Toolkit source | Bindings source | GPU |' + echo '| --- | --- | --- | --- | --- | --- |' + jq -r '.include[] | "| \(.SUITES) | \(.PY_VER) | \(.CUDA_VER) | \(if .LOCAL_CTK == "1" then "local CTK" else "wheels" end) | \(if .BINDINGS_SOURCE == "local" then .BINDINGS_SOURCE_DIR else "published floor" end) | \(.GPU) |"' <<< "$MATRIX" + } >> "$GITHUB_STEP_SUMMARY" echo "MATRIX=${MATRIX}" | tee --append "${GITHUB_OUTPUT}" test: env: TEST_PATHFINDER: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.pathfinder.needs_test }} - TEST_BINDINGS: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.bindings.needs_test }} - TEST_CORE: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.core.needs_test }} - TEST_PYTHON: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.python.needs_test }} - name: Python ${{ matrix.PY_VER }}, CUDA ${{ matrix.CUDA_VER }} (${{ (matrix.LOCAL_CTK == '1' && 'local') || 'wheels' }}), GPU ${{ matrix.GPU }}${{ matrix.GPU_COUNT != '1' && format(' (x{0})', matrix.GPU_COUNT) || '' }} (${{ matrix.DRIVER_MODE }})${{ matrix.ENV.TORCH_VER && format(', {0}+{1}', matrix.ENV.TORCH_VER, matrix.ENV.TORCH_CUDA) || '' }}${{ matrix.ENV.MODE == 'nightly-numba-cuda' && ', latest' || '' }} + TEST_BINDINGS: ${{ inputs.workplan == '' || (matrix.BINDINGS_SOURCE == 'local' && fromJSON(inputs.workplan).modules.bindings.package_roots[matrix.BINDINGS_SOURCE_DIR].needs_test) }} + TEST_CORE: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.core.cuda_majors[matrix.CUDA_VARIANT].needs_test }} + TEST_PYTHON: ${{ inputs.workplan == '' || (matrix.BINDINGS_SOURCE == 'local' && fromJSON(inputs.workplan).modules.python.package_roots[matrix.BINDINGS_SOURCE_DIR].needs_test) }} + name: ${{ matrix.SUITES }} / py${{ matrix.PY_VER }}, CTK${{ matrix.CUDA_VER }} (${{ (matrix.LOCAL_CTK == '1' && 'local CTK') || 'wheels' }}; bindings ${{ matrix.BINDINGS_SOURCE == 'local' && 'PR' || 'floor' }}), GPU ${{ matrix.GPU }}${{ matrix.GPU_COUNT != '1' && format(' (x{0})', matrix.GPU_COUNT) || '' }} (${{ matrix.DRIVER_MODE }})${{ matrix.ENV.TORCH_VER && format(', {0}+{1}', matrix.ENV.TORCH_VER, matrix.ENV.TORCH_CUDA) || '' }}${{ matrix.ENV.MODE == 'nightly-numba-cuda' && ', latest' || '' }} timeout-minutes: 60 # The build stage could fail but we want the CI to keep moving. needs: compute-matrix strategy: fail-fast: false matrix: ${{ fromJSON(needs.compute-matrix.outputs.MATRIX) }} - if: ${{ github.repository_owner == 'nvidia' && !cancelled() }} + if: ${{ github.repository_owner == 'nvidia' && !cancelled() && + (inputs.workplan == '' || contains(fromJSON(inputs.workplan).jobs.test_cuda_majors.*, true)) }} runs-on: "windows-${{ matrix.ARCH }}-gpu-${{ matrix.GPU }}-${{ matrix.RUNNER_DRIVER }}-${{ matrix.GPU_COUNT }}" steps: - name: Enable Git symlinks (Windows, must precede checkout) @@ -157,10 +238,13 @@ jobs: - name: Set environment variables env: - BUILD_CUDA_VER: ${{ inputs.build-ctk-ver }} + BINDINGS_SOURCE: ${{ matrix.BINDINGS_SOURCE }} + BINDINGS_SOURCE_DIR: ${{ matrix.BINDINGS_SOURCE_DIR }} + DEFAULT_BINDINGS_SOURCE_DIR: ${{ matrix.DEFAULT_BINDINGS_SOURCE_DIR }} CUDA_VER: ${{ matrix.CUDA_VER }} HOST_PLATFORM: ${{ inputs.host-platform }} LOCAL_CTK: ${{ matrix.LOCAL_CTK }} + PATHFINDER_SOURCE: ${{ matrix.PATHFINDER_SOURCE }} PY_VER: ${{ matrix.PY_VER }} SHA: ${{ inputs.sha || github.sha }} SKIP_BINDINGS_TEST_OVERRIDE: ${{ env.TEST_BINDINGS != 'true' && '1' || '0' }} @@ -175,7 +259,8 @@ jobs: run: echo "$MATRIX_ENV" | jq -r 'to_entries[] | "\(.key)=\(.value)"' >> "$GITHUB_ENV" - name: Download cuda-pathfinder build artifacts - if: ${{ env.TEST_PATHFINDER == 'true' || env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true' }} + if: ${{ (env.TEST_PATHFINDER == 'true' || env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && + env.PATHFINDER_SOURCE == 'artifact' }} uses: $/.github/actions/download-artifact-retry with: name: cuda-pathfinder-wheel @@ -184,17 +269,17 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Download cuda-python build artifacts - if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'main' }} + if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'local' }} uses: $/.github/actions/download-artifact-retry with: - name: cuda-python-wheel + name: ${{ env.CUDA_PYTHON_ARTIFACT_NAME }} path: . run-id: ${{ inputs.run-id || github.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} - name: Download cuda.bindings build artifacts if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - (env.BINDINGS_SOURCE == 'main' || (env.BINDINGS_SOURCE == 'floor' && env.TEST_CORE == 'true')) }} + (env.BINDINGS_SOURCE == 'local' || (env.BINDINGS_SOURCE == 'floor' && env.TEST_CORE == 'true')) }} uses: $/.github/actions/download-artifact-retry with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} @@ -202,69 +287,15 @@ jobs: run-id: ${{ inputs.run-id || github.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Look up prior-branch artifacts run ID - id: prior - if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE == 'backport' }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash --noprofile --norc -xeuo pipefail {0} - run: | - OLD_BRANCH=$(yq '.backport_branch' ci/versions.yml) - OLD_ARTIFACT_PATTERN="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda*-${{ inputs.host-platform }}-*[0-9a-f]" - LOOKUP_ARGS=( - --branch "${OLD_BRANCH}" - --artifact "${OLD_ARTIFACT_PATTERN}" - ) - if ${{ env.TEST_PYTHON == 'true' }}; then - LOOKUP_ARGS+=(--artifact cuda-python-wheel) - fi - LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id \ - "${LOOKUP_ARGS[@]}" NVIDIA/cuda-python "CI") - echo "run-id=${LATEST_PRIOR_RUN_ID}" >> "${GITHUB_OUTPUT}" - echo "artifact-pattern=${OLD_ARTIFACT_PATTERN}" >> "${GITHUB_OUTPUT}" - - - name: Download prior-branch cuda.bindings build artifacts - if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE == 'backport' }} - uses: $/.github/actions/download-artifact-retry - with: - pattern: ${{ steps.prior.outputs.artifact-pattern }} - merge-multiple: "true" - path: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }} - run-id: ${{ steps.prior.outputs.run-id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Download prior-branch cuda-python wheel - if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'backport' }} - uses: $/.github/actions/download-artifact-retry - with: - name: cuda-python-wheel - path: . - run-id: ${{ steps.prior.outputs.run-id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Verify prior-branch artifact layout - if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE == 'backport' }} - shell: bash --noprofile --norc -xeuo pipefail {0} - run: | - bindings_wheels=("${CUDA_BINDINGS_ARTIFACTS_DIR}"/cuda_bindings-*.whl) - if [[ "${#bindings_wheels[@]}" -ne 1 ]] || [[ ! -f "${bindings_wheels[0]}" ]]; then - echo "::error::Expected exactly one cuda_bindings wheel in ${CUDA_BINDINGS_ARTIFACTS_DIR}; found: ${bindings_wheels[*]}" - exit 1 - fi - ls -al "${CUDA_BINDINGS_ARTIFACTS_DIR}" - - name: Display structure of downloaded cuda-python artifacts - if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE != 'floor' }} + if: ${{ env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'local' }} run: | Get-Location Get-ChildItem cuda_python*.whl | Select-Object Mode, LastWriteTime, Length, FullName - name: Display structure of downloaded cuda.bindings artifacts if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && - env.BINDINGS_SOURCE != 'floor' }} + env.BINDINGS_SOURCE == 'local' }} run: | Get-Location Get-ChildItem -Recurse -Force $env:CUDA_BINDINGS_ARTIFACTS_DIR | Select-Object Mode, LastWriteTime, Length, FullName @@ -303,7 +334,7 @@ jobs: if: ${{ env.TEST_CORE == 'true' && env.SKIP_CYTHON_TEST == '0' }} uses: $/.github/actions/download-artifact-retry with: - name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-tests + name: ${{ env.CUDA_CORE_CYTHON_TEST_ARTIFACT_NAME }} path: ${{ env.CUDA_CORE_CYTHON_TESTS_DIR }} run-id: ${{ inputs.run-id || github.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} @@ -384,25 +415,28 @@ jobs: run: run-tests core - name: Ensure cuda-python installable - if: ${{ inputs.test-mode == 'standard' && env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'main' }} + if: ${{ inputs.test-mode == 'standard' && env.TEST_PYTHON == 'true' && env.BINDINGS_SOURCE == 'local' }} + env: + LOCAL_CTK: ${{ matrix.LOCAL_CTK }} + shell: bash --noprofile --norc -xeuo pipefail {0} run: | - # Package suites install their own dependencies. A metapackage-only - # run has no preceding suite, so install the exact local internal - # wheels in one transaction while resolving released dependencies - # such as cuda-core from the package index. - if ('${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }}' -eq 'true') { - $dependencyArgs = @('--no-deps') - } else { - $dependencyArgs = @( - (Get-Item ./cuda_pathfinder/cuda_pathfinder-*.whl).FullName - (Get-Item "$env:CUDA_BINDINGS_ARTIFACTS_DIR/cuda_bindings-*.whl").FullName - ) - } - $pythonRequirements = @((Get-Item ./cuda_python*.whl).FullName) - if ('${{ matrix.LOCAL_CTK }}' -ne '1') { - $pythonRequirements = @($pythonRequirements | ForEach-Object { "$($_)[all]" }) - } - pip install --only-binary=:all: @dependencyArgs @pythonRequirements + # CUDA 13's metapackage also requires Core. A bindings-only release + # must resolve published Core while preserving the selected wheels. + if [[ "$TEST_CORE" == true || ("$TEST_BINDINGS" == true && "$TEST_CUDA_MAJOR" == 12) ]]; then + dependency_args=(--no-deps) + else + dependency_args=("${CUDA_BINDINGS_ARTIFACTS_DIR}"/cuda_bindings-*.whl) + if [[ "$PATHFINDER_SOURCE" == artifact ]]; then + dependency_args+=(./cuda_pathfinder/cuda_pathfinder-*.whl) + else + dependency_args+=("$(python -c 'from importlib.metadata import version; print("cuda-pathfinder==" + version("cuda-pathfinder"))')") + fi + fi + python_requirements=(cuda_python*.whl) + if [[ "$LOCAL_CTK" != 1 ]]; then + python_requirements=("${python_requirements[@]/%/[all]}") + fi + pip install --only-binary=:all: "${dependency_args[@]}" "${python_requirements[@]}" - name: Install cuda.pathfinder extra wheels for testing if: ${{ inputs.test-mode == 'standard' && env.TEST_PATHFINDER == 'true' }} diff --git a/ci/tools/run-tests b/ci/tools/run-tests index 68fe5768deb..88c4fc40792 100755 --- a/ci/tools/run-tests +++ b/ci/tools/run-tests @@ -38,9 +38,11 @@ fi test_module=${1} TEST_FT_GROUP=() +TEST_FT_REQUIREMENT=() PYTEST_PARALLEL_ARGS=() if python -c 'import sys; assert not sys._is_gil_enabled()' 2> /dev/null; then TEST_FT_GROUP=(--group test-ft) + TEST_FT_REQUIREMENT=(pytest-run-parallel==0.10.0) PYTEST_PARALLEL_ARGS=(--parallel-threads=4) fi @@ -49,8 +51,21 @@ fi # all wheels together in a single pip call further below. if [[ "${test_module}" != nightly-* ]]; then pushd ./cuda_pathfinder - echo "Installing pathfinder wheel" - pip install ./*.whl --group test "${TEST_FT_GROUP[@]}" + if [[ "${PATHFINDER_SOURCE:-}" == "artifact" ]]; then + echo "Installing pathfinder wheel from CI artifacts" + pathfinder_wheels=(./*.whl) + if [[ "${#pathfinder_wheels[@]}" -ne 1 || ! -f "${pathfinder_wheels[0]}" ]]; then + echo "Expected exactly one cuda-pathfinder wheel in $(pwd)" >&2 + exit 1 + fi + pip install "${pathfinder_wheels[0]}" --group test "${TEST_FT_GROUP[@]}" + elif [[ "${PATHFINDER_SOURCE:-}" == "published" && "${test_module}" == "bindings" ]]; then + echo "Installing published pathfinder for a focused bindings release" + pip install cuda-pathfinder --group test "${TEST_FT_GROUP[@]}" + else + echo "Error: PATHFINDER_SOURCE must be artifact, or published for bindings release tests; got ${PATHFINDER_SOURCE:-}" >&2 + exit 1 + fi popd fi @@ -68,15 +83,33 @@ if [[ "${test_module}" == "pathfinder" ]]; then popd elif [[ "${test_module}" == "bindings" ]]; then echo "Installing bindings wheel" - pushd ./cuda_bindings_13 - if [[ "${LOCAL_CTK}" == 1 ]]; then - pip install "${CUDA_BINDINGS_ARTIFACTS_DIR}"/*.whl --group test "${TEST_FT_GROUP[@]}" - else + pushd "./${CUDA_BINDINGS_ROOT}" + bindings_wheels=("${CUDA_BINDINGS_ARTIFACTS_DIR}"/*.whl) + if [[ "${#bindings_wheels[@]}" -ne 1 || ! -f "${bindings_wheels[0]}" ]]; then + echo "Expected exactly one cuda-bindings wheel in ${CUDA_BINDINGS_ARTIFACTS_DIR}" >&2 + exit 1 + fi + bindings_requirement="${bindings_wheels[0]}" + bindings_toolkit_requirement=() + if [[ "${LOCAL_CTK}" != 1 ]]; then TEST_CUDA_MAJOR_MINOR="$(cut -d '.' -f 1-2 <<< "${CUDA_VER}")" # Keep the wheel-installed Toolkit aligned with the matrix-selected minor; # newer minors can grow ABI structs beyond the size compiled into this wheel. - pip install $(ls "${CUDA_BINDINGS_ARTIFACTS_DIR}"/*.whl)[all] --group test "${TEST_FT_GROUP[@]}" \ - "cuda-toolkit==${TEST_CUDA_MAJOR_MINOR}.*" + bindings_toolkit_requirement=("cuda-toolkit==${TEST_CUDA_MAJOR_MINOR}.*") + fi + if grep -q '^\[dependency-groups\][[:space:]]*$' pyproject.toml; then + if [[ "${LOCAL_CTK}" != 1 ]]; then + bindings_requirement+="[all]" + fi + python -m pip install "${bindings_requirement}" --group pyproject.toml:test \ + "${TEST_FT_GROUP[@]}" "${bindings_toolkit_requirement[@]}" + else + bindings_extras="test" + if [[ "${LOCAL_CTK}" != 1 ]]; then + bindings_extras="all,test" + fi + python -m pip install "${bindings_requirement}[${bindings_extras}]" \ + "${TEST_FT_REQUIREMENT[@]}" "${bindings_toolkit_requirement[@]}" fi echo "Running bindings tests" ${SANITIZER_CMD} pytest -rsxXfE -v --randomly-dont-reorganize --exclude-warning-annotations "${PYTEST_PARALLEL_ARGS[@]}" --junitxml "${TEST_RESULTS_DIR}/junit-bindings.xml" tests/ | fold_pytest_log @@ -86,13 +119,13 @@ elif [[ "${test_module}" == "bindings" ]]; then popd elif [[ "${test_module}" == "core" || "${test_module}" == nightly-* ]]; then # Shared setup for core and nightly modes. - TEST_CUDA_MAJOR="$(cut -d '.' -f 1 <<< ${CUDA_VER})" + TEST_CUDA_MAJOR="$(cut -d '.' -f 1 <<< "${CUDA_VER}")" TEST_CUDA_MAJOR_MINOR="$(cut -d '.' -f 1-2 <<< "${CUDA_VER}")" # Resolve bindings based on BINDINGS_SOURCE (set by env-vars): - # main/backport → local wheel from artifacts dir - # floor → the oldest cuda-bindings that the cuda-core wheel under test - # supports, read from that wheel and installed from PyPI + # local → matching configured-package wheel from this run + # floor -> the oldest supported bindings, read from the Core wheel + # and installed from PyPI BINDINGS_ARGS=() if [[ "${BINDINGS_SOURCE}" == "floor" ]]; then CORE_WHL_FOR_FLOOR=("${CUDA_CORE_ARTIFACTS_DIR}"/*.whl) @@ -131,10 +164,19 @@ elif [[ "${test_module}" == "core" || "${test_module}" == nightly-* ]]; then fi if [[ "${test_module}" == nightly-* ]]; then + if [[ "${PATHFINDER_SOURCE:-}" != "artifact" ]]; then + echo "Error: nightly tests require PATHFINDER_SOURCE=artifact" >&2 + exit 1 + fi # Resolve pathfinder wheel to absolute path before pushd. # CUDA_BINDINGS_ARTIFACTS_DIR and CUDA_CORE_ARTIFACTS_DIR are already # absolute (set via realpath in env-vars). - PATHFINDER_WHL=($(realpath ./cuda_pathfinder/*.whl)) + pathfinder_wheels=(./cuda_pathfinder/*.whl) + if [[ "${#pathfinder_wheels[@]}" -ne 1 || ! -f "${pathfinder_wheels[0]}" ]]; then + echo "Expected exactly one cuda-pathfinder wheel in ./cuda_pathfinder" >&2 + exit 1 + fi + PATHFINDER_WHL=("$(realpath "${pathfinder_wheels[0]}")") fi if [[ "${test_module}" == "core" ]]; then @@ -167,8 +209,8 @@ elif [[ "${test_module}" == "core" || "${test_module}" == nightly-* ]]; then pip list echo "Running core tests" ${SANITIZER_CMD} pytest -rsxXfE -v --randomly-dont-reorganize --exclude-warning-annotations "${PYTEST_PARALLEL_ARGS[@]}" --junitxml "${TEST_RESULTS_DIR}/junit-core.xml" tests/ | fold_pytest_log - # Currently our CI always installs the latest bindings (from either major version). - # This is not compatible with the test requirements. + # Compiled Cython tests require the bindings used to build them. Older + # toolkit rows intentionally exercise published floor bindings instead. if [[ "${SKIP_CYTHON_TEST}" == 0 ]]; then ${SANITIZER_CMD} pytest -rsxXfE -v --randomly-dont-reorganize --exclude-warning-annotations "${PYTEST_PARALLEL_ARGS[@]}" --junitxml "${TEST_RESULTS_DIR}/junit-core-cython.xml" tests/cython | fold_pytest_log fi diff --git a/ci/tools/tests/test_test_sources.py b/ci/tools/tests/test_test_sources.py new file mode 100644 index 00000000000..e99c0ad4c16 --- /dev/null +++ b/ci/tools/tests/test_test_sources.py @@ -0,0 +1,340 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import os +import subprocess +import sys +import zipfile +from pathlib import Path + +import pytest + +from ci.tools.bindings_config import load_config + +REPO_ROOT = Path(__file__).resolve().parents[3] +RUN_TESTS = REPO_ROOT / "ci" / "tools" / "run-tests" +ENV_VARS = REPO_ROOT / "ci" / "tools" / "env-vars" + + +def _write_command(directory: Path, name: str, body: str) -> None: + path = directory / name + path.write_text(f"#!/usr/bin/env bash\nset -eu\n{body}\n", encoding="utf-8") + path.chmod(0o755) + + +def _run_tests_env(tmp_path: Path) -> tuple[dict[str, str], Path]: + fake_bin = tmp_path / "bin" + fake_bin.mkdir() + command_log = tmp_path / "commands.log" + _write_command( + fake_bin, + "python", + """ +if [[ "${1:-}" == "-c" ]]; then + exit 1 +fi +if [[ "${1:-}" == "ci/tools/cuda_core_bindings_floor.py" ]]; then + exec "$REAL_PYTHON" "$@" +fi +printf 'python %s\\n' "$*" >> "$COMMAND_LOG" +""".strip(), + ) + _write_command(fake_bin, "pip", 'printf \'pip %s\\n\' "$*" >> "$COMMAND_LOG"') + _write_command(fake_bin, "pytest", ":") + env = { + **os.environ, + "COMMAND_LOG": str(command_log), + "REAL_PYTHON": sys.executable, + "PATH": f"{fake_bin}{os.pathsep}{os.environ['PATH']}", + "CUDA_PATHFINDER_TEST_LOAD_NVIDIA_DYNAMIC_LIB_STRICTNESS": "see_what_works", + "CUDA_PATHFINDER_TEST_FIND_NVIDIA_HEADERS_STRICTNESS": "see_what_works", + "CUDA_PATHFINDER_TEST_FIND_NVIDIA_BITCODE_LIB_STRICTNESS": "see_what_works", + } + (tmp_path / "cuda_pathfinder").mkdir() + return env, command_log + + +def _run_env_vars( + tmp_path: Path, + *, + bindings_source: str, + pathfinder_source: str, + cuda_version: str = "13.3.0", + bindings_root: str = "cuda_bindings", +) -> subprocess.CompletedProcess[str]: + for relative in ( + f"{bindings_root}/dist", + f"{bindings_root}/tests/cython", + "cuda_core/dist", + "cuda_core/tests/cython", + "cuda_core/tests/test_binaries", + ): + (tmp_path / relative).mkdir(parents=True, exist_ok=True) + github_env = tmp_path / "github-env" + github_path = tmp_path / "github-path" + env = { + **os.environ, + "BINDINGS_SOURCE": bindings_source, + "CUDA_VER": cuda_version, + "GITHUB_ENV": str(github_env), + "GITHUB_PATH": str(github_path), + "HOST_PLATFORM": "linux-64", + "LOCAL_CTK": "0", + "PATHFINDER_SOURCE": pathfinder_source, + "PY_VER": "3.13", + "SHA": "abcdef", + "SKIP_BINDINGS_TEST_OVERRIDE": "0", + } + if bindings_source == "local": + env["BINDINGS_SOURCE_DIR"] = bindings_root + else: + env["DEFAULT_BINDINGS_SOURCE_DIR"] = bindings_root + return subprocess.run( # noqa: S603 - invokes the repository script under test + [str(ENV_VARS), "test"], + cwd=tmp_path, + env=env, + check=False, + capture_output=True, + text=True, + ) + + +def _read_github_env(path: Path) -> dict[str, str]: + return dict(line.split("=", 1) for line in path.read_text(encoding="utf-8").splitlines()) + + +@pytest.mark.agent_authored(model="gpt-5.6-sol") +def test_env_vars_rejects_unknown_mode() -> None: + result = subprocess.run( # noqa: S603 - invokes the repository script under test + [str(ENV_VARS), "unknown"], + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode == 1 + assert "build mode must be build or test" in result.stderr + + +@pytest.mark.agent_authored(model="gpt-5.6") +def test_build_env_uses_registry_packages_without_generic_bindings_aliases(tmp_path: Path) -> None: + github_env = tmp_path / "github-env" + env = { + **os.environ, + "GITHUB_ENV": str(github_env), + "GITHUB_PATH": str(tmp_path / "github-path"), + "HOST_PLATFORM": "linux-64", + "PY_VER": "3.13", + "SHA": "abcdef0", + } + + result = subprocess.run( # noqa: S603 - invokes the repository script under test + [str(ENV_VARS), "build"], + cwd=REPO_ROOT, + env=env, + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode == 0, result.stderr + values = _read_github_env(github_env) + config = load_config() + for status in ("current", "maintenance"): + package = config.package_for_release_status(status) + prefix = status.upper() + assert values[f"{prefix}_BINDINGS_ROOT"] == package.package_root + assert values[f"{prefix}_CUDA_VERSION"] == package.toolkit_version + assert values[f"{prefix}_CUDA_MAJOR"] == package.cuda_major + assert values[f"{prefix}_CUDA_VARIANT"] == package.cuda_variant + assert values[f"{prefix}_BINDINGS_ARTIFACT_NAME"].endswith("-abcdef0") + + assert ( + not { + "CUDA_BINDINGS_ARTIFACT_BASENAME", + "CUDA_BINDINGS_ARTIFACT_NAME", + "CUDA_BINDINGS_ARTIFACTS_DIR", + "CUDA_BINDINGS_CYTHON_TESTS_DIR", + } + & values.keys() + ) + + +@pytest.mark.parametrize("wheel_count", [0, 2]) +@pytest.mark.agent_authored(model="gpt-5.6") +def test_artifact_pathfinder_requires_exactly_one_wheel(tmp_path: Path, wheel_count: int) -> None: + env, _ = _run_tests_env(tmp_path) + env["PATHFINDER_SOURCE"] = "artifact" + for index in range(wheel_count): + (tmp_path / "cuda_pathfinder" / f"cuda_pathfinder-{index}.whl").touch() + + result = subprocess.run( # noqa: S603 - invokes the repository script under test + [str(RUN_TESTS), "pathfinder"], + cwd=tmp_path, + env=env, + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode != 0 + assert "Expected exactly one cuda-pathfinder wheel" in result.stderr + + +@pytest.mark.agent_authored(model="gpt-5.6") +def test_artifact_pathfinder_installs_the_only_wheel(tmp_path: Path) -> None: + env, command_log = _run_tests_env(tmp_path) + env["PATHFINDER_SOURCE"] = "artifact" + wheel = tmp_path / "cuda_pathfinder" / "cuda_pathfinder-1.0-py3-none-any.whl" + wheel.touch() + + subprocess.run( # noqa: S603 - invokes the repository script under test + [str(RUN_TESTS), "pathfinder"], + cwd=tmp_path, + env=env, + check=True, + capture_output=True, + text=True, + ) + + assert f"pip install ./{wheel.name} --group test" in command_log.read_text(encoding="utf-8") + + +@pytest.mark.agent_authored(model="gpt-5.6") +def test_published_pathfinder_is_limited_to_bindings_release_tests(tmp_path: Path) -> None: + env, _ = _run_tests_env(tmp_path) + env["PATHFINDER_SOURCE"] = "published" + + result = subprocess.run( # noqa: S603 - invokes the repository script under test + [str(RUN_TESTS), "pathfinder"], + cwd=tmp_path, + env=env, + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode != 0 + assert "published for bindings release tests" in result.stderr + + +@pytest.mark.agent_authored(model="gpt-5.6") +def test_published_pathfinder_supports_bindings_release_tests(tmp_path: Path) -> None: + env, command_log = _run_tests_env(tmp_path) + env.update( + CUDA_BINDINGS_ARTIFACTS_DIR=str(tmp_path / "bindings-artifacts"), + CUDA_BINDINGS_ROOT="cuda_bindings", + LOCAL_CTK="1", + PATHFINDER_SOURCE="published", + SANITIZER_CMD="", + SKIP_CYTHON_TEST="1", + ) + (tmp_path / "cuda_bindings").mkdir() + (tmp_path / "cuda_bindings" / "pyproject.toml").write_text("[dependency-groups]\ntest = []\n", encoding="utf-8") + (tmp_path / "bindings-artifacts").mkdir() + (tmp_path / "bindings-artifacts" / "cuda_bindings-13.3.whl").touch() + + subprocess.run( # noqa: S603 - invokes the repository script under test + [str(RUN_TESTS), "bindings"], + cwd=tmp_path, + env=env, + check=True, + capture_output=True, + text=True, + ) + + calls = command_log.read_text(encoding="utf-8").splitlines() + assert calls[0].startswith("pip install cuda-pathfinder --group test") + assert calls[1].startswith("python -m pip install ") + + +@pytest.mark.parametrize( + ("bindings_source", "cuda_version", "bindings_root", "expected_artifact"), + [ + ("local", "13.4.2", "cuda_bindings", "cuda-python-wheel-cuda13.4.2"), + ("local", "12.9.1", "cuda_bindings_12", "cuda-python-wheel-cuda12.9.1"), + ("floor", "13.0.2", "cuda_bindings", None), + ("floor", "12.6.3", "cuda_bindings", None), + ], +) +@pytest.mark.agent_authored(model="gpt-6-astra") +def test_cuda_python_artifact_name_exists_only_for_local_bindings( + tmp_path: Path, + bindings_source: str, + cuda_version: str, + bindings_root: str, + expected_artifact: str | None, +) -> None: + result = _run_env_vars( + tmp_path, + bindings_source=bindings_source, + pathfinder_source="artifact", + cuda_version=cuda_version, + bindings_root=bindings_root, + ) + + assert result.returncode == 0, result.stderr + github_env = _read_github_env(tmp_path / "github-env") + assert { + "CUDA_BINDINGS_ARTIFACT_BASENAME", + "CUDA_BINDINGS_ARTIFACT_NAME", + "CUDA_BINDINGS_ARTIFACTS_DIR", + "CUDA_BINDINGS_CYTHON_TESTS_DIR", + "CUDA_CORE_CYTHON_TEST_ARTIFACT_NAME", + } <= github_env.keys() + assert github_env["BINDINGS_SOURCE"] == bindings_source + assert github_env["CUDA_BINDINGS_ROOT"] == bindings_root + assert github_env["SKIP_CUDA_BINDINGS_TEST"] == ("1" if bindings_source == "floor" else "0") + assert github_env["SKIP_CYTHON_TEST"] == ("1" if bindings_source == "floor" else "0") + assert github_env["CUDA_CORE_CYTHON_TEST_ARTIFACT_NAME"].endswith(f"-cu{cuda_version.split('.')[0]}-tests") + if expected_artifact is None: + assert "CUDA_PYTHON_ARTIFACT_NAME" not in github_env + else: + assert github_env["CUDA_PYTHON_ARTIFACT_NAME"] == expected_artifact + + +@pytest.mark.agent_authored(model="gpt-6-astra") +@pytest.mark.parametrize(("cuda_version", "wheel_floor"), [("12.6.3", "12.9.9"), ("13.0.2", "13.4.2")]) +def test_core_install_uses_wheel_floor_with_older_toolkit(tmp_path: Path, cuda_version: str, wheel_floor: str) -> None: + env, command_log = _run_tests_env(tmp_path) + (tmp_path / "cuda_pathfinder" / "cuda_pathfinder-1.0-py3-none-any.whl").touch() + core_dist = tmp_path / "cuda_core" / "dist" + core_dist.mkdir(parents=True) + wheel = core_dist / "cuda_core-1.3.0-cp313-cp313-linux_x86_64.whl" + major = int(cuda_version.split(".")[0]) + with zipfile.ZipFile(wheel, "w") as archive: + archive.writestr( + "cuda_core-1.3.0.dist-info/METADATA", + f'Requires-Dist: cuda-bindings[all]>={wheel_floor},<{major + 1}; extra == "cu{major}"\n', + ) + tools = tmp_path / "ci" / "tools" + tools.mkdir(parents=True) + floor_script = REPO_ROOT / "ci" / "tools" / "cuda_core_bindings_floor.py" + (tools / floor_script.name).write_text(floor_script.read_text(encoding="utf-8"), encoding="utf-8") + env.update( + BINDINGS_SOURCE="floor", + CUDA_CORE_ARTIFACTS_DIR=str(core_dist), + CUDA_VER=cuda_version, + LOCAL_CTK="0", + PATHFINDER_SOURCE="artifact", + SANITIZER_CMD="", + SKIP_CYTHON_TEST="1", + ) + + result = subprocess.run( # noqa: S603 - repository script with install/test commands replaced by recording stubs + [str(RUN_TESTS), "core"], + cwd=tmp_path, + env=env, + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode == 0, result.stderr + calls = command_log.read_text(encoding="utf-8").splitlines() + assert f"pip install cuda-bindings=={wheel_floor}" in calls + toolkit_minor = ".".join(cuda_version.split(".")[:2]) + assert any(f"cuda-toolkit=={toolkit_minor}.*" in call for call in calls) diff --git a/cuda_bindings_13/docs/build_docs.sh b/cuda_bindings_13/docs/build_docs.sh index 199ababce60..53d0e14d0ea 100755 --- a/cuda_bindings_13/docs/build_docs.sh +++ b/cuda_bindings_13/docs/build_docs.sh @@ -16,13 +16,12 @@ fi # SPHINX_CUDA_BINDINGS_VER is used to create a subdir under build/html # (the Makefile file for sphinx-build also honors it if defined). -# If there's a post release (ex: .post1) we don't want it to show up in the -# version selector or directory structure. +# Post releases reuse their base release directory, while prerelease and +# development releases remain distinct so they cannot overwrite stable docs. if [[ -z "${SPHINX_CUDA_BINDINGS_VER}" ]]; then export SPHINX_CUDA_BINDINGS_VER=$(python -c "from importlib.metadata import version; \ - ver = '.'.join(str(version('cuda-bindings')).split('.')[:3]); \ - print(ver)" \ - | awk -F'+' '{print $1}') + ver = str(version('cuda-bindings')).split('+', 1)[0]; \ + print(ver if '.dev' in ver else ver.split('.post', 1)[0])") fi if [[ "${LATEST_ONLY}" == "1" && -z "${BUILD_PREVIEW:-}" && -z "${BUILD_LATEST:-}" ]]; then diff --git a/cuda_bindings_13/docs/source/install.rst b/cuda_bindings_13/docs/source/install.rst index 4958b096238..568af77837b 100644 --- a/cuda_bindings_13/docs/source/install.rst +++ b/cuda_bindings_13/docs/source/install.rst @@ -108,9 +108,9 @@ obtain the most recent build, use the following commands: Replace ``python312`` with your Python version (e.g. ``python311``, ``python313``, ``python314``, ``python314t``, ``python315``, ``python315t``). For aarch64, replace ``linux-64`` -with ``linux-aarch64``; for Windows, use ``win-64``. Only the current CUDA -major version is built on ``main``; wheels for the prior CUDA major are -published from the corresponding backport branch. +with ``linux-aarch64``; for Windows, use ``win-64``. To use the CUDA 12 line, +replace ``cuda13`` with ``cuda12`` in both commands. Both supported CUDA major +lines are published by CI from ``main``. Installing from Source ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/cuda_python/docs/environment-docs.yml b/cuda_python/docs/environment-docs.yml index 3152f0a3a93..a82fd02aa5e 100644 --- a/cuda_python/docs/environment-docs.yml +++ b/cuda_python/docs/environment-docs.yml @@ -19,6 +19,8 @@ dependencies: - sphinx-copybutton - myst-nb - enum_tools + # cuda_bindings_12/docs/source/conf.py uses the Furo theme. + - furo - sphinx-toolbox - pyclibrary - pip: diff --git a/toolshed/setup-docs-env.sh b/toolshed/setup-docs-env.sh index 9acbaa8e391..73afbbf06f9 100755 --- a/toolshed/setup-docs-env.sh +++ b/toolshed/setup-docs-env.sh @@ -51,6 +51,7 @@ conda create -y -n "${ENV_NAME}" \ sphinx-copybutton \ myst-nb \ enum_tools \ + furo \ sphinx-toolbox \ pyclibrary From d498ae23549988934f090a8024e8fa1c75d246e4 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 20 Oct 2026 22:14:26 -0700 Subject: [PATCH 02/24] CI: label metapackage GPU suites as meta Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/test-wheel-linux.yml | 2 +- .github/workflows/test-wheel-windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 8ca234af1ad..87fcd906ffc 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -138,7 +138,7 @@ jobs: [if $workplan == null or $workplan.modules.pathfinder.needs_test then "pathfinder" else empty end, if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.bindings.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "bindings" + ($row.CUDA_VER | split(".")[0]) else empty end, if $workplan == null or $workplan.modules.core.cuda_majors[$row.CUDA_VARIANT].needs_test then "core" + ($row.CUDA_VER | split(".")[0]) else empty end, - if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "python" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) + if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "meta" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) }) | map(select(.SUITES != "")) | if any(.[]; .DRIVER != "latest" and .DRIVER != "earliest" and .FLAVOR == "wsl") then "Error: custom DRIVER is not supported with FLAVOR=wsl\n" | halt_error(1) else . end diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 4286aa002a3..b5a78ea9db6 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -133,7 +133,7 @@ jobs: [if $workplan == null or $workplan.modules.pathfinder.needs_test then "pathfinder" else empty end, if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.bindings.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "bindings" + ($row.CUDA_VER | split(".")[0]) else empty end, if $workplan == null or $workplan.modules.core.cuda_majors[$row.CUDA_VARIANT].needs_test then "core" + ($row.CUDA_VER | split(".")[0]) else empty end, - if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "python" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) + if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "meta" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) }) | map(select(.SUITES != "")) | map(. + {RUNNER_DRIVER: (if .DRIVER == "latest" or .DRIVER == "earliest" then .DRIVER else "latest" end)}) From 714ad621084b53945c2471e313a075b7bf5efbdf Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 21 Oct 2026 10:23:29 -0700 Subject: [PATCH 03/24] Adapt CUDA 12 NVML tests for current CI runners Relocate the established hardware, driver, visibility and timing accommodations from the source-integration layer. Preserve their contents exactly; the FieldId correction moves with the test maintenance. Signed-off-by: Ralf W. Grosse-Kunstleve --- cuda_bindings_12/tests/nvml/test_cuda.py | 26 ++++++++++++++++++---- cuda_bindings_12/tests/nvml/test_init.py | 14 +++++++++++- cuda_bindings_12/tests/nvml/test_pynvml.py | 18 --------------- cuda_bindings_12/tests/nvml/util.py | 2 +- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/cuda_bindings_12/tests/nvml/test_cuda.py b/cuda_bindings_12/tests/nvml/test_cuda.py index d05e2634a12..aaab23e55fe 100644 --- a/cuda_bindings_12/tests/nvml/test_cuda.py +++ b/cuda_bindings_12/tests/nvml/test_cuda.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +import pytest + import cuda.bindings.driver as cuda from cuda.bindings import nvml @@ -15,10 +17,15 @@ def get_nvml_device_names(): for idx in range(num_devices): handle = nvml.device_get_handle_by_index_v2(idx) name = nvml.device_get_name(handle) - info = nvml.device_get_pci_info_v3(handle) - assert isinstance(info.bus, int) + try: + info = nvml.device_get_pci_info_v3(handle) + except nvml.NotSupportedError: + bus_id = -1 + else: + bus_id = info.bus + assert isinstance(bus_id, int) assert isinstance(name, str) - result.append({"name": name, "id": info.bus}) + result.append({"name": name, "id": bus_id}) return result @@ -54,4 +61,15 @@ def test_cuda_device_order(): cuda_devices = get_cuda_device_names() nvml_devices = get_nvml_device_names() - assert cuda_devices == nvml_devices, "CUDA and NVML device lists do not match" + for kind in ("Orin", "Thor"): + if any(kind in device["name"] for device in nvml_devices): + pytest.skip(f"Skipping test on {kind}, which has non-standard device naming") + + def compare(cuda_device, nvml_device): + return cuda_device["name"] == nvml_device["name"] and ( + nvml_device["id"] == -1 or cuda_device["id"] == nvml_device["id"] + ) + + assert len(cuda_devices) <= len(nvml_devices) + for cuda_device in cuda_devices: + assert any(compare(cuda_device, nvml_device) for nvml_device in nvml_devices) diff --git a/cuda_bindings_12/tests/nvml/test_init.py b/cuda_bindings_12/tests/nvml/test_init.py index 2be94434591..fcde20853d7 100644 --- a/cuda_bindings_12/tests/nvml/test_init.py +++ b/cuda_bindings_12/tests/nvml/test_init.py @@ -6,7 +6,15 @@ import pytest -from cuda.bindings import nvml +from cuda.bindings import driver, nvml + + +def _driver_version_at_least_13_4(): + (status,) = driver.cuInit(0) + assert status == driver.CUresult.CUDA_SUCCESS + status, version = driver.cuDriverGetVersion() + assert status == driver.CUresult.CUDA_SUCCESS + return version >= 13040 def assert_nvml_is_initialized(): @@ -36,10 +44,14 @@ def test_devices_are_the_same_architecture(all_devices): @pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") +@pytest.mark.thread_unsafe(reason="nvml init affects other threads") def test_init_ref_count(): """ Verifies that we can call NVML shutdown and init(2) multiple times, and that ref counting works """ + if _driver_version_at_least_13_4(): + pytest.skip("Init behavior changed in CUDA 13.4") + with pytest.raises(nvml.UninitializedError): nvml.shutdown() diff --git a/cuda_bindings_12/tests/nvml/test_pynvml.py b/cuda_bindings_12/tests/nvml/test_pynvml.py index a96b2c87575..e3ea533dd83 100644 --- a/cuda_bindings_12/tests/nvml/test_pynvml.py +++ b/cuda_bindings_12/tests/nvml/test_pynvml.py @@ -4,8 +4,6 @@ # A set of tests ported from https://github.com/gpuopenanalytics/pynvml/blob/11.5.3/pynvml/tests/test_nvml.py import os -import time - import pytest from cuda.bindings import nvml @@ -147,22 +145,6 @@ def test_device_get_power_usage(ngpus, handles): assert power_mwatts >= 0.0 -def test_device_get_total_energy_consumption(ngpus, handles): - for i in range(ngpus): - with unsupported_before(handles[i], nvml.DeviceArch.VOLTA): - energy_mjoules1 = nvml.device_get_total_energy_consumption(handles[i]) - - for j in range(10): # idle for 150 ms - time.sleep(0.015) # and check for increase every 15 ms - with unsupported_before(handles[i], nvml.DeviceArch.VOLTA): - energy_mjoules2 = nvml.device_get_total_energy_consumption(handles[i]) - assert energy_mjoules2 >= energy_mjoules1 - if energy_mjoules2 > energy_mjoules1: - break - else: - raise AssertionError("energy did not increase across 150 ms interval") - - # [Skipping] pynvml.nvmlDeviceGetGpuOperationMode # [Skipping] pynvml.nvmlDeviceGetCurrentGpuOperationMode # [Skipping] pynvml.nvmlDeviceGetPendingGpuOperationMode diff --git a/cuda_bindings_12/tests/nvml/util.py b/cuda_bindings_12/tests/nvml/util.py index 038fe58d8be..06cb1c2277a 100644 --- a/cuda_bindings_12/tests/nvml/util.py +++ b/cuda_bindings_12/tests/nvml/util.py @@ -43,5 +43,5 @@ def supports_ecc(device): def supports_nvlink(device): fields = nvml.FieldValue(1) - fields[0].field_id = nvml.FI.DEV_NVLINK_GET_STATE + fields[0].field_id = nvml.FieldId.DEV_NVLINK_GET_STATE return nvml.device_get_field_values(device, fields)[0].nvml_return == nvml.Return.SUCCESS From 5ffc0b01ed18ce44edb81cf1ba5f1178fe475ee3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 21 Oct 2026 21:14:54 -0700 Subject: [PATCH 04/24] Bound cuDSS test dependencies to the supported loader ABI Signed-off-by: Ralf W. Grosse-Kunstleve --- cuda_pathfinder/pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cuda_pathfinder/pyproject.toml b/cuda_pathfinder/pyproject.toml index 904463b7022..7cc17150020 100644 --- a/cuda_pathfinder/pyproject.toml +++ b/cuda_pathfinder/pyproject.toml @@ -28,6 +28,7 @@ test-ft = [ "pytest-run-parallel==0.10.0", ] # Internal organization of test dependencies. +# The cuDSS bounds match the 0.x library ABI currently supported by the loader. cu12 = [ "cuda-toolkit[nvcc,cublas,nvrtc,cudart,cufft,curand,cusolver,cusparse,npp,nvfatbin,nvjitlink,nvjpeg,cccl,cupti,profiler]==12.*", "cuda-toolkit[cufile]==12.*; sys_platform != 'win32'", @@ -35,7 +36,7 @@ cu12 = [ "cutensor-cu12", "nvidia-cublasmp-cu12; sys_platform != 'win32'", "nvidia-cudnn-cu12>=9,<10", - "nvidia-cudss-cu12", + "nvidia-cudss-cu12<1", "nvidia-cufftmp-cu12; sys_platform != 'win32'", "nvidia-cusolvermp-cu12; sys_platform != 'win32'", "nvidia-cusparselt-cu12", @@ -51,7 +52,7 @@ cu13 = [ "nvidia-cublasmp-cu13; sys_platform != 'win32'", "nvidia-cudla; platform_system == 'Linux' and platform_machine == 'aarch64'", "nvidia-cudnn-cu13>=9,<10", - "nvidia-cudss-cu13", + "nvidia-cudss-cu13<1", "nvidia-cufftmp-cu13; sys_platform != 'win32'", "nvidia-cusolvermp-cu13; sys_platform != 'win32'", "nvidia-cusparselt-cu13", From 0c6aa6e2bd4c1357e2e5d18d8fc3b89e28852bce Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 25 Oct 2026 16:53:01 -0700 Subject: [PATCH 05/24] Align multi-major CI with main's security and loader updates Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/build-docs.yml | 6 ++++++ .github/workflows/ci-nightly.yml | 3 +++ .github/workflows/ci-pixi-source-test.yml | 10 ++++++++++ .github/workflows/coverage.yml | 5 +++++ cuda_pathfinder/pyproject.toml | 5 ++--- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index c461ba2b219..eb23f599eed 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -78,6 +78,12 @@ jobs: ref: ${{ github.sha }} path: .ci-control + - name: Harden pip + # The release checkout can predate this action; use the workflow revision. + uses: $/.github/actions/harden-pip + with: + python: python3 + - name: Install CI tool dependencies run: python3 -m pip install -e ./.ci-control/ci diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index 86317af1b8e..f1594951817 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -81,6 +81,9 @@ jobs: with: fetch-depth: 1 + - name: Harden pip + uses: ./.github/actions/harden-pip + - name: Install CI tool dependencies run: python -m pip install -e ./ci diff --git a/.github/workflows/ci-pixi-source-test.yml b/.github/workflows/ci-pixi-source-test.yml index c9f7b213dc1..ef9a7349f2a 100644 --- a/.github/workflows/ci-pixi-source-test.yml +++ b/.github/workflows/ci-pixi-source-test.yml @@ -100,6 +100,11 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive + - name: Harden pip + uses: ./.github/actions/harden-pip + with: + python: python3 + - name: Install CI tool dependencies run: python3 -m pip install -e ./ci @@ -282,6 +287,11 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive + - name: Harden pip + uses: ./.github/actions/harden-pip + with: + python: python3 + - name: Install CI tool dependencies run: python3 -m pip install -e ./ci diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 87127900bcd..ccef659efec 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -25,6 +25,11 @@ jobs: steps: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Harden pip + uses: ./.github/actions/harden-pip + with: + python: python3 + - name: Install CI tool dependencies run: python3 -m pip install -e ./ci - name: Resolve current CUDA bindings package diff --git a/cuda_pathfinder/pyproject.toml b/cuda_pathfinder/pyproject.toml index 7cc17150020..904463b7022 100644 --- a/cuda_pathfinder/pyproject.toml +++ b/cuda_pathfinder/pyproject.toml @@ -28,7 +28,6 @@ test-ft = [ "pytest-run-parallel==0.10.0", ] # Internal organization of test dependencies. -# The cuDSS bounds match the 0.x library ABI currently supported by the loader. cu12 = [ "cuda-toolkit[nvcc,cublas,nvrtc,cudart,cufft,curand,cusolver,cusparse,npp,nvfatbin,nvjitlink,nvjpeg,cccl,cupti,profiler]==12.*", "cuda-toolkit[cufile]==12.*; sys_platform != 'win32'", @@ -36,7 +35,7 @@ cu12 = [ "cutensor-cu12", "nvidia-cublasmp-cu12; sys_platform != 'win32'", "nvidia-cudnn-cu12>=9,<10", - "nvidia-cudss-cu12<1", + "nvidia-cudss-cu12", "nvidia-cufftmp-cu12; sys_platform != 'win32'", "nvidia-cusolvermp-cu12; sys_platform != 'win32'", "nvidia-cusparselt-cu12", @@ -52,7 +51,7 @@ cu13 = [ "nvidia-cublasmp-cu13; sys_platform != 'win32'", "nvidia-cudla; platform_system == 'Linux' and platform_machine == 'aarch64'", "nvidia-cudnn-cu13>=9,<10", - "nvidia-cudss-cu13<1", + "nvidia-cudss-cu13", "nvidia-cufftmp-cu13; sys_platform != 'win32'", "nvidia-cusolvermp-cu13; sys_platform != 'win32'", "nvidia-cusparselt-cu13", From b7e3041bd86d4e760767ab029ef05be4bc4aec17 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 25 Oct 2026 16:55:06 -0700 Subject: [PATCH 06/24] Use the checked-out docs control plane for pip hardening Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index eb23f599eed..ea50308c964 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -80,7 +80,7 @@ jobs: - name: Harden pip # The release checkout can predate this action; use the workflow revision. - uses: $/.github/actions/harden-pip + uses: ./.ci-control/.github/actions/harden-pip with: python: python3 From 3eb66cb6f26f66c1d95128eb26d90a6b805a95dc Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 25 Oct 2026 17:15:51 -0700 Subject: [PATCH 07/24] Build CUDA 13 documentation against the checkout's bindings Signed-off-by: Ralf W. Grosse-Kunstleve --- cuda_bindings_13/pixi.lock | 888 ++++++++++++++++++++++++++++++------- cuda_bindings_13/pixi.toml | 3 +- 2 files changed, 737 insertions(+), 154 deletions(-) diff --git a/cuda_bindings_13/pixi.lock b/cuda_bindings_13/pixi.lock index da78181c381..9c97e6117a6 100644 --- a/cuda_bindings_13/pixi.lock +++ b/cuda_bindings_13/pixi.lock @@ -2,13 +2,13 @@ version: 7 platforms: - name: linux-64 virtual-packages: - - __unix=0 + - __unix=0=0 - __linux=4.18 - __glibc=2.28 - __archspec=0=x86_64 - name: linux-aarch64 virtual-packages: - - __unix=0 + - __unix=0=0 - __linux=4.18 - __glibc=2.28 - __archspec=0=aarch64 @@ -2027,9 +2027,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/charset-normalizer-3.5.2-h1114479_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-bindings-13.2.0-py312hf79963d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.5.37-h393e864_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-13.5.37-h69a702a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-13.5.37-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-13.5.37-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cython-3.2.9-py312h68e6be4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.22-py312h80505a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda @@ -2101,7 +2102,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.15-py312hd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-pathfinder-1.8.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-13.5.37-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-13.5-h3499c3b_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dict2css-0.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda @@ -2194,15 +2195,18 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.1-pyh5ded981_0.conda + - conda_source: cuda-bindings[c92b487d] @ . + - conda_source: cuda-pathfinder[10e1cc92] @ ../cuda_pathfinder - pypi: https://files.pythonhosted.org/packages/8c/79/017fab2f7167a9a9795665f894d04f77aafceca80821b51589bb4b23ff5c/nvidia_sphinx_theme-0.0.9.post1-py3-none-any.whl linux-aarch64: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/backports.zstd-1.7.0-py312h22d1088_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.2.0-py312h41c1d46_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-bindings-13.2.0-py312hdc0efb6_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.5.37-hc1d3098_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-13.5.37-he9431aa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-impl-13.5.37-hbe86820_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-tools-13.5.37-hbe86820_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cython-3.2.9-py312hbda70bc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.22-py312ha0853ce_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h5bc82ec_1.conda @@ -2276,7 +2280,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.15-py312hd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-pathfinder-1.8.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-aarch64-13.5.37-h579c4fd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-13.5-h3499c3b_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dict2css-0.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda @@ -2369,6 +2373,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.1-pyh5ded981_0.conda + - conda_source: cuda-bindings[d088a16d] @ . + - conda_source: cuda-pathfinder[f06c437b] @ ../cuda_pathfinder - pypi: https://files.pythonhosted.org/packages/8c/79/017fab2f7167a9a9795665f894d04f77aafceca80821b51589bb4b23ff5c/nvidia_sphinx_theme-0.0.9.post1-py3-none-any.whl win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda @@ -2388,7 +2394,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.15-py312hd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-pathfinder-1.8.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_win-64-13.5.37-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-13.5-h3499c3b_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dict2css-0.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda @@ -2485,9 +2491,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py312ha763cb9_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/charset-normalizer-3.5.2-h308a848_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-bindings-13.2.0-py312hc128f0a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-13.5.37-h719f0c7_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-impl-13.5.37-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-tools-13.5.37-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cython-3.2.9-py312hd245ac3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.22-py312ha1a9051_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda @@ -2538,6 +2545,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h3a581c9_12.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda + - conda_source: cuda-bindings[c345f5cd] @ . + - conda_source: cuda-pathfinder[6a6a6b0a] @ ../cuda_pathfinder - pypi: https://files.pythonhosted.org/packages/8c/79/017fab2f7167a9a9795665f894d04f77aafceca80821b51589bb4b23ff5c/nvidia_sphinx_theme-0.0.9.post1-py3-none-any.whl packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda @@ -2597,7 +2606,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - - pkg:pypi/backports-zstd?source=compressed-mapping + - pkg:pypi/backports-zstd?source=hash-mapping run_exports: {} size: 241113 timestamp: 1788491295568 @@ -2614,6 +2623,18 @@ packages: run_exports: {} size: 3713752 timestamp: 1784214522814 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_103.conda + sha256: 98f9ddc6dc5d56a824b7eaea5bab6ad4bb47642288fc70d6b1b4024ac39d5595 + md5: e86f9dc615d7533c5be8b15b3a1dee8d + depends: + - ld_impl_linux-64 2.46.1 default_hbd61a6d_103 + - sysroot_linux-64 + - zstd >=1.5.7,<1.6.0a0 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 3706604 + timestamp: 1791565481839 - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.46.1-default_h4852527_102.conda sha256: 08d7238663fc408ba2ab60b02fa3d06a7ca9d872962e03e90c7e0fdecb7ed1d0 md5: 32fd07abe84eb14f17c7f5cc6fa8df82 @@ -2624,6 +2645,16 @@ packages: run_exports: {} size: 36337 timestamp: 1784214551894 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.46.1-default_h4852527_103.conda + sha256: 606a42c754752fb905fe2012d52ff370d47b5b6adefd5f7befc4ec30ce758f23 + md5: ca26f0a4f8d04fd83147af557fbd932c + depends: + - binutils_impl_linux-64 2.46.1 default_hfdba357_103 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 36335 + timestamp: 1791565508736 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_4.conda sha256: 61b78574f002390b6a7fc79834fc344beaab09550068a51100eea5a485dac741 md5: 746aa619a1bcaf4da541101179d7c60e @@ -2764,31 +2795,6 @@ packages: run_exports: {} size: 331601 timestamp: 1789746783338 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-bindings-13.2.0-py312hf79963d_1.conda - sha256: 749c0e2e664f76833a24a7a4d970a643888ca582fba1c46ca1b7c01b245dc99d - md5: 13d8b143b3513c95a1d3aec4e65a9529 - depends: - - __glibc >=2.17,<3.0.a0 - - cuda-nvrtc >=13,<14.0a0 - - cuda-nvvm-impl >=13,<14.0a0 - - cuda-pathfinder >=1.1.0,<2 - - cuda-version >=13,<14.0a0 - - libcufile >=1,<2.0a0 - - libgcc >=14 - - libnvfatbin - - libnvjitlink >=13.0,<14.0a0 - - libstdcxx >=14 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - constrains: - - cuda-python >=13.2.0,<13.3.0a0 - - cuda-cudart >=13,<14.0a0 - license: LicenseRef-NVIDIA-SOFTWARE-LICENSE - purls: - - pkg:pypi/cuda-bindings?source=hash-mapping - run_exports: {} - size: 4143685 - timestamp: 1779249651391 - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-12.9.79-h5888daf_0.conda sha256: 57d1294ecfaf9dc8cdb5fc4be3e63ebc7614538bddb5de53cfd9b1b7de43aed5 md5: cb15315d19b58bd9cd424084e58ad081 @@ -3968,6 +3974,19 @@ packages: run_exports: {} size: 77893 timestamp: 1790346247149 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.9.0-hd2095e1_0.conda + sha256: 7b930a8b6bc6553cb754b4977a11f9c862c719a652fc2d128d89270992ce8beb + md5: 317ecabe4a58b3068c7958dc5eb89e43 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + constrains: + - expat 2.9.0.* + license: MIT + license_family: MIT + run_exports: {} + size: 78894 + timestamp: 1793054221966 - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda sha256: c8c7583ef063bc3c430f1d48298e5ad24f796a35c22ed5b14183325825a61106 md5: 6525a0b06aa4fd390795f0740636a9dd @@ -5005,6 +5024,17 @@ packages: run_exports: {} size: 572864 timestamp: 1790777898360 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-262.1-h26e0ef7_0.conda + sha256: fc930c169a0844dcb1f3084032d3f4e53798b2f48d4c7f4a151a87012833008a + md5: a6bb6ea85431d21bebdf7184517524ca + depends: + - __glibc >=2.17,<3.0.a0 + - libcap >=2.78,<2.79.0a0 + - libgcc >=15 + license: LGPL-2.1-or-later + run_exports: {} + size: 574718 + timestamp: 1792224216335 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda sha256: 10e125da82ca93e09191e66e5a94d566b2cf8d4024e2a0db3a9114297447e0d9 md5: 0907d876f460ebc941ae590338b6102f @@ -5038,6 +5068,17 @@ packages: run_exports: {} size: 184629 timestamp: 1790777902394 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-262.1-h26e0ef7_0.conda + sha256: 63bc48418ed3be6166c27f294504153bf3da8579aa5d565ae4fee8bdd1682e54 + md5: b7e3531ad0f828ee27f5731fcb9ebdc3 + depends: + - __glibc >=2.17,<3.0.a0 + - libcap >=2.78,<2.79.0a0 + - libgcc >=15 + license: LGPL-2.1-or-later + run_exports: {} + size: 184945 + timestamp: 1792224219990 - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda sha256: 71c8b9d5c72473752a0bb6e91b01dd209a03916cb71f36cc6a564e3a2a132d7a md5: e179a69edd30d75c0144d7a380b88f28 @@ -5096,6 +5137,19 @@ packages: - libuuid >=2.42.4,<3.0a0 size: 34442 timestamp: 1790121449161 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_1.conda + sha256: 0e2e8f2daa9306e67f046e31092d637431d8cf0a26c407ab22401d108c2d4631 + md5: d852293faea3c04a6e896b908b43aded + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libuuid >=2.42.4,<3.0a0 + size: 36134 + timestamp: 1793891599274 - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.24.1-hb83e432_1.conda sha256: ce7fbe2855257467196613b9fa2bdedb36d4fc3419c43804e52cfe9eb094a35e md5: c3e6df2790fff34ba708722052f02c0e @@ -6038,7 +6092,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/ruamel-yaml-clib?source=compressed-mapping + - pkg:pypi/ruamel-yaml-clib?source=hash-mapping run_exports: {} size: 158648 timestamp: 1788881211578 @@ -6272,6 +6326,20 @@ packages: run_exports: {} size: 14755479 timestamp: 1792672505702 +- conda: https://conda.anaconda.org/conda-forge/linux-64/uv-0.13.7-h535ca38_0.conda + sha256: a3014d1c201d62e6192309d67ed50075ce492981b4721bd5b8e7cb222d0c257f + md5: 53bdba3e216e191f2f0ea261efc3c9c2 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=15 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - __glibc >=2.17 + license: Apache-2.0 OR MIT + run_exports: {} + size: 14855143 + timestamp: 1793689081389 - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda sha256: df65b987979e6d7f88e756494da5a230d4f2d08d17437d04eecd35559c3a04e4 md5: 88a47e22b53e50865b1cabe2eb648352 @@ -6833,6 +6901,18 @@ packages: run_exports: {} size: 4677171 timestamp: 1784214549910 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_impl_linux-aarch64-2.46.1-default_h5f4c503_103.conda + sha256: 1827893f9d6aa13794196b1820b8003bfff0105774a323a61026a16cec6dc66d + md5: 1ebaa45e5556156b3c7bf5bd5c527634 + depends: + - ld_impl_linux-aarch64 2.46.1 default_h1979696_103 + - sysroot_linux-aarch64 + - zstd >=1.5.7,<1.6.0a0 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 4911258 + timestamp: 1791565579362 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_linux-aarch64-2.46.1-default_hf1166c9_102.conda sha256: d41099a3fe2809a3ab3e4c718ecff4d5f8879e7949f3c8e8efa97fa06a90248c md5: 9a340123cb7217eae51c5cae24484631 @@ -6843,6 +6923,16 @@ packages: run_exports: {} size: 36196 timestamp: 1784214578949 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_linux-aarch64-2.46.1-default_hf1166c9_103.conda + sha256: 60e63ac488aac474f8a22ea465d317daa02fccd039cca75e288bd0fc37e8745a + md5: 9074b4a634a2be79ee49cd85b54a7f34 + depends: + - binutils_impl_linux-aarch64 2.46.1 default_h5f4c503_103 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 36474 + timestamp: 1791565619784 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-1.2.0-h52fc18c_4.conda sha256: 73cccc8488da976cbdf32e3b5c6182aef6d77c9dfabd0e5f5fa2ca6e03d51921 md5: 207d4f45f94f6d755a7fe1455e3148d7 @@ -6887,7 +6977,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/brotli?source=compressed-mapping + - pkg:pypi/brotli?source=hash-mapping run_exports: {} size: 366485 timestamp: 1788480367792 @@ -6960,31 +7050,6 @@ packages: run_exports: {} size: 350096 timestamp: 1789746782725 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-bindings-13.2.0-py312hdc0efb6_1.conda - sha256: b8784c43ccd7c61c3944b48e7751e454e8c4b9ae50cabf42696e432b2e5b6b3b - md5: c7560ec15413c9b14e46d26c2ce94fb3 - depends: - - cuda-nvrtc >=13,<14.0a0 - - cuda-nvvm-impl >=13,<14.0a0 - - cuda-pathfinder >=1.1.0,<2 - - cuda-version >=13,<14.0a0 - - libcufile >=1,<2.0a0 - - libgcc >=14 - - libnvfatbin - - libnvjitlink >=13.0,<14.0a0 - - libstdcxx >=14 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - constrains: - - cuda-python >=13.2.0,<13.3.0a0 - - cuda-cudart >=13,<14.0a0 - license: LicenseRef-NVIDIA-SOFTWARE-LICENSE - purls: - - pkg:pypi/cuda-bindings?source=hash-mapping - run_exports: {} - size: 3866781 - timestamp: 1779249873318 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-12.9.79-h3ae8b8a_0.conda sha256: 3d6699fc27ffabf28a9d359b48e7b88437e4d945844718a58608627998db5d1b md5: df78e19e5fe656631d1470aa0fcf6ced @@ -8093,6 +8158,18 @@ packages: run_exports: {} size: 79463 timestamp: 1790346233159 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.9.0-h4154aff_0.conda + sha256: d3327ab1086334309cab3cf533c95f70c44dcbb8f66a99a92b23fb0f3f20f1da + md5: cb3ce9ae94bc74f792c119312e03bcf7 + depends: + - libgcc >=15 + constrains: + - expat 2.9.0.* + license: MIT + license_family: MIT + run_exports: {} + size: 80340 + timestamp: 1793054214232 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.7.0-hdaad0be_1.conda sha256: b1879d78b622c5f4817fd6d77fe7ae3ab7b501542e3c2f4ac77198c54c62c189 md5: f9e5b3e446b526b34a7e25ecec08efd9 @@ -9042,6 +9119,16 @@ packages: run_exports: {} size: 592152 timestamp: 1790777808830 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsystemd0-262.1-hd1ea486_0.conda + sha256: aea93da32087cc4e1f46bd0596f487ad92e1dbab282dec0d739a7f406cb42df6 + md5: 5f1e0ce7fe60051e614f505b00055cb9 + depends: + - libcap >=2.78,<2.79.0a0 + - libgcc >=15 + license: LGPL-2.1-or-later + run_exports: {} + size: 593299 + timestamp: 1792224110959 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.2-h45f9f85_1.conda sha256: fa6daf2301dee912def3e74344fae792a8fc2bbbf7880dedee0e93ee8fb98fc5 md5: c6f35f8f9695623b9055341bc280a642 @@ -9073,6 +9160,16 @@ packages: run_exports: {} size: 195674 timestamp: 1790777812289 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libudev1-262.1-hd1ea486_0.conda + sha256: ed708af321e72984beb4cbf2c9127b6d0a0b2e484d4cca670f0ae02ceee76a83 + md5: 7d5081ffee4a31823c3af8dc2cb1cf24 + depends: + - libcap >=2.78,<2.79.0a0 + - libgcc >=15 + license: LGPL-2.1-or-later + run_exports: {} + size: 196616 + timestamp: 1792224114009 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libunwind-1.8.3-h6470e1d_0.conda sha256: 86c013d522975b76e16a74341bfcb22f6ec2e9b8b87ec3e15380f46c435eaa7b md5: 5d8191a950e492a06dc29b491dd5f7c5 @@ -9127,6 +9224,18 @@ packages: - libuuid >=2.42.4,<3.0a0 size: 36296 timestamp: 1790121454455 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.4-hd6fdeab_1.conda + sha256: 66d4d6201d52b4e3679dcfcc73658902a1cfb1a096f9d72a7b2d93d78e13948a + md5: 394dc5a24e06bdea0b41389d1eee9a23 + depends: + - libgcc >=15 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libuuid >=2.42.4,<3.0a0 + size: 37956 + timestamp: 1793891605926 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libvorbis-1.3.7-h7ac5ae9_2.conda sha256: 066708ca7179a1c6e5639d015de7ed6e432b93ad50525843db67d57eb1ba1faf md5: 9d099329070afe52d797462ca7bf35f3 @@ -9942,7 +10051,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/rpds-py?source=compressed-mapping + - pkg:pypi/rpds-py?source=hash-mapping run_exports: {} size: 296806 timestamp: 1791202315258 @@ -10177,6 +10286,19 @@ packages: run_exports: {} size: 14411564 timestamp: 1792672542028 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uv-0.13.7-h779c035_0.conda + sha256: 998533e9479fb1478d674a95ad9fc9e3170d9fcc4b150e3b28003b097a408b2a + md5: cefd4c0fba2e7a6e4fb01082ebbec392 + depends: + - libstdcxx >=15 + - libgcc >=15 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - __glibc >=2.17 + license: Apache-2.0 OR MIT + run_exports: {} + size: 14511868 + timestamp: 1793689082823 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wayland-1.26.0-h637a836_2.conda sha256: 0692327145e39b3484382c2b069003b08398e961575bb6d35d3246f1cafa5d58 md5: cee65dc8a90933553988b50f5a03079a @@ -10809,6 +10931,24 @@ packages: run_exports: {} size: 131396 timestamp: 1792483552251 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-h4c7d964_0.conda + sha256: f1d280d2618de987204102090e83733089c092dc2dd45686fd3dcbe809034f6a + md5: ea9dc39dcd2815202749c1e5bc2a8973 + depends: + - __win + license: ISC + run_exports: {} + size: 130423 + timestamp: 1792772460189 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-hbd8a1cb_0.conda + sha256: 95619b9c1184be7175392eeb0ee7eeb5ccf25d280951f4225f2d62e725790be3 + md5: 3fd02989038a1e1cee627c14c2d73f29 + depends: + - __unix + license: ISC + run_exports: {} + size: 130057 + timestamp: 1792772446739 - conda: https://conda.anaconda.org/conda-forge/noarch/cachecontrol-0.14.4-pyha770c72_0.conda sha256: cca3a26282a5bc37a10afb1aa2006a21c45033cbc4ff012f9501f56f2a115c12 md5: 13bdbb9b693b29134c56a9a00c23de41 @@ -11300,20 +11440,6 @@ packages: run_exports: {} size: 37579 timestamp: 1793849341859 -- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-pathfinder-1.8.3-pyhc364b38_0.conda - sha256: 25b8f625cebe056be045bf9e484549188296b79793a340e059f13de4338939be - md5: cb2babe521da8b1acaaea1cc12a282af - depends: - - python >=3.10 - - cuda-version >=12.0,<14 - - python - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/cuda-pathfinder?source=compressed-mapping - run_exports: {} - size: 53393 - timestamp: 1790912935126 - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-12.9-h4f385c5_3.conda sha256: 5f5f428031933f117ff9f7fcc650e6ea1b3fef5936cf84aa24af79167513b656 md5: b6d5d7f1c171cbd228ea06b556cfa859 @@ -11440,7 +11566,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/filelock?source=compressed-mapping + - pkg:pypi/filelock?source=hash-mapping run_exports: {} size: 85969 timestamp: 1791210321663 @@ -11791,7 +11917,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/jsonschema?source=hash-mapping + - pkg:pypi/jsonschema?source=compressed-mapping run_exports: {} size: 82084 timestamp: 1787579299493 @@ -12183,7 +12309,7 @@ packages: license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/nbclient?source=hash-mapping + - pkg:pypi/nbclient?source=compressed-mapping run_exports: {} size: 29971 timestamp: 1788212974885 @@ -12292,7 +12418,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/platformdirs?source=compressed-mapping + - pkg:pypi/platformdirs?source=hash-mapping run_exports: {} size: 33902 timestamp: 1791424402874 @@ -13118,6 +13244,13 @@ packages: run_exports: {} size: 118849 timestamp: 1784250406640 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + sha256: 2b7ca858879f183ce2dcc65b300f48a0133d5430550f35b23d80d405a0788dbf + md5: abccc3810d2b97e570dee487f2b46e38 + license: LicenseRef-Public-Domain + run_exports: {} + size: 119208 + timestamp: 1792758382821 - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.8.0-pyhd8ed1ab_0.conda sha256: c5511c190ab55168ca412f3c54592fbf677ae6250784cb72b227a7cf3f538f7d md5: 407a3d3778570bae8e8fcf554e803e43 @@ -13416,30 +13549,6 @@ packages: run_exports: {} size: 252875 timestamp: 1789746805191 -- conda: https://conda.anaconda.org/conda-forge/win-64/cuda-bindings-13.2.0-py312hc128f0a_1.conda - sha256: 31113f2c14b5e549aa59863f711890e9bf442defd7af3bd6ff192ec96e5dcdf1 - md5: a17fac259dd34ef9ee1f508933614967 - depends: - - cuda-nvrtc >=13,<14.0a0 - - cuda-nvvm-impl >=13,<14.0a0 - - cuda-pathfinder >=1.1.0,<2 - - cuda-version >=13,<14.0a0 - - libnvfatbin - - libnvjitlink >=13.0,<14.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - cuda-python >=13.2.0,<13.3.0a0 - - cuda-cudart >=13,<14.0a0 - license: LicenseRef-NVIDIA-SOFTWARE-LICENSE - purls: - - pkg:pypi/cuda-bindings?source=hash-mapping - run_exports: {} - size: 3509245 - timestamp: 1779249639234 - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-cudart-12.9.79-he0c23c2_0.conda sha256: a30cd9adf3a70d069d4d87c5728ec16778b77071629612ca5d8513cd92d89c09 md5: 0a243d4f000a0d2f51dd94ee9132b234 @@ -14221,6 +14330,20 @@ packages: run_exports: {} size: 72107 timestamp: 1790346254561 +- conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.9.0-hac47afa_0.conda + sha256: d4a8e5aac8999a90cd71aa6f9f5c40d8594ed2b89cd7f898f997767ecc6cd52d + md5: 74ac404611024dc488ae71cfb7a5bf8f + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - expat 2.9.0.* + license: MIT + license_family: MIT + run_exports: {} + size: 72983 + timestamp: 1793054271728 - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda sha256: 613e8077c5cca5df7fe84ade7d5050301bee3c6c4c450ffe61d34a349ab4f11c md5: ceb38b0ba900847d8da4289c3c8e5708 @@ -14930,7 +15053,7 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause purls: - - pkg:pypi/markupsafe?source=compressed-mapping + - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 28515 timestamp: 1791548981413 @@ -15044,7 +15167,7 @@ packages: license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/numpy?source=compressed-mapping + - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.25,<3 @@ -15638,6 +15761,18 @@ packages: run_exports: {} size: 13387537 timestamp: 1792672517235 +- conda: https://conda.anaconda.org/conda-forge/win-64/uv-0.13.7-hf7a7c96_0.conda + sha256: b662d664862bf73941b02ce76868398956cf893475dc3e31ff49de198b14b4a8 + md5: bab575f975c8a087dbbc56d8188265d7 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 OR MIT + run_exports: {} + size: 13299866 + timestamp: 1793689103658 - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda sha256: 35444c55a92e2f7f7ba26bc70f81e56e52344f7d064c0fd4b40a46a58517b79c md5: aa805b5522c2a98fa286e551a1f48546 @@ -15651,6 +15786,18 @@ packages: run_exports: {} size: 21383 timestamp: 1785359368566 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_42.conda + sha256: 0598ca1ce913618cff9bd90391bcf75cd9edb22057c85dd6784dd762b7a888e2 + md5: 14166cbeafd1f1d255bd21abcc742a65 + depends: + - vc14_runtime >=14.51.36247 + track_features: + - vc14 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 21309 + timestamp: 1793305687162 - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda sha256: 4e4cb599cdc41bf2109d1464c127b5bcbddf548ce3e322e612afb691338b48f8 md5: ac5333bb3d429361f23adf704cc49a78 @@ -15665,6 +15812,19 @@ packages: run_exports: {} size: 767955 timestamp: 1785359364369 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_42.conda + sha256: 8dfbfd68ebf53249d11e528cbb80ad5a2460b0c5b4636eed82ee0d3950827a3d + md5: bc3b057d4b2458cf95326daa33acf9da + depends: + - ucrt >=10.0.20348.0 + - vcomp14 14.51.36247 habf1de7_42 + constrains: + - vs2015_runtime 14.51.36247.* *_42 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + run_exports: {} + size: 767099 + timestamp: 1793305684782 - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda sha256: 731e043390c9457299484d39e427221fc868a9249540a498a5a4f6456c7744d1 md5: 350bb67a5c8e5f1c53347ac544ab6600 @@ -15680,6 +15840,20 @@ packages: - vcomp14 >=14.51.36247 size: 155910 timestamp: 1785359349999 +- conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_42.conda + sha256: b3558ed059d6272ef919ea959311af73c35b5fe43c789cd43f71b398671fec49 + md5: 948483905faab0e83c9aed0414004194 + depends: + - ucrt >=10.0.20348.0 + constrains: + - vs2015_runtime 14.51.36247.* *_42 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + run_exports: + strong: + - vcomp14 >=14.51.36247 + size: 155686 + timestamp: 1793305669217 - conda: https://conda.anaconda.org/conda-forge/win-64/vs2022_win-64-19.44.35207-ha74f236_41.conda sha256: 9d7d1b43cf4af5a8e8b1646175c9f899ffbcab189a33ac41127a96e7bcf41af0 md5: 04190e0ebd886433300ce9343ee98942 @@ -15698,6 +15872,24 @@ packages: - ucrt >=10.0.20348.0 size: 25462 timestamp: 1785358620723 +- conda: https://conda.anaconda.org/conda-forge/win-64/vs2022_win-64-19.44.35207-ha74f236_42.conda + sha256: 872123d66fbc41898bea34973cbf64544c41cdcd5123a00abe747c5f35b92898 + md5: 410be03ef3a0eb26e1b20cb90be5493c + depends: + - vswhere + constrains: + - vs_win-64 2022.14 + track_features: + - vc14 + license: BSD-3-Clause + license_family: BSD + run_exports: + strong: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + size: 25374 + timestamp: 1793120002126 - conda: https://conda.anaconda.org/conda-forge/win-64/wcwidth-0.9.2-py311hdbcae8a_0.conda noarch: python sha256: ad6a9373bed593daf3f8645044a2ac793726b4482ce78dbdb46a81cb9dfb4448 @@ -15714,7 +15906,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/wcwidth?source=compressed-mapping + - pkg:pypi/wcwidth?source=hash-mapping run_exports: {} size: 348834 timestamp: 1791194558195 @@ -16289,68 +16481,351 @@ packages: - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda - conda_source: cuda-pathfinder[f6abc7c0] @ ../cuda_pathfinder -- conda_source: cuda-bindings[e3183a23] @ . +- conda_source: cuda-bindings[c345f5cd] @ . variants: - c_stdlib: sysroot - c_stdlib_version: '2.28' - cuda_version: 12.* - python: 3.14.* - target_platform: linux-aarch64 + c_compiler: vs2022 + cuda_version: 13.5.* + cxx_compiler: vs2022 + python: 3.12.* + target_platform: win-64 depends: - - __glibc >=2.28,<3.0.a0 - cuda-nvrtc - - cuda-nvrtc >=12.9.86,<13.0a0 + - cuda-nvrtc >=13.5.37,<14.0a0 - cuda-nvvm - cuda-pathfinder - cuda-version - - libcufile - - libcufile >=1.14.1.1,<2.0a0 - - libgcc >=16 - - libgcc >=16 - libnvfatbin - libnvjitlink - - libstdcxx >=16 - python - python >=3.11 - - python_abi 3.14.* *_cp314 + - python_abi 3.12.* *_cp312 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: Apache-2.0 source_depends: cuda-pathfinder: path: ../cuda_pathfinder build_packages: - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_impl_linux-aarch64-2.46.1-default_h5f4c503_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_linux-aarch64-2.46.1-default_hf1166c9_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gcc_impl_linux-aarch64-16.2.0-h5425ead_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gcc_linux-aarch64-16.2.0-h25cc031_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gxx_impl_linux-aarch64-16.2.0-h1e3c31f_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gxx_linux-aarch64-16.2.0-h423111d_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsanitizer-16.2.0-h73cac2c_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-aarch64-4.18.0-h05a177a_9.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-aarch64-16.2.0-hc0c2482_107.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-aarch64-16.2.0-h082e5f6_107.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-aarch64-2.28-h585391f_9.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/vswhere-3.1.7-h40126e0_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vs2022_win-64-19.44.35207-ha74f236_42.conda host_packages: - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-12.9.79-h3ae8b8a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-dev-12.9.79-h3ae8b8a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-static-12.9.79-h3ae8b8a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-12.9.86-h8f3c8d4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-dev-12.9.86-h8f3c8d4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-12.9.86-he9431aa_106.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-impl-12.9.86-h7b14b0b_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-tools-12.9.86-h7b14b0b_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-profiler-api-12.9.79-h16bee8c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cython-3.2.9-py314hc6b4731_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_win-64-13.3.5.0.6-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_win-64-13.5.37-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_win-64-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_win-64-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_win-64-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_win-64-13.5.37-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-13.5-h3499c3b_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.2.1-pyhe730653_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-10.3.4-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-cudart-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-cudart-dev-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-cudart-static-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-dev-13.5.37-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-13.5.37-h719f0c7_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-impl-13.5.37-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-tools-13.5.37-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-profiler-api-13.5.25-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cython-3.2.9-py312hd245ac3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.9.0-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.12.16-hfb4101e_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.54.1-hf5d6505_101.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.5-hf411b9b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.16-hb12b558_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/uv-0.13.7-hf7a7c96_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_42.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_42.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_42.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda + - conda_source: cuda-pathfinder[6a6a6b0a] @ ../cuda_pathfinder +- conda_source: cuda-bindings[c92b487d] @ . + variants: + c_stdlib: sysroot + c_stdlib_version: '2.28' + cuda_version: 13.5.* + python: 3.12.* + target_platform: linux-64 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-nvrtc + - cuda-nvrtc >=13.5.37,<14.0a0 + - cuda-nvvm + - cuda-pathfinder + - cuda-version + - libcufile + - libcufile >=1.20.0.107,<2.0a0 + - libgcc >=16 + - libgcc >=16 + - libnvfatbin + - libnvjitlink + - libstdcxx >=16 + - python + - python >=3.11 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + source_depends: + cuda-pathfinder: + path: ../cuda_pathfinder + build_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.46.1-default_h4852527_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-16.2.0-h9891192_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-16.2.0-h0ab548f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-16.2.0-h0d273dc_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-16.2.0-h23aca61_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-16.2.0-h3048135_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-16.2.0-he3ce08f_107.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-16.2.0-h86e191b_107.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + host_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-13.5.37-h393e864_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-dev-13.5.37-h393e864_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-13.5.37-h393e864_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.5.37-h393e864_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-dev-13.5.37-h393e864_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-13.5.37-h69a702a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-13.5.37-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-13.5.37-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-profiler-api-13.5.25-h7938cbb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cython-3.2.9-py312h68e6be4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.78-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.20.0.107-h63c01a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-dev-1.20.0.107-h393e864_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.9.0-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.16-ha57cdbe_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.1-h0737f62_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-262.1-h26e0ef7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-262.1-h26e0ef7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.16-h5f976f7_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-65.0-h7b4f2e6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uv-0.13.7-h535ca38_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-13.3.5.0.6-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-64-13.5.37-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-64-13.5.37-h528332f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-13.5.37-h528332f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-13.5.37-h528332f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-13.5.37-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-13.5-h3499c3b_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.2.1-pyhe730653_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-10.3.4-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda + - conda_source: cuda-pathfinder[10e1cc92] @ ../cuda_pathfinder +- conda_source: cuda-bindings[d088a16d] @ . + variants: + c_stdlib: sysroot + c_stdlib_version: '2.28' + cuda_version: 13.5.* + python: 3.12.* + target_platform: linux-aarch64 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-nvrtc + - cuda-nvrtc >=13.5.37,<14.0a0 + - cuda-nvvm + - cuda-pathfinder + - cuda-version + - libcufile + - libcufile >=1.20.0.107,<2.0a0 + - libgcc >=16 + - libgcc >=16 + - libnvfatbin + - libnvjitlink + - libstdcxx >=16 + - python + - python >=3.11 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + source_depends: + cuda-pathfinder: + path: ../cuda_pathfinder + build_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_impl_linux-aarch64-2.46.1-default_h5f4c503_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_linux-aarch64-2.46.1-default_hf1166c9_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gcc_impl_linux-aarch64-16.2.0-h5425ead_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gcc_linux-aarch64-16.2.0-h25cc031_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gxx_impl_linux-aarch64-16.2.0-h1e3c31f_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gxx_linux-aarch64-16.2.0-h423111d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsanitizer-16.2.0-h73cac2c_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-aarch64-4.18.0-h05a177a_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-aarch64-16.2.0-hc0c2482_107.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-aarch64-16.2.0-h082e5f6_107.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-aarch64-2.28-h585391f_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + host_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-13.5.37-hc1d3098_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-dev-13.5.37-hc1d3098_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-static-13.5.37-hc1d3098_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.5.37-hc1d3098_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-dev-13.5.37-hc1d3098_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-13.5.37-he9431aa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-impl-13.5.37-hbe86820_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-tools-13.5.37-hbe86820_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-profiler-api-13.5.25-h16bee8c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cython-3.2.9-py312hbda70bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcap-2.78-hfcc8634_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.20.0.107-hc7dfe37_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-dev-1.20.0.107-hc1d3098_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.9.0-h4154aff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.7.0-hdaad0be_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnl-3.11.0-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpython-3.12.16-h3cfa5a6_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.54.1-hd26f1b6_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsystemd0-262.1-hd1ea486_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libudev1-262.1-hd1ea486_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.4-hd6fdeab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.38-h80f16a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.6-h2b6f883_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.5-he6ad1d5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.16-h94ad73b_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rdma-core-65.0-h807d763_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.3-ha7194a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_hf03c496_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uv-0.13.7-h779c035_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/arm-variant-1.2.0-sbsa.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-aarch64-13.3.5.0.6-h579c4fd_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-aarch64-13.5.37-h579c4fd_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-aarch64-13.5.37-hf3a43d7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-aarch64-13.5.37-hf3a43d7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-aarch64-13.5.37-hf3a43d7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-aarch64-13.5.37-h579c4fd_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-13.5-h3499c3b_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.2.1-pyhe730653_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-10.3.4-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda + - conda_source: cuda-pathfinder[f06c437b] @ ../cuda_pathfinder +- conda_source: cuda-bindings[e3183a23] @ . + variants: + c_stdlib: sysroot + c_stdlib_version: '2.28' + cuda_version: 12.* + python: 3.14.* + target_platform: linux-aarch64 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-nvrtc + - cuda-nvrtc >=12.9.86,<13.0a0 + - cuda-nvvm + - cuda-pathfinder + - cuda-version + - libcufile + - libcufile >=1.14.1.1,<2.0a0 + - libgcc >=16 + - libgcc >=16 + - libnvfatbin + - libnvjitlink + - libstdcxx >=16 + - python + - python >=3.11 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + source_depends: + cuda-pathfinder: + path: ../cuda_pathfinder + build_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_impl_linux-aarch64-2.46.1-default_h5f4c503_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/binutils_linux-aarch64-2.46.1-default_hf1166c9_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gcc_impl_linux-aarch64-16.2.0-h5425ead_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gcc_linux-aarch64-16.2.0-h25cc031_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gxx_impl_linux-aarch64-16.2.0-h1e3c31f_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gxx_linux-aarch64-16.2.0-h423111d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsanitizer-16.2.0-h73cac2c_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-aarch64-4.18.0-h05a177a_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-aarch64-16.2.0-hc0c2482_107.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-aarch64-16.2.0-h082e5f6_107.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-aarch64-2.28-h585391f_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + host_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-12.9.79-h3ae8b8a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-dev-12.9.79-h3ae8b8a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-static-12.9.79-h3ae8b8a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-12.9.86-h8f3c8d4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-dev-12.9.86-h8f3c8d4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-12.9.86-he9431aa_106.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-impl-12.9.86-h7b14b0b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-tools-12.9.86-h7b14b0b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-profiler-api-12.9.79-h16bee8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cython-3.2.9-py314hc6b4731_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcap-2.78-hfcc8634_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.14.1.1-had8bf56_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-dev-1.14.1.1-he38c790_1.conda @@ -16394,6 +16869,43 @@ packages: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda - conda_source: cuda-pathfinder[a51836e4] @ ../cuda_pathfinder +- conda_source: cuda-pathfinder[10e1cc92] @ ../cuda_pathfinder + variants: + target_platform: noarch + depends: + - python * + - python >=3.11 + license: Apache-2.0 + host_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20260512-pl5321h373387f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.9.0-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.1-h0737f62_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.15.2-h6e52b48_100_cp315.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uv-0.13.7-h535ca38_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.15-9_cp315.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.2.1-pyhe730653_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-10.3.4-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda - conda_source: cuda-pathfinder[3da77f45] @ ../cuda_pathfinder variants: target_platform: noarch @@ -16431,6 +16943,39 @@ packages: - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda +- conda_source: cuda-pathfinder[6a6a6b0a] @ ../cuda_pathfinder + variants: + target_platform: noarch + depends: + - python * + - python >=3.11 + license: Apache-2.0 + host_packages: + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.15-9_cp315.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.2.1-pyhe730653_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-10.3.4-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.9.0-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.54.1-hf5d6505_101.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.5-hf411b9b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.15.2-h53f6dd8_100_cp315.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/uv-0.13.7-hf7a7c96_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_42.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_42.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_42.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda - conda_source: cuda-pathfinder[8f545c34] @ ../cuda_pathfinder variants: target_platform: noarch @@ -16575,6 +17120,43 @@ packages: - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda +- conda_source: cuda-pathfinder[f06c437b] @ ../cuda_pathfinder + variants: + target_platform: noarch + depends: + - python * + - python >=3.11 + license: Apache-2.0 + host_packages: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20260512-pl5321hc48eb74_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.9.0-h4154aff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.7.0-hdaad0be_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-he30d5cf_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.54.1-hd26f1b6_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.4-hd6fdeab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.6-h2b6f883_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.5-he6ad1d5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.15.2-h0c7c799_100_cp315.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_hf03c496_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uv-0.13.7-h779c035_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.10.23-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.15-9_cp315.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.2.1-pyhe730653_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-10.3.4-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026e-h5d301ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/vcs_versioning-2.6.0-pyh5ded981_0.conda - conda_source: cuda-pathfinder[f6abc7c0] @ ../cuda_pathfinder variants: target_platform: noarch diff --git a/cuda_bindings_13/pixi.toml b/cuda_bindings_13/pixi.toml index 854ca2fadac..a3d33e67819 100644 --- a/cuda_bindings_13/pixi.toml +++ b/cuda_bindings_13/pixi.toml @@ -28,7 +28,8 @@ cuda-python-test-helpers = { path = "../cuda_python_test_helpers", editable = tr # Keep this dependency set aligned with cuda_python/docs/environment-docs.yml. [feature.docs.dependencies] -cuda-bindings = "13.2.*" +# Document the checkout's API, including modules absent from published packages. +cuda-bindings = { path = "." } python = "3.12.*" cython = ">=3.2.5,<3.3" enum_tools = "*" From 8f3823195dfde1490b5390cfdf0f0f89b8a293ed Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 14:12:09 -0700 Subject: [PATCH 08/24] Share wheel-test matrix selection across platforms Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/test-wheel-linux.yml | 76 +-------------- .github/workflows/test-wheel-windows.yml | 73 +------------- ci/tools/select_test_matrix.py | 113 ++++++++++++++++++++++ ci/tools/tests/test_select_test_matrix.py | 71 ++++++++++++++ 4 files changed, 192 insertions(+), 141 deletions(-) create mode 100644 ci/tools/select_test_matrix.py create mode 100644 ci/tools/tests/test_select_test_matrix.py diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 87fcd906ffc..6e7c23461cc 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -91,78 +91,10 @@ jobs: # Read base matrix from YAML file for the specific architecture TEST_MATRIX=$(yq -o json ".linux[\"${MATRIX_TYPE}\"] | map(select(.ARCH == \"${ARCH}\"))" ci/test-matrix.yml) - BINDINGS_PACKAGES=$(jq -c '.package_roots' <<< "$BINDINGS_CONFIG") - CURRENT_BINDINGS_SOURCE_DIR=$(jq -r ' - .[] | select(.release_status == "current") | .package_root - ' <<< "$BINDINGS_PACKAGES") - - if [[ -z "$WORKPLAN" ]]; then - TEST_CUDA_MAJORS=$(jq -c ' - [.[].cuda_variant] | map({key: ., value: true}) | from_entries - ' <<< "$BINDINGS_PACKAGES") - PATHFINDER_SOURCE=artifact - else - TEST_CUDA_MAJORS=$(jq -c '.jobs.test_cuda_majors' <<< "$WORKPLAN") - PATHFINDER_SOURCE=$(jq -r '.sources.pathfinder' <<< "$WORKPLAN") - fi - - # Apply matrix filter; reject custom DRIVER + FLAVOR=wsl (the - # in-container driver swap doesn't work under WSL); add a - # RUNNER_DRIVER field that maps any custom version back to - # 'latest' (the install script swaps the driver itself, so we - # need to land on the runner that ships with the most recent - # pre-installed driver); wrap in include structure. - MATRIX=$(echo "$TEST_MATRIX" | jq -c \ - --argjson test_cuda_majors "$TEST_CUDA_MAJORS" \ - --argjson bindings_packages "$BINDINGS_PACKAGES" \ - --arg default_bindings_source_dir "$CURRENT_BINDINGS_SOURCE_DIR" \ - --arg pathfinder_source "$PATHFINDER_SOURCE" \ - --argjson workplan "${WORKPLAN:-null}" \ - --arg test_mode "$TEST_MODE" \ - '${{ inputs.matrix_filter }} - | map( - . as $row - | ("cu" + ($row.CUDA_VER | split(".")[0])) as $cuda_variant - | ($bindings_packages | map(select(.toolkit_version == $row.CUDA_VER)) | first) as $bindings_package - | $row + { - CUDA_VARIANT: $cuda_variant, - BINDINGS_SOURCE: (if $bindings_package == null then "floor" else "local" end), - BINDINGS_SOURCE_DIR: ($bindings_package.package_root // ""), - DEFAULT_BINDINGS_SOURCE_DIR: $default_bindings_source_dir, - PATHFINDER_SOURCE: $pathfinder_source - } - ) - | map(select($test_cuda_majors[.CUDA_VARIANT] == true)) - | map(. as $row | . + { - SUITES: (if $test_mode != "standard" then $test_mode else - [if $workplan == null or $workplan.modules.pathfinder.needs_test then "pathfinder" else empty end, - if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.bindings.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "bindings" + ($row.CUDA_VER | split(".")[0]) else empty end, - if $workplan == null or $workplan.modules.core.cuda_majors[$row.CUDA_VARIANT].needs_test then "core" + ($row.CUDA_VER | split(".")[0]) else empty end, - if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "meta" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) - }) - | map(select(.SUITES != "")) - | if any(.[]; .DRIVER != "latest" and .DRIVER != "earliest" and .FLAVOR == "wsl") then "Error: custom DRIVER is not supported with FLAVOR=wsl\n" | halt_error(1) else . end - | map(. + {RUNNER_DRIVER: (if .DRIVER == "latest" or .DRIVER == "earliest" then .DRIVER else "latest" end)}) - | if (. | length) > 0 then {include: .} else "Error: Empty matrix after CUDA-major workplan filtering\n" | halt_error(1) end') - - # Compatibility nightly modes intentionally use published floor bindings. - # Standard PR/release runs must retain each selected local bindings line. - if [[ "$TEST_MODE" == "standard" && "$BUILD_TYPE" == "pull-request" ]]; then - jq -en --argjson packages "$BINDINGS_PACKAGES" \ - --argjson enabled "$TEST_CUDA_MAJORS" --argjson matrix "$MATRIX" \ - --argjson workplan "${WORKPLAN:-null}" ' - [$packages[] | . as $package - | select($enabled[.cuda_variant] == true) - | select($workplan == null or - $workplan.modules.bindings.package_roots[.package_root].needs_test or - $workplan.modules.python.package_roots[.package_root].needs_test) - | select(all($matrix.include[]; - .BINDINGS_SOURCE != "local" or .BINDINGS_SOURCE_DIR != $package.package_root)) - | .package_root] as $missing - | if $missing | length == 0 then true else - error("filtered test matrix omits required local-wheel coverage: \($missing | join(", "))") end - ' > /dev/null - fi + # Keep the caller's free-form jq filter; shared Python code handles + # source selection, test suites, runner routing, and coverage checks. + MATRIX=$(jq -c '${{ inputs.matrix_filter }}' <<< "$TEST_MATRIX" \ + | python3 -m ci.tools.select_test_matrix --platform linux) { echo '### Selected wheel test coverage' diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index b5a78ea9db6..25a79e61670 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -88,75 +88,10 @@ jobs: # Read base matrix from YAML file for the specific architecture TEST_MATRIX=$(yq -o json ".windows[\"${MATRIX_TYPE}\"] | map(select(.ARCH == \"${ARCH}\"))" ci/test-matrix.yml) - BINDINGS_PACKAGES=$(jq -c '.package_roots' <<< "$BINDINGS_CONFIG") - CURRENT_BINDINGS_SOURCE_DIR=$(jq -r ' - .[] | select(.release_status == "current") | .package_root - ' <<< "$BINDINGS_PACKAGES") - - if [[ -z "$WORKPLAN" ]]; then - TEST_CUDA_MAJORS=$(jq -c ' - [.[].cuda_variant] | map({key: ., value: true}) | from_entries - ' <<< "$BINDINGS_PACKAGES") - PATHFINDER_SOURCE=artifact - else - TEST_CUDA_MAJORS=$(jq -c '.jobs.test_cuda_majors' <<< "$WORKPLAN") - PATHFINDER_SOURCE=$(jq -r '.sources.pathfinder' <<< "$WORKPLAN") - fi - - # Apply matrix filter; add a RUNNER_DRIVER field that maps any - # custom DRIVER version back to 'latest' (install_gpu_driver.ps1 - # swaps the driver itself, so the runner must be the one that - # ships the most recent pre-installed driver); wrap in include. - MATRIX=$(echo "$TEST_MATRIX" | jq -c \ - --argjson test_cuda_majors "$TEST_CUDA_MAJORS" \ - --argjson bindings_packages "$BINDINGS_PACKAGES" \ - --arg default_bindings_source_dir "$CURRENT_BINDINGS_SOURCE_DIR" \ - --arg pathfinder_source "$PATHFINDER_SOURCE" \ - --argjson workplan "${WORKPLAN:-null}" \ - --arg test_mode "$TEST_MODE" \ - '${{ inputs.matrix_filter }} - | map( - . as $row - | ("cu" + ($row.CUDA_VER | split(".")[0])) as $cuda_variant - | ($bindings_packages | map(select(.toolkit_version == $row.CUDA_VER)) | first) as $bindings_package - | $row + { - CUDA_VARIANT: $cuda_variant, - BINDINGS_SOURCE: (if $bindings_package == null then "floor" else "local" end), - BINDINGS_SOURCE_DIR: ($bindings_package.package_root // ""), - DEFAULT_BINDINGS_SOURCE_DIR: $default_bindings_source_dir, - PATHFINDER_SOURCE: $pathfinder_source - } - ) - | map(select($test_cuda_majors[.CUDA_VARIANT] == true)) - | map(. as $row | . + { - SUITES: (if $test_mode != "standard" then $test_mode else - [if $workplan == null or $workplan.modules.pathfinder.needs_test then "pathfinder" else empty end, - if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.bindings.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "bindings" + ($row.CUDA_VER | split(".")[0]) else empty end, - if $workplan == null or $workplan.modules.core.cuda_majors[$row.CUDA_VARIANT].needs_test then "core" + ($row.CUDA_VER | split(".")[0]) else empty end, - if $row.BINDINGS_SOURCE == "local" and ($workplan == null or $workplan.modules.python.package_roots[$row.BINDINGS_SOURCE_DIR].needs_test) then "meta" + ($row.CUDA_VER | split(".")[0]) else empty end] | join("+") end) - }) - | map(select(.SUITES != "")) - | map(. + {RUNNER_DRIVER: (if .DRIVER == "latest" or .DRIVER == "earliest" then .DRIVER else "latest" end)}) - | if (. | length) > 0 then {include: .} else "Error: Empty matrix after CUDA-major workplan filtering\n" | halt_error(1) end') - - # Compatibility nightly modes intentionally use published floor bindings. - # Standard PR/release runs must retain each selected local bindings line. - if [[ "$TEST_MODE" == "standard" && "$BUILD_TYPE" == "pull-request" ]]; then - jq -en --argjson packages "$BINDINGS_PACKAGES" \ - --argjson enabled "$TEST_CUDA_MAJORS" --argjson matrix "$MATRIX" \ - --argjson workplan "${WORKPLAN:-null}" ' - [$packages[] | . as $package - | select($enabled[.cuda_variant] == true) - | select($workplan == null or - $workplan.modules.bindings.package_roots[.package_root].needs_test or - $workplan.modules.python.package_roots[.package_root].needs_test) - | select(all($matrix.include[]; - .BINDINGS_SOURCE != "local" or .BINDINGS_SOURCE_DIR != $package.package_root)) - | .package_root] as $missing - | if $missing | length == 0 then true else - error("filtered test matrix omits required local-wheel coverage: \($missing | join(", "))") end - ' > /dev/null - fi + # Keep the caller's free-form jq filter; shared Python code handles + # source selection, test suites, runner routing, and coverage checks. + MATRIX=$(jq -c '${{ inputs.matrix_filter }}' <<< "$TEST_MATRIX" \ + | python3 -m ci.tools.select_test_matrix --platform windows) { echo '### Selected wheel test coverage' diff --git a/ci/tools/select_test_matrix.py b/ci/tools/select_test_matrix.py new file mode 100644 index 00000000000..e9cf82d243c --- /dev/null +++ b/ci/tools/select_test_matrix.py @@ -0,0 +1,113 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Select wheel-test rows and their local or published bindings sources.""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +from typing import Any + + +def select_test_matrix( + rows: list[dict[str, Any]], + packages: list[dict[str, Any]], + *, + workplan: dict[str, Any] | None = None, + build_type: str = "pull-request", + test_mode: str = "standard", + platform: str = "linux", +) -> dict[str, list[dict[str, Any]]]: + """Apply the workplan after the caller's optional jq matrix filter.""" + current_root = next(package["package_root"] for package in packages if package["release_status"] == "current") + enabled = ( + {package["cuda_variant"]: True for package in packages} + if workplan is None + else workplan["jobs"]["test_cuda_majors"] + ) + modules = None if workplan is None else workplan["modules"] + selected = [] + for source_row in rows: + major = source_row["CUDA_VER"].split(".")[0] + variant = f"cu{major}" + if not enabled.get(variant, False): + continue + package = next((package for package in packages if package["toolkit_version"] == source_row["CUDA_VER"]), None) + root = "" if package is None else package["package_root"] + suites = [] + if test_mode != "standard": + suites.append(test_mode) + else: + if modules is None or modules["pathfinder"]["needs_test"]: + suites.append("pathfinder") + if package is not None and (modules is None or modules["bindings"]["package_roots"][root]["needs_test"]): + suites.append(f"bindings{major}") + if modules is None or modules["core"]["cuda_majors"][variant]["needs_test"]: + suites.append(f"core{major}") + if package is not None and (modules is None or modules["python"]["package_roots"][root]["needs_test"]): + suites.append(f"meta{major}") + if not suites: + continue + driver = source_row["DRIVER"] + # Custom versions are installed after scheduling on a latest-driver + # runner. WSL cannot perform that driver swap. + runner_driver = driver if driver in ("latest", "earliest") else "latest" + if platform == "linux" and runner_driver != driver and source_row.get("FLAVOR") == "wsl": + raise ValueError("custom DRIVER is not supported with FLAVOR=wsl") + selected.append( + { + **source_row, + "CUDA_VARIANT": variant, + "BINDINGS_SOURCE": "floor" if package is None else "local", + "BINDINGS_SOURCE_DIR": root, + "DEFAULT_BINDINGS_SOURCE_DIR": current_root, + "PATHFINDER_SOURCE": "artifact" if workplan is None else workplan["sources"]["pathfinder"], + "SUITES": "+".join(suites), + "RUNNER_DRIVER": runner_driver, + } + ) + if not selected: + raise ValueError("Empty matrix after CUDA-major workplan filtering") + # Nightly integrations may deliberately use published bindings with older + # toolkits. Ordinary PRs must not silently lose a required local-wheel row. + if test_mode == "standard" and build_type == "pull-request": + covered = {row["BINDINGS_SOURCE_DIR"] for row in selected if row["BINDINGS_SOURCE"] == "local"} + missing = [ + package["package_root"] + for package in packages + if enabled.get(package["cuda_variant"], False) + and ( + modules is None + or modules["bindings"]["package_roots"][package["package_root"]]["needs_test"] + or modules["python"]["package_roots"][package["package_root"]]["needs_test"] + ) + and package["package_root"] not in covered + ] + if missing: + raise ValueError(f"filtered test matrix omits required local-wheel coverage: {', '.join(missing)}") + return {"include": selected} + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--platform", choices=("linux", "windows"), required=True) + args = parser.parse_args() + try: + result = select_test_matrix( + json.load(sys.stdin), + json.loads(os.environ["BINDINGS_CONFIG"])["package_roots"], + workplan=json.loads(os.environ.get("WORKPLAN") or "null"), + build_type=os.environ["BUILD_TYPE"], + test_mode=os.environ["TEST_MODE"], + platform=args.platform, + ) + except ValueError as error: + parser.error(str(error)) + print(json.dumps(result, separators=(",", ":"))) + + +if __name__ == "__main__": + main() diff --git a/ci/tools/tests/test_select_test_matrix.py b/ci/tools/tests/test_select_test_matrix.py new file mode 100644 index 00000000000..7e913fa8a0d --- /dev/null +++ b/ci/tools/tests/test_select_test_matrix.py @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import pytest + +from ci.tools.select_test_matrix import select_test_matrix + +PACKAGES = [ + { + "package_root": "bindings12", + "toolkit_version": "12.9.1", + "cuda_variant": "cu12", + "release_status": "maintenance", + }, + {"package_root": "bindings13", "toolkit_version": "13.4.2", "cuda_variant": "cu13", "release_status": "current"}, +] + + +def rows(*toolkits): + return [{"CUDA_VER": toolkit, "DRIVER": "latest"} for toolkit in toolkits] + + +def plan(*, bindings=False, core=True, meta=False): + return { + "jobs": {"test_cuda_majors": {"cu12": True, "cu13": False}}, + "sources": {"pathfinder": "artifact"}, + "modules": { + "pathfinder": {"needs_test": False}, + "bindings": {"package_roots": {package["package_root"]: {"needs_test": bindings} for package in PACKAGES}}, + "core": {"cuda_majors": {"cu12": {"needs_test": core}, "cu13": {"needs_test": core}}}, + "python": {"package_roots": {package["package_root"]: {"needs_test": meta} for package in PACKAGES}}, + }, + } + + +@pytest.mark.agent_authored(model="gpt-6.1-sol") +def test_exact_toolkit_rows_use_local_bindings_and_older_toolkits_use_published_floors(): + matrix = select_test_matrix(rows("12.9.1", "12.6.3", "13.4.2", "13.0.2"), PACKAGES) + + assert [(row["CUDA_VARIANT"], row["BINDINGS_SOURCE"], row["BINDINGS_SOURCE_DIR"]) for row in matrix["include"]] == [ + ("cu12", "local", "bindings12"), + ("cu12", "floor", ""), + ("cu13", "local", "bindings13"), + ("cu13", "floor", ""), + ] + + +@pytest.mark.agent_authored(model="gpt-6.1-sol") +def test_workplan_retains_only_the_required_cuda_major_and_suites(): + matrix = select_test_matrix( + rows("12.9.1", "12.6.3", "13.4.2"), PACKAGES, workplan=plan(bindings=True, core=False, meta=True) + ) + + # Without Core or Pathfinder tests the older-toolkit row has no applicable + # suites. The required maintenance bindings/metapackage row must remain. + assert [(row["CUDA_VER"], row["BINDINGS_SOURCE_DIR"]) for row in matrix["include"]] == [("12.9.1", "bindings12")] + + +@pytest.mark.parametrize("component", ("bindings", "meta")) +@pytest.mark.agent_authored(model="gpt-6.1-sol") +def test_filter_cannot_silently_drop_required_local_wheel_coverage(component): + with pytest.raises(ValueError, match="omits required local-wheel coverage: bindings12"): + select_test_matrix(rows("12.6.3", "13.4.2"), PACKAGES, workplan=plan(**{component: True})) + + +@pytest.mark.parametrize("test_mode", ("nightly-pytorch", "standard")) +@pytest.mark.agent_authored(model="gpt-6.1-sol") +def test_nightly_subsets_may_use_published_floor_bindings(test_mode): + matrix = select_test_matrix(rows("12.6.3"), PACKAGES, build_type="nightly", test_mode=test_mode) + + assert [row["BINDINGS_SOURCE"] for row in matrix["include"]] == ["floor"] From 6c9dba88a7da3927a6397293cc14837cedf35d0f Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 14:12:25 -0700 Subject: [PATCH 09/24] Remove obsolete artifact filters from CI run lookup Signed-off-by: Ralf W. Grosse-Kunstleve --- ci/tools/lookup-run-id | 57 +++--------------------------------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/ci/tools/lookup-run-id b/ci/tools/lookup-run-id index 4f7eb2c043f..7ece969ded5 100755 --- a/ci/tools/lookup-run-id +++ b/ci/tools/lookup-run-id @@ -25,14 +25,11 @@ Options: --tag Find run by git tag (requires local git repo with the tag) --branch Find latest successful run on the given branch --event Restrict branch-mode runs to a trigger event (for example push) - --artifact Require an unexpired artifact matching this shell glob - (repeatable; branch mode only) --head-sha Also print the run's head commit SHA (second line) Examples: $0 --tag v13.0.1 NVIDIA/cuda-python $0 --branch main NVIDIA/cuda-python - $0 --branch 12.9.x --artifact 'cuda-bindings-python313-*' NVIDIA/cuda-python $0 --branch main --head-sha NVIDIA/cuda-python "CI" $0 --branch main --event push --head-sha NVIDIA/cuda-python "CI" EOF @@ -44,7 +41,6 @@ MODE="" REF="" HEAD_SHA_FLAG=0 EVENT="" -ARTIFACT_PATTERNS=() while [[ $# -gt 0 ]]; do case "${1}" in @@ -56,9 +52,6 @@ while [[ $# -gt 0 ]]; do [[ $# -ge 2 ]] || usage [[ -n "${MODE}" && "${MODE}" != "branch" ]] && { echo "Error: --tag and --branch are mutually exclusive" >&2; exit 1; } MODE="branch"; REF="${2}"; shift 2 ;; - --artifact) - [[ $# -ge 2 ]] || usage - ARTIFACT_PATTERNS+=("${2}"); shift 2 ;; --event) [[ $# -ge 2 && -n "${2}" ]] || usage EVENT="${2}"; shift 2 ;; @@ -84,11 +77,6 @@ WORKFLOW_NAME="${1:-CI}" if [[ -z "${REPOSITORY}" ]]; then usage; fi -if [[ "${MODE}" != "branch" && ${#ARTIFACT_PATTERNS[@]} -gt 0 ]]; then - echo "Error: --artifact is only supported with --branch" >&2 - exit 1 -fi - if [[ "${MODE}" != "branch" && -n "${EVENT}" ]]; then echo "Error: --event is only supported with --branch" >&2 exit 1 @@ -110,8 +98,8 @@ done # ── Mode: branch ── # # Walk the branch's commit history from HEAD and return the first commit -# whose workflow has a successful run matching any required artifact -# patterns. The one Actions-API filter used is ?head_sha=, which is +# whose workflow has a successful run. The one Actions-API filter used is +# ?head_sha=, which is # not on the list of filters flagged as returning stale/cached slices in # https://github.com/orgs/community/discussions/24626. if [[ "${MODE}" == "branch" ]]; then @@ -153,13 +141,7 @@ if [[ "${MODE}" == "branch" ]]; then exit 1 fi - if [[ ${#ARTIFACT_PATTERNS[@]} -gt 0 ]]; then - echo "Requiring unexpired artifacts matching:" >&2 - printf ' - %s\n' "${ARTIFACT_PATTERNS[@]}" >&2 - fi - - # Walk branch commits newest-first; accept the first commit with a - # successful run that passes the artifact check. + # Walk branch commits newest-first; accept the first successful run. COMMIT_SCAN_LIMIT="${LOOKUP_RUN_ID_COMMIT_SCAN_LIMIT:-200}" RUN_ID="" HEAD_SHA="" @@ -202,37 +184,6 @@ if [[ "${MODE}" == "branch" ]]; then for ((i=0; i&2 - exit 1 - fi - - for pattern in "${ARTIFACT_PATTERNS[@]}"; do - pattern_matched=0 - while IFS= read -r artifact_name; do - # The caller supplies a shell glob; RHS must stay unquoted. - # shellcheck disable=SC2053 - if [[ -n "${artifact_name}" && "${artifact_name}" == ${pattern} ]]; then - pattern_matched=1 - break - fi - done <<< "${ARTIFACT_NAMES}" - if [[ ${pattern_matched} == 0 ]]; then - missing_patterns+=("${pattern}") - fi - done - fi - - if [[ ${#missing_patterns[@]} -gt 0 ]]; then - echo "Skipping run ${candidate_id} (sha ${candidate_sha:0:12}, ${candidate_created_at}); missing unexpired artifact(s): ${missing_patterns[*]}" >&2 - continue - fi RUN_ID="${candidate_id}" HEAD_SHA="${candidate_sha}" @@ -244,7 +195,7 @@ if [[ "${MODE}" == "branch" ]]; then done if [[ -z "${RUN_ID}" ]]; then - echo "Error: No successful '${WORKFLOW_NAME}' run with required artifacts found in the ${commits_scanned} most-recent commits of branch '${REF}'" >&2 + echo "Error: No successful '${WORKFLOW_NAME}' run found in the ${commits_scanned} most-recent commits of branch '${REF}'" >&2 echo "Branch HEAD: ${BRANCH_HEAD_SHA}" >&2 exit 1 fi From 19349103f66b233fbb79d5e9a5d1fc65cd7668e0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 14:12:37 -0700 Subject: [PATCH 10/24] Keep CI source and wheel tests focused on silent failures Signed-off-by: Ralf W. Grosse-Kunstleve --- ci/tools/tests/test_merge_cuda_core_wheels.py | 21 +- ci/tools/tests/test_test_sources.py | 242 ------------------ 2 files changed, 1 insertion(+), 262 deletions(-) diff --git a/ci/tools/tests/test_merge_cuda_core_wheels.py b/ci/tools/tests/test_merge_cuda_core_wheels.py index bdabb168819..b1eab55e291 100644 --- a/ci/tools/tests/test_merge_cuda_core_wheels.py +++ b/ci/tools/tests/test_merge_cuda_core_wheels.py @@ -1,31 +1,12 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -from pathlib import Path from zipfile import ZipFile import pytest from wheel.wheelfile import WheelFile -from ci.tools.merge_cuda_core_wheels import cuda_variant_from_wheel, merge_wheels - - -@pytest.mark.agent_authored(model="gpt-5.6-sol") -@pytest.mark.parametrize( - ("name", "expected"), - [ - ("cuda_core-1.2.3-cp312-cp312-manylinux_x86_64.cu12.whl", "cu12"), - ("cuda_core-2.0.0-cp315-cp315-win_amd64.cu14.whl", "cu14"), - ], -) -def test_cuda_variant_from_wheel(name, expected): - assert cuda_variant_from_wheel(Path(name)) == expected - - -@pytest.mark.agent_authored(model="gpt-5.6-sol") -def test_cuda_variant_from_wheel_rejects_missing_suffix(): - with pytest.raises(ValueError, match=r"does not contain a \.cuN suffix"): - cuda_variant_from_wheel(Path("cuda_core-1.2.3-py3-none-any.whl")) +from ci.tools.merge_cuda_core_wheels import merge_wheels @pytest.mark.agent_authored(model="gpt-6-astra") diff --git a/ci/tools/tests/test_test_sources.py b/ci/tools/tests/test_test_sources.py index e99c0ad4c16..c7d4add8a90 100644 --- a/ci/tools/tests/test_test_sources.py +++ b/ci/tools/tests/test_test_sources.py @@ -12,11 +12,8 @@ import pytest -from ci.tools.bindings_config import load_config - REPO_ROOT = Path(__file__).resolve().parents[3] RUN_TESTS = REPO_ROOT / "ci" / "tools" / "run-tests" -ENV_VARS = REPO_ROOT / "ci" / "tools" / "env-vars" def _write_command(directory: Path, name: str, body: str) -> None: @@ -57,245 +54,6 @@ def _run_tests_env(tmp_path: Path) -> tuple[dict[str, str], Path]: return env, command_log -def _run_env_vars( - tmp_path: Path, - *, - bindings_source: str, - pathfinder_source: str, - cuda_version: str = "13.3.0", - bindings_root: str = "cuda_bindings", -) -> subprocess.CompletedProcess[str]: - for relative in ( - f"{bindings_root}/dist", - f"{bindings_root}/tests/cython", - "cuda_core/dist", - "cuda_core/tests/cython", - "cuda_core/tests/test_binaries", - ): - (tmp_path / relative).mkdir(parents=True, exist_ok=True) - github_env = tmp_path / "github-env" - github_path = tmp_path / "github-path" - env = { - **os.environ, - "BINDINGS_SOURCE": bindings_source, - "CUDA_VER": cuda_version, - "GITHUB_ENV": str(github_env), - "GITHUB_PATH": str(github_path), - "HOST_PLATFORM": "linux-64", - "LOCAL_CTK": "0", - "PATHFINDER_SOURCE": pathfinder_source, - "PY_VER": "3.13", - "SHA": "abcdef", - "SKIP_BINDINGS_TEST_OVERRIDE": "0", - } - if bindings_source == "local": - env["BINDINGS_SOURCE_DIR"] = bindings_root - else: - env["DEFAULT_BINDINGS_SOURCE_DIR"] = bindings_root - return subprocess.run( # noqa: S603 - invokes the repository script under test - [str(ENV_VARS), "test"], - cwd=tmp_path, - env=env, - check=False, - capture_output=True, - text=True, - ) - - -def _read_github_env(path: Path) -> dict[str, str]: - return dict(line.split("=", 1) for line in path.read_text(encoding="utf-8").splitlines()) - - -@pytest.mark.agent_authored(model="gpt-5.6-sol") -def test_env_vars_rejects_unknown_mode() -> None: - result = subprocess.run( # noqa: S603 - invokes the repository script under test - [str(ENV_VARS), "unknown"], - check=False, - capture_output=True, - text=True, - ) - - assert result.returncode == 1 - assert "build mode must be build or test" in result.stderr - - -@pytest.mark.agent_authored(model="gpt-5.6") -def test_build_env_uses_registry_packages_without_generic_bindings_aliases(tmp_path: Path) -> None: - github_env = tmp_path / "github-env" - env = { - **os.environ, - "GITHUB_ENV": str(github_env), - "GITHUB_PATH": str(tmp_path / "github-path"), - "HOST_PLATFORM": "linux-64", - "PY_VER": "3.13", - "SHA": "abcdef0", - } - - result = subprocess.run( # noqa: S603 - invokes the repository script under test - [str(ENV_VARS), "build"], - cwd=REPO_ROOT, - env=env, - check=False, - capture_output=True, - text=True, - ) - - assert result.returncode == 0, result.stderr - values = _read_github_env(github_env) - config = load_config() - for status in ("current", "maintenance"): - package = config.package_for_release_status(status) - prefix = status.upper() - assert values[f"{prefix}_BINDINGS_ROOT"] == package.package_root - assert values[f"{prefix}_CUDA_VERSION"] == package.toolkit_version - assert values[f"{prefix}_CUDA_MAJOR"] == package.cuda_major - assert values[f"{prefix}_CUDA_VARIANT"] == package.cuda_variant - assert values[f"{prefix}_BINDINGS_ARTIFACT_NAME"].endswith("-abcdef0") - - assert ( - not { - "CUDA_BINDINGS_ARTIFACT_BASENAME", - "CUDA_BINDINGS_ARTIFACT_NAME", - "CUDA_BINDINGS_ARTIFACTS_DIR", - "CUDA_BINDINGS_CYTHON_TESTS_DIR", - } - & values.keys() - ) - - -@pytest.mark.parametrize("wheel_count", [0, 2]) -@pytest.mark.agent_authored(model="gpt-5.6") -def test_artifact_pathfinder_requires_exactly_one_wheel(tmp_path: Path, wheel_count: int) -> None: - env, _ = _run_tests_env(tmp_path) - env["PATHFINDER_SOURCE"] = "artifact" - for index in range(wheel_count): - (tmp_path / "cuda_pathfinder" / f"cuda_pathfinder-{index}.whl").touch() - - result = subprocess.run( # noqa: S603 - invokes the repository script under test - [str(RUN_TESTS), "pathfinder"], - cwd=tmp_path, - env=env, - check=False, - capture_output=True, - text=True, - ) - - assert result.returncode != 0 - assert "Expected exactly one cuda-pathfinder wheel" in result.stderr - - -@pytest.mark.agent_authored(model="gpt-5.6") -def test_artifact_pathfinder_installs_the_only_wheel(tmp_path: Path) -> None: - env, command_log = _run_tests_env(tmp_path) - env["PATHFINDER_SOURCE"] = "artifact" - wheel = tmp_path / "cuda_pathfinder" / "cuda_pathfinder-1.0-py3-none-any.whl" - wheel.touch() - - subprocess.run( # noqa: S603 - invokes the repository script under test - [str(RUN_TESTS), "pathfinder"], - cwd=tmp_path, - env=env, - check=True, - capture_output=True, - text=True, - ) - - assert f"pip install ./{wheel.name} --group test" in command_log.read_text(encoding="utf-8") - - -@pytest.mark.agent_authored(model="gpt-5.6") -def test_published_pathfinder_is_limited_to_bindings_release_tests(tmp_path: Path) -> None: - env, _ = _run_tests_env(tmp_path) - env["PATHFINDER_SOURCE"] = "published" - - result = subprocess.run( # noqa: S603 - invokes the repository script under test - [str(RUN_TESTS), "pathfinder"], - cwd=tmp_path, - env=env, - check=False, - capture_output=True, - text=True, - ) - - assert result.returncode != 0 - assert "published for bindings release tests" in result.stderr - - -@pytest.mark.agent_authored(model="gpt-5.6") -def test_published_pathfinder_supports_bindings_release_tests(tmp_path: Path) -> None: - env, command_log = _run_tests_env(tmp_path) - env.update( - CUDA_BINDINGS_ARTIFACTS_DIR=str(tmp_path / "bindings-artifacts"), - CUDA_BINDINGS_ROOT="cuda_bindings", - LOCAL_CTK="1", - PATHFINDER_SOURCE="published", - SANITIZER_CMD="", - SKIP_CYTHON_TEST="1", - ) - (tmp_path / "cuda_bindings").mkdir() - (tmp_path / "cuda_bindings" / "pyproject.toml").write_text("[dependency-groups]\ntest = []\n", encoding="utf-8") - (tmp_path / "bindings-artifacts").mkdir() - (tmp_path / "bindings-artifacts" / "cuda_bindings-13.3.whl").touch() - - subprocess.run( # noqa: S603 - invokes the repository script under test - [str(RUN_TESTS), "bindings"], - cwd=tmp_path, - env=env, - check=True, - capture_output=True, - text=True, - ) - - calls = command_log.read_text(encoding="utf-8").splitlines() - assert calls[0].startswith("pip install cuda-pathfinder --group test") - assert calls[1].startswith("python -m pip install ") - - -@pytest.mark.parametrize( - ("bindings_source", "cuda_version", "bindings_root", "expected_artifact"), - [ - ("local", "13.4.2", "cuda_bindings", "cuda-python-wheel-cuda13.4.2"), - ("local", "12.9.1", "cuda_bindings_12", "cuda-python-wheel-cuda12.9.1"), - ("floor", "13.0.2", "cuda_bindings", None), - ("floor", "12.6.3", "cuda_bindings", None), - ], -) -@pytest.mark.agent_authored(model="gpt-6-astra") -def test_cuda_python_artifact_name_exists_only_for_local_bindings( - tmp_path: Path, - bindings_source: str, - cuda_version: str, - bindings_root: str, - expected_artifact: str | None, -) -> None: - result = _run_env_vars( - tmp_path, - bindings_source=bindings_source, - pathfinder_source="artifact", - cuda_version=cuda_version, - bindings_root=bindings_root, - ) - - assert result.returncode == 0, result.stderr - github_env = _read_github_env(tmp_path / "github-env") - assert { - "CUDA_BINDINGS_ARTIFACT_BASENAME", - "CUDA_BINDINGS_ARTIFACT_NAME", - "CUDA_BINDINGS_ARTIFACTS_DIR", - "CUDA_BINDINGS_CYTHON_TESTS_DIR", - "CUDA_CORE_CYTHON_TEST_ARTIFACT_NAME", - } <= github_env.keys() - assert github_env["BINDINGS_SOURCE"] == bindings_source - assert github_env["CUDA_BINDINGS_ROOT"] == bindings_root - assert github_env["SKIP_CUDA_BINDINGS_TEST"] == ("1" if bindings_source == "floor" else "0") - assert github_env["SKIP_CYTHON_TEST"] == ("1" if bindings_source == "floor" else "0") - assert github_env["CUDA_CORE_CYTHON_TEST_ARTIFACT_NAME"].endswith(f"-cu{cuda_version.split('.')[0]}-tests") - if expected_artifact is None: - assert "CUDA_PYTHON_ARTIFACT_NAME" not in github_env - else: - assert github_env["CUDA_PYTHON_ARTIFACT_NAME"] == expected_artifact - - @pytest.mark.agent_authored(model="gpt-6-astra") @pytest.mark.parametrize(("cuda_version", "wheel_floor"), [("12.6.3", "12.9.9"), ("13.0.2", "13.4.2")]) def test_core_install_uses_wheel_floor_with_older_toolkit(tmp_path: Path, cuda_version: str, wheel_floor: str) -> None: From 74146a00895768521c81dfae74fffc6e19dbe492 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 14:14:59 -0700 Subject: [PATCH 11/24] Keep matrix tooling separate from artifact source checkouts Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/test-wheel-linux.yml | 11 ++++++++++- .github/workflows/test-wheel-windows.yml | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 6e7c23461cc..daf2d099656 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -72,6 +72,15 @@ jobs: with: ref: ${{ inputs.sha || github.sha }} + - name: Checkout CI matrix helper + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # Artifact sources can predate this helper; use the workflow's revision. + ref: ${{ github.sha }} + path: .ci-control + sparse-checkout: ci/tools/select_test_matrix.py + sparse-checkout-cone-mode: false + - name: Validate Test Type run: | if [[ "$BUILD_TYPE" != "pull-request" ]] && [[ "$BUILD_TYPE" != "nightly" ]] && [[ "$BUILD_TYPE" != "branch" ]]; then @@ -94,7 +103,7 @@ jobs: # Keep the caller's free-form jq filter; shared Python code handles # source selection, test suites, runner routing, and coverage checks. MATRIX=$(jq -c '${{ inputs.matrix_filter }}' <<< "$TEST_MATRIX" \ - | python3 -m ci.tools.select_test_matrix --platform linux) + | python3 .ci-control/ci/tools/select_test_matrix.py --platform linux) { echo '### Selected wheel test coverage' diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 25a79e61670..25f87937849 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -70,6 +70,15 @@ jobs: with: ref: ${{ inputs.sha || github.sha }} + - name: Checkout CI matrix helper + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # Artifact sources can predate this helper; use the workflow's revision. + ref: ${{ github.sha }} + path: .ci-control + sparse-checkout: ci/tools/select_test_matrix.py + sparse-checkout-cone-mode: false + - name: Validate Test Type run: | if [[ "$BUILD_TYPE" != "pull-request" ]] && [[ "$BUILD_TYPE" != "nightly" ]] && [[ "$BUILD_TYPE" != "branch" ]]; then @@ -91,7 +100,7 @@ jobs: # Keep the caller's free-form jq filter; shared Python code handles # source selection, test suites, runner routing, and coverage checks. MATRIX=$(jq -c '${{ inputs.matrix_filter }}' <<< "$TEST_MATRIX" \ - | python3 -m ci.tools.select_test_matrix --platform windows) + | python3 .ci-control/ci/tools/select_test_matrix.py --platform windows) { echo '### Selected wheel test coverage' From 0b84c0ef293d3702d0017c53d697ad68b1605acd Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 14:23:23 -0700 Subject: [PATCH 12/24] Align build and test documentation with both bindings roots Signed-off-by: Ralf W. Grosse-Kunstleve --- CONTRIBUTING.md | 28 ++++++------- ci/README.md | 52 +++++++++++++++++++++--- cuda_bindings_12/docs/README.md | 18 ++++++-- cuda_bindings_12/docs/source/install.md | 39 +++++++++++++++--- cuda_bindings_13/docs/README.md | 8 +++- cuda_bindings_13/docs/source/install.rst | 12 ++++-- cuda_bindings_13/docs/source/support.rst | 4 +- cuda_core/docs/source/install.rst | 8 ++-- cuda_python/docs/README.md | 6 ++- 9 files changed, 133 insertions(+), 42 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c47db15541d..31d6406757d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,9 +28,7 @@ reproducers, documentation improvements, and code changes are welcome. - [CI infrastructure overview](#ci-infrastructure-overview) - [CI Pipeline Flow](#ci-pipeline-flow) - [Pipeline Execution Details](#pipeline-execution-details) - - [Branch-specific Artifact Flow](#branch-specific-artifact-flow) - - [Main Branch](#main-branch) - - [Backport Branches](#backport-branches) + - [Source and Artifact Flow](#source-and-artifact-flow) - [Key Infrastructure Details](#key-infrastructure-details) - [Code coverage](#code-coverage) @@ -453,20 +451,20 @@ The CI pipeline diagram is maintained as Mermaid source in [`ci/ci-pipeline.mmd` - **Build Stage**: Different architectures/operating systems (linux-64, linux-aarch64, win-64) are built in parallel across their respective runners - **Test Stage**: Different architectures/operating systems/CUDA versions are tested in parallel; documentation preview is also built in parallel with testing -### Branch-specific Artifact Flow +### Source and Artifact Flow -#### Main Branch -- **Build** → **Test** → **Documentation** → **Potential Release** -- Artifacts stored as `{component}-python{version}-{platform}-{sha}` -- Full test coverage across all platforms and CUDA versions -- **Artifact flow out**: `cuda-pathfinder` artifacts → backport branches +- Pushes to `main` build and test both registered bindings lines and their + dependent packages from the same source revision. +- Pull requests build affected packages and test their consumers. Unchanged + wheels and test artifacts can reuse a successful exact-baseline CI run; + without a complete usable baseline, CI builds and tests everything. +- Bindings tags select their matching bindings root and metapackage, using + published Pathfinder. They do not require a Core release. +- Other branches retain their own release procedure and compatible tooling. + Main-tree CI no longer fetches CUDA 12 bindings artifacts from `12.9.x`. -#### Backport Branches -- **Build** → **Test** → **Backport PR Creation** -- Artifacts used for validation before creating backport pull requests -- Maintains compatibility with older CUDA versions -- **Artifact flow in**: `cuda-pathfinder` artifacts ← main branch -- **Artifact flow out**: older `cuda-bindings` artifacts → main branch +See [Reading CI Jobs and Summaries](ci/README.md#reading-ci-jobs-and-summaries) +for package/major names, local versus published bindings, and reuse details. ### Key Infrastructure Details diff --git a/ci/README.md b/ci/README.md index 47fd99aeda6..2eb304e733b 100644 --- a/ci/README.md +++ b/ci/README.md @@ -222,12 +222,12 @@ committed lockfiles to address the eMudhra/emSign CA incident; no general lockfile refresh was performed. Re-enable maintenance only after a released Pixi passes both upstream issue -reproducers and repeated byte-stable update/lock/check cycles across all six -workspaces: the repository root, `cuda_pathfinder`, `cuda_bindings_13`, `cuda_core`, -`benchmarks/cuda_bindings`, and `benchmarks/cuda_core`. Verify both `pixi update ---no-install` and `pixi lock`, followed by repeated `pixi lock --check` commands, -converge to unchanged lockfile bytes. Then restore strict freshness checks, -scheduled refreshes, and `PIXI_LOCKED=true` in source-build CI. +reproducers and repeated byte-stable update/lock/check cycles across every +workspace reported by `python ci/tools/list_pixi_workspaces.py`, including both +bindings roots. Verify `pixi update --no-install` and `pixi lock`, followed by +repeated `pixi lock --check` commands, converge to unchanged lockfile bytes. +Then restore strict freshness checks, scheduled refreshes, and `PIXI_LOCKED=true` +in source-build CI. ## Repository Customizations @@ -323,3 +323,43 @@ are unreachable from `main`. Keep pushed tags immutable and follow the A toolkit minor rollover does not change the major-specific SCM selectors. Let reachable tags govern preview versions; release tags establish the intended release number. + +## Reading CI Jobs and Summaries + +A name such as `pathfinder+bindings12+core12+meta12 / py3.11, CTK12.9.1 +(wheels; bindings PR)` identifies the enabled test suites, Python version, +and toolkit. `meta12` means the CUDA 12 `cuda-python` metapackage; `core12` +means Core tested with CUDA 12. The corresponding CUDA 13 suites use `13`. +`pathfinder` is shared across majors. + +The two source descriptions answer different questions: + +- `wheels` or `local CTK` describes how the toolkit libraries are supplied. +- `bindings PR` uses the selected root's artifact from this CI run. Unaffected + packages can reuse exact-baseline wheels, so this label alone does not mean + the bindings were rebuilt for the PR. +- `bindings floor` uses the oldest published bindings supported by the Core + wheel for that CUDA major, as declared in its metadata. These rows test Core + or the selected nightly integration against published bindings, without + running the bindings or metapackage suites. + +Open the change-detection job's **CI workplan** summary and expand its JSON. +The component build/test flags identify selected work; the baseline records +the original run and exact source SHA for reusable artifacts. Reuse requires a +successful push CI run for the exact merge-base commit on the PR's base branch, +with the complete required artifact set. If that baseline is unavailable, CI +builds and tests everything. A bindings-only change reuses the unchanged +merged Core wheel while testing Core against the affected bindings major. + +The platform matrix-setup jobs' **Selected wheel test coverage** summaries +show the final suites, toolkit source, bindings root or published floor, and +GPU. Shared infrastructure changes, regular branch runs, and scheduled CI +cover both majors; a bindings tag deliberately selects one bindings/metapackage +line and published Pathfinder. Windows ARM64 omits CUDA 12 because that toolkit +has no Windows ARM64 build. + +`ci/tools/select_test_matrix.py` applies the shared selection and local-wheel +coverage rules after each platform reads its source matrix and applies an +optional jq filter. Matrix data comes from the artifact source revision; the +helper comes from the workflow control revision so older artifact sources do +not need to contain newly introduced CI tooling. diff --git a/cuda_bindings_12/docs/README.md b/cuda_bindings_12/docs/README.md index 8b445f41e65..9465dcedf39 100644 --- a/cuda_bindings_12/docs/README.md +++ b/cuda_bindings_12/docs/README.md @@ -1,10 +1,22 @@ # Build the documentation -1. Install the `cuda-bindings` package of the version that we need to document. +Use a separate environment from CUDA 13 documentation builds: both roots +install the same `cuda-bindings` distribution and `cuda.bindings` namespace. +The aggregate `cuda_python/docs/build_all_docs.sh` script builds the current +CUDA 13 line, so build this maintenance documentation separately. + +1. Install the CUDA 12 `cuda-bindings` build that you need to document. 2. Ensure the version is included in the canonical [`nv-versions.json`](../../cuda_bindings_13/docs/nv-versions.json). 3. Build the docs with `./build_docs.sh`. 4. The html artifacts should be available under both `./build/html/latest` and `./build/html/`. -To publish the docs with the built version, it is important to note that the html files of older versions -should be kept intact, in order for the version selection (through `nv-versions.json`) to work. +The Pixi `docs` environment currently installs a published `12.9.*` package. +To document APIs changed in the checkout, install the matching source build +in your documentation environment before running `./build_docs.sh`. + +`build/html/latest` is for local preview only. Follow the selected branch's +release procedure for publication. Maintenance publication must exclude `latest` +and the shared version-selector files so it cannot overwrite the current-major +site. Keep older versioned pages intact; do not publish the entire local build +directory over the shared site. diff --git a/cuda_bindings_12/docs/source/install.md b/cuda_bindings_12/docs/source/install.md index 728c06729a4..cb80bd52720 100644 --- a/cuda_bindings_12/docs/source/install.md +++ b/cuda_bindings_12/docs/source/install.md @@ -6,14 +6,24 @@ * Linux (x86-64, arm64) and Windows (x86-64) * Python 3.11 - 3.15 -* Driver: Linux (450.80.02 or later) Windows (456.38 or later) -* Optionally, NVRTC, nvJitLink, and NVVM from CUDA Toolkit 12.x +* Driver: Linux (525.60.13 or later), Windows (528.33 or later) +* Optionally, NVRTC, nvJitLink, NVVM, nvFatbin, and cuFile (Linux only) from CUDA Toolkit 12.x + +These driver versions are the CUDA 12 minor-version compatibility baseline in the +[CUDA 12.9 release notes](https://docs.nvidia.com/cuda/archive/12.9.1/cuda-toolkit-release-notes/index.html#cuda-driver). +Features introduced with newer drivers and PTX compilation can require a newer +driver; see the +[minor-version compatibility limitations](https://docs.nvidia.com/deploy/cuda-compatibility/minor-version-compatibility.html#application-considerations-for-minor-version-compatibility). ```{note} The optional CUDA Toolkit components can be installed via PyPI, Conda, OS-specific package managers, or local installers (as described in the CUDA Toolkit [Windows](https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html) and [Linux](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html) Installation Guides). ``` -Starting from v12.8.0, `cuda-python` becomes a meta package which currently depends only on `cuda-bindings`; in the future more sub-packages will be added to `cuda-python`. In the instructions below, we still use `cuda-python` as example to serve existing users, but everything is applicable to `cuda-bindings` as well. +The CUDA 12 releases of `cuda-python` are a metapackage that depends directly +on `cuda-bindings`. The examples below use `cuda-python` for existing users; +install `cuda-bindings` directly if you only need the low-level interfaces. + +Both main-tree CUDA 12 packages require Python 3.11 or newer. ## Installing from PyPI @@ -32,6 +42,8 @@ Where the optional dependencies are: * nvidia-cuda-nvrtc-cu12 (Provides NVRTC shared library) * nvidia-nvjitlink-cu12>=12.3 (Provides nvJitLink shared library) * nvidia-cuda-nvcc-cu12 (Provides NVVM shared library) +* nvidia-nvfatbin-cu12 (Provides nvFatbin shared library) +* nvidia-cufile-cu12 (Provides cuFile shared library, Linux only) ## Installing from Conda @@ -43,6 +55,19 @@ $ conda install -c conda-forge cuda-python=12.9 ## Installing from Source +Start with the repository +[recommended clone](https://github.com/NVIDIA/cuda-python/blob/main/CONTRIBUTING.md#recommended-clone). +On native Windows outside WSL, complete its symlink setup before cloning. +CUDA 12 sources live in `cuda_bindings_12/`; CUDA 13 sources have a separate root. +Both build the same `cuda-bindings` distribution, so use a separate Python +environment for each major. + +Versions are derived from reachable `v12.9.*` tags using `setuptools-scm`. +Historical tags from the old maintenance branch may not be ancestors of `main`. +Before a suitable tag is reachable, follow the +[maintenance preview-build guidance](https://github.com/NVIDIA/cuda-python/blob/main/ci/README.md#cuda-bindings-package-root-registry) +in an independent clone; worktrees share tags with the rest of their repository. + ### Requirements * CUDA Toolkit headers[^1] @@ -67,10 +92,12 @@ Only `cydriver`, `cyruntime` and `cynvrtc` are impacted by the header requiremen ### Editable Install -You can use +From the repository root, run: ```console -$ pip install -v -e . +$ cd cuda_bindings_12 +$ python -m pip install -v -e . ``` -to install the module as editable in your current Python environment (e.g. for testing of porting other libraries to use the binding). +This installs the CUDA 12 bindings as editable in your current Python environment +for development and testing. diff --git a/cuda_bindings_13/docs/README.md b/cuda_bindings_13/docs/README.md index 54d670d0917..c0311407c28 100644 --- a/cuda_bindings_13/docs/README.md +++ b/cuda_bindings_13/docs/README.md @@ -1,11 +1,15 @@ # Build the documentation -1. Install the `cuda-bindings` package of the version that we need to document. +1. Install the CUDA 13 `cuda-bindings` build that you need to document. 2. Ensure the version is included in the [`nv-versions.json`](./nv-versions.json). 3. Build the docs with `./build_docs.sh`. 4. The html artifacts should be available under both `./build/html/latest` and `./build/html/`. -Alternatively, we can build all the docs at once by running [`cuda_python/docs/build_all_docs.sh`](../../cuda_python/docs/build_all_docs.sh). +[`cuda_python/docs/build_all_docs.sh`](../../cuda_python/docs/build_all_docs.sh) +builds this current bindings line together with the metapackage, Core, and +Pathfinder documentation. Build [CUDA 12 maintenance documentation](../../cuda_bindings_12/docs/README.md) +separately in its own environment; both roots install the same `cuda-bindings` +distribution and `cuda.bindings` namespace. To publish the docs with the built version, it is important to note that the html files of older versions should be kept intact, in order for the version selection (through `nv-versions.json`) to work. diff --git a/cuda_bindings_13/docs/source/install.rst b/cuda_bindings_13/docs/source/install.rst index 568af77837b..d5e7dee4839 100644 --- a/cuda_bindings_13/docs/source/install.rst +++ b/cuda_bindings_13/docs/source/install.rst @@ -18,7 +18,11 @@ Runtime Requirements The optional CUDA Toolkit components are now installed via the ``cuda-toolkit`` metapackage from PyPI for improved dependency resolution. Components can also be installed via Conda, OS-specific package managers, or local installers (as described in the CUDA Toolkit `Windows `_ and `Linux `_ Installation Guides). -Starting from v12.8.0, ``cuda-python`` becomes a meta package which currently depends only on ``cuda-bindings``; in the future more sub-packages will be added to ``cuda-python``. In the instructions below, we still use ``cuda-python`` as example to serve existing users, but everything is applicable to ``cuda-bindings`` as well. +The CUDA 13 releases of ``cuda-python`` are a metapackage that installs +``cuda-bindings``, ``cuda-core``, and ``cuda-pathfinder``. The examples below use +``cuda-python`` for existing users; install ``cuda-bindings`` directly if you +only need the low-level interfaces. Its ``all`` extra installs the optional +Toolkit components listed below. Free-threading Build Support @@ -133,8 +137,10 @@ Requirements .. [#] The CUDA Runtime static library (``libcudart_static.a`` on Linux, ``cudart_static.lib`` on Windows) is part of the CUDA Toolkit. If using conda packages, it is contained in the ``cuda-cudart-static`` package. .. [#] The version is derived from git tags via ``setuptools-scm``, so the clone - must include tags reaching back to at least the latest ``v*`` tag. Do not use - ``--depth`` or ``--no-tags``: a clone without reachable tags can build with a + must include a reachable tag matching the release line configured in this + package's ``pyproject.toml`` (currently ``v13.*``). A tag from the other CUDA + major does not determine this package's version. Do not use ``--depth`` or + ``--no-tags``: a clone without matching reachable tags can build with a bogus version such as ``0.1.dev1+g0d22cb444``. See `Cloning the repository `_ for details. diff --git a/cuda_bindings_13/docs/source/support.rst b/cuda_bindings_13/docs/source/support.rst index 665c29c029f..b9617826d5c 100644 --- a/cuda_bindings_13/docs/source/support.rst +++ b/cuda_bindings_13/docs/source/support.rst @@ -12,8 +12,8 @@ The ``cuda.bindings`` module has the following support policy: third number in the version string), however, is reserved to reflect Python-only changes and is out of sync with the Toolkit patch version. 2. The module is actively maintained to support the latest CUDA major version and its prior major - version. For example, as of writing the bindings for CUDA 12 & 13 are maintained. Any fix in the - latest bindings would be backported to the prior major version. + version. Both active source trees live on the main branch so applicable fixes can update the + CUDA 12 and CUDA 13 packages in one change. 3. The module supports `CUDA minor version compatibility`_, meaning that ``cuda.bindings`` 12.x supports any Toolkit 12.y. (Whether or not a binding API would actually correctly function depends on the underlying driver and the Toolkit versions, as described in the compatibility diff --git a/cuda_core/docs/source/install.rst b/cuda_core/docs/source/install.rst index e2d74063140..299c9ebb7be 100644 --- a/cuda_core/docs/source/install.rst +++ b/cuda_core/docs/source/install.rst @@ -101,10 +101,10 @@ obtain the most recent build, use the following commands: $ # Install the downloaded wheel: $ pip install cuda-core-python312-linux-64-*/cuda_core*.whl[cu13] -Replace ``python312`` with your Python version (e.g. ``python310``, ``python311``, -``python313``, ``python314``, ``python314t``). For aarch64, replace ``linux-64`` -with ``linux-aarch64``; for Windows, use ``win-64``. Replace ``cu13`` with -``cu12`` for CUDA 12.x environments. +Replace ``python312`` with your Python version (e.g. ``python311``, +``python313``, ``python314``, ``python314t``, ``python315``, ``python315t``). +For aarch64, replace ``linux-64`` with ``linux-aarch64``; for Windows, use +``win-64``. Replace ``cu13`` with ``cu12`` for CUDA 12.x environments. .. _cuda-core-source-install: diff --git a/cuda_python/docs/README.md b/cuda_python/docs/README.md index 4cf18ee74d0..76b46c7d061 100644 --- a/cuda_python/docs/README.md +++ b/cuda_python/docs/README.md @@ -4,7 +4,11 @@ 2. Build the docs with `./build_docs.sh`. 3. The html artifacts should be available under both `./build/html/latest` and `./build/html/`. -Alternatively, we can build all the docs at once by running [`./build_all_docs.sh`](./build_all_docs.sh). +[`./build_all_docs.sh`](./build_all_docs.sh) builds the metapackage, current +CUDA 13 bindings, Core, and Pathfinder documentation together. It does not +build the [CUDA 12 maintenance documentation](../../cuda_bindings_12/docs/README.md). +Build that separately in its own environment: both bindings roots install the +same `cuda-bindings` distribution and `cuda.bindings` namespace. When building the docs, some (but not all) of the urls can be rendered/examined locally by setting the environment variable `CUDA_PYTHON_DOMAIN` as follows: From 5f89fd0ce0235e2283f4773f15c3bb0d3ba36e92 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 14:32:04 -0700 Subject: [PATCH 13/24] Describe artifact retention as workflow-specific Signed-off-by: Ralf W. Grosse-Kunstleve --- CONTRIBUTING.md | 2 +- ci/ci-pipeline.mmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31d6406757d..0e454b16af3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -470,7 +470,7 @@ for package/major names, local versus published bindings, and reuse details. - **Self-hosted runners**: Used for Linux builds and GPU testing (more resources, faster builds) - **GitHub-hosted runners**: Used for Windows builds and general tasks -- **Artifact retention**: 30 days for GitHub Artifacts (wheels, docs, tests) +- **Artifact retention**: Set per workflow for wheels, documentation, and test results - **Cache retention**: GitHub Cache for build dependencies and environments - **Security**: All commits must be signed, untrusted code blocked - **Parallel execution**: Matrix builds across Python versions and platforms diff --git a/ci/ci-pipeline.mmd b/ci/ci-pipeline.mmd index 6dcce24e4f9..a9c9abab092 100644 --- a/ci/ci-pipeline.mmd +++ b/ci/ci-pipeline.mmd @@ -42,7 +42,7 @@ flowchart TB subgraph ARTIFACTS["ARTIFACT STORAGE"] direction TB subgraph GITHUB_ARTIFACTS["GitHub Artifacts"] - GA1["Wheel files (.whl)
Test artifacts
Documentation
(30-day retention)"] + GA1["Wheel files (.whl)
Test artifacts
Documentation
(Workflow-specific retention)"] end subgraph GITHUB_CACHE["GitHub Cache"] GC1["Mini CTK cache"] From 841f3606ff1b8e27ec4735b42de5b0aef225fcc0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 17:15:58 -0700 Subject: [PATCH 14/24] Prepare maintenance versions before Pixi GPU source tests Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/ci-pixi-source-test.yml | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-pixi-source-test.yml b/.github/workflows/ci-pixi-source-test.yml index ef9a7349f2a..c253032990c 100644 --- a/.github/workflows/ci-pixi-source-test.yml +++ b/.github/workflows/ci-pixi-source-test.yml @@ -355,10 +355,11 @@ jobs: run: | apt-get update # ca-certificates + git for checkout; libgl1/libegl1 for pyglet - # (pulled in by the test env). PyYAML and packaging drive the - # bindings registry resolver; pixi itself is installed by setup-pixi. + # (pulled in by the test env). Python packages support the bindings + # registry and maintenance SCM preparation; setup-pixi installs pixi. apt-get install -y --no-install-recommends \ - ca-certificates git jq libgl1 libegl1 python3 python3-packaging python3-yaml + ca-certificates git jq libgl1 libegl1 python3 \ + python3-packaging python3-setuptools-scm python3-yaml - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -376,22 +377,27 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive - - name: Select CUDA environment + - name: Prepare selected CUDA environment env: REQUESTED_CUDA_ENV: ${{ inputs.cuda-env || '' }} run: | if [[ -n "${REQUESTED_CUDA_ENV}" ]]; then bindings_packages=$(python3 -m ci.tools.bindings_config --package-roots) - if ! jq -e --arg cuda_env "${REQUESTED_CUDA_ENV}" \ - 'any(.[]; .cuda_variant == $cuda_env)' <<< "${bindings_packages}" > /dev/null; then + bindings_package=$(jq -c --arg cuda_env "${REQUESTED_CUDA_ENV}" \ + '.[] | select(.cuda_variant == $cuda_env)' <<< "${bindings_packages}") + if [[ -z "${bindings_package}" ]]; then registered_envs=$(jq -r '[.[].cuda_variant] | join(", ")' <<< "${bindings_packages}") echo "::error::cuda-env must be a registered pixi environment: ${registered_envs}" exit 1 fi - cuda_env="${REQUESTED_CUDA_ENV}" else - current_package=$(python3 -m ci.tools.bindings_config --release-status current) - cuda_env=$(jq -r '.cuda_variant' <<< "${current_package}") + bindings_package=$(python3 -m ci.tools.bindings_config --release-status current) + fi + cuda_env=$(jq -r '.cuda_variant' <<< "${bindings_package}") + if [[ $(jq -r '.release_status' <<< "${bindings_package}") == maintenance ]]; then + bindings_root=$(jq -r '.package_root' <<< "${bindings_package}") + # Match build-smoke's version preparation before any pixi source build. + python3 -m ci.tools.bindings_scm_version --package-root "${bindings_root}" --sha "$GITHUB_SHA" fi echo "CUDA_ENV=${cuda_env}" | tee -a "$GITHUB_ENV" From 1ee03d76b77fd0913afdd4709f2904b017faebef Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 17:16:03 -0700 Subject: [PATCH 15/24] Explain compatible nightly sources and historical release reruns Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/ci-nightly.yml | 7 +++-- ci/README.md | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index f1594951817..05d112975de 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -120,8 +120,11 @@ jobs: snapshot=$(mktemp -d) trap 'rm -rf -- "$snapshot"' EXIT git archive "$HEAD_SHA" | tar -x -C "$snapshot" - BINDINGS_CONFIG=$(python -m ci.tools.bindings_config \ - --config "$snapshot/ci/versions.yml") + if ! BINDINGS_CONFIG=$(python -m ci.tools.bindings_config \ + --config "$snapshot/ci/versions.yml"); then + echo "::error::CI run $RUN_ID ($HEAD_SHA) has no bindings registry compatible with this nightly workflow. Wait for a successful main CI run with the current registry, or dispatch ci-nightly.yml with a compatible run-id. See ci/README.md#nightly-artifact-sources-and-rollout." + exit 1 + fi echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT echo "head_sha=$HEAD_SHA" >> $GITHUB_OUTPUT diff --git a/ci/README.md b/ci/README.md index 2eb304e733b..03fbdb9a05f 100644 --- a/ci/README.md +++ b/ci/README.md @@ -324,6 +324,33 @@ A toolkit minor rollover does not change the major-specific SCM selectors. Let reachable tags govern preview versions; release tags establish the intended release number. +## Nightly Artifact Sources And Rollout + +`ci-nightly.yml` downloads wheels from the latest successful main CI run and +reads the bindings registry from that run's source commit. The source registry +must be compatible with the nightly workflow; the current workflow requires +schema 2. It does not translate an older registry into the new layout. + +After multi-root CI lands on `main`, wait for the first successful, complete +main CI run containing the new registry and both bindings majors before using +it as the nightly artifact source. Until that run succeeds, auto-detection can +still select an older successful run; the nightly job then reports the +incompatible source and stops. PR CI builds and tests everything when it has +no complete, compatible artifact baseline. + +To test the nightly workflow before that main run is available, supply the ID +of a successful CI run built with the compatible registry and required wheel +artifacts, such as a validated PR run: + +```console +gh workflow run ci-nightly.yml --repo NVIDIA/cuda-python --ref main -f 'run-id=' +``` + +The override selects both the wheel artifacts and their source revision. It +does not convert old artifacts or change the default selection for later +nightly runs. Once a compatible main run succeeds, ordinary nightly dispatches +can leave `run-id` empty. + ## Reading CI Jobs and Summaries A name such as `pathfinder+bindings12+core12+meta12 / py3.11, CTK12.9.1 @@ -363,3 +390,30 @@ coverage rules after each platform reads its source matrix and applies an optional jq filter. Matrix data comes from the artifact source revision; the helper comes from the workflow control revision so older artifact sources do not need to contain newly introduced CI tooling. + +## Historical Release And Documentation Reruns + +Current release and documentation workflows read package paths and toolkit +settings from the schema-2 registry committed with the release tag. For tags +that predate that registry, dispatch compatible historical tooling instead. +In particular, CUDA 13.4 releases from `13.4.x` use the release workflow on +that branch, including when rebuilding their documentation. + +For example, validate the existing `v13.4.4` release and rebuild its docs with: + +```console +gh workflow run release.yml --repo NVIDIA/cuda-python --ref 13.4.x \ + -f component=cuda-bindings -f release-action=dry-run \ + -f git-tag=v13.4.4 -f 'backport-git-tag=not planned' +``` + +Here `--ref 13.4.x` selects the workflow and its reusable documentation +workflow; `git-tag` selects the immutable release source. Leave `run-id` empty +to find that tag's successful CI run, or supply its validated artifact run ID +if needed. The artifacts must still exist. A dry run validates artifacts and +builds docs without publishing a release; its documentation is retained as a +workflow artifact. Optionally add `-f dry-run-docs-branch=gh-pages-dry-run` to +deploy the documentation to that preview branch. Run once per component when +validating more than one package; use `component=cuda-python` for the matching +metapackage documentation. Other historical release lines likewise need +tooling compatible with their tagged layout. From 7f116f9949a6158b245aa2c8b41af2b11a886ee2 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 17:17:38 -0700 Subject: [PATCH 16/24] Serialize the matching CUDA 12 global coredump test Signed-off-by: Ralf W. Grosse-Kunstleve --- cuda_bindings_12/tests/test_cuda.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cuda_bindings_12/tests/test_cuda.py b/cuda_bindings_12/tests/test_cuda.py index a03844a2b84..57744c95df8 100644 --- a/cuda_bindings_12/tests/test_cuda.py +++ b/cuda_bindings_12/tests/test_cuda.py @@ -610,6 +610,7 @@ def test_cuda_graphMem_attr(): or not supportsCudaAPI("cuCoredumpGetAttributeGlobal"), reason="Coredump API not present", ) +@pytest.mark.thread_unsafe(reason="coredump settings are process-global") def test_cuda_coredump_attr(): (err,) = cuda.cuInit(0) assert err == cuda.CUresult.CUDA_SUCCESS From 910e2ea2a79c5ca69a5732957b5de44bf607da50 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 26 Oct 2026 17:20:20 -0700 Subject: [PATCH 17/24] Record the CUDA 12 driver documentation correction Signed-off-by: Ralf W. Grosse-Kunstleve --- cuda_bindings_12/docs/source/release/12.X.Y-notes.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cuda_bindings_12/docs/source/release/12.X.Y-notes.rst b/cuda_bindings_12/docs/source/release/12.X.Y-notes.rst index 08a35aaf194..4aaf9485ad6 100644 --- a/cuda_bindings_12/docs/source/release/12.X.Y-notes.rst +++ b/cuda_bindings_12/docs/source/release/12.X.Y-notes.rst @@ -24,3 +24,11 @@ Packaging declarations and includes (``.pxd`` and ``.pxi``), together with required headers, remain available for downstream Cython builds. Source distributions retain the sources, templates, and other inputs needed to build the package. + +Documentation +------------- + +* Corrected the CUDA 12 driver guidance to its existing minor-version + compatibility baseline: Linux 525.60.13 and Windows 528.33. This is a + documentation correction, not a new runtime version check. Newer features + and PTX compilation can still require a newer driver. From 9a4067860d8614b84421002f4fc7fadf45755325 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 29 Oct 2026 20:05:46 -0700 Subject: [PATCH 18/24] Harden CI tooling across docs and source workflows codex on behalf of Ralf: Use verified workflow-owned Python for registry, nightly, coverage and Pixi helpers. Preserve hardened interpreter PATH in early documentation bootstrap shells and keep historical sources separate from control tools. Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/build-docs.yml | 11 +++++---- .github/workflows/ci-nightly.yml | 6 +++-- .github/workflows/ci-pixi-source-test.yml | 28 ++++++++++++++--------- .github/workflows/coverage.yml | 6 ++--- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index ea50308c964..08b61ee9820 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -78,16 +78,19 @@ jobs: ref: ${{ github.sha }} path: .ci-control - - name: Harden pip + - name: Set up Python for CI tools # The release checkout can predate this action; use the workflow revision. - uses: ./.ci-control/.github/actions/harden-pip + uses: ./.ci-control/.github/actions/setup-hardened-python with: - python: python3 + python-version: "3.12" - name: Install CI tool dependencies + # Keep the hardened interpreter ahead of any login-shell activation. + shell: bash --noprofile --norc -euo pipefail {0} run: python3 -m pip install -e ./.ci-control/ci - name: Resolve CUDA bindings package + shell: bash --noprofile --norc -euo pipefail {0} env: BINDINGS_PACKAGE: ${{ inputs.bindings-package }} IS_RELEASE: ${{ inputs.is-release }} @@ -353,7 +356,7 @@ jobs: # interpreter instead of the conda environment used above. - name: Set up Python for link checks if: ${{ !inputs.is-release && startsWith(github.ref_name, 'pull-request/') }} - uses: ./.github/actions/setup-hardened-python + uses: ./.ci-control/.github/actions/setup-hardened-python with: python-version: "3.14" diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index 05d112975de..499b43ca3da 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -81,8 +81,10 @@ jobs: with: fetch-depth: 1 - - name: Harden pip - uses: ./.github/actions/harden-pip + - name: Set up Python + uses: ./.github/actions/setup-hardened-python + with: + python-version: "3.12" - name: Install CI tool dependencies run: python -m pip install -e ./ci diff --git a/.github/workflows/ci-pixi-source-test.yml b/.github/workflows/ci-pixi-source-test.yml index c253032990c..6c4a2ee797a 100644 --- a/.github/workflows/ci-pixi-source-test.yml +++ b/.github/workflows/ci-pixi-source-test.yml @@ -100,10 +100,10 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive - - name: Harden pip - uses: ./.github/actions/harden-pip + - name: Set up Python + uses: ./.github/actions/setup-hardened-python with: - python: python3 + python-version: "3.12" - name: Install CI tool dependencies run: python3 -m pip install -e ./ci @@ -287,10 +287,10 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive - - name: Harden pip - uses: ./.github/actions/harden-pip + - name: Set up Python + uses: ./.github/actions/setup-hardened-python with: - python: python3 + python-version: "3.12" - name: Install CI tool dependencies run: python3 -m pip install -e ./ci @@ -354,12 +354,10 @@ jobs: - name: Install system packages run: | apt-get update - # ca-certificates + git for checkout; libgl1/libegl1 for pyglet - # (pulled in by the test env). Python packages support the bindings - # registry and maintenance SCM preparation; setup-pixi installs pixi. + # ca-certificates + git for checkout; libgl1/libegl1 for pyglet. + # The hardened Python setup below supplies the CI helper interpreter. apt-get install -y --no-install-recommends \ - ca-certificates git jq libgl1 libegl1 python3 \ - python3-packaging python3-setuptools-scm python3-yaml + ca-certificates git jq libgl1 libegl1 - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -377,6 +375,14 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive + - name: Set up Python + uses: ./.github/actions/setup-hardened-python + with: + python-version: "3.12" + + - name: Install CI tool dependencies + run: python3 -m pip install -e ./ci + - name: Prepare selected CUDA environment env: REQUESTED_CUDA_ENV: ${{ inputs.cuda-env || '' }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ccef659efec..ec88bef912e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -25,10 +25,10 @@ jobs: steps: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Harden pip - uses: ./.github/actions/harden-pip + - name: Set up Python + uses: ./.github/actions/setup-hardened-python with: - python: python3 + python-version: "3.12" - name: Install CI tool dependencies run: python3 -m pip install -e ./ci From 40cd48e4536d887512a06a311f93e4444ec82ca2 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 29 Oct 2026 20:12:08 -0700 Subject: [PATCH 19/24] Qualify historical CI sources with current security prerequisites codex on behalf of Ralf: Require checksum-bearing driver matrices for nightly source overrides and current control-workflow security fixes before dispatching historical release tooling. Keep immutable release sources and fail-closed verification unchanged. Signed-off-by: Ralf W. Grosse-Kunstleve --- ci/README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ci/README.md b/ci/README.md index 03fbdb9a05f..1b02b3c4fde 100644 --- a/ci/README.md +++ b/ci/README.md @@ -330,6 +330,11 @@ intended release number. reads the bindings registry from that run's source commit. The source registry must be compatible with the nightly workflow; the current workflow requires schema 2. It does not translate an older registry into the new layout. +Selected rows that install a custom driver must also include its +`DRIVER_SHA256` in the source matrix. Registry compatibility alone does not +make a pre-hardening source compatible: choose a newer successful run rather +than disabling installer verification. Rows using preinstalled drivers do not +need an installer checksum. After multi-root CI lands on `main`, wait for the first successful, complete main CI run containing the new registry and both bindings majors before using @@ -339,8 +344,8 @@ incompatible source and stops. PR CI builds and tests everything when it has no complete, compatible artifact baseline. To test the nightly workflow before that main run is available, supply the ID -of a successful CI run built with the compatible registry and required wheel -artifacts, such as a validated PR run: +of a successful CI run built with the compatible registry, required wheel +artifacts, and driver-checksum metadata, such as a validated PR run: ```console gh workflow run ci-nightly.yml --repo NVIDIA/cuda-python --ref main -f 'run-id=' @@ -399,7 +404,14 @@ that predate that registry, dispatch compatible historical tooling instead. In particular, CUDA 13.4 releases from `13.4.x` use the release workflow on that branch, including when rebuilding their documentation. -For example, validate the existing `v13.4.4` release and rebuild its docs with: +Before dispatching historical tooling, bring its control branch up to date +with the applicable CI security fixes, including a verified patched Python +and current CA/download protections. Source-layout compatibility alone is +insufficient. The example below assumes those safeguards are in place; it +does not require changing the immutable release source. + +After meeting that prerequisite, validate the existing `v13.4.4` release and +rebuild its docs with: ```console gh workflow run release.yml --repo NVIDIA/cuda-python --ref 13.4.x \ From 51e3c5a4ffec35d62cd7b7ee2c8751a779831286 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 1 Nov 2026 14:46:16 -0800 Subject: [PATCH 20/24] Remove command-shaped source tests and unused checkout credentials Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/build-docs.yml | 2 + ci/tools/tests/test_test_sources.py | 98 ----------------------------- 2 files changed, 2 insertions(+), 98 deletions(-) delete mode 100644 ci/tools/tests/test_test_sources.py diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 08b61ee9820..92427d2a902 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -67,6 +67,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 + persist-credentials: false ref: ${{ inputs.is-release && format('refs/tags/{0}', inputs.git-tag) || inputs.git-tag }} # Release sources and workflow tools can come from different commits. @@ -77,6 +78,7 @@ jobs: with: ref: ${{ github.sha }} path: .ci-control + persist-credentials: false - name: Set up Python for CI tools # The release checkout can predate this action; use the workflow revision. diff --git a/ci/tools/tests/test_test_sources.py b/ci/tools/tests/test_test_sources.py deleted file mode 100644 index c7d4add8a90..00000000000 --- a/ci/tools/tests/test_test_sources.py +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# SPDX-License-Identifier: Apache-2.0 - -from __future__ import annotations - -import os -import subprocess -import sys -import zipfile -from pathlib import Path - -import pytest - -REPO_ROOT = Path(__file__).resolve().parents[3] -RUN_TESTS = REPO_ROOT / "ci" / "tools" / "run-tests" - - -def _write_command(directory: Path, name: str, body: str) -> None: - path = directory / name - path.write_text(f"#!/usr/bin/env bash\nset -eu\n{body}\n", encoding="utf-8") - path.chmod(0o755) - - -def _run_tests_env(tmp_path: Path) -> tuple[dict[str, str], Path]: - fake_bin = tmp_path / "bin" - fake_bin.mkdir() - command_log = tmp_path / "commands.log" - _write_command( - fake_bin, - "python", - """ -if [[ "${1:-}" == "-c" ]]; then - exit 1 -fi -if [[ "${1:-}" == "ci/tools/cuda_core_bindings_floor.py" ]]; then - exec "$REAL_PYTHON" "$@" -fi -printf 'python %s\\n' "$*" >> "$COMMAND_LOG" -""".strip(), - ) - _write_command(fake_bin, "pip", 'printf \'pip %s\\n\' "$*" >> "$COMMAND_LOG"') - _write_command(fake_bin, "pytest", ":") - env = { - **os.environ, - "COMMAND_LOG": str(command_log), - "REAL_PYTHON": sys.executable, - "PATH": f"{fake_bin}{os.pathsep}{os.environ['PATH']}", - "CUDA_PATHFINDER_TEST_LOAD_NVIDIA_DYNAMIC_LIB_STRICTNESS": "see_what_works", - "CUDA_PATHFINDER_TEST_FIND_NVIDIA_HEADERS_STRICTNESS": "see_what_works", - "CUDA_PATHFINDER_TEST_FIND_NVIDIA_BITCODE_LIB_STRICTNESS": "see_what_works", - } - (tmp_path / "cuda_pathfinder").mkdir() - return env, command_log - - -@pytest.mark.agent_authored(model="gpt-6-astra") -@pytest.mark.parametrize(("cuda_version", "wheel_floor"), [("12.6.3", "12.9.9"), ("13.0.2", "13.4.2")]) -def test_core_install_uses_wheel_floor_with_older_toolkit(tmp_path: Path, cuda_version: str, wheel_floor: str) -> None: - env, command_log = _run_tests_env(tmp_path) - (tmp_path / "cuda_pathfinder" / "cuda_pathfinder-1.0-py3-none-any.whl").touch() - core_dist = tmp_path / "cuda_core" / "dist" - core_dist.mkdir(parents=True) - wheel = core_dist / "cuda_core-1.3.0-cp313-cp313-linux_x86_64.whl" - major = int(cuda_version.split(".")[0]) - with zipfile.ZipFile(wheel, "w") as archive: - archive.writestr( - "cuda_core-1.3.0.dist-info/METADATA", - f'Requires-Dist: cuda-bindings[all]>={wheel_floor},<{major + 1}; extra == "cu{major}"\n', - ) - tools = tmp_path / "ci" / "tools" - tools.mkdir(parents=True) - floor_script = REPO_ROOT / "ci" / "tools" / "cuda_core_bindings_floor.py" - (tools / floor_script.name).write_text(floor_script.read_text(encoding="utf-8"), encoding="utf-8") - env.update( - BINDINGS_SOURCE="floor", - CUDA_CORE_ARTIFACTS_DIR=str(core_dist), - CUDA_VER=cuda_version, - LOCAL_CTK="0", - PATHFINDER_SOURCE="artifact", - SANITIZER_CMD="", - SKIP_CYTHON_TEST="1", - ) - - result = subprocess.run( # noqa: S603 - repository script with install/test commands replaced by recording stubs - [str(RUN_TESTS), "core"], - cwd=tmp_path, - env=env, - check=False, - capture_output=True, - text=True, - ) - - assert result.returncode == 0, result.stderr - calls = command_log.read_text(encoding="utf-8").splitlines() - assert f"pip install cuda-bindings=={wheel_floor}" in calls - toolkit_minor = ".".join(cuda_version.split(".")[:2]) - assert any(f"cuda-toolkit=={toolkit_minor}.*" in call for call in calls) From 7175c60ec540612acd123b54d8d34e2f3704cf6f Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 1 Nov 2026 19:34:07 -0800 Subject: [PATCH 21/24] Exercise every registered source ABI in scheduled CI Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/ci-nightly.yml | 2 +- .github/workflows/ci-pixi-source-test.yml | 57 +++++++++++++++-------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index 499b43ca3da..4ea28b266a8 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -121,7 +121,7 @@ jobs: git fetch --depth=1 origin "$HEAD_SHA" snapshot=$(mktemp -d) trap 'rm -rf -- "$snapshot"' EXIT - git archive "$HEAD_SHA" | tar -x -C "$snapshot" + git archive "$HEAD_SHA" ci/versions.yml | tar -x -C "$snapshot" if ! BINDINGS_CONFIG=$(python -m ci.tools.bindings_config \ --config "$snapshot/ci/versions.yml"); then echo "::error::CI run $RUN_ID ($HEAD_SHA) has no bindings registry compatible with this nightly workflow. Wait for a successful main CI run with the current registry, or dispatch ci-nightly.yml with a compatible run-id. See ci/README.md#nightly-artifact-sources-and-rollout." diff --git a/.github/workflows/ci-pixi-source-test.yml b/.github/workflows/ci-pixi-source-test.yml index 6c4a2ee797a..ca036557a30 100644 --- a/.github/workflows/ci-pixi-source-test.yml +++ b/.github/workflows/ci-pixi-source-test.yml @@ -270,6 +270,8 @@ jobs: if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository_owner == 'nvidia' }} runs-on: ubuntu-latest timeout-minutes: 60 + outputs: + bindings-packages: ${{ steps.roundtrip.outputs.bindings-packages }} steps: - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -299,6 +301,10 @@ jobs: uses: ./.github/actions/setup-pixi - name: Build current, another ABI, then current again in one checkout + id: roundtrip + env: + EVENT_NAME: ${{ github.event_name }} + REQUESTED_CUDA_ENV: ${{ inputs.cuda-env || '' }} run: | current_package=$(python3 -m ci.tools.bindings_config --release-status current) bindings_packages=$(python3 -m ci.tools.bindings_config --package-roots) @@ -307,6 +313,22 @@ jobs: '[.[] | select(.cuda_variant != $current_env) | .cuda_variant] | first // empty' \ <<< "${bindings_packages}") + # Scheduled source tests cover both registered ABIs; manual runs + # retain a single selected ABI, defaulting to the current package. + if [[ "${EVENT_NAME}" == schedule ]]; then + selected_packages="${bindings_packages}" + else + selected_env="${REQUESTED_CUDA_ENV:-${current_env}}" + selected_packages=$(jq -c --arg cuda_env "${selected_env}" \ + '[.[] | select(.cuda_variant == $cuda_env)]' <<< "${bindings_packages}") + if [[ "${selected_packages}" == '[]' ]]; then + registered_envs=$(jq -r '[.[].cuda_variant] | join(", ")' <<< "${bindings_packages}") + echo "::error::cuda-env must be a registered pixi environment: ${registered_envs}" + exit 1 + fi + fi + echo "bindings-packages=${selected_packages}" >> "$GITHUB_OUTPUT" + for cuda_env in "${current_env}" "${other_env}" "${current_env}"; do echo "::group::${cuda_env}" pixi run --manifest-path cuda_core -e "${cuda_env}" \ @@ -338,8 +360,13 @@ jobs: # ── Nightly: full `pixi run test` on a GPU runner ── full-test: - name: "pixi run test (${{ inputs.cuda-env || 'current' }}, linux-64, GPU)" + name: "pixi run test (${{ matrix.bindings-package.cuda_variant }}, linux-64, GPU)" if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository_owner == 'nvidia' }} + needs: build-identity-roundtrip + strategy: + fail-fast: false + matrix: + bindings-package: ${{ fromJSON(needs.build-identity-roundtrip.outputs.bindings-packages) }} runs-on: "linux-amd64-gpu-l4-latest-1" # same label scheme as test-wheel-linux.yml timeout-minutes: 90 container: @@ -375,6 +402,10 @@ jobs: git submodule sync --recursive git submodule update --init --force --depth=1 --filter=blob:none --recursive + - name: Trust the checkout for container source builds + # checkout's temporary Git configuration does not reach later steps. + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: Set up Python uses: ./.github/actions/setup-hardened-python with: @@ -385,27 +416,15 @@ jobs: - name: Prepare selected CUDA environment env: - REQUESTED_CUDA_ENV: ${{ inputs.cuda-env || '' }} + CUDA_ENV: ${{ matrix.bindings-package.cuda_variant }} + BINDINGS_ROOT: ${{ matrix.bindings-package.package_root }} + RELEASE_STATUS: ${{ matrix.bindings-package.release_status }} run: | - if [[ -n "${REQUESTED_CUDA_ENV}" ]]; then - bindings_packages=$(python3 -m ci.tools.bindings_config --package-roots) - bindings_package=$(jq -c --arg cuda_env "${REQUESTED_CUDA_ENV}" \ - '.[] | select(.cuda_variant == $cuda_env)' <<< "${bindings_packages}") - if [[ -z "${bindings_package}" ]]; then - registered_envs=$(jq -r '[.[].cuda_variant] | join(", ")' <<< "${bindings_packages}") - echo "::error::cuda-env must be a registered pixi environment: ${registered_envs}" - exit 1 - fi - else - bindings_package=$(python3 -m ci.tools.bindings_config --release-status current) - fi - cuda_env=$(jq -r '.cuda_variant' <<< "${bindings_package}") - if [[ $(jq -r '.release_status' <<< "${bindings_package}") == maintenance ]]; then - bindings_root=$(jq -r '.package_root' <<< "${bindings_package}") + if [[ "${RELEASE_STATUS}" == maintenance ]]; then # Match build-smoke's version preparation before any pixi source build. - python3 -m ci.tools.bindings_scm_version --package-root "${bindings_root}" --sha "$GITHUB_SHA" + python3 -m ci.tools.bindings_scm_version --package-root "${BINDINGS_ROOT}" --sha "$GITHUB_SHA" fi - echo "CUDA_ENV=${cuda_env}" | tee -a "$GITHUB_ENV" + echo "CUDA_ENV=${CUDA_ENV}" | tee -a "$GITHUB_ENV" - name: Setup proxy cache uses: nv-gha-runners/setup-proxy-cache@main From 173a59ca1cda698b6ea24fc1bded194cab130cd2 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 3 Nov 2026 21:57:25 -0800 Subject: [PATCH 22/24] ci: address migration review feedback Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/ci-nightly.yml | 2 +- .github/workflows/ci-pixi-source-test.yml | 6 +++--- .github/workflows/coverage.yml | 1 + .github/workflows/test-wheel-linux.yml | 1 + .github/workflows/test-wheel-windows.yml | 1 + ci/tools/env-vars | 2 +- ci/tools/select_test_matrix.py | 4 +++- ci/tools/tests/test_select_test_matrix.py | 21 ++++++++++++++++++++- cuda_bindings_12/docs/source/install.md | 2 +- 9 files changed, 32 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index 4ea28b266a8..475f68d0839 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -84,7 +84,7 @@ jobs: - name: Set up Python uses: ./.github/actions/setup-hardened-python with: - python-version: "3.12" + python-version: "3.11" - name: Install CI tool dependencies run: python -m pip install -e ./ci diff --git a/.github/workflows/ci-pixi-source-test.yml b/.github/workflows/ci-pixi-source-test.yml index ca036557a30..2622b17985e 100644 --- a/.github/workflows/ci-pixi-source-test.yml +++ b/.github/workflows/ci-pixi-source-test.yml @@ -103,7 +103,7 @@ jobs: - name: Set up Python uses: ./.github/actions/setup-hardened-python with: - python-version: "3.12" + python-version: "3.11" - name: Install CI tool dependencies run: python3 -m pip install -e ./ci @@ -292,7 +292,7 @@ jobs: - name: Set up Python uses: ./.github/actions/setup-hardened-python with: - python-version: "3.12" + python-version: "3.11" - name: Install CI tool dependencies run: python3 -m pip install -e ./ci @@ -409,7 +409,7 @@ jobs: - name: Set up Python uses: ./.github/actions/setup-hardened-python with: - python-version: "3.12" + python-version: "3.11" - name: Install CI tool dependencies run: python3 -m pip install -e ./ci diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ec88bef912e..199b458b6c6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -109,6 +109,7 @@ jobs: - name: Set environment variables env: + BUILD_CUDA_VER: ${{ env.CUDA_VER }} BINDINGS_SOURCE: local BINDINGS_SOURCE_DIR: ${{ env.BINDINGS_ROOT }} DEFAULT_BINDINGS_SOURCE_DIR: ${{ env.BINDINGS_ROOT }} diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index daf2d099656..785aa8b97ae 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -190,6 +190,7 @@ jobs: - name: Set environment variables env: + BUILD_CUDA_VER: ${{ matrix.BINDINGS_BUILD_VERSION }} BINDINGS_SOURCE: ${{ matrix.BINDINGS_SOURCE }} BINDINGS_SOURCE_DIR: ${{ matrix.BINDINGS_SOURCE_DIR }} DEFAULT_BINDINGS_SOURCE_DIR: ${{ matrix.DEFAULT_BINDINGS_SOURCE_DIR }} diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 25f87937849..6fbf92bec1d 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -182,6 +182,7 @@ jobs: - name: Set environment variables env: + BUILD_CUDA_VER: ${{ matrix.BINDINGS_BUILD_VERSION }} BINDINGS_SOURCE: ${{ matrix.BINDINGS_SOURCE }} BINDINGS_SOURCE_DIR: ${{ matrix.BINDINGS_SOURCE_DIR }} DEFAULT_BINDINGS_SOURCE_DIR: ${{ matrix.DEFAULT_BINDINGS_SOURCE_DIR }} diff --git a/ci/tools/env-vars b/ci/tools/env-vars index 2d01dd12a39..3f434d122f7 100755 --- a/ci/tools/env-vars +++ b/ci/tools/env-vars @@ -82,7 +82,7 @@ else if [[ "${BINDINGS_SOURCE}" == "local" ]]; then : "${BINDINGS_SOURCE_DIR:?BINDINGS_SOURCE_DIR is required for local bindings}" - BINDINGS_BUILD_CUDA_VER="${CUDA_VER}" + BINDINGS_BUILD_CUDA_VER="${BUILD_CUDA_VER}" CUDA_BINDINGS_ROOT="${BINDINGS_SOURCE_DIR}" elif [[ "${BINDINGS_SOURCE}" == "floor" ]]; then # The matrix selects floor bindings for toolkit rows without an exact diff --git a/ci/tools/select_test_matrix.py b/ci/tools/select_test_matrix.py index e9cf82d243c..6c178fa9ba6 100644 --- a/ci/tools/select_test_matrix.py +++ b/ci/tools/select_test_matrix.py @@ -35,7 +35,8 @@ def select_test_matrix( variant = f"cu{major}" if not enabled.get(variant, False): continue - package = next((package for package in packages if package["toolkit_version"] == source_row["CUDA_VER"]), None) + ctk_target = ".".join(source_row["CUDA_VER"].split(".")[:2]) + package = next((package for package in packages if package["ctk_target"] == ctk_target), None) root = "" if package is None else package["package_root"] suites = [] if test_mode != "standard": @@ -63,6 +64,7 @@ def select_test_matrix( "CUDA_VARIANT": variant, "BINDINGS_SOURCE": "floor" if package is None else "local", "BINDINGS_SOURCE_DIR": root, + "BINDINGS_BUILD_VERSION": "" if package is None else package["toolkit_version"], "DEFAULT_BINDINGS_SOURCE_DIR": current_root, "PATHFINDER_SOURCE": "artifact" if workplan is None else workplan["sources"]["pathfinder"], "SUITES": "+".join(suites), diff --git a/ci/tools/tests/test_select_test_matrix.py b/ci/tools/tests/test_select_test_matrix.py index 7e913fa8a0d..81c47ef2e1c 100644 --- a/ci/tools/tests/test_select_test_matrix.py +++ b/ci/tools/tests/test_select_test_matrix.py @@ -9,10 +9,17 @@ { "package_root": "bindings12", "toolkit_version": "12.9.1", + "ctk_target": "12.9", "cuda_variant": "cu12", "release_status": "maintenance", }, - {"package_root": "bindings13", "toolkit_version": "13.4.2", "cuda_variant": "cu13", "release_status": "current"}, + { + "package_root": "bindings13", + "toolkit_version": "13.4.2", + "ctk_target": "13.4", + "cuda_variant": "cu13", + "release_status": "current", + }, ] @@ -69,3 +76,15 @@ def test_nightly_subsets_may_use_published_floor_bindings(test_mode): matrix = select_test_matrix(rows("12.6.3"), PACKAGES, build_type="nightly", test_mode=test_mode) assert [row["BINDINGS_SOURCE"] for row in matrix["include"]] == ["floor"] + + +@pytest.mark.agent_authored(model="gpt-6") +def test_toolkit_patch_updates_keep_nightly_bindings_coverage(): + packages = [dict(package) for package in PACKAGES] + packages[1]["toolkit_version"] = "13.4.3" + + row = select_test_matrix(rows("13.4.2"), packages, build_type="nightly")["include"][0] + + assert row["BINDINGS_SOURCE"] == "local" + assert row["BINDINGS_BUILD_VERSION"] == "13.4.3" + assert {"bindings13", "meta13"} <= set(row["SUITES"].split("+")) diff --git a/cuda_bindings_12/docs/source/install.md b/cuda_bindings_12/docs/source/install.md index cb80bd52720..3696157ab8c 100644 --- a/cuda_bindings_12/docs/source/install.md +++ b/cuda_bindings_12/docs/source/install.md @@ -62,7 +62,7 @@ CUDA 12 sources live in `cuda_bindings_12/`; CUDA 13 sources have a separate roo Both build the same `cuda-bindings` distribution, so use a separate Python environment for each major. -Versions are derived from reachable `v12.9.*` tags using `setuptools-scm`. +Versions are derived from reachable `v12.*` tags using `setuptools-scm`. Historical tags from the old maintenance branch may not be ancestors of `main`. Before a suitable tag is reachable, follow the [maintenance preview-build guidance](https://github.com/NVIDIA/cuda-python/blob/main/ci/README.md#cuda-bindings-package-root-registry) From e8785471a3d87c9339a1a27e82e3ae0c5475135e Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 3 Nov 2026 22:25:48 -0800 Subject: [PATCH 23/24] docs: align SCM selector and tagged-build guidance Signed-off-by: Ralf W. Grosse-Kunstleve --- CONTRIBUTING.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0e454b16af3..0afd7bbba6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -126,14 +126,16 @@ correct.** Packages select tags for their own release line: | Package | Tag pattern | | --- | --- | -| `cuda-bindings` from `cuda_bindings_12/` | `v12.9.*` (e.g. `v12.9.10`) | -| `cuda-bindings` from `cuda_bindings_13/` | `v13.4.*` (e.g. `v13.4.2`) | -| `cuda-python` | The matching bindings selector, chosen with `CUDA_PYTHON_BUILD_MAJOR=12` or `13` (default) | +| `cuda-bindings` from `cuda_bindings_12/` | `v12.*` (e.g. `v12.9.10`) | +| `cuda-bindings` from `cuda_bindings_13/` | `v13.*` (e.g. `v13.4.2`) | +| `cuda-python` | The matching bindings selector, chosen explicitly with `CUDA_PYTHON_BUILD_MAJOR=12` or `13`, or inferred as described below | | `cuda-core` | `cuda-core-v*` (e.g. `cuda-core-v1.1.0`) | | `cuda-pathfinder` | `cuda-pathfinder-v*` (e.g. `cuda-pathfinder-v1.6.0`) | -Git source builds of `cuda-python` default to CUDA 13. Set -`CUDA_PYTHON_BUILD_MAJOR=12` to select CUDA 12, or `13` explicitly for CUDA 13. +Git source builds of `cuda-python` honor `CUDA_PYTHON_BUILD_MAJOR=12` or `13` +when set. Without it, an exact `v12.*` tag at `HEAD` selects CUDA 12 when no +`v13.*` tag points to that commit. Untagged commits and commits tagged for +both majors default to CUDA 13. The resolved version determines dependencies: CUDA 12 depends on matching bindings, while CUDA 13 also depends on Core and Pathfinder. A standalone sdist retains its version and dependency major without requiring this variable. From 7ab8e15289d3997cfda5630024177020341dca11 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 Nov 2026 09:52:55 -0800 Subject: [PATCH 24/24] ci: retire source bootstrap and preserve floor routing Signed-off-by: Ralf W. Grosse-Kunstleve --- .github/workflows/ci-pixi-source-test.yml | 19 +------------------ .github/workflows/test-wheel-linux.yml | 1 + .github/workflows/test-wheel-windows.yml | 1 + ci/tools/env-vars | 11 ++++++++--- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci-pixi-source-test.yml b/.github/workflows/ci-pixi-source-test.yml index 2622b17985e..1cd22c60d5d 100644 --- a/.github/workflows/ci-pixi-source-test.yml +++ b/.github/workflows/ci-pixi-source-test.yml @@ -211,9 +211,6 @@ jobs: BINDINGS_ROOT=$(jq -r '.package_root' <<< "${bindings_package}") CUDA_ENV=$(jq -r '.cuda_variant' <<< "${bindings_package}") echo "::group::Source-build ${BINDINGS_ROOT} (${CUDA_ENV})" - if [[ $(jq -r '.release_status' <<< "${bindings_package}") == maintenance ]]; then - python3 -m ci.tools.bindings_scm_version --package-root "${BINDINGS_ROOT}" --sha "$GITHUB_SHA" - fi # Avoid carrying Cython outputs between bindings packages or CUDA ABIs. rm -rf cuda_core/build/cython cuda_core/cython_debug find cuda_core/cuda/core -type f -name '*.so' -delete @@ -382,7 +379,7 @@ jobs: run: | apt-get update # ca-certificates + git for checkout; libgl1/libegl1 for pyglet. - # The hardened Python setup below supplies the CI helper interpreter. + # Pixi supplies the package build and test environments. apt-get install -y --no-install-recommends \ ca-certificates git jq libgl1 libegl1 @@ -406,24 +403,10 @@ jobs: # checkout's temporary Git configuration does not reach later steps. run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - - name: Set up Python - uses: ./.github/actions/setup-hardened-python - with: - python-version: "3.11" - - - name: Install CI tool dependencies - run: python3 -m pip install -e ./ci - - name: Prepare selected CUDA environment env: CUDA_ENV: ${{ matrix.bindings-package.cuda_variant }} - BINDINGS_ROOT: ${{ matrix.bindings-package.package_root }} - RELEASE_STATUS: ${{ matrix.bindings-package.release_status }} run: | - if [[ "${RELEASE_STATUS}" == maintenance ]]; then - # Match build-smoke's version preparation before any pixi source build. - python3 -m ci.tools.bindings_scm_version --package-root "${BINDINGS_ROOT}" --sha "$GITHUB_SHA" - fi echo "CUDA_ENV=${CUDA_ENV}" | tee -a "$GITHUB_ENV" - name: Setup proxy cache diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 785aa8b97ae..6a4475848e8 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -116,6 +116,7 @@ jobs: test: env: + BINDINGS_CONFIG: ${{ inputs.bindings-config }} TEST_PATHFINDER: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.pathfinder.needs_test }} TEST_BINDINGS: ${{ inputs.workplan == '' || (matrix.BINDINGS_SOURCE == 'local' && fromJSON(inputs.workplan).modules.bindings.package_roots[matrix.BINDINGS_SOURCE_DIR].needs_test) }} TEST_CORE: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.core.cuda_majors[matrix.CUDA_VARIANT].needs_test }} diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 6fbf92bec1d..fbf9c17815b 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -113,6 +113,7 @@ jobs: test: env: + BINDINGS_CONFIG: ${{ inputs.bindings-config }} TEST_PATHFINDER: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.pathfinder.needs_test }} TEST_BINDINGS: ${{ inputs.workplan == '' || (matrix.BINDINGS_SOURCE == 'local' && fromJSON(inputs.workplan).modules.bindings.package_roots[matrix.BINDINGS_SOURCE_DIR].needs_test) }} TEST_CORE: ${{ inputs.workplan == '' || fromJSON(inputs.workplan).modules.core.cuda_majors[matrix.CUDA_VARIANT].needs_test }} diff --git a/ci/tools/env-vars b/ci/tools/env-vars index 3f434d122f7..b036c95c620 100755 --- a/ci/tools/env-vars +++ b/ci/tools/env-vars @@ -64,6 +64,9 @@ if [[ "${BUILD_MODE}" == "build" ]]; then --host-platform "$HOST_PLATFORM" --sha "$SHA" else + CURRENT_BUILD_CUDA_VER="$(jq -r '.package_roots[] | select(.release_status == "current") | .toolkit_version' <<< "${BINDINGS_CONFIG}")" + BUILD_CUDA_MAJOR="${CURRENT_BUILD_CUDA_VER%%.*}" + BUILD_CUDA_MINOR="$(cut -d '.' -f 2 <<< "${CURRENT_BUILD_CUDA_VER}")" TEST_CUDA_MAJOR="$(cut -d '.' -f 1 <<< "${CUDA_VER}")" # BINDINGS_SOURCE controls which cuda-bindings to install at test time: @@ -89,6 +92,7 @@ else # registry match. Core declares the supported minimum for each major. : "${DEFAULT_BINDINGS_SOURCE_DIR:?DEFAULT_BINDINGS_SOURCE_DIR is required for floor bindings}" CUDA_BINDINGS_ROOT="${DEFAULT_BINDINGS_SOURCE_DIR}" + BINDINGS_BUILD_CUDA_VER="${CURRENT_BUILD_CUDA_VER}" SKIP_CUDA_BINDINGS_TEST=1 SKIP_CYTHON_TEST=1 else @@ -108,9 +112,8 @@ else CUDA_BINDINGS_CYTHON_TESTS_DIR="$(realpath "${REPO_DIR}/${CUDA_BINDINGS_ROOT}/tests/cython")" CUDA_PYTHON_ARTIFACT_NAME="cuda-python-wheel-cuda${BINDINGS_BUILD_CUDA_VER}" else - # These are unused for floor bindings, but deterministic paths make - # environment dumps and ad-hoc invocations easier to understand. - CUDA_BINDINGS_ARTIFACT_BASENAME="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda${CUDA_VER}-${HOST_PLATFORM}" + # Core can use the current local wheel during a toolkit minor rollover. + CUDA_BINDINGS_ARTIFACT_BASENAME="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda${BINDINGS_BUILD_CUDA_VER}-${HOST_PLATFORM}" CUDA_BINDINGS_ARTIFACTS_DIR="$(realpath "${REPO_DIR}/${CUDA_BINDINGS_ROOT}/dist")" CUDA_BINDINGS_CYTHON_TESTS_DIR="$(realpath "${REPO_DIR}/${CUDA_BINDINGS_ROOT}/tests/cython")" fi @@ -136,6 +139,8 @@ else echo "SKIP_CYTHON_TEST=${SKIP_CYTHON_TEST}" echo "TEST_CUDA_MAJOR=${TEST_CUDA_MAJOR}" echo "TEST_CUDA_MINOR=${TEST_CUDA_MINOR}" + echo "BUILD_CUDA_MAJOR=${BUILD_CUDA_MAJOR}" + echo "BUILD_CUDA_MINOR=${BUILD_CUDA_MINOR}" echo "CUDA_BINDINGS_ARTIFACT_BASENAME=${CUDA_BINDINGS_ARTIFACT_BASENAME}" echo "CUDA_BINDINGS_ARTIFACT_NAME=${CUDA_BINDINGS_ARTIFACT_BASENAME}-${SHA}" echo "CUDA_BINDINGS_ARTIFACTS_DIR=${CUDA_BINDINGS_ARTIFACTS_DIR}"