Skip to content

Commit de75c55

Browse files
authored
resolve the tbb library path against the installed package on windows (#273)
tbbLibraryPath() returned NULL on Windows (#270). The cause is the set of filenames it looks for: the static archives 'libtbb12.a' and 'libtbb.a'. Neither is ever installed with the package. TBB is linked statically into RcppParallel.dll on Windows, and the only TBB library installed alongside it is the tbb.dll stub, so look for that first. The archives are kept as later candidates, for a TBB_LIB pointed at an Rtools tree at runtime. Note this is not the TBB_LIB problem the report proposed, though the report is right that TBB_LIB is also wrong here. The archive lookup fails even when the search directory is correct: a bundled-oneTBB build records no TBB_LIB, so tbbRoot() already answered with the package's own lib directory, and tbbLibraryPath("tbb") still returned NULL. The lookup only ever succeeded when the package happened to run on the machine that built it against an Rtools TBB -- and then answered with a path into Rtools, a build dependency, rather than with anything the installed package uses. Separately, tbbLibraryPath(NULL) and tbbRoot() return the search directory itself, with no existence check, so there the configure-time TBB_LIB really was the defect: for a pre-built binary it names a directory on the build machine ('D:/rtools45/...' in the report). Answer with our own library directory on Windows instead. This is already how tbbLdFlags() and loadTbbLibrary() behave; tbbRoot() was the last place trusting TBB_LIB. Scoped to Windows deliberately. Off Windows a configured TBB_LIB names where the libraries genuinely are -- the package symlinks them into its own lib directory rather than copying -- and tbbLdFlags() emits it as an -rpath. tbbLibraryPath("tbbmalloc") now returns NULL on Windows, which is honest: no separate tbbmalloc library is installed there.
1 parent 69dac53 commit de75c55

4 files changed

Lines changed: 105 additions & 3 deletions

File tree

NEWS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,24 @@
88
loaded into the process, and failed with "Library not loaded:
99
@rpath/libtbb.dylib" otherwise. (#209)
1010

11+
* Fixed `RcppParallel::tbbLibraryPath()` returning `NULL` on Windows. It looked
12+
for the static archives `libtbb12.a` / `libtbb.a`, which are never installed
13+
with the package: TBB is linked statically into `RcppParallel.dll` there, and
14+
the only TBB library installed alongside it is the `tbb.dll` stub, which is
15+
what it now finds. Previously it succeeded only when the package happened to
16+
be running on the machine that built it against an Rtools TBB, and even then
17+
answered with a path into Rtools -- a build dependency -- rather than with
18+
anything the installed package uses. Note that `tbbLibraryPath("tbbmalloc")`
19+
returns `NULL` on Windows, as no separate tbbmalloc library is installed
20+
there. (#270)
21+
22+
* `RcppParallel::tbbLibraryPath()` called with no arguments, and the internal
23+
`tbbRoot()`, no longer report the Rtools directory recorded when the package
24+
was configured. Unlike the named-library form, these return their answer
25+
without checking that it exists, so for a pre-built binary (e.g. the CRAN
26+
build) they named a directory on the machine that built the package. On
27+
Windows both now report the package's own library directory. (#270)
28+
1129
* Fixed an issue where compiling code including `tbb/parallel_for_each.h`
1230
could fail with toolchains that accept `-std=c++20` but provide a
1331
pre-C++20 standard library, with errors of the form "no member named

R/tbb.R

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ tbbLibraryPath <- function(name = NULL) {
2222
return(tbbRoot)
2323

2424
# form library names
25+
#
26+
# on Windows the library we install is the 'tbb.dll' stub -- TBB itself is
27+
# inside RcppParallel.dll -- so look for that first. the static archives are
28+
# still tried afterwards, for a TBB_LIB pointed at an Rtools tree at runtime
2529
tbbLibNames <- list(
2630
"Darwin" = paste0("lib", name, ".dylib"),
27-
"Windows" = paste0("lib", name, c("12", ""), ".a"),
31+
"Windows" = c(paste0(name, ".dll"), paste0("lib", name, c("12", ""), ".a")),
2832
"SunOS" = paste0("lib", name, ".so"),
2933
"Linux" = paste0("lib", name, c(".so.2", ".so"))
3034
)
@@ -146,6 +150,15 @@ tbbLdFlags <- function() {
146150

147151
tbbRoot <- function() {
148152

153+
# on Windows, always answer with our own library directory. TBB is linked
154+
# statically into RcppParallel.dll there, and the only TBB library we
155+
# install is the tbb.dll stub, so that directory is the whole story. TBB_LIB
156+
# is worse than useless here: it records the Rtools tree of whichever
157+
# machine ran configure, which for a pre-built binary is a path that need
158+
# not exist on the user's machine at all (#270)
159+
if (is_windows())
160+
return(archSystemFile("lib"))
161+
149162
if (nzchar(TBB_LIB))
150163
return(TBB_LIB)
151164

R/zzz.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ loadTbbLibrary <- function(name) {
2121
if (is_windows()) {
2222

2323
# NOTE: resolve against the package's own library directory, where
24-
# install.libs.R places the stub; tbbRoot() would prefer TBB_LIB,
25-
# which on Windows points at Rtools and holds only static libraries
24+
# install.libs.R places the stub. this is what tbbRoot() reports on
25+
# Windows too; spell it out here so that loading during .onLoad does
26+
# not depend on the configured TBB_LIB in any way
2627
path <- archSystemFile("lib", paste0(name, ".dll"))
2728
if (!file.exists(path)) {
2829
verboseMessage("tbb library '%s' not found in package 'lib' folder; skipping", name)

tests/test-tbb-library-path.R

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Unit tests for tbbRoot() and tbbLibraryPath(), which resolve the TBB library
2+
# that the *installed* package uses.
3+
#
4+
# Regression tests for #270: on Windows these consulted TBB_LIB, the library
5+
# directory recorded when configure ran. For a package built from source that
6+
# happens to be a real path, but for a pre-built binary it names the Rtools
7+
# tree of the build machine -- so tbbRoot() reported a directory that need not
8+
# exist on the user's machine (a 'D:/rtools45/...' path, in the report), and
9+
# tbbLibraryPath() then found nothing and returned NULL.
10+
#
11+
# The invariant these tests pin down is that both functions describe the
12+
# installation actually in use: whatever they return must exist.
13+
14+
RcppParallel:::test_init()
15+
16+
# tbbRoot() must never report a directory that isn't there. system.file()
17+
# returns "" when it cannot resolve a path, so that sentinel is allowed; any
18+
# non-empty answer has to be a real directory.
19+
root <- tbbRoot()
20+
assert(is.character(root))
21+
assert(length(root) == 1L)
22+
if (nzchar(root))
23+
assert(dir.exists(root))
24+
25+
# on Windows the answer is always the package's own library directory, since
26+
# TBB is statically linked into RcppParallel.dll and the tbb.dll stub is the
27+
# only TBB library installed. in particular it must not depend on TBB_LIB.
28+
if (is_windows()) {
29+
30+
assert(identical(root, archSystemFile("lib")))
31+
32+
saved <- Sys.getenv("TBB_LIB", unset = NA)
33+
Sys.setenv(TBB_LIB = "Z:/nonexistent/rtools/lib")
34+
assert(identical(tbbRoot(), archSystemFile("lib")))
35+
if (is.na(saved)) Sys.unsetenv("TBB_LIB") else Sys.setenv(TBB_LIB = saved)
36+
37+
}
38+
39+
# tbbLibraryPath(NULL) reports the directory, and agrees with tbbRoot() when
40+
# TBB_LIB is not set in the environment
41+
if (is.na(Sys.getenv("TBB_LIB", unset = NA)))
42+
assert(identical(tbbLibraryPath(), root))
43+
44+
# a named library either resolves to a file that exists, or isn't found at all.
45+
# it must never name a file that isn't there -- that was the shape of #270.
46+
for (name in c("tbb", "tbbmalloc", "tbbmalloc_proxy")) {
47+
48+
path <- tbbLibraryPath(name)
49+
if (is.null(path))
50+
next
51+
52+
assert(is.character(path))
53+
assert(length(path) == 1L)
54+
assert(file.exists(path))
55+
56+
}
57+
58+
# the user-visible symptom from #270: tbbLibraryPath("tbb") returned nothing
59+
# from a perfectly good installation. assert this only on Windows, where the
60+
# installed layout is known exactly -- install.libs.R always builds the
61+
# tbb.dll stub when TBB is enabled, and fails the install if it cannot. off
62+
# Windows the library may legitimately be unresolvable, e.g. a system TBB
63+
# whose distro package ships no unversioned 'libtbb.so' development symlink.
64+
if (is_windows() && TBB_ENABLED) {
65+
tbb <- tbbLibraryPath("tbb")
66+
assert(!is.null(tbb))
67+
assert(file.exists(tbb))
68+
}
69+
70+
writeLines("all tbbLibraryPath() tests passed")

0 commit comments

Comments
 (0)