Skip to content

Commit 2161c01

Browse files
committed
cuda.core build: parallelize only compilers that use CCompiler.compile
The base CCompiler defines a placeholder _compile(), so testing for that attribute matched MSVC as well. MSVCCompiler overrides compile() wholesale and never calls _compile(), so the override produced no objects on Windows and every Windows build failed at link time. Gate on the compile() method itself: apply the thread pool only when the compiler still uses CCompiler.compile(), which is what drives _compile() (the Unix family).
1 parent 35ecf47 commit 2161c01

2 files changed

Lines changed: 18 additions & 9 deletions

File tree

cuda_core/setup.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import contextlib
66
import os
77
from concurrent.futures import ThreadPoolExecutor
8+
from distutils.ccompiler import CCompiler
89
from pathlib import Path
910

1011
import build_hooks # our build backend
@@ -88,11 +89,12 @@ def _parallel_source_compilation(self):
8889
cuda.core._rt (a dozen .cpp files) becomes the critical path. This
8990
mirrors CCompiler.compile() and fans its per-object _compile() calls out
9091
to a pool shared by all extensions, so at most `nthreads` compiler
91-
processes run at once. MSVC's compiler class has no _compile(); it keeps
92-
the stock path.
92+
processes run at once. It applies only to compilers that still use
93+
CCompiler.compile(), which drives the per-object _compile() hook (the
94+
Unix family); MSVC overrides compile() wholesale and keeps the stock path.
9395
"""
9496
compiler = self.compiler
95-
if nthreads <= 1 or not hasattr(compiler, "_compile"):
97+
if nthreads <= 1 or type(compiler).compile is not CCompiler.compile:
9698
yield
9799
return
98100
stock_compile = compiler.compile

cuda_core/tests/test_build_hooks.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import sys
2323
import tempfile
2424
import threading
25-
import types
25+
from distutils.ccompiler import CCompiler
2626
from pathlib import Path
2727
from unittest import mock
2828

@@ -386,8 +386,13 @@ def test_headers_under_module_directories_only(self, tmp_path, monkeypatch):
386386
class TestParallelSourceCompilation:
387387
"""setup.py compiles an extension's sources through one shared thread pool."""
388388

389-
class FakeCompiler:
389+
class FakeCompiler(CCompiler):
390+
"""Uses the stock CCompiler.compile(), like the Unix compilers."""
391+
392+
executables = {}
393+
390394
def __init__(self, fail_on=None):
395+
super().__init__()
391396
self.compiled = []
392397
self.fail_on = fail_on
393398
self.lock = threading.Lock()
@@ -406,6 +411,9 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
406411
with self.lock:
407412
self.compiled.append((obj, src, ext, tuple(cc_args), tuple(extra_postargs), tuple(pp_opts)))
408413

414+
class MsvcLikeCompiler(FakeCompiler):
415+
"""Overrides compile() wholesale, like MSVCCompiler."""
416+
409417
def compile(self, *args, **kwargs):
410418
return "stock"
411419

@@ -427,7 +435,7 @@ def test_every_source_compiles_once_and_the_object_order_is_kept(self, monkeypat
427435
assert objects == [source + ".o" for source in sources]
428436
assert sorted(entry[0] for entry in cmd.compiler.compiled) == sorted(objects)
429437
assert {entry[2:] for entry in cmd.compiler.compiled} == {(".cpp", ("-c", "-Dpp"), ("-O2",), ("-Dpp",))}
430-
assert cmd.compiler.compile(sources) == "stock" # restored on exit
438+
assert cmd.compiler.compile.__func__ is CCompiler.compile # restored on exit
431439

432440
@pytest.mark.agent_authored(model="claude-fable-5-1")
433441
def test_a_failing_source_fails_the_extension(self, monkeypatch):
@@ -439,8 +447,7 @@ def test_a_failing_source_fails_the_extension(self, monkeypatch):
439447
def test_serial_builds_and_compilers_without_the_hook_keep_the_stock_path(self, monkeypatch):
440448
cmd = self._build_ext(monkeypatch, 1, self.FakeCompiler())
441449
with cmd._parallel_source_compilation():
442-
assert cmd.compiler.compile(["a.cpp"]) == "stock"
443-
msvc_like = types.SimpleNamespace(compile=self.FakeCompiler().compile) # no _compile()
444-
cmd = self._build_ext(monkeypatch, 4, msvc_like)
450+
assert cmd.compiler.compile.__func__ is CCompiler.compile
451+
cmd = self._build_ext(monkeypatch, 4, self.MsvcLikeCompiler())
445452
with cmd._parallel_source_compilation():
446453
assert cmd.compiler.compile(["a.cpp"]) == "stock"

0 commit comments

Comments
 (0)