Skip to content

Conversation

@eddelbuettel
Copy link
Member

@eddelbuettel eddelbuettel commented Nov 13, 2025

As discussed with @coatless in #493 -- marking it draft for now.

Edit: Draft status now removed.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR improves OpenMP support on macOS by removing the previous blanket special-casing and introducing a more flexible configuration approach that respects compiler capabilities while still allowing opt-out.

Key changes:

  • Introduced macOS-specific OpenMP detection that enables OpenMP by default when the compiler supports it (_OPENMP is defined), with an opt-out mechanism via a new configuration macro
  • Removed the blanket exclusion of OpenMP flags for macOS in the inline plugin function
  • Conditionally restricted ARMA_CRIPPLED_LAPACK definition to only legacy Armadillo versions on Windows

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
inst/include/RcppArmadillo/config/RcppArmadilloConfigGenerated.h.in Adds macOS-specific OpenMP logic with conditional enabling based on compiler support and new opt-out macro; updates copyright years
inst/include/RcppArmadillo/config/RcppArmadilloConfig.h Restricts ARMA_CRIPPLED_LAPACK to legacy Armadillo versions only; updates copyright years
R/inline.R Removes macOS special case for OpenMP flags, now consistently uses $(SHLIB_OPENMP_CFLAGS) across all platforms; updates copyright year
ChangeLog Documents the R/inline.R change

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@eddelbuettel eddelbuettel marked this pull request as ready for review November 15, 2025 00:02
Copy link
Contributor

@coatless coatless left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, this is solid; however, we need to tweak Rcpp::plugins(openmp).

Specifics:

  1. We now get the appropriate number of cores if the local computer configuration has OpenMP.
    • under the apple compiler setting we're still setting the #define ARMA_DONT_USE_OPENMP 1.
  2. Users must explicitly set in their ~/.R/Makevars the required OpenMP flags to opt-in:
CPPFLAGS += -Xclang -fopenmp
LDFLAGS += -lomp
  1. If we switch over to trying to compile with OpenMP enabled via // [[Rcpp::plugins(openmp)]] we're still getting a compile error on a locally configured mac due to only -fopenmp being set instead of -Xclang -fopenmp.
Failed compilation using `Rcpp::plugins(openmp)`
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::plugins(openmp)]]

// [[Rcpp::export]]
arma::ivec openmp_limits() {
  arma::ivec limit_data(3);

#ifdef _OPENMP
  limit_data[0] = omp_get_num_procs();
  limit_data[1] = omp_get_max_threads();
  limit_data[2] = omp_get_thread_limit();
#endif
  return limit_data;
}

// [[Rcpp::export]]
bool openmp_enabled() {
  bool enabled = false;

#ifdef _OPENMP
  enabled = true;
#endif

  return enabled;
}

// [[Rcpp::export]]
arma::ivec timesTwo(arma::ivec x) {

#pragma omp parallel for
  for (size_t i = 0; i < x.size(); ++i) {
    x[i] *= 2;
  }

  return x ;
}

/*** R
openmp_enabled()
openmp_limits()
timesTwo(42)
*/
using C++ compiler: ‘Apple clang version 17.0.0 (clang-1700.4.4.1)’
using SDK: ‘MacOSX26.1.sdk’
clang++ -arch arm64 -std=gnu++17 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I../inst/include   -I"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library/Rcpp/include" -I"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library/RcppArmadillo/include" -I"/Users/ronin/Documents/GitHub/rcppcore/RcppArmadillo" -I/opt/R/arm64/include -I/opt/R/arm64/include   -fopenmp -fPIC  -falign-functions=64 -Wall -g -O2   -c test.cpp -o test.o
clang++: error: unsupported option '-fopenmp'
make: *** [test.o] Error 1
Error in Rcpp::sourceCpp("test.cpp") : 
  Error 1 occurred building shared library.
Standalone compilation via `sourceCpp()` with explicit `~/.R/Makevars` set

Set in ~/.R/Makevars:

CPPFLAGS += -Xclang -fopenmp
LDFLAGS += -lomp

