Skip to content

Commit 03592e9

Browse files
committed
Repair the separators on the staged and backup executable paths
tempfile() joins with a backslash on Windows, so staging beside a WSL destination produced "//wsl$/distro/path/to/dir\exe-new-1234". The Win32 calls tolerate the mixed separators, but wsl_safe_path() only rewrites the prefix, so the POSIX chmod inside WSL was handed a path that does not exist and every real compile failed. The previous code chmod'ed the destination, which had been through repair_path() already, and ignored the status besides, so this only surfaced once the staged candidate became the thing being made executable and its status was checked. Both temporary paths now go through repair_path(). That also collapses the duplicated separator withr::local_tempdir() can return, so the snapshot transforms match every spelling of the fixture directory rather than the one literal form. Also clears variables_ in format(overwrite_file = TRUE). The program on disk is rewritten and stan_code_ reloaded from it, but anything already parsed stayed cached, so $code() and $variables() could describe different programs and the fitting methods validate data and initial values against $variables().
1 parent b0e8d25 commit 03592e9

4 files changed

Lines changed: 50 additions & 5 deletions

File tree

R/model.R

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,11 @@ format <- function(overwrite_file = FALSE,
12911291
cat(run_log$stdout, file = out_file, sep = "\n")
12921292
if (isTRUE(overwrite_file)) {
12931293
private$stan_code_ <- readLines(self$stan_file())
1294+
# The program on disk has been rewritten, so anything parsed from it is
1295+
# stale. $variables() reparses when this is NULL; leaving it would let
1296+
# $code() and $variables() describe different programs, and the fitting
1297+
# methods validate data and inits against $variables(). (#1228)
1298+
private$variables_ <- NULL
12941299
}
12951300

12961301
invisible(TRUE)

R/utils.R

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,11 @@ copy_temp_files <-
302302
#' signalling from here would unwind before the caller could record the state
303303
#' describing it.
304304
install_executable <- function(from, to) {
305-
candidate <- tempfile(pattern = "exe-new-", tmpdir = dirname(to))
305+
# repair_path() because tempfile() joins with a backslash on Windows, giving
306+
# "//wsl$/distro/path/to/dir\\exe-new-1234". The Win32 calls below tolerate the
307+
# mixed separators, but wsl_safe_path() only rewrites the prefix, so the POSIX
308+
# chmod inside WSL would be handed a path that does not exist.
309+
candidate <- repair_path(tempfile(pattern = "exe-new-", tmpdir = dirname(to)))
306310
# Discarding the staged copy can fail too, so the diagnostics say where it was
307311
# left rather than implying it is gone and sending the user looking for a file
308312
# that is still there.
@@ -339,7 +343,7 @@ install_executable <- function(from, to) {
339343

340344
backup <- NULL
341345
if (file.exists(to)) {
342-
backup <- tempfile(pattern = "exe-old-", tmpdir = dirname(to))
346+
backup <- repair_path(tempfile(pattern = "exe-old-", tmpdir = dirname(to)))
343347
if (!isTRUE(suppressWarnings(file.rename(to, backup)))) {
344348
stop(
345349
"Could not move the existing executable '", to, "' aside. ",

tests/testthat/test-model-compile.R

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,9 @@ test_that("a leftover backup doesn't unwind a compile when warnings are errors",
582582
# "<dir>\exe-old-1234", since tempfile() joins with a backslash.
583583
transform = function(lines) {
584584
lines <- gsub("\\\\", "/", lines)
585-
lines <- gsub(gsub("\\\\", "/", model_dir), "<dir>", lines, fixed = TRUE)
585+
for (dir in unique(c(model_dir, repair_path(model_dir)))) {
586+
lines <- gsub(dir, "<dir>", lines, fixed = TRUE)
587+
}
586588
gsub("exe-old-[0-9a-f]+", "exe-old-<random>", lines)
587589
}
588590
)
@@ -1107,6 +1109,31 @@ test_that("cmdstan_model cpp_options dont captialize cxxflags ", {
11071109
expect_output(print(out), "-Dsomething_not_used")
11081110
})
11091111

1112+
test_that("format(overwrite_file = TRUE) refreshes cached variables", {
1113+
model_dir <- withr::local_tempdir()
1114+
stan_file <- write_stan_file(
1115+
"parameters { real alpha; } model { alpha ~ std_normal(); }",
1116+
dir = model_dir,
1117+
basename = "reformat.stan"
1118+
)
1119+
model <- cmdstan_model(stan_file, compile = FALSE)
1120+
expect_equal(names(model$variables()$parameters), "alpha")
1121+
1122+
# The program is edited behind the object's back, then formatted in place.
1123+
# $format() reloads $code() from disk, so a cached $variables() would go on
1124+
# describing a different program than $code() does -- and the fitting methods
1125+
# validate data and initial values against $variables(). (#1228)
1126+
writeLines(
1127+
"parameters { real beta; } model { beta ~ std_normal(); }",
1128+
stan_file
1129+
)
1130+
model$format(overwrite_file = TRUE, quiet = TRUE)
1131+
1132+
expect_equal(names(model$variables()$parameters), "beta")
1133+
expect_match(paste(model$code(), collapse = " "), "beta")
1134+
})
1135+
1136+
11101137
test_that("format() works", {
11111138
code <- "
11121139
parameters {

tests/testthat/test-utils.R

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,19 @@ local_exe_fixture <- function(destination_exists = TRUE,
232232
# and tempfile() use backslashes, so tempfile(tmpdir = dirname(to)) yields
233233
# "C:/a/b\exe-new-1234".
234234
exe_path_transform <- function(fixture) {
235-
dir <- gsub("\\\\", "/", fixture$dir)
235+
# Every spelling the directory can appear in: withr::local_tempdir() can
236+
# return "/tmp//Rtmpx", file.path() keeps that, and install_executable()
237+
# passes its own paths through repair_path(), which collapses it.
238+
dirs <- unique(c(
239+
fixture$dir,
240+
repair_path(fixture$dir),
241+
gsub("\\\\", "/", fixture$dir)
242+
))
236243
function(lines) {
237244
lines <- gsub("\\\\", "/", lines)
238-
lines <- gsub(dir, "<dir>", lines, fixed = TRUE)
245+
for (dir in dirs) {
246+
lines <- gsub(dir, "<dir>", lines, fixed = TRUE)
247+
}
239248
gsub("exe-(new|old)-[0-9a-f]+", "exe-\\1-<random>", lines)
240249
}
241250
}

0 commit comments

Comments
 (0)