Skip to content

Commit 099386b

Browse files
committed
Cache compiler configuration probes
1 parent 869c683 commit 099386b

6 files changed

Lines changed: 277 additions & 29 deletions

File tree

R/aaa-utils.R

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,67 @@ quickr_r_cmd <- function(
5454
r_cmd
5555
}
5656

57+
quickr_compiler_probe_cache <- new.env(parent = emptyenv())
58+
59+
quickr_cached_r_cmd_config_value <- function(
60+
name,
61+
cache = quickr_compiler_probe_cache
62+
) {
63+
stopifnot(is_string(name), is.environment(cache))
64+
65+
cached <- get0(name, envir = cache, inherits = FALSE, ifnotfound = NULL)
66+
if (is.null(cached)) {
67+
probe <- quickr_r_cmd_config_probe(name)
68+
cached <- probe$value
69+
if (isTRUE(probe$ok)) {
70+
assign(name, cached, envir = cache)
71+
}
72+
}
73+
74+
cached
75+
}
76+
5777
quickr_r_cmd_config_value <- function(
5878
name,
5979
r_cmd = quickr_r_cmd(),
6080
system2 = base::system2
6181
) {
82+
quickr_r_cmd_config_probe(
83+
name = name,
84+
r_cmd = r_cmd,
85+
system2 = system2
86+
)$value
87+
}
88+
89+
quickr_r_cmd_config_probe <- function(
90+
name,
91+
r_cmd = quickr_r_cmd(),
92+
system2 = base::system2
93+
) {
94+
stopifnot(is_string(name), is_string(r_cmd), is.function(system2))
95+
6296
out <- tryCatch(
6397
suppressWarnings(system2(
6498
r_cmd,
6599
c("CMD", "config", name),
66100
stdout = TRUE,
67101
stderr = FALSE
68102
)),
69-
error = function(e) character()
103+
error = function(e) structure(character(), status = 1L)
70104
)
71105
status <- attr(out, "status")
72106
if (!is.null(status)) {
73-
return("")
107+
return(list(value = "", ok = FALSE))
74108
}
75109
value <- trimws(paste(out, collapse = " "))
76-
if (!nzchar(value) || grepl("^ERROR:", value)) {
77-
return("")
110+
if (!nzchar(value)) {
111+
return(list(value = "", ok = TRUE))
78112
}
79-
value
113+
if (grepl("^ERROR:", value)) {
114+
return(list(value = "", ok = FALSE))
115+
}
116+
117+
list(value = value, ok = TRUE)
80118
}
81119

82120

R/compiler.R

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ quickr_flang_available <- function(
1515
system2 = base::system2
1616
) {
1717
flang <- quickr_flang_path(which = which)
18+
quickr_flang_available_at_path(flang, system2 = system2)
19+
}
20+
21+
quickr_flang_available_at_path <- function(
22+
flang,
23+
system2 = base::system2
24+
) {
25+
stopifnot(is_string(flang), is.function(system2))
26+
1827
if (!nzchar(flang)) {
1928
return(list(path = "", available = FALSE))
2029
}
@@ -28,6 +37,36 @@ quickr_flang_available <- function(
2837
list(path = flang, available = TRUE)
2938
}
3039

40+
quickr_cached_flang_available <- function(
41+
which = Sys.which,
42+
system2 = base::system2,
43+
cache = quickr_compiler_probe_cache
44+
) {
45+
stopifnot(is.function(which), is.function(system2), is.environment(cache))
46+
47+
flang <- quickr_flang_path(which = which)
48+
if (!identical(system2, base::system2)) {
49+
return(quickr_flang_available_at_path(flang, system2 = system2))
50+
}
51+
52+
cache_key <- paste("flang_available", flang, sep = "\r")
53+
cached <- get0(cache_key, envir = cache, inherits = FALSE, ifnotfound = NULL)
54+
if (!is.null(cached)) {
55+
fresh <- quickr_flang_available_at_path(flang)
56+
if (!isTRUE(fresh$available)) {
57+
rm(list = cache_key, envir = cache)
58+
}
59+
return(fresh)
60+
}
61+
62+
cached <- quickr_flang_available_at_path(flang)
63+
if (isTRUE(cached$available)) {
64+
assign(cache_key, cached, envir = cache)
65+
}
66+
67+
cached
68+
}
69+
3170
quickr_flang_state <- local({
3271
state <- new.env(parent = emptyenv())
3372
state$auto_disabled <- FALSE
@@ -164,15 +203,15 @@ quickr_prefer_flang <- function(
164203

165204
# Best-effort: on macOS, prefer flang if it is available.
166205
if (sysname == "Darwin") {
167-
info <- quickr_flang_available(which = which, system2 = system2)
206+
info <- quickr_cached_flang_available(which = which, system2 = system2)
168207
return(isTRUE(info$available))
169208
}
170209

171210
FALSE
172211
}
173212

174213
quickr_default_fortran_makevars_lines <- function(
175-
config_value = quickr_r_cmd_config_value
214+
config_value = quickr_cached_r_cmd_config_value
176215
) {
177216
fc <- trimws(config_value("FC"))
178217
if (!nzchar(fc)) {
@@ -200,7 +239,7 @@ quickr_fcompiler_env <- function(
200239
sysname = Sys.info()[["sysname"]],
201240
use_openmp = FALSE,
202241
link_flags = character(),
203-
config_value = quickr_r_cmd_config_value
242+
config_value = quickr_cached_r_cmd_config_value
204243
) {
205244
stopifnot(is.character(build_dir), length(build_dir) == 1L, nzchar(build_dir))
206245

@@ -217,7 +256,10 @@ quickr_fcompiler_env <- function(
217256
system2 = system2
218257
))
219258
if (use_flang) {
220-
flang_info <- quickr_flang_available(which = which, system2 = system2)
259+
flang_info <- quickr_cached_flang_available(
260+
which = which,
261+
system2 = system2
262+
)
221263
flang <- flang_info$path
222264
if (!isTRUE(flang_info$available)) {
223265
if (isTRUE(explicit_request)) {

R/quick.R

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ compile <- function(fsub, build_dir = tempfile(paste0(fsub@name, "-build-"))) {
213213

214214
# Link against the same BLAS/LAPACK/Fortran libs as the running R
215215
# to support generated calls to vendor BLAS (e.g., dgemm, dgesv).
216-
cfg <- quickr_r_cmd_config_value
216+
cfg <- quickr_cached_r_cmd_config_value
217217
BLAS_LIBS <- strsplit(cfg("BLAS_LIBS"), "[[:space:]]+")[[1]]
218218
LAPACK_LIBS <- strsplit(cfg("LAPACK_LIBS"), "[[:space:]]+")[[1]]
219219
FLIBS <- strsplit(cfg("FLIBS"), "[[:space:]]+")[[1]]
@@ -326,7 +326,7 @@ compile <- function(fsub, build_dir = tempfile(paste0(fsub@name, "-build-"))) {
326326
quickr_windows_add_dll_paths <- function(
327327
flags,
328328
os_type = .Platform$OS.type,
329-
config_value = quickr_r_cmd_config_value,
329+
config_value = quickr_cached_r_cmd_config_value,
330330
which = Sys.which
331331
) {
332332
if (!identical(os_type, "windows")) {
@@ -338,27 +338,30 @@ quickr_windows_add_dll_paths <- function(
338338

339339
bin_siblings <- file.path(dirs, "..", "bin")
340340

341+
config_path <- function(value) {
342+
value <- trimws(value)
343+
if (!nzchar(value)) {
344+
return("")
345+
}
346+
value <- sub("^\"([^\"]+)\".*", "\\1", value)
347+
value <- sub("^'([^']+)'.*", "\\1", value)
348+
strsplit(value, "\\s+")[[1L]][[1L]]
349+
}
350+
351+
config_binpref <- config_path(config_value("BINPREF"))
352+
if (nzchar(config_binpref) && !dir.exists(config_binpref)) {
353+
config_binpref <- dirname(config_binpref)
354+
}
341355
config_values <- c(
342-
config_value("BINPREF"),
343356
config_value("FC"),
344357
config_value("F77"),
345358
config_value("CC"),
346359
config_value("CXX")
347360
)
348-
config_paths <- vapply(
349-
config_values,
350-
function(value) {
351-
value <- trimws(value)
352-
if (!nzchar(value)) {
353-
return("")
354-
}
355-
value <- sub("^\"([^\"]+)\".*", "\\1", value)
356-
value <- sub("^'([^']+)'.*", "\\1", value)
357-
strsplit(value, "\\s+")[[1L]][[1L]]
358-
},
359-
character(1)
360-
)
361-
config_bins <- unique(dirname(config_paths[nzchar(config_paths)]))
361+
config_paths <- vapply(config_values, config_path, character(1))
362+
config_bins <- dirname(config_paths[nzchar(config_paths)])
363+
config_bins <- config_bins[nzchar(config_bins) & config_bins != "."]
364+
config_bins <- unique(c(config_binpref, config_bins))
362365

363366
r_bin <- R.home("bin")
364367
r_bin_x64 <- file.path(r_bin, "x64")
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
skip_on_cran()
2+
3+
test_that("quickr_cached_r_cmd_config_value retries failed lookups", {
4+
cache <- new.env(parent = emptyenv())
5+
calls <- 0L
6+
system2_stub <- function(command, args, stdout = "", stderr = "", ...) {
7+
calls <<- calls + 1L
8+
if (calls == 1L) {
9+
return(structure(" value ", status = 1L))
10+
}
11+
"gfortran"
12+
}
13+
14+
local_mocked_bindings(system2 = system2_stub, .package = "base")
15+
16+
expect_identical(quickr_cached_r_cmd_config_value("FC", cache = cache), "")
17+
expect_identical(
18+
quickr_cached_r_cmd_config_value("FC", cache = cache),
19+
"gfortran"
20+
)
21+
expect_identical(
22+
quickr_cached_r_cmd_config_value("FC", cache = cache),
23+
"gfortran"
24+
)
25+
expect_equal(calls, 2L)
26+
})
27+
28+
test_that("quickr_cached_flang_available retries failed probes", {
29+
cache <- new.env(parent = emptyenv())
30+
calls <- 0L
31+
which_stub <- function(x) if (x == "flang-new") "/tmp/flang-new" else ""
32+
system2_stub <- function(...) {
33+
calls <<- calls + 1L
34+
if (calls == 1L) {
35+
return(structure("error", status = 1L))
36+
}
37+
"flang version"
38+
}
39+
40+
local_mocked_bindings(system2 = system2_stub, .package = "base")
41+
42+
result <- quickr_cached_flang_available(which = which_stub, cache = cache)
43+
expect_identical(result$path, "/tmp/flang-new")
44+
expect_false(result$available)
45+
46+
result <- quickr_cached_flang_available(which = which_stub, cache = cache)
47+
expect_identical(result$path, "/tmp/flang-new")
48+
expect_true(result$available)
49+
expect_equal(calls, 2L)
50+
})
51+
52+
test_that("quickr_cached_flang_available revalidates successful probes", {
53+
cache <- new.env(parent = emptyenv())
54+
calls <- 0L
55+
which_stub <- function(x) if (x == "flang-new") "/tmp/flang-new" else ""
56+
system2_stub <- function(...) {
57+
calls <<- calls + 1L
58+
if (calls == 1L) {
59+
return("flang version")
60+
}
61+
structure("error", status = 1L)
62+
}
63+
64+
local_mocked_bindings(system2 = system2_stub, .package = "base")
65+
66+
result <- quickr_cached_flang_available(which = which_stub, cache = cache)
67+
expect_identical(result$path, "/tmp/flang-new")
68+
expect_true(result$available)
69+
70+
result <- quickr_cached_flang_available(which = which_stub, cache = cache)
71+
expect_identical(result$path, "/tmp/flang-new")
72+
expect_false(result$available)
73+
expect_equal(calls, 2L)
74+
})

tests/testthat/test-compiler.R

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ test_that("quickr_fortran_compiler_option validates values", {
131131
})
132132

133133
test_that("quickr_fcompiler_env writes Makevars when flang is usable", {
134+
rm(
135+
list = ls(envir = quickr_compiler_probe_cache, all.names = TRUE),
136+
envir = quickr_compiler_probe_cache
137+
)
138+
134139
temp <- withr::local_tempdir()
135140
prefix <- file.path(temp, "flang")
136141
dir.create(file.path(prefix, "bin"), recursive = TRUE)

0 commit comments

Comments
 (0)