Same script without plugins(openmp).

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
arma::ivec openmp_limits() {
  arma::ivec limit_data(3);

#ifdef _OPENMP
  limit_data[0] = omp_get_num_procs();
  limit_data[1] = omp_get_max_threads();
  limit_data[2] = omp_get_thread_limit();
#endif
  return limit_data;
}

// [[Rcpp::export]]
bool openmp_enabled() {
  bool enabled = false;

#ifdef _OPENMP
  enabled = true;
#endif

  return enabled;
}

// [[Rcpp::export]]
arma::ivec timesTwo(arma::ivec x) {

#pragma omp parallel for
  for (size_t i = 0; i < x.size(); ++i) {
    x[i] *= 2;
  }

  return x ;
}

/*** R
openmp_enabled()
openmp_limits()
timesTwo(42)
*/

Compilation trail

/Library/Frameworks/R.framework/Resources/bin/R CMD SHLIB --preclean -o 'sourceCpp_2.so' 'test.cpp' 
using C++ compiler: ‘Apple clang version 17.0.0 (clang-1700.4.4.1)’
using SDK: ‘MacOSX26.1.sdk’
clang++ -arch arm64 -std=gnu++17 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I../inst/include   -I"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library/Rcpp/include" -I"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library/RcppArmadillo/include" -I"/Users/ronin/Documents/GitHub/rcppcore/RcppArmadillo" -I/opt/R/arm64/include -I/opt/R/arm64/include -Xclang -fopenmp    -fPIC  -falign-functions=64 -Wall -g -O2   -c test.cpp -o test.o
clang++ -arch arm64 -std=gnu++17 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -L/Library/Frameworks/R.framework/Resources/lib -L/opt/R/arm64/lib -L/opt/R/arm64/lib -lomp -o sourceCpp_2.so test.o -L/Library/Frameworks/R.framework/Resources/lib -lRlapack -L/Library/Frameworks/R.framework/Resources/lib -lRblas -L/opt/gfortran/lib/gcc/aarch64-apple-darwin20.0/14.2.0 -L/opt/gfortran/lib -lemutls_w -lheapt_w -lgfortran -lquadmath -F/Library/Frameworks/R.framework/.. -framework R

Output

> openmp_enabled()
[1] TRUE

> openmp_limits()
           [,1]
[1,]         12
[2,]         12
[3,] 2147483647

> timesTwo(42)
     [,1]
[1,]   84

