Skip to content

Commit a3c7cd7

Browse files
authored
forward ccache to cmake via CMAKE_<LANG>_COMPILER_LAUNCHER (#267)
When CXX/CC is configured with a leading compiler launcher (e.g. CXX='ccache g++'), splitCompilerVar treated 'ccache' as the compiler and demoted the real compiler to a flag. CMake then ran the launcher directly during its assembler probe (ccache ... -Wa,-v), which ccache rejects with 'invalid option -- W', aborting the oneTBB build. Extract a leading ccache/sccache launcher from CC/CXX and forward it via CMAKE_<LANG>_COMPILER_LAUNCHER, the variable CMake provides for exactly this purpose.
1 parent 6140682 commit a3c7cd7

3 files changed

Lines changed: 88 additions & 0 deletions

File tree

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# RcppParallel (development version)
22

3+
* Fixed an issue where building the bundled oneTBB could fail when `CXX`
4+
(or `CC`) was configured with a leading compiler launcher such as `ccache`
5+
(e.g. `CXX = "ccache g++"`). The launcher is now forwarded to cmake via
6+
`CMAKE_<LANG>_COMPILER_LAUNCHER` instead of being mistaken for the compiler
7+
itself.
8+
39

410
# RcppParallel 6.1.1
511

src/install.libs.R

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,13 @@ useBundledTbb <- function() {
325325
buildType <- Sys.getenv("CMAKE_BUILD_TYPE", unset = "Release")
326326
verbose <- Sys.getenv("VERBOSE", unset = "0")
327327

328+
# pull any leading compiler launcher (e.g. ccache) out of CC / CXX before
329+
# splitting, so it can be forwarded to CMake the way CMake expects it (see
330+
# extractCompilerLauncher); otherwise ccache would be treated as the
331+
# compiler itself, breaking CMake's compiler / assembler probes
332+
ccLauncher <- extractCompilerLauncher("CC")
333+
cxxLauncher <- extractCompilerLauncher("CXX")
334+
328335
splitCompilerVar("CC", "CFLAGS")
329336
splitCompilerVar("CXX", "CXXFLAGS")
330337

@@ -341,6 +348,10 @@ useBundledTbb <- function() {
341348
cmakeFlags <- c(
342349
forwardEnvVar("CC", "CMAKE_C_COMPILER"),
343350
forwardEnvVar("CXX", "CMAKE_CXX_COMPILER"),
351+
if (!is.na(ccLauncher))
352+
sprintf("-DCMAKE_C_COMPILER_LAUNCHER=%s", ccLauncher),
353+
if (!is.na(cxxLauncher))
354+
sprintf("-DCMAKE_CXX_COMPILER_LAUNCHER=%s", cxxLauncher),
344355
forwardEnvVar("CFLAGS", "CMAKE_C_FLAGS"),
345356
forwardEnvVar("CXXFLAGS", "CMAKE_CXX_FLAGS"),
346357
forwardEnvVar("CMAKE_BUILD_TYPE", "CMAKE_BUILD_TYPE"),
@@ -434,6 +445,33 @@ setenv <- function(key, value) {
434445
}
435446

436447

448+
# Compiler launchers such as ccache are commonly injected by prefixing the
449+
# compiler (e.g. CXX='ccache g++'). CMake models these via the separate
450+
# CMAKE_<LANG>_COMPILER_LAUNCHER variable rather than as part of the compiler
451+
# command, so detect a leading launcher, strip it from the compiler variable,
452+
# and return it for forwarding. Returns NA when no launcher is present.
453+
extractCompilerLauncher <- function(compilerVar) {
454+
455+
compiler <- Sys.getenv(compilerVar, unset = NA)
456+
if (is.na(compiler))
457+
return(NA_character_)
458+
459+
tokens <- scan(text = compiler, what = character(), quiet = TRUE)
460+
if (length(tokens) == 0L)
461+
return(NA_character_)
462+
463+
launcher <- tokens[[1L]]
464+
name <- sub("[.]exe$", "", basename(launcher), ignore.case = TRUE)
465+
if (!name %in% c("ccache", "sccache"))
466+
return(NA_character_)
467+
468+
# drop the launcher, leaving the real compiler (and any flags) behind
469+
setenv(compilerVar, tokens[-1L])
470+
launcher
471+
472+
}
473+
474+
437475
# CMake doesn't support flags specified directly as part of the compiler
438476
# definition, so manually split it here.
439477
splitCompilerVar <- function(compilerVar, flagsVar) {

tests/test-install-libs.R

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ if (length(marker))
4040
env <- new.env(parent = globalenv())
4141
eval(parse(text = paste(lines, collapse = "\n")), envir = env)
4242
splitCompilerVar <- get("splitCompilerVar", envir = env)
43+
extractCompilerLauncher <- get("extractCompilerLauncher", envir = env)
4344
patchTbbMachineHeader <- get("patchTbbMachineHeader", envir = env)
4445

4546
# minimal assertion harness
@@ -113,6 +114,49 @@ Sys.unsetenv("TEST_CXX_UNSET")
113114
check(identical(splitCompilerVar("TEST_CXX_UNSET", "TEST_CXXFLAGS"), FALSE),
114115
"unset compiler variable returns FALSE")
115116

117+
# a leading compiler launcher (ccache) must be split off from the compiler so
118+
# CMake can receive it via CMAKE_<LANG>_COMPILER_LAUNCHER. Left in place, CMake
119+
# would treat 'ccache' as the compiler and run 'ccache ... -Wa,-v' during its
120+
# assembler probe, which ccache rejects with "invalid option -- 'W'".
121+
withEnv(c(TEST_CXX = "ccache g++"), {
122+
launcher <- extractCompilerLauncher("TEST_CXX")
123+
check(identical(launcher, "ccache"), "ccache launcher is extracted")
124+
check(identical(Sys.getenv("TEST_CXX"), "g++"),
125+
"ccache launcher is stripped from the compiler")
126+
})
127+
128+
# the launcher may be a full path and carry trailing compiler flags; only the
129+
# launcher token is removed, leaving the compiler and its flags intact
130+
withEnv(c(TEST_CXX = "/usr/bin/ccache g++ -std=c++17"), {
131+
launcher <- extractCompilerLauncher("TEST_CXX")
132+
check(identical(launcher, "/usr/bin/ccache"),
133+
"ccache launcher path is extracted")
134+
check(identical(Sys.getenv("TEST_CXX"), "g++ -std=c++17"),
135+
"compiler and flags survive launcher extraction")
136+
})
137+
138+
# sccache is recognized as a launcher too
139+
withEnv(c(TEST_CXX = "sccache clang++"), {
140+
launcher <- extractCompilerLauncher("TEST_CXX")
141+
check(identical(launcher, "sccache"), "sccache launcher is extracted")
142+
check(identical(Sys.getenv("TEST_CXX"), "clang++"),
143+
"sccache launcher is stripped from the compiler")
144+
})
145+
146+
# a plain compiler has no launcher: return NA and leave the variable untouched
147+
withEnv(c(TEST_CXX = "g++ -O2"), {
148+
launcher <- extractCompilerLauncher("TEST_CXX")
149+
check(identical(launcher, NA_character_),
150+
"plain compiler yields no launcher")
151+
check(identical(Sys.getenv("TEST_CXX"), "g++ -O2"),
152+
"plain compiler is left unmodified")
153+
})
154+
155+
# an unset compiler variable is a no-op that reports NA
156+
Sys.unsetenv("TEST_CXX_UNSET")
157+
check(identical(extractCompilerLauncher("TEST_CXX_UNSET"), NA_character_),
158+
"unset compiler variable yields no launcher")
159+
116160
# the mingw cpuid guard should be applied exactly once to a header with the
117161
# expected form, and applying it again should leave the header untouched
118162
header <- tempfile(fileext = ".h")

0 commit comments

Comments
 (0)