Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions cuda_core/cuda/core/_linker.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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"


# =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion cuda_core/cuda/core/_program.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions cuda_core/docs/source/release/1.0.0-notes.rst
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we are shooting for 1.0.0, unless @mdboom concludes that a 0.8.0 is a must for RAPIDS adoption.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to 1.0.0-notes.rst.

Original file line number Diff line number Diff line change
@@ -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.
Comment on lines +13 to +17
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

41 changes: 40 additions & 1 deletion cuda_core/tests/test_linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#
# SPDX-License-Identifier: Apache-2.0

import inspect

import pytest

from cuda.core import Device, Linker, LinkerOptions, Program, ProgramOptions, _linker
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Loading