inlineCxxPlugin <- function(...) {
ismacos <- Sys.info()[["sysname"]] == "Darwin"
openmpflag <- if (ismacos) "" else "$(SHLIB_OPENMP_CFLAGS)"
openmpflag <- "$(SHLIB_OPENMP_CFLAGS)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be SHLIB_OPENMP_CXXFLAGS instead?

Suggested change
openmpflag <- "$(SHLIB_OPENMP_CFLAGS)"
openmpflag <- "$(SHLIB_OPENMP_CXXFLAGS)"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not matter in practice. There are three, they are always identical (?) and CRAN only complains if you use different ones between compiler and linker. I have long used the C variant for simplicity (though also probably not always).

edd@paul:~$ grep SHLIB_OPENMP_ /etc/R/Makeconf 
SHLIB_OPENMP_CFLAGS = -fopenmp
SHLIB_OPENMP_CXXFLAGS = -fopenmp
SHLIB_OPENMP_FFLAGS = -fopenmp
edd@paul:~$ 

#if !defined(ARMA_USE_OPENMP)
#if defined(__APPLE__) && defined(_OPENMP)
// User has OpenMP available, but check if they want it disabled
#ifndef RCPPARMA_MACOS_DISABLE_OPENMP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the standard prefix RCPPARMA? Or should we go RCPPARMADILLO ?

Copy link
Member Author

@eddelbuettel eddelbuettel Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Both better and more consistent. Will switch to long form.

(I think I followed your suggestion letter by letter though 😜 )

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eddelbuettel I know. I couldn't remember what was settled on for the prefix though post legacy/current defines.

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 15, 2025

I think given how messy the macOS situation is in general when it comes to OpenMP I am ok with requiring the file ~/.R/Makevars to have the right choices. Didn't using that file be a requirement anyway in the past in order to get the right compiler (flags) selected?

Or do you think we can do substantially better? By altering the openmp plugin in Rcpp?

Even if so (and we can look at that for Rcpp) it seems like this PR is ready and improves over the previous one? Big thank you for all the very detailed and helpful feedback.

@coatless
Copy link
Contributor

coatless commented Nov 18, 2025

@eddelbuettel yes to a large extent. The other option is setting session options via PKG_CXXFLAGS / PKG_LIBS.

There shouldn't be anymore hold up on this PR.

I'll shoot over a change PR for Rcpp openmp plugin. There's three parts:

  1. Checks if macOS
  2. Pulls R CMD config CXX
  3. Queries compiler and checks if Apple clang
    • Enables -Xclang -fopenmp
    • Otherwise, it's gcc so the usual -fopenmp works.

I'm not going to add a check on the plugin itself for whether OpenMP is correctly setup.

The goal of making the plugins PR is to have some level of "unity" across standalone C++ scripts so there isn't a "gotcha" if // [[Rcpp::plugins(openmp)]] is declared but the end user runs into an issue because -fopenmp isn't setup for Clang.

Edit: PR now available at:

RcppCore/Rcpp#1409

@eddelbuettel eddelbuettel merged commit 0828e75 into master Nov 18, 2025
6 checks passed
@eddelbuettel eddelbuettel deleted the feature/macos_openmp_tweaks branch November 18, 2025 12:14
@eddelbuettel
Copy link
Member Author

Hey @coatless I am going to hang this here for context... I had noticed that RcppArmadillo started to fail at r-universe for arm64 and (finally) pinged @jeroen who, apparently without having to look at it, asked me about OpenMP.

So I guess still more compilcations. Copying what I had sent to @jeroen:

Bad at r-universe

* checking examples ... OK
* checking for unstated dependencies in 'tests' ... OK
* checking tests ...
  Running 'tinytest.R'/Library/Frameworks/R.framework/Resources/bin/BATCH: line 60:  5715 Segmentation fault: 11  ${R_HOME}/bin/R -f ${in} ${opts} ${R_BATCH_OPTIONS} > ${out} 2>&1
 [20s/21s]
 [20s/21s] ERROR
Running the tests in 'tests/tinytest.R' failed.
Last 13 lines of output:
  test_Rlapack.R................    0 tests    
   *** caught segfault ***
  address 0x1, cause 'invalid permissions'
  
  Traceback:
   1: .Call(<pointer: 0x1030d5580>, n)
   2: cx_solve_band_test(n_tri)
   3: norm(cx_solve_band_test(n_tri), "2")
   4: eval(expr, envir = e)
   5: eval(expr, envir = e)
   6: FUN(X[[i]], ...)
   7: lapply(basename(testfiles), run_test_file, at_home = at_home,     verbose = verbose, color = color, remove_side_effects = remove_side_effects,     ...)
   8: run_test_dir(testdir, at_home = at_home, cluster = cluster, ...)
   9: tinytest::test_package("RcppArmadillo")
  An irrecoverable exception occurred. R is aborting now ...
* checking for unstated dependencies in vignettes ... OK

Good at macbuilder (when I wrap it up and submit it)

* checking examples ... [0s/1s] OK
* checking for unstated dependencies in 'tests' ... OK
* checking tests ... [29s/34s] OK
  Running 'tinytest.R' [29s/34s]
* checking for unstated dependencies in vignettes ... OK
* checking package vignettes ... OK
* checking re-building of vignette outputs ... [6s/10s] OK
* checking PDF version of manual ... [2s/2s] OK
* DONE
Status: OK
* using check arguments '--no-clean-on-error '

* elapsed time (check, wall clock): 1:17

@coatless
Copy link
Contributor

@eddelbuettel @jeroen I think this might be a result of forking happening since tinytest has the tests running in parallel to speed up execution:

markvanderloo/tinytest@e3b686e#diff-a79c7fbf2579d9427b9632cbf851d8c0ca78bbda730fbbd0bdf7687b60005f3cR837-R839

Maybe we add Sys.setenv(OMP_NUM_THREADS = 1) for the macOS case?

@eddelbuettel
Copy link
Member Author

Are you sure? I know tinytest quite well, may even have been the one in the suggesting an explicit parallel mode to Mark and am fairly certain he is not in parallel by default.

The entry point via tests/tinytest.R is commonly tinytest::test_package() and ncpu=NULL it will not parallelise:

Usage:

     test_package(
       pkgname,
       testdir = "tinytest",
       lib.loc = NULL,
       at_home = FALSE,
       ncpu = NULL,
       ...
     )

[....]

    ncpu: A positive integer, or a makeCluster object.

Glancing at the code seems to confirm that NULL for ncpu leads to serial tests. Happy to test and/or add the env var. But all our gymnastics where about making sure this case would "just work", no?

@coatless
Copy link
Contributor

@eddelbuettel my gut is saying something is being forked somewhere causing the OpenMP process to be a child and that's what is causing the segfault on macOS ARM64. From the r-universe check matrix, only the ARM64 macOS build fails. The x86_64 build works as desired.

segfault-openmp-arm64-but-not-on-x86-64-macos

I'm not at all familiar with r-universe infrastructure; but, that's my guess as @jeroen likely would have seen this issue appear elsewhere. I'll look later tonight.

This change can be pulled back if needed.

@eddelbuettel
Copy link
Member Author

I am all for a working hypothesis, and I (or you) can easily branch off RcppArmadillo and tickle a build with the number of threads forced to 1. I just don't think that tinytest is the smoking gun as I use it many other places too...

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 19, 2025

The other, even simpler, test would of course be GHA. I install mac and fortran via

    wget -q ${CRAN}/bin/macosx/big-sur-${ARCH}/base/R-${RVER}-${ARCH}.pkg -O /tmp/R-latest.pkg
    sudo installer -pkg "/tmp/R-latest.pkg" -target /

    wget -q https://mac.r-project.org/tools/gfortran-14.2-universal.pkg -O /tmp/gfortran.pkg
    sudo installer -pkg "/tmp/gfortran.pkg" -target /

which gets me default R and Fortran. What does one then do for OpenMP or is this enough as it is included these days?

@coatless
Copy link
Contributor

@eddelbuettel this should work:

- name: Install R for macOS
  if: runner.os == 'macOS'
  run: |
    CRAN="https://cran.r-project.org"
    RVER="4.5.2"
    ARCH="arm64"
    wget -q ${CRAN}/bin/macosx/big-sur-${ARCH}/base/R-${RVER}-${ARCH}.pkg -O /tmp/R-latest.pkg
    sudo installer -pkg "/tmp/R-latest.pkg" -target /

- name: Install gfortran for macOS
  if: runner.os == 'macOS'
  run: |
    wget -q https://mac.r-project.org/tools/gfortran-14.2-universal.pkg -O /tmp/gfortran.pkg
    sudo installer -pkg "/tmp/gfortran.pkg" -target /

- name: Install OpenMP for macOS
  if: runner.os == 'macOS'
  run: |
    curl -fsSL https://raw.githubusercontent.com/coatless-shell/openmp/main/install-openmp.sh | bash -s -- --yes

- name: Configure R Makevars for OpenMP
  if: runner.os == 'macOS'
  run: |
    mkdir -p ~/.R
    cat <<EOF > ~/.R/Makevars
    CPPFLAGS += -Xclang -fopenmp
    LDFLAGS += -lomp
    EOF

This is shaky on the gfortran part if the R version changes drastically. (R 4.3. would fail as it uses an earlier version, et cetera).

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 19, 2025

Adding this to my CI file now. One thing I realized is that I do have macOS in the matrix, so I given how GH flipped to arm64 for it I also test it ... meaning maybe @jeroen is the outlier? From last week's check log in [this macOS log file](https://github.com/RcppCore/RcppArmadillo/actions/runs/19465791346/job/55700383925) (under 'Configure') with the configure and the generated ~/.R/Makevars: no OpenMP here on macOS:

checking whether R CMD SHLIB can already compile programs using OpenMP... no
checking for macOS... found
checking for macOS Apple compiler... configure: creating ./config.status
config.status: creating inst/include/RcppArmadillo/config/RcppArmadilloConfigGenerated.h
config.status: creating src/Makevars
## -*- mode: makefile; -*-
PKG_CPPFLAGS = -I../inst/include -DARMA_USE_CURRENT
PKG_CXXFLAGS = 
PKG_LIBS=  $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)

