diff --git a/cuda_core/build_hooks.py b/cuda_core/build_hooks.py index ea0b8802308..fc112b5a74e 100644 --- a/cuda_core/build_hooks.py +++ b/cuda_core/build_hooks.py @@ -174,6 +174,15 @@ def record_build_major() -> None: _BUILD_MAJOR_STAMP.write_text(_determine_cuda_major_version() + "\n", encoding="utf-8") +def _relativize_extension_sources(extensions) -> None: + """Keep absolute source paths out of setuptools' temporary build tree.""" + for extension in extensions: + extension.sources = [ + os.path.relpath(source, start=Path.cwd()) if os.path.isabs(source) else source + for source in extension.sources + ] + + def _build_cuda_core(debug=False): # Customizing the build hooks is needed because we must defer cythonization until cuda-bindings, # now a required build-time dependency that's dynamically installed via the other hook below, @@ -292,6 +301,10 @@ def get_sources(mod_name): compile_time_env=compile_time_env, **extra_cythonize_kwargs, ) + # Cython returns generated sources under the absolute build_dir above. + # setuptools mirrors absolute source paths into build/temp, which can push + # MSVC linker output paths past MAX_PATH in deeper Windows checkouts. + _relativize_extension_sources(_extensions) return diff --git a/cuda_core/tests/test_build_hooks.py b/cuda_core/tests/test_build_hooks.py index da410257e24..8d286999b52 100644 --- a/cuda_core/tests/test_build_hooks.py +++ b/cuda_core/tests/test_build_hooks.py @@ -267,6 +267,19 @@ def test_dir_is_anchored_not_relative_to_cwd(self, monkeypatch): assert build_dir.parent.parent == build_hooks._BUILD_MAJOR_STAMP.parent +class TestSetuptoolsSourcePaths: + @pytest.mark.agent_authored(model="gpt-5.6-sol") + def test_absolute_sources_are_made_relative(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + generated = tmp_path / "build" / "cython" / "cu13" / "cuda" / "core" / "_device.cpp" + relative = "cuda/core/_cpp/helper.cpp" + extension = build_hooks.Extension("cuda.core._device", [str(generated), relative]) + + build_hooks._relativize_extension_sources([extension]) + + assert extension.sources == [os.path.relpath(generated, start=tmp_path), relative] + + def _load_setup_py(monkeypatch): """Import setup.py for its command classes.