Skip to content

Commit bc1220e

Browse files
committed
Use distro-aware fallback for WSL UNC makefile reads
1 parent 5b9717f commit bc1220e

2 files changed

Lines changed: 98 additions & 4 deletions

File tree

R/path.R

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,17 +267,65 @@ latest_cmdstan_installed <- function(installs_path) {
267267
latest_cmdstan
268268
}
269269

270+
is_wsl_unc_path <- function(path) {
271+
is.character(path) &&
272+
length(path) == 1 &&
273+
!is.na(path) &&
274+
startsWith(repair_path(path), "//wsl$/")
275+
}
276+
277+
# Extract the distro name from a WSL UNC path like //wsl$/Ubuntu-22.04/...
278+
wsl_unc_distro_name <- function(path) {
279+
sub("^//wsl\\$/([^/]+).*$", "\\1", repair_path(path))
280+
}
281+
282+
# Convert a WSL UNC path to the corresponding Linux path within the distro.
283+
wsl_unc_path_to_linux <- function(path) {
284+
sub("^//wsl\\$/[^/]+", "", repair_path(path))
285+
}
286+
287+
read_lines_direct <- function(path) {
288+
tryCatch(
289+
suppressWarnings(readLines(path, warn = FALSE)),
290+
error = function(e) NULL
291+
)
292+
}
293+
294+
# Fall back to reading through `wsl` when Windows R can't read a WSL UNC path.
295+
read_lines_via_wsl <- function(path) {
296+
file_contents <- processx::run(
297+
command = "wsl",
298+
args = c("-d", wsl_unc_distro_name(path), "cat", wsl_unc_path_to_linux(path)),
299+
error_on_status = FALSE
300+
)
301+
if (file_contents$status != 0) {
302+
return(NULL)
303+
}
304+
if (!nzchar(file_contents$stdout)) {
305+
return(character(0))
306+
}
307+
con <- textConnection(file_contents$stdout)
308+
on.exit(close(con), add = TRUE)
309+
readLines(con, warn = FALSE)
310+
}
311+
312+
# Preserve existing direct reads and only use the WSL fallback when needed.
313+
read_lines_with_wsl_fallback <- function(path) {
314+
file_contents <- read_lines_direct(path)
315+
if (!is.null(file_contents) || !is_wsl_unc_path(path)) {
316+
return(file_contents)
317+
}
318+
read_lines_via_wsl(path)
319+
}
320+
270321

271322
#' Find the version of CmdStan from makefile
272323
#' @noRd
273324
#' @param path Path to installation.
274325
#' @return Version number as a string.
275326
read_cmdstan_version <- function(path) {
276327
makefile_path <- file.path(path, "makefile")
277-
makefile <- tryCatch(
278-
suppressWarnings(readLines(makefile_path, warn = FALSE)),
279-
error = function(e) NULL
280-
)
328+
makefile <- read_lines_with_wsl_fallback(makefile_path)
281329
if (is.null(makefile)) {
282330
warning(
283331
"Can't find CmdStan makefile to detect version number. ",

tests/testthat/test-path.R

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,52 @@ test_that("CmdStan version helpers handle invalid inputs", {
221221
expect_false(is_supported_cmdstan_version("not-a-version"))
222222
})
223223

224+
test_that("WSL UNC path helpers work", {
225+
wsl_path <- "//wsl$/Ubuntu-22.04/root/.cmdstan/cmdstan-2.38.0"
226+
expect_true(is_wsl_unc_path(wsl_path))
227+
expect_false(is_wsl_unc_path(PATH))
228+
expect_equal(wsl_unc_distro_name(wsl_path), "Ubuntu-22.04")
229+
expect_equal(
230+
wsl_unc_path_to_linux(file.path(wsl_path, "makefile")),
231+
"/root/.cmdstan/cmdstan-2.38.0/makefile"
232+
)
233+
})
234+
235+
test_that("read_cmdstan_version() prefers direct reads for UNC paths", {
236+
wsl_path <- "//wsl$/Ubuntu-22.04/root/.cmdstan/cmdstan-2.38.0"
237+
local_mocked_bindings(
238+
read_lines_direct = function(path) {
239+
expect_equal(
240+
path,
241+
file.path(wsl_path, "makefile")
242+
)
243+
"CMDSTAN_VERSION := 2.38.0"
244+
},
245+
read_lines_via_wsl = function(path) {
246+
stop("WSL fallback should not be used when direct reads succeed.")
247+
}
248+
)
249+
expect_equal(read_cmdstan_version(wsl_path), "2.38.0")
250+
})
251+
252+
test_that("read_cmdstan_version() falls back to distro-aware WSL reads", {
253+
wsl_path <- "//wsl$/Ubuntu-22.04/root/.cmdstan/cmdstan-2.38.0"
254+
local_mocked_bindings(
255+
read_lines_direct = function(path) {
256+
expect_equal(
257+
path,
258+
file.path(wsl_path, "makefile")
259+
)
260+
NULL
261+
},
262+
read_lines_via_wsl = function(path) {
263+
expect_equal(path, file.path(wsl_path, "makefile"))
264+
"CMDSTAN_VERSION := 2.38.0"
265+
}
266+
)
267+
expect_equal(read_cmdstan_version(wsl_path), "2.38.0")
268+
})
269+
224270
test_that("cmdstan_ext() works", {
225271
if (os_is_windows() && !os_is_wsl()) {
226272
expect_identical(cmdstan_ext(), ".exe")

0 commit comments

Comments
 (0)