So if @jeroen is right, adding OpenMP to the mix should break things under the current setup implying it may need work.

@jeroen
Copy link

jeroen commented Nov 19, 2025

Yes building with OpenMP on MacOS requires some extra setup on the server, see https://mac.r-project.org/openmp/

Although from the log file of the failing build, I don't think your configure is picking up on it? https://github.com/r-universe/rcppcore/actions/runs/19474394547/job/55752797364

@eddelbuettel
Copy link
Member Author

We seem to be converging because @coatless (see above) suggested to pull in a script he offers (which seems to follow that page) and also to set the ~/.R/Makevars accordingly. So I now do this (after bungling the matrix condition twice even though I used it correctly below in the same file to turn on coverage, sigh...)

      - name: OpenMP for macOS
        if: ${{ matrix.os == 'macos-latest' }}
        run: |
          curl -fsSL https://raw.githubusercontent.com/coatless-shell/openmp/main/install-openmp.sh | bash -s -- --yes
          mkdir -p ~/.R
          cat <<EOF > ~/.R/Makevars
          CPPFLAGS += -Xclang -fopenmp
          LDFLAGS += -lomp
          EOF
          cat ~/.R/Makevars

and this now gets us green:

image

(Coverage under Linux takes longer, and compiler is slower, sadly)

