From e1051b86de78b41d93bb1ea82703c4fd711893ac Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 09:40:15 -0700 Subject: [PATCH 1/4] ship versioned tbb libraries on linux again The oneTBB cmake build only versions its output on Windows, so on Linux RcppParallel 6.0.x installed unversioned libraries (e.g. 'libtbb.so' with SONAME 'libtbb.so'). RcppParallel 5.1.11 and earlier shipped a real 'libtbb.so.2' plus a 'libtbb.so' redirect, inherited from Intel TBB's make-based build (which set the SONAME from TBB_COMPATIBLE_INTERFACE_VERSION). Binaries compiled against those releases recorded a load-time dependency on 'libtbb.so.2', and so fail to load after an upgrade with "libtbb.so.2: cannot open shared object file". Restore the versioned layout for the bundled TBB on Linux by renaming 'libtbb*.so' to 'libtbb*.so.2' and re-creating 'libtbb*.so' as a symlink. A symlink (rather than a linker script) is used so the name resolves at runtime as well as at link time. --- NEWS.md | 8 ++++++++ src/install.libs.R | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/NEWS.md b/NEWS.md index 0d676c97..272dd3c3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,13 @@ # RcppParallel (development version) +* On Linux, the bundled TBB libraries are once again installed with versioned + names (e.g. `libtbb.so.2`) plus an unversioned `libtbb.so` symlink, matching + the layout shipped by RcppParallel 5.1.11 and earlier. The oneTBB cmake build + produces only unversioned libraries on Linux, so binaries compiled against + those releases (which recorded a load-time dependency on `libtbb.so.2`) would + otherwise fail to load after an upgrade with "libtbb.so.2: cannot open shared + object file". + * Fixed installation on Windows toolchains providing an older (non-oneTBB) copy of TBB, e.g. Rtools42: the tbb stub library is now built by re-exporting the static TBB library, rather than wrapping the oneTBB diff --git a/src/install.libs.R b/src/install.libs.R index aeab1dc2..324dad61 100644 --- a/src/install.libs.R +++ b/src/install.libs.R @@ -50,6 +50,11 @@ system2("cp", c("-P", shQuote(tbbLib), shQuote(tbbDest))) } + # restore the versioned library layout shipped by RcppParallel 5.1.11 + # and earlier (a real 'libtbb.so.2' plus a 'libtbb.so' symlink) + if (Sys.info()[["sysname"]] == "Linux") + versionBundledTbbLibraries(tbbDest) + } else { # using system tbb @@ -145,6 +150,46 @@ buildTbbStub <- function(tbbDest) { } +# Give the bundled TBB libraries the versioned '.so.' names, plus an +# unversioned 'libtbb.so' compatibility symlink, that RcppParallel shipped on +# Linux through 5.1.11. That layout came from Intel TBB's make-based build, +# which set the library SONAME from TBB_COMPATIBLE_INTERFACE_VERSION (2) and +# emitted 'libtbb.so' as a linker script pointing at 'libtbb.so.2'. The +# oneTBB cmake build only versions its output on Windows, so on Linux it +# produces unversioned libraries (e.g. 'libtbb.so' with SONAME 'libtbb.so') +# instead. Binaries compiled against RcppParallel <= 5.1.11 recorded a +# load-time dependency on 'libtbb.so.2'; without this, they fail to load after +# an upgrade with "libtbb.so.2: cannot open shared object file". +versionBundledTbbLibraries <- function(tbbDest) { + + # downstream binaries look for exactly '.so.2', matching the SONAME suffix + # the old TBB build derived from TBB_COMPATIBLE_INTERFACE_VERSION + suffix <- "2" + + libs <- list.files(tbbDest, pattern = "^libtbb.*\\.so$", full.names = TRUE) + for (lib in libs) { + + # leave symlinks and linker scripts (i.e. an already-versioned layout) + # untouched; only real, unversioned libraries need renaming + if (nzchar(Sys.readlink(lib))) + next + + versioned <- paste0(lib, ".", suffix) + if (file.exists(versioned)) + next + + writeLines(sprintf("** versioning tbb library '%s' -> '%s'", + basename(lib), basename(versioned))) + + # 'libtbb.so' -> 'libtbb.so.2', then re-create 'libtbb.so' as a + # relative symlink pointing back at the versioned library + file.rename(lib, versioned) + file.symlink(basename(versioned), lib) + + } + +} + # Report which TBB libraries are about to be installed, and from where. # Stale or unexpected libraries copied here have historically been a # subtle source of load failures in downstream packages, so make the From 16bb5670e44438dbe021a84731990b90edb85911 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 09:43:57 -0700 Subject: [PATCH 2/4] split versioning log message onto separate lines --- src/install.libs.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/install.libs.R b/src/install.libs.R index 324dad61..20a63ea1 100644 --- a/src/install.libs.R +++ b/src/install.libs.R @@ -178,8 +178,9 @@ versionBundledTbbLibraries <- function(tbbDest) { if (file.exists(versioned)) next - writeLines(sprintf("** versioning tbb library '%s' -> '%s'", - basename(lib), basename(versioned))) + fmt <- "** versioning tbb library '%s' -> '%s'" + msg <- sprintf(fmt, basename(lib), basename(versioned)) + writeLines(msg) # 'libtbb.so' -> 'libtbb.so.2', then re-create 'libtbb.so' as a # relative symlink pointing back at the versioned library From d7be8ab1afaec199159305bff26f296fecc36e41 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 09:44:51 -0700 Subject: [PATCH 3/4] fall back to copying when the libtbb.so symlink can't be created --- src/install.libs.R | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/install.libs.R b/src/install.libs.R index 20a63ea1..b1f1edae 100644 --- a/src/install.libs.R +++ b/src/install.libs.R @@ -185,7 +185,18 @@ versionBundledTbbLibraries <- function(tbbDest) { # 'libtbb.so' -> 'libtbb.so.2', then re-create 'libtbb.so' as a # relative symlink pointing back at the versioned library file.rename(lib, versioned) - file.symlink(basename(versioned), lib) + linked <- tryCatch( + file.symlink(basename(versioned), lib), + warning = function(w) FALSE + ) + + # if the symlink couldn't be created (e.g. a filesystem without symlink + # support), fall back to a plain copy so that 'libtbb.so' still exists -- + # RcppParallel's own shared object records a load-time dependency on it + if (!isTRUE(linked)) { + writeLines("** could not create symlink; copying instead") + file.copy(versioned, lib, overwrite = TRUE) + } } From 4a5a1fe5ed764a227b30984866abc8739227c3c3 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 10:28:16 -0700 Subject: [PATCH 4/4] fail loudly when tbb libraries cannot be versioned --- src/install.libs.R | 92 +++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/src/install.libs.R b/src/install.libs.R index b1f1edae..77732a58 100644 --- a/src/install.libs.R +++ b/src/install.libs.R @@ -167,39 +167,73 @@ versionBundledTbbLibraries <- function(tbbDest) { suffix <- "2" libs <- list.files(tbbDest, pattern = "^libtbb.*\\.so$", full.names = TRUE) - for (lib in libs) { - - # leave symlinks and linker scripts (i.e. an already-versioned layout) - # untouched; only real, unversioned libraries need renaming - if (nzchar(Sys.readlink(lib))) - next - - versioned <- paste0(lib, ".", suffix) - if (file.exists(versioned)) - next - - fmt <- "** versioning tbb library '%s' -> '%s'" - msg <- sprintf(fmt, basename(lib), basename(versioned)) - writeLines(msg) - - # 'libtbb.so' -> 'libtbb.so.2', then re-create 'libtbb.so' as a - # relative symlink pointing back at the versioned library - file.rename(lib, versioned) - linked <- tryCatch( - file.symlink(basename(versioned), lib), - warning = function(w) FALSE - ) + for (lib in libs) + versionBundledTbbLibrary(lib, paste0(lib, ".", suffix)) - # if the symlink couldn't be created (e.g. a filesystem without symlink - # support), fall back to a plain copy so that 'libtbb.so' still exists -- - # RcppParallel's own shared object records a load-time dependency on it - if (!isTRUE(linked)) { - writeLines("** could not create symlink; copying instead") - file.copy(versioned, lib, overwrite = TRUE) - } +} +# Version a single bundled TBB library. Failing to produce the versioned +# layout is an error: a partial or silently-skipped layout would only +# resurface later as load failures in downstream binaries, so fail the +# install loudly instead. +versionBundledTbbLibrary <- function(lib, versioned) { + + # leave symlinks (i.e. an already-versioned layout) untouched; only real, + # unversioned libraries need renaming + if (nzchar(Sys.readlink(lib))) + return(invisible(FALSE)) + + # only version actual ELF libraries; this leaves linker scripts alone + # (in old-style layouts, 'libtbb.so' is an INPUT() script and the real + # library already sits at 'libtbb.so.2'), while still replacing a stale + # 'libtbb.so.2' left behind by a previous installation + if (!isElfFile(lib)) + return(invisible(FALSE)) + + fmt <- "** versioning tbb library '%s' -> '%s'" + msg <- sprintf(fmt, basename(lib), basename(versioned)) + writeLines(msg) + + # 'libtbb.so' -> 'libtbb.so.2' + if (!isTRUE(file.rename(lib, versioned))) { + fmt <- "couldn't rename '%s' to '%s'" + stop(sprintf(fmt, lib, versioned)) } + # re-create 'libtbb.so' as a relative symlink pointing back at the + # versioned library + linked <- tryCatch( + file.symlink(basename(versioned), lib), + warning = function(w) FALSE + ) + if (isTRUE(linked)) + return(invisible(TRUE)) + + # if the symlink couldn't be created (e.g. a filesystem without symlink + # support), fall back to a plain copy so that 'libtbb.so' still exists. + # the library has already been renamed at this point, so a failed copy + # would leave no 'libtbb.so' at all; fail loudly rather than complete a + # broken install + writeLines("** couldn't create symlink; copying instead") + copied <- tryCatch( + file.copy(versioned, lib, overwrite = TRUE), + warning = function(w) FALSE + ) + if (!isTRUE(copied)) { + fmt <- "couldn't copy '%s' to '%s'" + stop(sprintf(fmt, versioned, lib)) + } + + invisible(TRUE) + +} + +isElfFile <- function(path) { + header <- tryCatch( + readBin(path, "raw", n = 4L), + condition = function(cnd) raw() + ) + identical(header, charToRaw("\x7fELF")) } # Report which TBB libraries are about to be installed, and from where.