Skip to content
Merged
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
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# RcppParallel (development version)

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


# RcppParallel 6.1.1

Expand Down
38 changes: 38 additions & 0 deletions src/install.libs.R
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,13 @@ useBundledTbb <- function() {
buildType <- Sys.getenv("CMAKE_BUILD_TYPE", unset = "Release")
verbose <- Sys.getenv("VERBOSE", unset = "0")

# pull any leading compiler launcher (e.g. ccache) out of CC / CXX before
# splitting, so it can be forwarded to CMake the way CMake expects it (see
# extractCompilerLauncher); otherwise ccache would be treated as the
# compiler itself, breaking CMake's compiler / assembler probes
ccLauncher <- extractCompilerLauncher("CC")
cxxLauncher <- extractCompilerLauncher("CXX")

splitCompilerVar("CC", "CFLAGS")
splitCompilerVar("CXX", "CXXFLAGS")

Expand All @@ -341,6 +348,10 @@ useBundledTbb <- function() {
cmakeFlags <- c(
forwardEnvVar("CC", "CMAKE_C_COMPILER"),
forwardEnvVar("CXX", "CMAKE_CXX_COMPILER"),
if (!is.na(ccLauncher))
sprintf("-DCMAKE_C_COMPILER_LAUNCHER=%s", ccLauncher),
if (!is.na(cxxLauncher))
sprintf("-DCMAKE_CXX_COMPILER_LAUNCHER=%s", cxxLauncher),
forwardEnvVar("CFLAGS", "CMAKE_C_FLAGS"),
forwardEnvVar("CXXFLAGS", "CMAKE_CXX_FLAGS"),
forwardEnvVar("CMAKE_BUILD_TYPE", "CMAKE_BUILD_TYPE"),
Expand Down Expand Up @@ -434,6 +445,33 @@ setenv <- function(key, value) {
}


# Compiler launchers such as ccache are commonly injected by prefixing the
# compiler (e.g. CXX='ccache g++'). CMake models these via the separate
# CMAKE_<LANG>_COMPILER_LAUNCHER variable rather than as part of the compiler
# command, so detect a leading launcher, strip it from the compiler variable,
# and return it for forwarding. Returns NA when no launcher is present.
extractCompilerLauncher <- function(compilerVar) {

compiler <- Sys.getenv(compilerVar, unset = NA)
if (is.na(compiler))
return(NA_character_)

tokens <- scan(text = compiler, what = character(), quiet = TRUE)
if (length(tokens) == 0L)
return(NA_character_)

launcher <- tokens[[1L]]
name <- sub("[.]exe$", "", basename(launcher), ignore.case = TRUE)
if (!name %in% c("ccache", "sccache"))
return(NA_character_)

# drop the launcher, leaving the real compiler (and any flags) behind
setenv(compilerVar, tokens[-1L])
launcher

}


# CMake doesn't support flags specified directly as part of the compiler
# definition, so manually split it here.
splitCompilerVar <- function(compilerVar, flagsVar) {
Expand Down
44 changes: 44 additions & 0 deletions tests/test-install-libs.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ if (length(marker))
env <- new.env(parent = globalenv())
eval(parse(text = paste(lines, collapse = "\n")), envir = env)
splitCompilerVar <- get("splitCompilerVar", envir = env)
extractCompilerLauncher <- get("extractCompilerLauncher", envir = env)
patchTbbMachineHeader <- get("patchTbbMachineHeader", envir = env)

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

# a leading compiler launcher (ccache) must be split off from the compiler so
# CMake can receive it via CMAKE_<LANG>_COMPILER_LAUNCHER. Left in place, CMake
# would treat 'ccache' as the compiler and run 'ccache ... -Wa,-v' during its
# assembler probe, which ccache rejects with "invalid option -- 'W'".
withEnv(c(TEST_CXX = "ccache g++"), {
launcher <- extractCompilerLauncher("TEST_CXX")
check(identical(launcher, "ccache"), "ccache launcher is extracted")
check(identical(Sys.getenv("TEST_CXX"), "g++"),
"ccache launcher is stripped from the compiler")
})

# the launcher may be a full path and carry trailing compiler flags; only the
# launcher token is removed, leaving the compiler and its flags intact
withEnv(c(TEST_CXX = "/usr/bin/ccache g++ -std=c++17"), {
launcher <- extractCompilerLauncher("TEST_CXX")
check(identical(launcher, "/usr/bin/ccache"),
"ccache launcher path is extracted")
check(identical(Sys.getenv("TEST_CXX"), "g++ -std=c++17"),
"compiler and flags survive launcher extraction")
})

# sccache is recognized as a launcher too
withEnv(c(TEST_CXX = "sccache clang++"), {
launcher <- extractCompilerLauncher("TEST_CXX")
check(identical(launcher, "sccache"), "sccache launcher is extracted")
check(identical(Sys.getenv("TEST_CXX"), "clang++"),
"sccache launcher is stripped from the compiler")
})

# a plain compiler has no launcher: return NA and leave the variable untouched
withEnv(c(TEST_CXX = "g++ -O2"), {
launcher <- extractCompilerLauncher("TEST_CXX")
check(identical(launcher, NA_character_),
"plain compiler yields no launcher")
check(identical(Sys.getenv("TEST_CXX"), "g++ -O2"),
"plain compiler is left unmodified")
})

# an unset compiler variable is a no-op that reports NA
Sys.unsetenv("TEST_CXX_UNSET")
check(identical(extractCompilerLauncher("TEST_CXX_UNSET"), NA_character_),
"unset compiler variable yields no launcher")

# the mingw cpuid guard should be applied exactly once to a header with the
# expected form, and applying it again should leave the header untouched
header <- tempfile(fileext = ".h")
Expand Down
Loading