So if I turn r-universe support on for this branch, it will break again as you do not have this server support so we need to make our configure test more potent to detect that? Is that your reading?

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 19, 2025

No beans, @coatless:

image

Frustrating. It has been a few weeks dancing with macOS on this with zero to show.

PS Mind you that is still us / me in the package as we have the important

checking whether R CMD SHLIB can already compile programs using OpenMP... yes

so question now is why we see

checking for macOS Apple compiler... checking for OpenMP... missing

@eddelbuettel
Copy link
Member Author

Yes there was a case missng for the newly add ~/.R/Makevars

I am now five more attempts in and have nothing to show. @coatless I am coming close to throwing all this out. You made me add variables CPPFLAGS and LIBS in ~/.R/Makevars but that is not what your test snippets test for so it all falls apart and ... remains so brittle. Not great.

@eddelbuettel
Copy link
Member Author

I may have fixed it. The key was to grep from R CMD config LDFLAGS and not from R CMD config --ldlflags. The latter one shows what we need to link against R in frontends:

  --ldflags         print linker flags needed for linking a front-end
                    against the R library 

We want

LDFLAGS       linker flags, e.g. -L<dir> if you have libraries in a                                    
              nonstandard directory <dir>   

@eddelbuettel
Copy link
Member Author

So I have it sorted in the 'worked at GH with OpenMP on linux and macos' sense (an improvement) yet it still conks under r-universe. @jeroen can we inject log printing from our end just how I could add to the GHA yaml to show me configure results and other variables?

@jeroen
Copy link

jeroen commented Nov 19, 2025

r-universe just logs the verbatim output from R CMD INSTALL so anything you print in configure should be in the log?

@eddelbuettel
Copy link
Member Author

Very good. I always get lost between R CMD check not showing it and R CMD INSTALL doing. I should have checked the log to see that it is in fact R CMD INSTALL.

@eddelbuettel
Copy link
Member Author

But it is still no solution. We now at your end get

checking whether R CMD SHLIB can already compile programs using OpenMP... yes
checking for macOS... found macOS
checking for macOS Apple compiler... not found
checking for OpenMP on macOS... missing
configure: creating ./config.status

because, I presume, the required OpenMP add-in is not installed. In that case it should just do what it always did, and what always worked. But now it falls over in linear algebra test that used to pass for years at your end too.

So I am lost.

* checking tests ...
  Running ‘tinytest.R’/Library/Frameworks/R.framework/Resources/bin/BATCH: line 60:  5117 Segmentation fault: 11  ${R_HOME}/bin/R -f ${in} ${opts} ${R_BATCH_OPTIONS} > ${out} 2>&1
 [28s/29s]
 [28s/29s] ERROR
