From 9d65e5522ae574f9ed737f068c6289489dea7de1 Mon Sep 17 00:00:00 2001 From: Andy Jost Date: Thu, 18 Dec 2025 11:05:39 -0800 Subject: [PATCH] Add CUDA version compatibility check Warn when cuda-bindings was compiled against a newer CUDA major version than the installed driver supports. This helps users understand why certain features may not work correctly. The check runs once after cuInit and can be suppressed via CUDA_PYTHON_DISABLE_VERSION_CHECK=1. --- .github/workflows/backport.yml | 4 +- .github/workflows/bandit.yml | 6 +- .github/workflows/build-docs.yml | 14 +-- .github/workflows/build-wheel.yml | 18 +-- .github/workflows/ci.yml | 4 +- .github/workflows/cleanup-pr-previews.yml | 2 +- .github/workflows/codeql.yml | 6 +- .github/workflows/coverage.yml | 8 +- .github/workflows/release-upload.yml | 2 +- .github/workflows/release.yml | 6 +- .github/workflows/test-wheel-linux.yml | 18 +-- .github/workflows/test-wheel-windows.yml | 18 +-- cuda_bindings/cuda/bindings/utils/__init__.py | 1 + .../cuda/bindings/utils/_version_check.py | 75 ++++++++++++ .../docs/source/environment_variables.rst | 2 + cuda_bindings/tests/test_version_check.py | 113 ++++++++++++++++++ cuda_core/cuda/core/_device.pyx | 8 ++ cuda_core/pixi.lock | 12 +- pixi.lock | 21 ++++ pixi.toml | 50 ++++++++ 20 files changed, 329 insertions(+), 59 deletions(-) create mode 100644 cuda_bindings/cuda/bindings/utils/_version_check.py create mode 100644 cuda_bindings/tests/test_version_check.py create mode 100644 pixi.lock create mode 100644 pixi.toml diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 83413bf762..c06ac0d120 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -23,7 +23,7 @@ jobs: }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Load branch name id: get-branch @@ -32,7 +32,7 @@ jobs: echo "OLD_BRANCH=${OLD_BRANCH}" >> $GITHUB_ENV - name: Create backport pull requests - uses: korthout/backport-action@d07416681cab29bf2661702f925f020aaa962997 # v3.4.1 + uses: korthout/backport-action@c656f5d5851037b2b38fb5db2691a03fa229e3b2 # v4.0.1 with: copy_assignees: true copy_labels_pattern: true diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml index 93cb8c7514..c9fee3938a 100644 --- a/.github/workflows/bandit.yml +++ b/.github/workflows/bandit.yml @@ -20,10 +20,10 @@ jobs: security-events: write steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install uv - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 + uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 with: enable-cache: false @@ -42,6 +42,6 @@ jobs: with: args: "check --select S --ignore ${{ steps.ignore-codes.outputs.codes }} --output-format sarif --output-file results.sarif" - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: results.sarif diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 06e28fd1bf..f2d8d4135e 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -48,7 +48,7 @@ jobs: shell: bash -el {0} steps: - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 ref: ${{ inputs.git-tag }} @@ -103,7 +103,7 @@ jobs: echo "CUDA_BINDINGS_ARTIFACTS_DIR=$(realpath "$REPO_DIR/cuda_bindings/dist")" >> $GITHUB_ENV - name: Download cuda-python build artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: cuda-python-wheel path: . @@ -116,7 +116,7 @@ jobs: ls -lahR . - name: Download cuda-pathfinder build artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: cuda-pathfinder-wheel path: ./cuda_pathfinder @@ -130,14 +130,14 @@ jobs: - name: Download cuda.bindings build artifacts if: ${{ !inputs.is-release }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} path: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }} - name: Download cuda.bindings build artifacts if: ${{ inputs.is-release }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: pattern: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} merge-multiple: true @@ -152,14 +152,14 @@ jobs: - name: Download cuda.core build artifacts if: ${{ !inputs.is-release }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_CORE_ARTIFACT_NAME }} path: ${{ env.CUDA_CORE_ARTIFACTS_DIR }} - name: Download cuda.core build artifacts if: ${{ inputs.is-release }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: pattern: ${{ env.CUDA_CORE_ARTIFACT_NAME }} merge-multiple: true diff --git a/.github/workflows/build-wheel.yml b/.github/workflows/build-wheel.yml index 49f999b2fb..823849be02 100644 --- a/.github/workflows/build-wheel.yml +++ b/.github/workflows/build-wheel.yml @@ -40,7 +40,7 @@ jobs: (inputs.host-platform == 'win-64' && 'windows-2022') }} steps: - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 @@ -65,7 +65,7 @@ jobs: - name: Set up Python id: setup-python1 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: # WAR: setup-python is not relocatable, and cibuildwheel hard-wires to 3.12... # see https://github.com/actions/setup-python/issues/871 @@ -134,7 +134,7 @@ jobs: - name: Upload cuda.pathfinder build artifacts if: ${{ strategy.job-index == 0 && inputs.host-platform == 'linux-64' }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: cuda-pathfinder-wheel path: cuda_pathfinder/*.whl @@ -195,7 +195,7 @@ jobs: twine check --strict ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}/*.whl - name: Upload cuda.bindings build artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} path: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}/*.whl @@ -282,7 +282,7 @@ jobs: - name: Upload cuda-python build artifacts if: ${{ strategy.job-index == 0 && inputs.host-platform == 'linux-64' }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: cuda-python-wheel path: cuda_python/*.whl @@ -290,7 +290,7 @@ jobs: - name: Set up Python id: setup-python2 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.python-version }} @@ -320,7 +320,7 @@ jobs: popd - name: Upload cuda.bindings Cython tests - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }}-tests path: ${{ env.CUDA_BINDINGS_CYTHON_TESTS_DIR }}/test_*${{ env.PY_EXT_SUFFIX }} @@ -334,7 +334,7 @@ jobs: popd - name: Upload cuda.core Cython tests - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-tests path: ${{ env.CUDA_CORE_CYTHON_TESTS_DIR }}/test_*${{ env.PY_EXT_SUFFIX }} @@ -453,7 +453,7 @@ jobs: twine check --strict ${{ env.CUDA_CORE_ARTIFACTS_DIR }}/*.whl - name: Upload cuda.core build artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ env.CUDA_CORE_ARTIFACT_NAME }} path: ${{ env.CUDA_CORE_ARTIFACTS_DIR }}/*.whl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8733498d91..96d78b945f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: CUDA_PREV_BUILD_VER: ${{ steps.get-vars.outputs.cuda_prev_build_ver }} steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 - name: Get CUDA build versions @@ -42,7 +42,7 @@ jobs: skip: ${{ steps.get-should-skip.outputs.skip }} steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Compute whether to skip builds and tests id: get-should-skip env: diff --git a/.github/workflows/cleanup-pr-previews.yml b/.github/workflows/cleanup-pr-previews.yml index 2207b87490..872b2fe37d 100644 --- a/.github/workflows/cleanup-pr-previews.yml +++ b/.github/workflows/cleanup-pr-previews.yml @@ -28,7 +28,7 @@ jobs: if: github.repository_owner == 'NVIDIA' steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v6.0.1 with: # Fetch all history and branches for worktree operations fetch-depth: 0 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3926e26887..a79182b5ef 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,16 +28,16 @@ jobs: build-mode: none steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d # v3.30.0 + uses: github/codeql-action/init@f47c8e6a9bd05ef3ee422fc8d8663be7fe4bdc61 # v3.31.8 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} queries: security-extended - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d # v3.30.0 + uses: github/codeql-action/analyze@f47c8e6a9bd05ef3ee422fc8d8663be7fe4bdc61 # v3.31.8 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5aad59a538..c7f3162ecd 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -49,7 +49,7 @@ jobs: apt-get install -y git - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install dependencies uses: ./.github/actions/install_unix_deps @@ -75,7 +75,7 @@ jobs: echo "CUDA_PYTHON_COVERAGE=1" >> $GITHUB_ENV - name: Set up Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ env.PY_VER }} env: @@ -145,13 +145,13 @@ jobs: mv htmlcov $REPO_ROOT/docs/coverage - name: Archive code coverage results - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: coverage path: docs/coverage/ - name: Deploy to gh-pages - uses: JamesIves/github-pages-deploy-action@4a3abc783e1a24aeb44c16e869ad83caf6b4cc23 # v4.7.4 + uses: JamesIves/github-pages-deploy-action@9d877eea73427180ae43cf98e8914934fe157a1a # v4.7.6 with: git-config-name: cuda-python-bot git-config-email: cuda-python-bot@users.noreply.github.com diff --git a/.github/workflows/release-upload.yml b/.github/workflows/release-upload.yml index 8ae08c5026..ab69a82e7f 100644 --- a/.github/workflows/release-upload.yml +++ b/.github/workflows/release-upload.yml @@ -41,7 +41,7 @@ jobs: ARCHIVE_NAME: ${{ github.event.repository.name }}-${{ inputs.git-tag }} steps: - name: Checkout Source - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 ref: ${{ inputs.git-tag }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d8201810c..530372240a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: run-id: ${{ steps.lookup-run-id.outputs.run-id }} steps: - name: Checkout Source - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: # fetch-depth: 0 is required so the lookup-run-id script can access all git tags fetch-depth: 0 @@ -74,7 +74,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Source - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 @@ -155,7 +155,7 @@ jobs: id-token: write steps: - name: Checkout Source - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Download component wheels env: diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 75a0c4193b..66e5a73508 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -36,7 +36,7 @@ jobs: OLD_BRANCH: ${{ steps.compute-matrix.outputs.OLD_BRANCH }} steps: - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Validate Test Type run: | @@ -93,7 +93,7 @@ jobs: run: nvidia-smi - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup proxy cache uses: nv-gha-runners/setup-proxy-cache@main @@ -118,21 +118,21 @@ jobs: run: ./ci/tools/env-vars test - name: Download cuda-pathfinder build artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: cuda-pathfinder-wheel path: ./cuda_pathfinder - name: Download cuda-python build artifacts if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0'}} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: cuda-python-wheel path: . - name: Download cuda.bindings build artifacts if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0'}} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} path: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }} @@ -184,7 +184,7 @@ jobs: - name: Download cuda.bindings Cython tests if: ${{ env.SKIP_CYTHON_TEST == '0' }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }}-tests path: ${{ env.CUDA_BINDINGS_CYTHON_TESTS_DIR }} @@ -196,7 +196,7 @@ jobs: ls -lahR $CUDA_BINDINGS_CYTHON_TESTS_DIR - name: Download cuda.core build artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_CORE_ARTIFACT_NAME }} path: ${{ env.CUDA_CORE_ARTIFACTS_DIR }} @@ -208,7 +208,7 @@ jobs: - name: Download cuda.core Cython tests if: ${{ env.SKIP_CYTHON_TEST == '0' }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-tests path: ${{ env.CUDA_CORE_CYTHON_TESTS_DIR }} @@ -220,7 +220,7 @@ jobs: ls -lahR $CUDA_CORE_CYTHON_TESTS_DIR - name: Set up Python ${{ matrix.PY_VER }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.PY_VER }} env: diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index b98e9ba2d7..e9c20a625c 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -33,7 +33,7 @@ jobs: MATRIX: ${{ steps.compute-matrix.outputs.MATRIX }} steps: - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Validate Test Type run: | @@ -69,7 +69,7 @@ jobs: runs-on: "windows-${{ matrix.ARCH }}-gpu-${{ matrix.GPU }}-${{ matrix.DRIVER }}-1" steps: - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup proxy cache uses: nv-gha-runners/setup-proxy-cache@main @@ -106,21 +106,21 @@ jobs: run: ./ci/tools/env-vars test - name: Download cuda-pathfinder build artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: cuda-pathfinder-wheel path: ./cuda_pathfinder - name: Download cuda-python build artifacts if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0'}} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: cuda-python-wheel path: . - name: Download cuda.bindings build artifacts if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0'}} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }} path: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }} @@ -163,7 +163,7 @@ jobs: - name: Download cuda.bindings Cython tests if: ${{ env.SKIP_CYTHON_TEST == '0' }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_BINDINGS_ARTIFACT_NAME }}-tests path: ${{ env.CUDA_BINDINGS_CYTHON_TESTS_DIR }} @@ -175,7 +175,7 @@ jobs: Get-ChildItem -Recurse -Force $env:CUDA_BINDINGS_CYTHON_TESTS_DIR | Select-Object Mode, LastWriteTime, Length, FullName - name: Download cuda.core build artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_CORE_ARTIFACT_NAME }} path: ${{ env.CUDA_CORE_ARTIFACTS_DIR }} @@ -187,7 +187,7 @@ jobs: - name: Download cuda.core Cython tests if: ${{ env.SKIP_CYTHON_TEST == '0' }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-tests path: ${{ env.CUDA_CORE_CYTHON_TESTS_DIR }} @@ -199,7 +199,7 @@ jobs: Get-ChildItem -Recurse -Force $env:CUDA_CORE_CYTHON_TESTS_DIR | Select-Object Mode, LastWriteTime, Length, FullName - name: Set up Python ${{ matrix.PY_VER }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.PY_VER }} diff --git a/cuda_bindings/cuda/bindings/utils/__init__.py b/cuda_bindings/cuda/bindings/utils/__init__.py index a2dfe7ce8b..e53bdf68de 100644 --- a/cuda_bindings/cuda/bindings/utils/__init__.py +++ b/cuda_bindings/cuda/bindings/utils/__init__.py @@ -3,6 +3,7 @@ from typing import Any, Callable from ._ptx_utils import get_minimal_required_cuda_ver_from_ptx_ver, get_ptx_ver +from ._version_check import check_cuda_version_compatibility _handle_getters: dict[type, Callable[[Any], int]] = {} diff --git a/cuda_bindings/cuda/bindings/utils/_version_check.py b/cuda_bindings/cuda/bindings/utils/_version_check.py new file mode 100644 index 0000000000..44c56c6e1d --- /dev/null +++ b/cuda_bindings/cuda/bindings/utils/_version_check.py @@ -0,0 +1,75 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +import os +import warnings + +# Track whether we've already checked version compatibility +_version_compatibility_checked = False + + +def check_cuda_version_compatibility(): + """Check if the CUDA driver version is compatible with cuda-bindings compile-time version. + + This function compares the CUDA version that cuda-bindings was compiled against + with the CUDA version supported by the installed driver. If the compile-time + major version is greater than the driver's major version, a warning is issued. + + The check runs only once per process. Subsequent calls are no-ops. + + The warning can be suppressed by setting the environment variable + ``CUDA_PYTHON_DISABLE_VERSION_CHECK=1``. + + Examples + -------- + >>> from cuda.bindings.utils import check_cuda_version_compatibility + >>> check_cuda_version_compatibility() # Issues warning if version mismatch + """ + global _version_compatibility_checked + if _version_compatibility_checked: + return + _version_compatibility_checked = True + + # Allow users to suppress the warning + if os.environ.get("CUDA_PYTHON_DISABLE_VERSION_CHECK"): + return + + # Import here to avoid circular imports and allow lazy loading + from cuda.bindings import driver + + # Get compile-time CUDA version from cuda-bindings + try: + compile_version = driver.CUDA_VERSION # e.g., 13010 + except AttributeError: + # Older cuda-bindings may not expose CUDA_VERSION + return + + # Get runtime driver version + err, runtime_version = driver.cuDriverGetVersion() + if err != driver.CUresult.CUDA_SUCCESS: + return # Can't check, skip silently + + compile_major = compile_version // 1000 + runtime_major = runtime_version // 1000 + + if compile_major > runtime_major: + compile_minor = (compile_version % 1000) // 10 + runtime_minor = (runtime_version % 1000) // 10 + warnings.warn( + f"cuda-bindings was built against CUDA {compile_major}.{compile_minor}, " + f"but the installed driver only supports CUDA {runtime_major}.{runtime_minor}. " + f"Some features may not work correctly. Consider updating your NVIDIA driver. " + f"Set CUDA_PYTHON_DISABLE_VERSION_CHECK=1 to suppress this warning.", + UserWarning, + stacklevel=3, + ) + + +def _reset_version_compatibility_check(): + """Reset the version compatibility check flag for testing purposes. + + This function is intended for use in tests to allow multiple test runs + to check the warning behavior. + """ + global _version_compatibility_checked + _version_compatibility_checked = False diff --git a/cuda_bindings/docs/source/environment_variables.rst b/cuda_bindings/docs/source/environment_variables.rst index a212bfe764..e3a99ee4cc 100644 --- a/cuda_bindings/docs/source/environment_variables.rst +++ b/cuda_bindings/docs/source/environment_variables.rst @@ -9,6 +9,8 @@ Runtime Environment Variables - ``CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM`` : When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See `Stream Synchronization Behavior `_ for an explanation of the legacy and per-thread default streams. +- ``CUDA_PYTHON_DISABLE_VERSION_CHECK`` : When set to 1, suppresses the warning that is issued when ``cuda.core`` detects that ``cuda-bindings`` was compiled against a newer CUDA major version than the installed driver supports. This warning helps identify version mismatches that may cause features to not work correctly. + Build-Time Environment Variables -------------------------------- diff --git a/cuda_bindings/tests/test_version_check.py b/cuda_bindings/tests/test_version_check.py new file mode 100644 index 0000000000..7eceda4853 --- /dev/null +++ b/cuda_bindings/tests/test_version_check.py @@ -0,0 +1,113 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +import os +import warnings +from unittest import mock + +from cuda.bindings import driver +from cuda.bindings.utils import check_cuda_version_compatibility +from cuda.bindings.utils._version_check import _reset_version_compatibility_check + + +class TestVersionCompatibilityCheck: + """Tests for CUDA version compatibility check function.""" + + def setup_method(self): + """Reset the version compatibility check flag before each test.""" + _reset_version_compatibility_check() + + def teardown_method(self): + """Reset the version compatibility check flag after each test.""" + _reset_version_compatibility_check() + + def test_no_warning_when_driver_newer(self): + """No warning should be issued when driver version >= compile version.""" + # Mock compile version 12.9 and driver version 13.0 + with ( + mock.patch.object(driver, "CUDA_VERSION", 12090), + mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 13000)), + warnings.catch_warnings(record=True) as w, + ): + warnings.simplefilter("always") + check_cuda_version_compatibility() + assert len(w) == 0 + + def test_no_warning_when_same_major_version(self): + """No warning should be issued when major versions match.""" + # Mock compile version 12.9 and driver version 12.8 + with ( + mock.patch.object(driver, "CUDA_VERSION", 12090), + mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), + warnings.catch_warnings(record=True) as w, + ): + warnings.simplefilter("always") + check_cuda_version_compatibility() + assert len(w) == 0 + + def test_warning_when_compile_major_newer(self): + """Warning should be issued when compile major version > driver major version.""" + # Mock compile version 13.0 and driver version 12.8 + with ( + mock.patch.object(driver, "CUDA_VERSION", 13000), + mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), + warnings.catch_warnings(record=True) as w, + ): + warnings.simplefilter("always") + check_cuda_version_compatibility() + assert len(w) == 1 + assert issubclass(w[0].category, UserWarning) + assert "cuda-bindings was built against CUDA 13.0" in str(w[0].message) + assert "driver only supports CUDA 12.8" in str(w[0].message) + + def test_warning_only_issued_once(self): + """Warning should only be issued once per process.""" + with ( + mock.patch.object(driver, "CUDA_VERSION", 13000), + mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), + warnings.catch_warnings(record=True) as w, + ): + warnings.simplefilter("always") + check_cuda_version_compatibility() + check_cuda_version_compatibility() + check_cuda_version_compatibility() + # Only one warning despite multiple calls + assert len(w) == 1 + + def test_warning_suppressed_by_env_var(self): + """Warning should be suppressed when CUDA_PYTHON_DISABLE_VERSION_CHECK is set.""" + with ( + mock.patch.object(driver, "CUDA_VERSION", 13000), + mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), + mock.patch.dict(os.environ, {"CUDA_PYTHON_DISABLE_VERSION_CHECK": "1"}), + warnings.catch_warnings(record=True) as w, + ): + warnings.simplefilter("always") + check_cuda_version_compatibility() + assert len(w) == 0 + + def test_silent_when_driver_version_fails(self): + """Should silently skip if cuDriverGetVersion fails.""" + with ( + mock.patch.object(driver, "CUDA_VERSION", 13000), + mock.patch.object( + driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_ERROR_NOT_INITIALIZED, 0) + ), + warnings.catch_warnings(record=True) as w, + ): + warnings.simplefilter("always") + check_cuda_version_compatibility() + assert len(w) == 0 + + def test_silent_when_cuda_version_not_available(self): + """Should silently skip if CUDA_VERSION attribute is not available.""" + # Simulate older cuda-bindings without CUDA_VERSION + original = driver.CUDA_VERSION + try: + del driver.CUDA_VERSION + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + check_cuda_version_compatibility() + assert len(w) == 0 + finally: + driver.CUDA_VERSION = original diff --git a/cuda_core/cuda/core/_device.pyx b/cuda_core/cuda/core/_device.pyx index 2d775b6580..1bf9d9f666 100644 --- a/cuda_core/cuda/core/_device.pyx +++ b/cuda_core/cuda/core/_device.pyx @@ -16,6 +16,11 @@ from cuda.core._event import Event, EventOptions from cuda.core._graph import GraphBuilder from cuda.core._stream import IsStreamT, Stream, StreamOptions from cuda.core._utils.clear_error_support import assert_type +try: + from cuda.bindings.utils import check_cuda_version_compatibility +except ImportError: + # Older cuda-bindings versions may not have this function + check_cuda_version_compatibility = None from cuda.core._utils.cuda_utils import ( ComputeCapability, CUDAError, @@ -963,6 +968,9 @@ class Device: with _lock, nogil: HANDLE_RETURN(cydriver.cuInit(0)) _is_cuInit = True + # Check version compatibility after CUDA is initialized + if check_cuda_version_compatibility is not None: + check_cuda_version_compatibility() # important: creating a Device instance does not initialize the GPU! cdef cydriver.CUdevice dev diff --git a/cuda_core/pixi.lock b/cuda_core/pixi.lock index 16a0d2460f..f818f586da 100644 --- a/cuda_core/pixi.lock +++ b/cuda_core/pixi.lock @@ -1051,7 +1051,7 @@ packages: timestamp: 1764878612030 - conda: . name: cuda-core - version: 0.4.2 + version: 0.5.0 build: py314h59f3c06_0 subdir: linux-64 variants: @@ -1068,12 +1068,12 @@ packages: - cuda-cudart >=13.1.80,<14.0a0 license: Apache-2.0 input: - hash: cccb645b22f775570680f1a9a62e415a09774e46645523bbd147226681155628 + hash: 1ca5e93f36385c628c555ad7ff2bbacd659238db16f10d477ece67c394b562f5 globs: - pyproject.toml - conda: . name: cuda-core - version: 0.4.2 + version: 0.5.0 build: py314h625260f_0 subdir: win-64 variants: @@ -1088,12 +1088,12 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 input: - hash: cccb645b22f775570680f1a9a62e415a09774e46645523bbd147226681155628 + hash: 1ca5e93f36385c628c555ad7ff2bbacd659238db16f10d477ece67c394b562f5 globs: - pyproject.toml - conda: . name: cuda-core - version: 0.4.2 + version: 0.5.0 build: py314ha479ada_0 subdir: linux-aarch64 variants: @@ -1110,7 +1110,7 @@ packages: - cuda-cudart >=13.1.80,<14.0a0 license: Apache-2.0 input: - hash: cccb645b22f775570680f1a9a62e415a09774e46645523bbd147226681155628 + hash: 1ca5e93f36385c628c555ad7ff2bbacd659238db16f10d477ece67c394b562f5 globs: - pyproject.toml - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-64-12.9.86-ha770c72_2.conda diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000000..fa9b0c1c11 --- /dev/null +++ b/pixi.lock @@ -0,0 +1,21 @@ +version: 6 +environments: + cu12: + channels: + - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit + packages: {} + cu13: + channels: + - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit + packages: {} + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit + packages: {} +packages: [] diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 0000000000..c85559e8a7 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +[workspace] +channels = ["conda-forge"] +platforms = ["linux-64", "linux-aarch64", "win-64"] + +[feature.cu12.dependencies] +[feature.cu13.dependencies] + +[environments] +cu12 = { features = ["cu12"] } +cu13 = { features = ["cu13"] } + +[activation.env] +PIXI_ENVIRONMENT_NAME = "${PIXI_ENVIRONMENT_NAME/default/cu13}" + +# Test Tasks +# Runs tests across all sub-packages: pathfinder → bindings → core (dependency order) +# Each sub-package has its own pixi.toml; the -e environment propagates via PIXI_ENVIRONMENT_NAME +# +# Usage: pixi run test | pixi run -e cu12 test | pixi run -e cu13 test +[target.linux.tasks.test-pathfinder] +cmd = [ + "pixi", + "run", + "--manifest-path", + "$PIXI_PROJECT_ROOT/cuda_pathfinder", + "test", +] + +[target.linux.tasks.test-bindings] +cmd = [ + "pixi", + "run", + "--manifest-path", + "$PIXI_PROJECT_ROOT/cuda_bindings", + "test", +] + +[target.linux.tasks.test-core] +cmd = ["pixi", "run", "--manifest-path", "$PIXI_PROJECT_ROOT/cuda_core", "test"] + +[target.linux.tasks.test] +depends-on = [ + { task = "test-pathfinder" }, + { task = "test-bindings" }, + { task = "test-core" }, +]