From da6266a59923f28116714a1171a3736b4a57db4e Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Mon, 1 Dec 2025 16:08:38 -0800 Subject: [PATCH 1/2] alternate openmp plugin changes --- ChangeLog | 4 ++++ R/Attributes.R | 63 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index b08ef1f73..4f11ca684 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2025-12-01 Kevin Ushey + + * R/Attributes.R: Update OpenMP plugin for macOS + 2025-11-24 Dirk Eddelbuettel * inst/include/Rcpp/r/check_r_headers.h: Add RCPP_NO_R_HEADERS_CHECK diff --git a/R/Attributes.R b/R/Attributes.R index 53e9f0a61..500fd60fd 100644 --- a/R/Attributes.R +++ b/R/Attributes.R @@ -586,10 +586,67 @@ compileAttributes <- function(pkgdir = ".", verbose = getOption("verbose")) { list(env = list(PKG_CXXFLAGS ="-std=c++2b")) } +.openmpPluginDefault <- function() { + list(env = list(PKG_CXXFLAGS = "-fopenmp", PKG_LIBS = "-fopenmp")) +} + +.openmpPluginDarwin <- function() { + + # get the C++ compiler from R + r <- file.path(R.home("bin"), "R") + cxx <- tryCatch( + system2(r, c("CMD", "config", "CXX"), stdout = TRUE), + condition = identity + ) + if (inherits(cxx, "condition")) + return(.openmpPluginDefault()) + + # check the compiler version + command <- paste(cxx, "--version") + version <- tryCatch( + system(command, intern = TRUE), + condition = identity + ) + if (inherits(version, "condition")) + return(.openmpPluginDefault()) + + # if we're using Apple clang, use alternate flags + # assume libomp was installed following https://mac.r-project.org/openmp/ + if (any(grepl("Apple clang", version))) { + + cxxflags <- "-Xclang -fopenmp" + libs <- "-lomp" + + } + + # if we're using Homebrew clang, add in libomp include paths + else if (any(grepl("Homebrew clang", version))) { + + # ensure Homebrew paths are included during compilation + machine <- Sys.info()[["machine"]] + prefix <- if (machine == "arm64") "/opt/homebrew" else "/usr/local" + + # build flags + cxxflags <- sprintf("-I%s/opt/libomp/include -fopenmp", prefix) + libs <- sprintf("-L%s/opt/libomp/lib -fopenmp", prefix) + + # otherwise, use default -fopenmp flags for other compilers (LLVM clang; gcc) + } else { + + cxxflags <- "-fopenmp" + libs <- "-fopenmp" + + } + + list(env = list(PKG_CXXFLAGS = cxxflags, PKG_LIBS = libs)) + +} + ## built-in OpenMP plugin -.plugins[["openmp"]] <- function() { - list(env = list(PKG_CXXFLAGS="-fopenmp", - PKG_LIBS="-fopenmp")) +.plugins[["openmp"]] <- if (Sys.info()[["sysname"]] == "Darwin") { + .openmpPluginDarwin +} else { + .openmpPluginDefault } .plugins[["unwindProtect"]] <- function() { # nocov start From 9ad19e6c259ef65f06cf65873426bcb40c37c8c2 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Mon, 1 Dec 2025 16:32:44 -0800 Subject: [PATCH 2/2] alternative using R CMD SHLIB --- R/Attributes.R | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/R/Attributes.R b/R/Attributes.R index 500fd60fd..a6019b1fe 100644 --- a/R/Attributes.R +++ b/R/Attributes.R @@ -592,15 +592,30 @@ compileAttributes <- function(pkgdir = ".", verbose = getOption("verbose")) { .openmpPluginDarwin <- function() { + # generate a test script for compilation + script <- tempfile("openmp-detect-", fileext = ".cpp") + writeLines("", con = script) + on.exit(unlink(script, force = TRUE), add = TRUE) + # get the C++ compiler from R r <- file.path(R.home("bin"), "R") - cxx <- tryCatch( - system2(r, c("CMD", "config", "CXX"), stdout = TRUE), + output <- tryCatch( + system2(r, c("CMD", "SHLIB", "--dry-run", shQuote(script)), stdout = TRUE), condition = identity ) - if (inherits(cxx, "condition")) + if (inherits(output, "condition")) return(.openmpPluginDefault()) + # extract the compiler invocation from the shlib output + # use some heuristics here... + index <- grep("make would use", output) + compile <- output[[index + 1L]] + + # use everything up to the first include flag, which is normally + # the R headers from CPPFLAGS + idx <- regexpr(" -I", compile, fixed = TRUE) + cxx <- substring(compile, 1L, idx - 1L) + # check the compiler version command <- paste(cxx, "--version") version <- tryCatch( @@ -613,29 +628,21 @@ compileAttributes <- function(pkgdir = ".", verbose = getOption("verbose")) { # if we're using Apple clang, use alternate flags # assume libomp was installed following https://mac.r-project.org/openmp/ if (any(grepl("Apple clang", version))) { - cxxflags <- "-Xclang -fopenmp" libs <- "-lomp" - } # if we're using Homebrew clang, add in libomp include paths else if (any(grepl("Homebrew clang", version))) { - - # ensure Homebrew paths are included during compilation machine <- Sys.info()[["machine"]] prefix <- if (machine == "arm64") "/opt/homebrew" else "/usr/local" - - # build flags cxxflags <- sprintf("-I%s/opt/libomp/include -fopenmp", prefix) libs <- sprintf("-L%s/opt/libomp/lib -fopenmp", prefix) # otherwise, use default -fopenmp flags for other compilers (LLVM clang; gcc) } else { - cxxflags <- "-fopenmp" libs <- "-fopenmp" - } list(env = list(PKG_CXXFLAGS = cxxflags, PKG_LIBS = libs))