Running the tests in ‘tests/tinytest.R’ failed.
Last 13 lines of output:
  test_Rlapack.R................    0 tests    
   *** caught segfault ***
  address 0x1, cause 'invalid permissions'
  
  Traceback:
   1: .Call(<pointer: 0x100ab5580>, n)
   2: cx_solve_band_test(n_tri)
   3: norm(cx_solve_band_test(n_tri), "2")
   4: eval(expr, envir = e)
   5: eval(expr, envir = e)
   6: FUN(X[[i]], ...)
   7: lapply(basename(testfiles), run_test_file, at_home = at_home,     verbose = verbose, color = color, remove_side_effects = remove_side_effects,     ...)
   8: run_test_dir(testdir, at_home = at_home, cluster = cluster, ...)
   9: tinytest::test_package("RcppArmadillo")
  An irrecoverable exception occurred. R is aborting now ...
* checking for unstated dependencies in vignettes ... OK

@jeroen
Copy link

jeroen commented Nov 19, 2025

I'm quite sure it is installed because data.table does pick up on OpenMP on MacOS. Maybe your autoconf script can be a bit more explicit on why it thinks macOS Apple compiler not found ?

The linear algebra error may be something else then? Nothing has changed on our end, only that R-4.5.1 was updated to R-4.5.2.

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 19, 2025

Do not focus on that one line, there is more context:

checking whether R CMD SHLIB can already compile programs using OpenMP... yes   <- so, we compiled
checking for macOS... found macOS
checking for macOS Apple compiler... not found                                  <- ignore this
checking for OpenMP on macOS... found and suitable                              <- this matters too
configure: creating ./config.status

@coatless is expert here, I am just trying to accommodate. Following his script we have

image

and that is used in the above (when OpenMP is around).

@eddelbuettel
Copy link
Member Author

Also, what is a bit weird is that the CI of course passed all this time at GitHub so in some subtle way your r-universe setup may be different from what I do on 'macos-latest' (following the default steps from my r-ci script which bring in what Simon provides as the R and Fortran download). And now augmented by what James has provided for OpenMP.

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 19, 2025

I like your data.table example though. It clearly compiles with -fopenmp and uses the -lomp linker choice:

[...]
clang -arch arm64 -std=gnu2x -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG   -I/opt/R/arm64/include   -Xclang -fopenmp   -fPIC  -falign-functions=64 -Wall -g -O2  -c wrappers.c -o wrappers.o
clang -arch arm64 -std=gnu2x -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -L/Library/Frameworks/R.framework/Resources/lib -L/opt/R/arm64/lib -o data.table.so assign.o between.o bmerge.o chmatch.o cj.o coalesce.o dogroups.o fastmean.o fcast.o fifelse.o fmelt.o forder.o frank.o fread.o freadR.o froll.o frollR.o frolladaptive.o frollapply.o fsort.o fwrite.o fwriteR.o gsumm.o idatetime.o ijoin.o init.o inrange.o mergelist.o nafill.o negate.o nqrecreateindices.o openmp-utils.o programming.o quickselect.o rbindlist.o reorder.o shellsort.o shift.o snprintf.o subset.o transpose.o types.o uniqlist.o utils.o vecseq.o wrappers.o -lomp -lz -F/Library/Frameworks/R.framework/.. -framework R
PKG_CFLAGS = -Xclang -fopenmp
PKG_LIBS = -lomp -lz
[...]

Maybe we should try that. Part of this may be on me; I had hoped/expected we could just rely on R's own $(SHLIB_OPENMP_CXXFLAGS) but apparently not. Sigh.

Edit: Turns out data.table does rely on SHLIB_OPENMP_CFLAGS.

if [ "${R_OPENMP_ENABLED}" = "0" ]; then
  echo "***"
  echo "*** OpenMP not supported! data.table uses OpenMP to automatically"
  echo "***   parallelize operations like sorting, grouping, file reading, etc."
  echo "*** For details on how to install the necessary toolchains on your OS see:"
  echo "***   https://github.com/Rdatatable/data.table/wiki/Installation"
  echo "*** Continuing installation without OpenMP support..."
  echo "***"
  sed -e "s|@openmp_cflags@||" src/Makevars.in > src/Makevars
else
  sed -e "s|@openmp_cflags@|\$(SHLIB_OPENMP_CFLAGS)|" src/Makevars.in > src/Makevars
fi

