diff --git a/cuda_bindings/build_hooks.py b/cuda_bindings/build_hooks.py index 9800630447f..b8a348d477e 100644 --- a/cuda_bindings/build_hooks.py +++ b/cuda_bindings/build_hooks.py @@ -127,18 +127,28 @@ def _resolve_toolchain_name(): return name, allowed, cc, cxx, explicit +def _with_sccache(current, compiler): + """Keep CC="sccache cc" as CC="sccache clang" when the toolchain picks a compiler.""" + if current: + launcher = current.split()[0] + if os.path.basename(launcher) == "sccache": + return f"{launcher} {compiler}" + return compiler + + def _apply_toolchain_env(cc, cxx, explicit): """Set CC/CXX/LDSHARED for an explicitly-chosen toolchain. The default path (CUDA_PYTHON_TOOLCHAIN unset) intentionally does not touch the env, so an externally-set compiler (e.g. CC="sccache cc" in CI) keeps working. An explicit CUDA_PYTHON_TOOLCHAIN - override (incl. =gnu) governs the compiler and overrides CC/CXX/LDSHARED. + override (incl. =gnu) governs the compiler. An existing sccache prefix + is kept (CC="sccache cc" + llvm -> CC="sccache clang"). """ if explicit and cc is not None: - os.environ["CC"] = cc - os.environ["CXX"] = cxx - os.environ["LDSHARED"] = f"{cxx} -shared" + os.environ["CC"] = _with_sccache(os.environ.get("CC", ""), cc) + os.environ["CXX"] = _with_sccache(os.environ.get("CXX", ""), cxx) + os.environ["LDSHARED"] = f"{os.environ['CXX']} -shared" def _check_toolchain_available(name): diff --git a/cuda_bindings/tests/test_build_hooks.py b/cuda_bindings/tests/test_build_hooks.py index d4e1e1c8d29..57a69abe9f6 100644 --- a/cuda_bindings/tests/test_build_hooks.py +++ b/cuda_bindings/tests/test_build_hooks.py @@ -135,6 +135,18 @@ def test_gnu_sets_env_and_flags(self, monkeypatch): assert "-fpermissive" in cargs assert "-fno-var-tracking-assignments" in cargs + @pytest.mark.agent_authored(model="grok-4.6") + def test_llvm_keeps_sccache_prefix(self, monkeypatch): + if sys.platform == "win32": + pytest.skip("llvm only valid on Linux") + monkeypatch.setenv("CUDA_PYTHON_TOOLCHAIN", "llvm") + monkeypatch.setenv("CC", "sccache cc") + monkeypatch.setenv("CXX", "sccache c++") + _name, _cc, _cxx, _cargs, _largs = build_hooks._resolve_toolchain() + assert os.environ["CC"] == "sccache clang" + assert os.environ["CXX"] == "sccache clang++" + assert os.environ["LDSHARED"] == "sccache clang++ -shared" + @pytest.mark.agent_authored(model="glm-5.2") def test_gnu_keeps_gcc_only_flags(self, monkeypatch): if sys.platform == "win32": diff --git a/cuda_core/build_hooks.py b/cuda_core/build_hooks.py index 715afdfce4c..58b699e76a3 100644 --- a/cuda_core/build_hooks.py +++ b/cuda_core/build_hooks.py @@ -126,18 +126,28 @@ def _resolve_toolchain_name(): return name, allowed, cc, cxx, explicit +def _with_sccache(current, compiler): + """Keep CC="sccache cc" as CC="sccache clang" when the toolchain picks a compiler.""" + if current: + launcher = current.split()[0] + if os.path.basename(launcher) == "sccache": + return f"{launcher} {compiler}" + return compiler + + def _apply_toolchain_env(cc, cxx, explicit): """Set CC/CXX/LDSHARED for an explicitly-chosen toolchain. The default path (CUDA_PYTHON_TOOLCHAIN unset) intentionally does not touch the env, so an externally-set compiler (e.g. CC="sccache cc" in CI) keeps working. An explicit CUDA_PYTHON_TOOLCHAIN - override (incl. =gnu) governs the compiler and overrides CC/CXX/LDSHARED. + override (incl. =gnu) governs the compiler. An existing sccache prefix + is kept (CC="sccache cc" + llvm -> CC="sccache clang"). """ if explicit and cc is not None: - os.environ["CC"] = cc - os.environ["CXX"] = cxx - os.environ["LDSHARED"] = f"{cxx} -shared" + os.environ["CC"] = _with_sccache(os.environ.get("CC", ""), cc) + os.environ["CXX"] = _with_sccache(os.environ.get("CXX", ""), cxx) + os.environ["LDSHARED"] = f"{os.environ['CXX']} -shared" def _check_toolchain_available(name): diff --git a/cuda_core/tests/test_build_hooks.py b/cuda_core/tests/test_build_hooks.py index 5017c0d636e..32a51529ba5 100644 --- a/cuda_core/tests/test_build_hooks.py +++ b/cuda_core/tests/test_build_hooks.py @@ -696,16 +696,17 @@ def test_gnu_sets_env_and_flags(self, monkeypatch): assert "-fpermissive" not in cargs # cuda.core gnu flags don't include it; bindings do assert "-fno-var-tracking-assignments" not in cargs - @pytest.mark.agent_authored(model="glm-5.2") - def test_llvm_overrides_external_cc(self, monkeypatch): + @pytest.mark.agent_authored(model="grok-4.6") + def test_llvm_keeps_sccache_prefix(self, monkeypatch): if sys.platform == "win32": pytest.skip("llvm only valid on Linux") - # An explicit non-default toolchain governs the compiler, so a stale - # external CC (e.g. "sccache cc") is replaced, not kept. monkeypatch.setenv("CUDA_PYTHON_TOOLCHAIN", "llvm") monkeypatch.setenv("CC", "sccache cc") + monkeypatch.setenv("CXX", "sccache c++") _name, _cc, _cxx, _cargs, _largs = build_hooks._resolve_toolchain() - assert os.environ["CC"] == "clang" + assert os.environ["CC"] == "sccache clang" + assert os.environ["CXX"] == "sccache clang++" + assert os.environ["LDSHARED"] == "sccache clang++ -shared" class TestCheckToolchainAvailable: