diff --git a/cuda_core/cuda/core/_linker.pyx b/cuda_core/cuda/core/_linker.pyx index 09aa9863cd7..2a4bda413c2 100644 --- a/cuda_core/cuda/core/_linker.pyx +++ b/cuda_core/cuda/core/_linker.pyx @@ -167,10 +167,22 @@ cdef class Linker: else: return as_py(self._culink_handle) - @property - def backend(self) -> str: - """Return this Linker instance's underlying backend.""" - return "nvJitLink" if self._use_nvjitlink else "driver" + @classmethod + def backend(cls) -> str: + """Return which linking backend will be used. + + Returns ``"nvJitLink"`` when the nvJitLink library is available + and meets the minimum version requirement, otherwise ``"driver"``. + + .. note:: + + Prefer letting :class:`Linker` decide. Query ``backend()`` + only when you need to dispatch based on input format (for + example: choose PTX vs. LTOIR before constructing a + ``Linker``). The returned string names an implementation + detail whose support matrix may shift across CTK releases. + """ + return "driver" if _decide_nvjitlink_or_driver() else "nvJitLink" # ============================================================================= diff --git a/cuda_core/cuda/core/_program.pyx b/cuda_core/cuda/core/_program.pyx index 194ef6da53f..bcbd8ca3758 100644 --- a/cuda_core/cuda/core/_program.pyx +++ b/cuda_core/cuda/core/_program.pyx @@ -649,7 +649,7 @@ cdef inline int Program_init(Program self, object code, str code_type, object op self._linker = Linker( ObjectCode._init(code.encode(), code_type), options=_translate_program_options(options) ) - self._backend = self._linker.backend + self._backend = self._linker.backend() elif code_type == "nvvm": _get_nvvm_module() # Validate NVVM availability diff --git a/cuda_core/docs/source/release/1.0.0-notes.rst b/cuda_core/docs/source/release/1.0.0-notes.rst new file mode 100644 index 00000000000..d289500deff --- /dev/null +++ b/cuda_core/docs/source/release/1.0.0-notes.rst @@ -0,0 +1,17 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: Apache-2.0 + +.. currentmodule:: cuda.core + +``cuda.core`` 1.0.0 Release Notes +================================= + + +Breaking Changes +---------------- + +- :meth:`Linker.backend` is now a classmethod instead of an instance property. + Call sites must use ``Linker.backend()`` (with parentheses) instead of + ``linker.backend``. This allows querying the linking backend without + constructing a ``Linker`` instance — for example, to choose between PTX and + LTOIR input before linking. diff --git a/cuda_core/tests/test_linker.py b/cuda_core/tests/test_linker.py index 0d4ff91dcd9..9b3d33e891f 100644 --- a/cuda_core/tests/test_linker.py +++ b/cuda_core/tests/test_linker.py @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: Apache-2.0 +import inspect + import pytest from cuda.core import Device, Linker, LinkerOptions, Program, ProgramOptions, _linker @@ -92,7 +94,7 @@ def test_linker_init(compile_ptx_functions, options): linker = Linker(*compile_ptx_functions, options=options) object_code = linker.link("cubin") assert isinstance(object_code, ObjectCode) - assert linker.backend == ("driver" if is_culink_backend else "nvJitLink") + assert linker.backend() == ("driver" if is_culink_backend else "nvJitLink") def test_linker_init_invalid_arch(compile_ptx_functions): @@ -242,3 +244,40 @@ def test_linker_options_nvjitlink_options_as_str(): assert f"-arch={ARCH}" in options assert "-g" in options assert "-lineinfo" in options + + +class TestBackendClassmethod: + def test_backend_returns_nvjitlink(self, monkeypatch): + monkeypatch.setattr(_linker, "_use_nvjitlink_backend", True) + assert Linker.backend() == "nvJitLink" + + def test_backend_returns_driver(self, monkeypatch): + monkeypatch.setattr(_linker, "_use_nvjitlink_backend", False) + assert Linker.backend() == "driver" + + def test_backend_invokes_probe_when_not_memoised(self, monkeypatch): + monkeypatch.setattr(_linker, "_use_nvjitlink_backend", None) + called = [] + + def fake_decide(): + called.append(True) + return False # False = not falling back to driver = nvJitLink + + monkeypatch.setattr(_linker, "_decide_nvjitlink_or_driver", fake_decide) + result = Linker.backend() + assert result == "nvJitLink" + assert called, "_decide_nvjitlink_or_driver was not called" + + def test_backend_is_classmethod(self): + attr = inspect.getattr_static(Linker, "backend") + assert isinstance(attr, classmethod) + + def test_backend_is_not_property(self): + """backend is a classmethod, not a property. + + This is an intentional breaking change from the prior property API. + Attribute-style access (``linker.backend``) now returns a bound method, + not a string. All call sites must use parens: ``Linker.backend()``. + """ + attr = inspect.getattr_static(Linker, "backend") + assert not isinstance(attr, property)