Edit 2 And uses @PKG_CFLAGS@ and @PKG_LIBS@ in addition to @openmp_cflags@.

@jeroen
Copy link

jeroen commented Nov 19, 2025

After some digging, I found that the MacOS build for R-base has changed to another BLAS implementation in 4.5.2.

# R 4.5.0
> extSoftVersion()['BLAS']
                                                                               BLAS
"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib"
# R 4.5.2
> extSoftVersion()['BLAS']
                                                                                                             BLAS
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib"

Maybe this change is related to the test suddenly failing. Can you update https://eddelbuettel.github.io/r-ci/run.sh to use R-4.5.2 such that we can rule out this difference between your CI and r-universe?

@eddelbuettel
Copy link
Member Author

Nice work. That is a manual toggle I can flip later today.

I had in the meantime tossed my configure out and switch to the data.table one. That has not yet bubbled up at r-universe in the branch I opted into but should 'shortly'. We'll see what that says.

@jeroen
Copy link

jeroen commented Nov 19, 2025

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 19, 2025

When I just looked it still said '5 hours ago' for the update. Maybe the cache tricked me.

Looks like it did and this failed too. Boo. One more test of not manually download the OpenMP bits to do (to be closer to data.table).

@jeroen
Copy link

jeroen commented Nov 19, 2025

It did pick up on openmp, and that seems to work on all other macos flavors. I think the BLAS crash is another issue .

@eddelbuettel
Copy link
Member Author

Oh, no, wait, that is what I get at r-universe already so I don't need to simulate it at my CI. You are correct.

So ... still a mess. But at least I have a smoking gun from you with the 4.5.0 / 4.5.2 issue.

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 19, 2025

Slightly longer story (from this blog post) is that RcppArmadillo was actually running the containerised r-ci and that one was already at RVER=4.5.1. Anyway, I switched that to 4.5.2 as we can now, and tickled a new build.

And you were once again spot on. It also breaks, and in the same spot as r-universe. "Interesting." At least one less mystery there.

@eddelbuettel
Copy link
Member Author

And as it always goes there is no justice or fairness in this world. The required diff was to skip the one test file because R simply does not like complex variables.

modified   inst/tinytest/test_Rlapack.R
@@ -20,6 +20,8 @@
 
 library(RcppArmadillo)
 
+if (Sys.info()['sysname'] == "Darwin") exit_file("Skip on macOS")
+
 if (isFALSE(tryCatch({svd(matrix(complex(1, 1, 1),1,1)); TRUE}, error=function(e) FALSE)))
     exit_file("Skipping for lack of Fortran complex functions in this R build")

Can you eyeball why the test we already had there would no longer work? Looking at it it seems like an older/poorer way to get on the bad side of tryCatch()....

@eddelbuettel
Copy link
Member Author

And with that the r-universe issue is solved too (in the branch, mainline below still busted).

image

So it never was OpenMP. Sigh.

@coatless
Copy link
Contributor

coatless commented Nov 20, 2025

For the folks following at home:

  1. The underlying issue wasn't OpenMP related; but, is instead BLAS related due to a switch between R's crippled BLAS to Apple's vecLib library between R 4.5.0 and R 4.5.2
  2. R made known a shift starting with R 4.5.0 indicating their internal BLAS is being sunset in favor of external BLAS and LAPACK.
  3. Part of this sunsetting brought on with R 4.5 and onward regarding the addition of two new routines (dgemmtr and zgemmtr both mat-mat ops) that require R to be built with --with-2025blas, so this may bite further...
  4. The CI changes for this repo are taking place on a separate branch targeting the CI build system.

I suppose one question that arises given Armadillo interacts with the BLAS is:

Does the augmented BLAS (vecLib + additional routines) override how Armadillo is expecting to handle complex number matrix ops being performed similar to the hidden argument mess back in the day?

Appendix

Relevant R 4.5.0 release note section:

R 4.5.0 release note section discussing the BLAS

CI Build Branch: master...feature/macos_openmp

@eddelbuettel
Copy link
Member Author

eddelbuettel commented Nov 20, 2025

I also have the feeling we need to reach out to Simon / demonstrate that something is possibly borked here. We may need to create an R + C only little demo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants