Skip to content

Commit 05db507

Browse files
committed
address review findings on the windows onetbb build
- configure: derive TBB_NAME / TBB_MALLOC_NAME whenever an Rtools tree is adopted, not only when it is oneTBB. gating both on 'oneapi' broke an explicitly configured legacy TBB_LIB, which linked -ltbb against a directory holding libtbb_static.a - configure: don't let the cmake probe throw. it now runs on Windows, where an unrunnable CMAKE or unparseable --version output has to degrade to tinythread rather than fail the configure - configure: correct the required cmake version in the error message (3.5, matching the check and DESCRIPTION), and rename useBundledTbb to buildBundledTbb so it no longer collides with the install.libs.R function - install.libs.R: report the Windows static build explicitly instead of logging 'no tbb runtime libraries found', which reads like a failure - tbb.R: note that the stub carries its own oneTBB runtime, so resolving against it means a second scheduler rather than a free fallback - downstream check: assert the built library imports nothing from the tbb.dll stub, which is the property that comment now relies on - GNU.cmake: probe with an empty file rather than the null device, which drops the WIN32 special case and removes the unverified 'gcc -c NUL' - add windows-11-arm to the downstream matrix, restore the trailing newline in observer_proxy.cpp, pin rstan.yaml against DESCRIPTION, checkout@v4 - regenerate patches/legacy_tbb_abi.diff and patches/mingw_gnu_cmake.diff
1 parent 222d02e commit 05db507

11 files changed

Lines changed: 174 additions & 74 deletions

File tree

.github/scripts/tbb-downstream-check.R

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ if (!RcppParallel:::TBB_ENABLED)
7575
makevars <- c(
7676
"CXX_STD = CXX17",
7777
sprintf("PKG_CPPFLAGS = -I\"%s\"", system.file("include", package = "RcppParallel")),
78-
"PKG_CPPFLAGS += $(shell \"${R_HOME}/bin/Rscript\" -e \"RcppParallel::CxxFlags()\" | tail -n 1)",
78+
"PKG_CPPFLAGS += $(shell \"${R_HOME}/bin${R_ARCH_BIN}/Rscript\" -e \"RcppParallel::CxxFlags()\" | tail -n 1)",
7979
"PKG_LIBS += $(shell \"${R_HOME}/bin${R_ARCH_BIN}/Rscript\" -e \"RcppParallel::RcppParallelLibs()\" | tail -n 1)"
8080
)
8181

@@ -95,8 +95,45 @@ status <- system(paste(shQuote(file.path(R.home("bin"), "R")), "CMD SHLIB check.
9595
if (status != 0L)
9696
stop("downstream translation unit failed to build")
9797

98+
dllName <- paste0("check", .Platform$dynlib.ext)
99+
100+
# Check where the TBB symbols actually came from. RcppParallelLibs() offers
101+
# the 'tbb.dll' stub after '-lRcppParallel', so anything RcppParallel.dll
102+
# failed to re-export still links -- but the stub carries its own copy of the
103+
# oneTBB runtime, so binding to it means running against a second, independent
104+
# scheduler. That is a silent failure, unlike the link error it replaced, so
105+
# assert here that the whole TBB surface resolved from RcppParallel.dll.
106+
if (.Platform$OS.type == "windows") {
107+
108+
objdump <- Sys.which("objdump")
109+
if (!nzchar(objdump)) {
110+
111+
writeLines("** objdump not found; skipping the import table check")
112+
113+
} else {
114+
115+
output <- system2(objdump, c("-p", shQuote(dllName)), stdout = TRUE, stderr = TRUE)
116+
117+
# each imported library opens a 'DLL Name:' block listing the symbols
118+
# taken from it, and runs until the next such block
119+
starts <- grep("DLL Name:", output, fixed = TRUE)
120+
imported <- sub(".*DLL Name:[[:space:]]*", "", output[starts])
121+
writeLines(sprintf("imports: %s", paste(imported, collapse = ", ")))
122+
123+
index <- which(tolower(imported) == "tbb.dll")
124+
if (length(index)) {
125+
ends <- c(starts[-1L], length(output) + 1L)
126+
writeLines(output[seq(starts[[index]], ends[[index]] - 1L)])
127+
stop("the downstream library imports the above from the tbb.dll stub; ",
128+
"these should have resolved from RcppParallel.dll instead")
129+
}
130+
131+
}
132+
133+
}
134+
98135
# loading also proves any load-time dependency on the tbb stub resolves
99-
dll <- dyn.load(paste0("check", .Platform$dynlib.ext))
136+
dll <- dyn.load(dllName)
100137
on.exit(dyn.unload(dll[["path"]]), add = TRUE)
101138

102139
result <- .Call("tbb_downstream_check")

.github/workflows/R-CMD-check.yaml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
3232
R_KEEP_PKG_SOURCE: yes
3333
steps:
34-
- uses: actions/checkout@v3
34+
- uses: actions/checkout@v4
3535

3636
- uses: r-lib/actions/setup-r@v2
3737
with:
@@ -50,14 +50,21 @@ jobs:
5050
# depends on, and it is not covered by R CMD check: RcppParallel can install
5151
# perfectly well while leaving downstream packages unable to build.
5252
downstream:
53-
runs-on: windows-latest
53+
runs-on: ${{ matrix.config.os }}
5454

55-
name: downstream (${{ matrix.r }})
55+
name: downstream (${{ matrix.config.os }}, ${{ matrix.config.r }})
5656

5757
strategy:
5858
fail-fast: false
5959
matrix:
60-
r: ['4.2', 'release']
60+
config:
61+
# Rtools42 has no oneTBB, so this one builds the bundled copy
62+
- {os: windows-latest, r: '4.2'}
63+
- {os: windows-latest, r: 'release'}
64+
# RcppParallelLibs() changed for every Windows platform, and arm64
65+
# has both its own vendored patch and itt_notify compiled out, so
66+
# its export surface is the one most likely to differ
67+
- {os: windows-11-arm, r: 'release'}
6168

6269
env:
6370
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
@@ -70,11 +77,11 @@ jobs:
7077
shell: bash
7178

7279
steps:
73-
- uses: actions/checkout@v3
80+
- uses: actions/checkout@v4
7481

7582
- uses: r-lib/actions/setup-r@v2
7683
with:
77-
r-version: ${{ matrix.r }}
84+
r-version: ${{ matrix.config.r }}
7885
use-public-rspm: true
7986

8087
- name: install RcppParallel

.github/workflows/rstan.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
shell: bash
5050

5151
steps:
52-
- uses: actions/checkout@v3
52+
- uses: actions/checkout@v4
5353

5454
- uses: r-lib/actions/setup-r@v2
5555
with:
@@ -83,9 +83,11 @@ jobs:
8383
run: |
8484
Rscript -e 'install.packages(c("StanHeaders","rstan"), type = "source", dependencies = FALSE, INSTALL_opts = "--no-multiarch")'
8585
86+
# compare against this checkout's DESCRIPTION rather than looking for a
87+
# '.9000' suffix, so this keeps working across development version bumps
8688
- name: check RcppParallel is still the one we built
8789
run: |
88-
Rscript -e 'v <- as.character(packageVersion("RcppParallel")); writeLines(paste("RcppParallel", v)); if (!grepl("[.]9000$", v)) stop("RcppParallel was replaced by a released build; the rstan build did not test this branch")'
90+
Rscript -e 'installed <- packageVersion("RcppParallel"); expected <- package_version(read.dcf("DESCRIPTION")[1L, "Version"]); writeLines(sprintf("RcppParallel %s (expected %s)", installed, expected)); if (installed != expected) stop("RcppParallel was replaced by a different build; the rstan build did not test this branch")'
8991
9092
- name: load rstan
9193
run: |

NEWS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
2017, whose headers downstream packages cannot build against: StanHeaders
1313
uses `tbb::this_task_arena::isolate`, which that release still gates behind
1414
`TBB_PREVIEW_TASK_ISOLATION` and does not export from its library. As a
15-
result, rstan could no longer be built on R 4.2 for Windows.
15+
result, rstan could no longer be built on R 4.2 for Windows. A TBB
16+
configured explicitly via `TBB_LIB` is still used as-is, whatever its
17+
vintage.
1618

1719
* On Windows, `RcppParallel::RcppParallelLibs()` once again offers the TBB
1820
stub library, so that packages taking all of their linker flags from it

R/tbb.R

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,15 @@ tbbLdFlags <- function() {
9898
# one; the stub exports the runtime wholesale, so anything missing from
9999
# RcppParallel.dll can still be resolved. this comes last deliberately:
100100
# the linker satisfies symbols in order, so RcppParallel's own exports
101-
# still win, and no dependency on the stub is recorded unless something
102-
# actually needs it
101+
# still win, and no import of the stub is recorded unless something
102+
# actually needs it.
103+
#
104+
# that ordering matters for more than tidiness. the stub links its own
105+
# copy of the oneTBB runtime, so a package resolving some symbols here
106+
# and the rest against RcppParallel.dll would straddle two independent
107+
# schedulers -- an observer registered with one would never fire for
108+
# arenas owned by the other. .github/scripts/tbb-downstream-check.R
109+
# asserts that nothing actually lands on the stub
103110
tbbPath <- archSystemFile("lib")
104111
if (file.exists(file.path(tbbPath, "tbb.dll")))
105112
ldFlags <- paste(ldFlags, sprintf("-L%s -ltbb", asBuildPath(tbbPath)))

patches/legacy_tbb_abi.diff

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@
170170
+} // namespace tbb
171171
+
172172
+#endif /* _WIN32 */
173-
\ No newline at end of file
174173
--- a/src/tbb/src/tbb/def/lin32-tbb.def
175174
+++ b/src/tbb/src/tbb/def/lin32-tbb.def
176175
@@ -17,6 +17,26 @@

patches/mingw_gnu_cmake.diff

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
diff --git a/src/tbb/cmake/compilers/GNU.cmake b/src/tbb/cmake/compilers/GNU.cmake
2-
index cf6d8bdb..bc86acc1 100644
2+
index cf6d8bdb..8f9f0b59 100644
33
--- a/src/tbb/cmake/compilers/GNU.cmake
44
+++ b/src/tbb/cmake/compilers/GNU.cmake
5-
@@ -47,19 +47,42 @@ endif()
5+
@@ -47,19 +47,44 @@ endif()
66
# >=2.31.1). Capturing the output in CMake can be done like below. The version
77
# information is written to either stdout or stderr. To not make any
88
# assumptions, both are captured.
99
+
10-
+# The null device is spelled differently on Windows. A mingw build driven by
11-
+# a native Windows CMake (rather than cross-compiled from a POSIX host) has no
12-
+# '/dev/null', and the compiler would fail with "no input files".
13-
+if (WIN32)
14-
+ set(_tbb_null_device "NUL")
15-
+else()
16-
+ set(_tbb_null_device "/dev/null")
17-
+endif()
10+
+# Probe with a real (empty) source file rather than the null device. A mingw
11+
+# build driven by a native Windows CMake (rather than cross-compiled from a
12+
+# POSIX host) has no '/dev/null', and the compiler would fail with "no input
13+
+# files"; an actual file avoids having to spell the null device per platform.
14+
+set(_tbb_asm_probe_source ${CMAKE_BINARY_DIR}/CMakeFiles/tbb_asm_probe.c)
15+
+set(_tbb_asm_probe_object ${CMAKE_BINARY_DIR}/CMakeFiles/tbb_asm_probe.o)
16+
+file(WRITE ${_tbb_asm_probe_source} "")
1817
+
1918
execute_process(
2019
- COMMAND ${CMAKE_COMMAND} -E env "LANG=C" ${CMAKE_CXX_COMPILER} -xc -c /dev/null -Wa,-v -o/dev/null
21-
+ COMMAND ${CMAKE_COMMAND} -E env "LANG=C" ${CMAKE_CXX_COMPILER} -xc -c ${_tbb_null_device} -Wa,-v -o${_tbb_null_device}
20+
+ COMMAND ${CMAKE_COMMAND} -E env "LANG=C" ${CMAKE_CXX_COMPILER} -xc -c ${_tbb_asm_probe_source} -Wa,-v -o ${_tbb_asm_probe_object}
2221
OUTPUT_VARIABLE ASSEMBLER_VERSION_LINE_OUT
2322
ERROR_VARIABLE ASSEMBLER_VERSION_LINE_ERR
2423
OUTPUT_STRIP_TRAILING_WHITESPACE
2524
ERROR_STRIP_TRAILING_WHITESPACE
2625
)
27-
+unset(_tbb_null_device)
26+
+
27+
+file(REMOVE ${_tbb_asm_probe_source} ${_tbb_asm_probe_object})
28+
+unset(_tbb_asm_probe_source)
29+
+unset(_tbb_asm_probe_object)
2830
set(ASSEMBLER_VERSION_LINE ${ASSEMBLER_VERSION_LINE_OUT}${ASSEMBLER_VERSION_LINE_ERR})
2931
string(REGEX REPLACE ".*GNU assembler version ([0-9]+)\\.([0-9]+).*" "\\1" _tbb_gnu_asm_major_version "${ASSEMBLER_VERSION_LINE}")
3032
string(REGEX REPLACE ".*GNU assembler version ([0-9]+)\\.([0-9]+).*" "\\2" _tbb_gnu_asm_minor_version "${ASSEMBLER_VERSION_LINE}")
@@ -46,7 +48,7 @@ index cf6d8bdb..bc86acc1 100644
4648
message(TRACE "Extracted GNU assembler version: major=${_tbb_gnu_asm_major_version} minor=${_tbb_gnu_asm_minor_version}")
4749

4850
math(EXPR _tbb_gnu_asm_version_number "${_tbb_gnu_asm_major_version} * 1000 + ${_tbb_gnu_asm_minor_version}")
49-
@@ -81,7 +104,14 @@ endif()
51+
@@ -81,7 +106,14 @@ endif()
5052
if (NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL Intel)
5153
# gcc 6.0 and later have -flifetime-dse option that controls elimination of stores done outside the object lifetime
5254
set(TBB_DSE_FLAG $<$<NOT:$<VERSION_LESS:${CMAKE_CXX_COMPILER_VERSION},6.0>>:-flifetime-dse=1>)

src/install.libs.R

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,34 @@
3939

4040
# using bundled TBB
4141
tbbSrc <- "tbb/build/lib_release"
42-
tbbLibs <- list.files(
43-
path = tbbSrc,
44-
pattern = shlibPattern,
45-
full.names = TRUE
46-
)
4742

48-
logTbbLibraries(tbbLibs, tbbSrc)
49-
for (tbbLib in tbbLibs) {
50-
system2("cp", c("-P", shQuote(tbbLib), shQuote(tbbDest)))
51-
}
43+
# on Windows the bundled TBB was built as a static library, and is
44+
# already linked into (and re-exported from) RcppParallel.dll; say so
45+
# explicitly, rather than reporting that no runtime libraries were
46+
# found -- there are none to find, and that reads like a failure
47+
if (.Platform$OS.type == "windows") {
48+
49+
writeLines("** tbb is statically linked into RcppParallel.dll; no runtime libraries to install")
50+
51+
} else {
5252

53-
# restore the versioned library layout shipped by RcppParallel 5.1.11
54-
# and earlier (a real 'libtbb.so.2' plus a 'libtbb.so' symlink)
55-
if (Sys.info()[["sysname"]] == "Linux")
56-
versionBundledTbbLibraries(tbbDest)
53+
tbbLibs <- list.files(
54+
path = tbbSrc,
55+
pattern = shlibPattern,
56+
full.names = TRUE
57+
)
58+
59+
logTbbLibraries(tbbLibs, tbbSrc)
60+
for (tbbLib in tbbLibs) {
61+
system2("cp", c("-P", shQuote(tbbLib), shQuote(tbbDest)))
62+
}
63+
64+
# restore the versioned library layout shipped by RcppParallel 5.1.11
65+
# and earlier (a real 'libtbb.so.2' plus a 'libtbb.so' symlink)
66+
if (Sys.info()[["sysname"]] == "Linux")
67+
versionBundledTbbLibraries(tbbDest)
68+
69+
}
5770

5871
} else {
5972

src/tbb/cmake/compilers/GNU.cmake

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,25 @@ endif()
4848
# information is written to either stdout or stderr. To not make any
4949
# assumptions, both are captured.
5050

51-
# The null device is spelled differently on Windows. A mingw build driven by
52-
# a native Windows CMake (rather than cross-compiled from a POSIX host) has no
53-
# '/dev/null', and the compiler would fail with "no input files".
54-
if (WIN32)
55-
set(_tbb_null_device "NUL")
56-
else()
57-
set(_tbb_null_device "/dev/null")
58-
endif()
51+
# Probe with a real (empty) source file rather than the null device. A mingw
52+
# build driven by a native Windows CMake (rather than cross-compiled from a
53+
# POSIX host) has no '/dev/null', and the compiler would fail with "no input
54+
# files"; an actual file avoids having to spell the null device per platform.
55+
set(_tbb_asm_probe_source ${CMAKE_BINARY_DIR}/CMakeFiles/tbb_asm_probe.c)
56+
set(_tbb_asm_probe_object ${CMAKE_BINARY_DIR}/CMakeFiles/tbb_asm_probe.o)
57+
file(WRITE ${_tbb_asm_probe_source} "")
5958

6059
execute_process(
61-
COMMAND ${CMAKE_COMMAND} -E env "LANG=C" ${CMAKE_CXX_COMPILER} -xc -c ${_tbb_null_device} -Wa,-v -o${_tbb_null_device}
60+
COMMAND ${CMAKE_COMMAND} -E env "LANG=C" ${CMAKE_CXX_COMPILER} -xc -c ${_tbb_asm_probe_source} -Wa,-v -o ${_tbb_asm_probe_object}
6261
OUTPUT_VARIABLE ASSEMBLER_VERSION_LINE_OUT
6362
ERROR_VARIABLE ASSEMBLER_VERSION_LINE_ERR
6463
OUTPUT_STRIP_TRAILING_WHITESPACE
6564
ERROR_STRIP_TRAILING_WHITESPACE
6665
)
67-
unset(_tbb_null_device)
66+
67+
file(REMOVE ${_tbb_asm_probe_source} ${_tbb_asm_probe_object})
68+
unset(_tbb_asm_probe_source)
69+
unset(_tbb_asm_probe_object)
6870
set(ASSEMBLER_VERSION_LINE ${ASSEMBLER_VERSION_LINE_OUT}${ASSEMBLER_VERSION_LINE_ERR})
6971
string(REGEX REPLACE ".*GNU assembler version ([0-9]+)\\.([0-9]+).*" "\\1" _tbb_gnu_asm_major_version "${ASSEMBLER_VERSION_LINE}")
7072
string(REGEX REPLACE ".*GNU assembler version ([0-9]+)\\.([0-9]+).*" "\\2" _tbb_gnu_asm_minor_version "${ASSEMBLER_VERSION_LINE}")

src/tbb/src/tbb/observer_proxy.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,4 +336,4 @@ void __TBB_EXPORTED_FUNC task_scheduler_observer_v3::observe( bool enable ) {
336336
} // namespace internal
337337
} // namespace tbb
338338

339-
#endif /* _WIN32 */
339+
#endif /* _WIN32 */

0 commit comments

Comments
 (0)