diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 0cffb6f81..d2bd9982a 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -37,6 +37,7 @@ jobs: - {os: macOS-latest, r: 'release', opencl: true} - {os: windows-latest, r: 'devel' } - {os: windows-latest, r: 'release'} + - {os: windows-2022, r: '4.1'} - {os: windows-latest, r: 'oldrel'} - {os: ubuntu-latest, r: 'devel', opencl: true} - {os: ubuntu-latest, r: 'release', opencl: true} @@ -74,6 +75,28 @@ jobs: cache: "always" extra-packages: any::rcmdcheck, local::. + - name: Debug Windows toolchain resolution + if: ${{ runner.os == 'Windows' }} + run: | + rtools_home <- cmdstanr:::rtools4x_home_path() + candidates <- cmdstanr:::rtools4x_toolchain_candidates() + selected <- cmdstanr:::rtools4x_toolchain_path() + cat("R version: ", as.character(getRversion()), "\n", sep = "") + cat("Rtools home: ", rtools_home, "\n", sep = "") + cat("Toolchain candidates:\n") + if (length(candidates) == 0) { + cat(" - \n") + } else { + cat(paste0(" - ", candidates), sep = "\n") + cat("\n") + } + cat("Selected toolchain: ", selected, "\n", sep = "") + selected_ok <- cmdstanr:::is_rtools4x_toolchain_usable(selected) + if (!selected_ok) { + stop("Resolved Windows toolchain path is not usable.") + } + shell: Rscript {0} + - name: Install POCL on Ubuntu Runners if: ${{ matrix.config.os == 'ubuntu-latest' }} run: | diff --git a/DESCRIPTION b/DESCRIPTION index 3280baa04..b49e9ab82 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -36,12 +36,12 @@ RoxygenNote: 7.3.3 Roxygen: list(markdown = TRUE, r6 = FALSE) SystemRequirements: CmdStan (https://mc-stan.org/users/interfaces/cmdstan) Depends: - R (>= 3.5.0) + R (>= 4.0.0) Imports: checkmate, data.table, jsonlite (>= 1.2.0), - posterior (>= 1.4.1), + posterior (>= 1.5.0), processx (>= 3.5.0), R6 (>= 2.4.0), withr (>= 2.5.0), diff --git a/NEWS.md b/NEWS.md index e1b263ab7..cd82c6b9c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,10 @@ # cmdstanr (development version) +* CmdStan versions older than 2.35.0 are no longer supported. +* Minimum R version increased to 4.0.0. +* Removed legacy Windows toolchain paths for older CmdStan releases. +* `CMDSTANR_USE_MSYS_TOOLCHAIN` is now deprecated and ignored (with a warning). + * Removed deprecated items (replacements in parentheses): - `read_sample_csv()` (`read_cmdstan_csv()`) - `write_stan_tempfile()` (`write_stan_file()`) diff --git a/R/args.R b/R/args.R index 6373eb07a..e84d141f8 100644 --- a/R/args.R +++ b/R/args.R @@ -68,14 +68,8 @@ CmdStanArgs <- R6::R6Class( self$output_dir <- ifelse(is.null(output_dir), file.path(wsl_dir_prefix(), wsl_tempdir()), wsl_safe_path(output_dir)) - } else if (getRversion() < "3.5.0") { - self$output_dir <- output_dir %||% tempdir() } else { - if (getRversion() < "3.5.0") { - self$output_dir <- output_dir %||% tempdir() - } else { - self$output_dir <- output_dir %||% tempdir(check = TRUE) - } + self$output_dir <- output_dir %||% tempdir(check = TRUE) } self$output_dir <- repair_path(self$output_dir) self$output_basename <- output_basename diff --git a/R/install.R b/R/install.R index a75ff5cbe..3c58110f2 100644 --- a/R/install.R +++ b/R/install.R @@ -20,16 +20,11 @@ #' #' The `check_cmdstan_toolchain()` function attempts to check for the required #' C++ toolchain. It is called internally by `install_cmdstan()` but can also -#' be called directly by the user. On Windows only, calling the function with -#' the `fix = TRUE` argument will attempt to install the necessary toolchain -#' components if they are not found. For Windows users with RTools and CmdStan -#' versions >= 2.35 no additional toolchain configuration is required. +#' be called directly by the user. #' -#' NOTE: When installing CmdStan on Windows with RTools and CmdStan versions -#' prior to 2.35.0, the above additional toolchain configuration -#' is still required. To enable this configuration, set the environment variable -#' `CMDSTANR_USE_MSYS_TOOLCHAIN` to 'true' and call -#' `check_cmdstan_toolchain(fix = TRUE)`. +#' **CmdStan versions older than 2.35.0 are no longer supported.** If you need +#' to work with an older CmdStan version we recommend installing an older +#' CmdStanR release from GitHub. #' #' @export #' @param dir (string) The path to the directory in which to install CmdStan. @@ -57,11 +52,11 @@ #' @param release_url (string) The URL for the specific CmdStan release or #' release candidate to install. See . #' The URL should point to the tarball (`.tar.gz.` file) itself, e.g., -#' `release_url="https://github.com/stan-dev/cmdstan/releases/download/v2.25.0/cmdstan-2.25.0.tar.gz"`. +#' `release_url="https://github.com/stan-dev/cmdstan/releases/download/v2.35.0/cmdstan-2.35.0.tar.gz"`. #' If both `version` and `release_url` are specified then `version` will be used. #' @param release_file (string) A file path to a CmdStan release tar.gz file #' downloaded from the releases page: . -#' For example: `release_file=""./cmdstan-2.33.1.tar.gz"`. If `release_file` is +#' For example: `release_file=""./cmdstan-2.35.0.tar.gz"`. If `release_file` is #' specified then both `release_url` and `version` will be ignored. #' @param cpp_options (list) Any makefile flags/variables to be written to #' the `make/local` file. For example, `list("CXX" = "clang++")` will force @@ -98,6 +93,7 @@ install_cmdstan <- function(dir = NULL, cpp_options = list(), check_toolchain = TRUE, wsl = FALSE) { + warn_if_ignored_msys_toolchain_env() # Use environment variable to record WSL usage throughout install, # post-installation will simply check for 'wsl-' prefix in cmdstan path if (isTRUE(wsl)) { @@ -111,15 +107,6 @@ install_cmdstan <- function(dir = NULL, } else { .cmdstanr$WSL <- FALSE } - if (os_is_windows() && !os_is_wsl() && isTRUE(version < "2.35.0")) { - # RTools can be used unmodified with CmdStan 2.35+ - # For new installs of older versions, users need to install mingw32-make and MSYS gcc - if (Sys.getenv("CMDSTANR_USE_MSYS_TOOLCHAIN") == "") { - stop("CmdStan versions prior to 2.35.0 require additional toolchain configuration on Windows.\n", - "Please set the environment variable CMDSTANR_USE_MSYS_TOOLCHAIN to 'true' and \n", - "call `check_cmdstan_toolchain(fix = TRUE)` before installing CmdStan.", call. = FALSE) - } - } if (check_toolchain) { check_cmdstan_toolchain(fix = FALSE, quiet = quiet) } @@ -149,6 +136,7 @@ install_cmdstan <- function(dir = NULL, release_url <- release_file } if (!is.null(version) && is.null(release_file)) { + assert_supported_requested_cmdstan_version(version, source = "version") if (!is.null(release_url)) { warning("version and release_url shouldn't both be specified!", "\nrelease_url will be ignored.", call. = FALSE) @@ -158,6 +146,10 @@ install_cmdstan <- function(dir = NULL, version, "/cmdstan-", version, cmdstan_arch_suffix(version), ".tar.gz") } if (!is.null(release_url)) { + release_ver <- extract_cmdstan_version_from_archive_name(release_url) + if (!is.null(release_ver)) { + assert_supported_requested_cmdstan_version(release_ver, source = "release_url/release_file") + } if (!endsWith(release_url, ".tar.gz")) { stop(release_url, " is not a .tar.gz archive!", "cmdstanr supports installing from .tar.gz archives only.", call. = FALSE) @@ -173,6 +165,7 @@ install_cmdstan <- function(dir = NULL, dest_file <- file.path(dir, tar_gz_file) } else { ver <- latest_released_version(quiet = quiet) + assert_supported_requested_cmdstan_version(ver, source = "latest release") message("* Latest CmdStan release is v", ver) cmdstan_ver <- paste0("cmdstan-", ver) tar_gz_file <- paste0(cmdstan_ver, cmdstan_arch_suffix(ver), ".tar.gz") @@ -227,6 +220,10 @@ install_cmdstan <- function(dir = NULL, } file.remove(dest_file) } + extracted_version <- suppressWarnings(read_cmdstan_version(dir_cmdstan)) + if (!is.null(extracted_version)) { + assert_supported_requested_cmdstan_version(extracted_version, source = "archive") + } cmdstan_make_local(dir = dir_cmdstan, cpp_options = cpp_options, append = TRUE) # Setting up native M1 compilation of CmdStan and its downstream libraries @@ -240,15 +237,6 @@ install_cmdstan <- function(dir = NULL, ) } - # Building fails on Apple silicon with < v2.31 due to a makefiles setting - # for stanc3, so manually implement the patch if needed from: - # https://github.com/stan-dev/cmdstan/pull/1127 - stanc_makefile <- readLines(file.path(dir_cmdstan, "make", "stanc")) - stanc_makefile <- gsub("\\bxattr -d com.apple.quarantine bin/stanc", - "-xattr -d com.apple.quarantine bin/stanc", - stanc_makefile) - writeLines(stanc_makefile, con = file.path(dir_cmdstan, "make", "stanc")) - if (is_ucrt_toolchain() && !wsl) { cmdstan_make_local( dir = dir_cmdstan, @@ -349,18 +337,16 @@ cmdstan_make_local <- function(dir = cmdstan_path(), #' @rdname install_cmdstan #' @export #' @param fix For `check_cmdstan_toolchain()`, should CmdStanR attempt to fix -#' any detected toolchain problems? Currently this option is only available on -#' Windows. The default is `FALSE`, in which case problems are only reported -#' along with suggested fixes. +#' any detected toolchain problems? The default is `FALSE`. +#' This argument is currently ignored and retained for compatibility. #' check_cmdstan_toolchain <- function(fix = FALSE, quiet = FALSE) { + warn_if_ignored_msys_toolchain_env() if (os_is_windows()) { if (os_is_wsl()) { check_wsl_toolchain() - } else if (R.version$major >= "4") { - check_rtools4x_windows_toolchain(fix = fix, quiet = quiet) } else { - check_rtools35_windows_toolchain(fix = fix, quiet = quiet) + check_rtools4x_windows_toolchain(fix = fix, quiet = quiet) } } else { check_unix_make() @@ -575,36 +561,6 @@ build_status_ok <- function(process_log, quiet = FALSE) { TRUE } -install_toolchain <- function(quiet = FALSE) { - rtools_usr_bin <- file.path(rtools4x_home_path(), "usr", "bin") - rtools_version <- paste0("Rtools", rtools4x_version()) - if (is_ucrt_toolchain()) { - install_pkgs <- c("mingw-w64-ucrt-x86_64-make", "mingw-w64-ucrt-x86_64-gcc") - if (!quiet) message(paste0("Installing mingw32-make and g++ with ", rtools_version)) - } else { - install_pkgs <- "mingw-w64-x86_64-make" - if (!quiet) message(paste0("Installing mingw32-make with ", rtools_version)) - } - if (!checkmate::test_directory(rtools_usr_bin, access = "w")) { - warning("No write permissions in the RTools folder. This might prevent installing the toolchain.", - " Consider changing permissions or reinstalling RTools in a different folder.", call. = FALSE) - } - withr::with_path( - c( - toolchain_PATH_env_var() - ), - processx::run( - "pacman", - args = c("-Sy", install_pkgs, "--noconfirm"), - wd = rtools_usr_bin, - error_on_status = TRUE, - echo_cmd = is_verbose_mode(), - echo = is_verbose_mode() - ) - ) - invisible(NULL) -} - check_wsl_toolchain <- function() { if (!wsl_installed()) { stop("\n", "A WSL distribution is not installed or is not accessible.", @@ -638,9 +594,8 @@ check_wsl_toolchain <- function() { } check_rtools4x_windows_toolchain <- function(fix = FALSE, quiet = FALSE) { - rtools_path <- rtools_home_path() + rtools_path <- rtools4x_home_path() rtools_version <- paste0("Rtools", rtools4x_version()) - toolchain_path <- rtools4x_toolchain_path() # If RTOOLS4X_HOME is not set (the env. variable gets set on install) # we assume that RTools 40 is not installed. if (!nzchar(rtools_path)) { @@ -660,92 +615,34 @@ check_rtools4x_windows_toolchain <- function(fix = FALSE, quiet = FALSE) { call. = FALSE ) } - # No additional utilities/toolchains are needed with RTools4 - if (Sys.getenv("CMDSTANR_USE_MSYS_TOOLCHAIN") == "") { - return(invisible(NULL)) - } - if (!is_toolchain_installed(app = "g++", path = toolchain_path) || - !is_toolchain_installed(app = "mingw32-make", path = toolchain_path)) { - if (!fix) { - stop( - "\n", rtools_version, " installation found but the toolchain was not installed.", - "\nRun cmdstanr::check_cmdstan_toolchain(fix = TRUE) to fix the issue.", - call. = FALSE - ) - } else { - install_toolchain(quiet = quiet) - if (!is_toolchain_installed(app = "g++", path = toolchain_path) || - !is_toolchain_installed(app = "mingw32-make", path = toolchain_path)) { - stop( - "\nInstallation of the toolchain failed. Try reinstalling RTools and trying again.", - "\nIf the issue persists, open a bug report at https://github.com/stan-dev/cmdstanr.", - call. = FALSE - ) - } - return(invisible(NULL)) - } - } -} - -check_rtools35_windows_toolchain <- function(fix = FALSE, - quiet = FALSE, - paths = NULL) { - if (is.null(paths)) { - paths <- c(file.path("C:/", "Rtools"), file.path("C:/", "Rtools35")) + usr_bin <- repair_path(file.path(rtools_path, "usr", "bin")) + # Fail early with a clear message if the base make tool is missing + make_found <- any(file.exists(file.path(usr_bin, c("make.exe", "mingw32-make.exe")))) + if (!make_found) { + stop( + "\n", rtools_version, " is missing the required 'make' executable in ", usr_bin, ".", + "\nPlease reinstall ", rtools_version, " and run cmdstanr::check_cmdstan_toolchain().", + call. = FALSE + ) } - mingw32_make_path <- dirname(Sys.which("mingw32-make")) - gpp_path <- dirname(Sys.which("g++")) - # If mingw32-make and g++ are not found, we check typical RTools 3.5 folders. - # If found, we fix PATH, otherwise we recommend the user to install RTools 3.5. - if (!nzchar(mingw32_make_path) || !nzchar(gpp_path)) { - rtools_path <- Sys.getenv("RTOOLS35_HOME") - if (!nzchar(rtools_path)) { - found_rtools <- FALSE - for (p in paths) { - if (dir.exists(p)) { - if (found_rtools) { - stop( - "\nMultiple RTools 3.5 installations found. Please select the installation to use by running", - "\n\nwrite(\'RTOOLS35_HOME=rtools35/install/path/\', file = \"~/.Renviron\", append = TRUE)", - "\n\nThen restart R and run 'cmdstanr::check_cmdstan_toolchain(fix = TRUE)'.", - call. = FALSE - ) - } else { - rtools_path <- p - found_rtools <- TRUE - } - } - } - } - if (nzchar(rtools_path)) { - if (!fix) { - stop( - "\nRTools installation found but PATH was not properly set.", - "\nRun check_cmdstan_toolchain(fix = TRUE) to fix the issue.", - call. = FALSE - ) - } - if (!quiet) { - message("Writing RTools path to ~/.Renviron ...") - } - if (!nzchar(Sys.getenv("RTOOLS35_HOME"))) { - write(paste0("RTOOLS35_HOME=", rtools_path), file = "~/.Renviron", append = TRUE) - Sys.setenv(RTOOLS35_HOME = rtools_path) - } - write('PATH="${RTOOLS35_HOME}\\bin;${RTOOLS35_HOME}\\mingw_64\\bin;${PATH}"', file = "~/.Renviron", append = TRUE) - Sys.setenv(PATH = paste0(Sys.getenv("RTOOLS35_HOME"), "\\mingw_64\\bin;", Sys.getenv("PATH"))) - check_rtools35_windows_toolchain(fix = FALSE, quiet = quiet) - return(invisible(NULL)) + candidates <- rtools4x_toolchain_candidates() + # Validate candidate toolchains here so build errors later are not opaque + has_usable_toolchain <- any(vapply(candidates, is_rtools4x_toolchain_usable, logical(1))) + if (!has_usable_toolchain) { + if (length(candidates) == 0) { + candidates_message <- "\n- " } else { - stop( - "\nA toolchain was not found. Please install RTools 3.5 and run", - "\n\nwrite(\'RTOOLS35_HOME=rtools35/install/path/\', file = \"~/.Renviron\", append = TRUE)", - "\nreplacing 'rtools35/install/path/' with the actual install path of RTools 3.5.", - "\n\nThen restart R and run 'cmdstanr::check_cmdstan_toolchain(fix = TRUE)'.", - call. = FALSE - ) + candidates_message <- paste0("\n- ", paste(candidates, collapse = "\n- ")) } + stop( + "\n", rtools_version, " does not contain a supported C++ toolchain.", + "\nChecked the following paths:", + candidates_message, + "\nPlease reinstall ", rtools_version, " and run cmdstanr::check_cmdstan_toolchain().", + call. = FALSE + ) } + invisible(NULL) } check_unix_make <- function() { @@ -827,63 +724,70 @@ cmdstan_arch_suffix <- function(version = NULL) { paste0("-linux-", selected_arch) } -is_toolchain_installed <- function(app, path) { - res <- tryCatch({ - withr::with_path( - c( - toolchain_PATH_env_var() - ), - processx::run( - app, - args = c("--version"), - wd = path, - error_on_status = FALSE, - echo_cmd = is_verbose_mode(), - echo = is_verbose_mode() - ) - ) - app_path <- withr::with_path( - c( - toolchain_PATH_env_var() - ), - repair_path(dirname(Sys.which(app))) - ) - if (normalizePath(app_path) != normalizePath(rtools4x_toolchain_path())) { - return(FALSE) - } - return(TRUE) - }, - error = function(cond) { - return(FALSE) - } +toolchain_PATH_env_var <- function() { + if (!os_is_windows()) { + return(NULL) + } + rtools_home <- rtools4x_home_path() + if (!nzchar(rtools_home)) { + return(NULL) + } + paste0( + repair_path(file.path(rtools_home, "usr", "bin")), ";", + rtools4x_toolchain_path() ) - res } -toolchain_PATH_env_var <- function() { - path <- NULL - if (R.version$major == "4") { - rtools_home <- rtools4x_home_path() - path <- paste0( - repair_path(file.path(rtools_home, "usr", "bin")), ";", - rtools4x_toolchain_path() - ) +#' Ordered candidate RTools toolchain bin paths +#' +#' On x86_64, candidate order is ABI-aware so legacy fallback paths are tried +#' in an order compatible with the current R toolchain. +#' +#' @noRd +#' @return A character vector of normalized candidate toolchain bin paths +rtools4x_toolchain_candidates <- function() { + rtools_home <- rtools4x_home_path() + if (!nzchar(rtools_home)) { + return(character()) + } + # Prefer the modern static toolchain first, then ABI-compatible legacy + # fallbacks for older Rtools layouts + toolchains <- if (arch_is_aarch64()) { + "aarch64-w64-mingw32.static.posix" + } else if (is_ucrt_toolchain()) { + c("x86_64-w64-mingw32.static.posix", "ucrt64", "mingw64") + } else { + c("x86_64-w64-mingw32.static.posix", "mingw64", "ucrt64") } - path + repair_path(file.path(rtools_home, toolchains, "bin")) +} + +# A candidate is usable if the directory exists and contains a g++ executable +is_rtools4x_toolchain_usable <- function(path) { + if (!nzchar(path) || !dir.exists(path)) { + return(FALSE) + } + any(file.exists(file.path(path, c("g++.exe", "g++")))) } +#' Resolve the preferred RTools toolchain bin path +#' +#' Returns the first usable path from `rtools4x_toolchain_candidates()`. If no +#' candidate is usable, returns the first candidate for deterministic diagnostics. +#' +#' @noRd +#' @return A single path string, or `""` if no candidates are available. rtools4x_toolchain_path <- function() { - if (arch_is_aarch64()) { - toolchain <- "aarch64-w64-mingw32.static.posix" - } else { - if (Sys.getenv("CMDSTANR_USE_MSYS_TOOLCHAIN") != "" || - isTRUE(cmdstan_version(error_on_NA=FALSE) < "2.35.0")) { - toolchain <- ifelse(is_ucrt_toolchain(), "ucrt64", "mingw64") - } else { - toolchain <- "x86_64-w64-mingw32.static.posix" - } + candidates <- rtools4x_toolchain_candidates() + if (length(candidates) == 0) { + return("") + } + # Return the first usable candidate (ordered by preference above). + usable <- vapply(candidates, is_rtools4x_toolchain_usable, logical(1)) + if (any(usable)) { + return(candidates[which(usable)[1]]) } - repair_path(file.path(rtools4x_home_path(), toolchain, "bin")) + candidates[1] } rtools4x_version <- function() { @@ -923,13 +827,29 @@ rtools4x_home_path <- function() { path } -rtools_home_path <- function() { - path <- NULL - if (R.version$major == "3") { - path <- Sys.getenv("RTOOLS_HOME") - } - if (R.version$major == "4") { - path <- rtools4x_home_path() +assert_supported_requested_cmdstan_version <- function(version, source = "version") { + if (is_supported_cmdstan_version(version)) { + return(invisible(NULL)) } - path + stop( + "Requested CmdStan ", source, " (", version, ") is unsupported. ", + "cmdstanr now requires CmdStan v", cmdstan_min_version(), " or newer. ", + "If you need an older CmdStan release, install an older cmdstanr version from GitHub.", + call. = FALSE + ) +} + +extract_cmdstan_version_from_archive_name <- function(path_or_url) { + archive <- basename(path_or_url) + archive <- sub("\\?.*$", "", archive) + matches <- regmatches( + archive, + regexec("^cmdstan-([0-9]+\\.[0-9]+\\.[0-9]+(?:-rc[0-9]+)?)(?:-linux-[a-z0-9_]+)?\\.tar\\.gz$", + archive, + perl = TRUE) + )[[1]] + if (length(matches) >= 2) { + return(matches[2]) + } + NULL } diff --git a/R/path.R b/R/path.R index d754be92e..94acda56b 100644 --- a/R/path.R +++ b/R/path.R @@ -43,8 +43,20 @@ set_cmdstan_path <- function(path = NULL) { } if (dir.exists(path)) { path <- absolute_path(path) + version <- read_cmdstan_version(path) + if (!is.null(version) && !is_supported_cmdstan_version(version)) { + warning( + "CmdStan path not set. CmdStan v", version, " is no longer supported. ", + "cmdstanr now requires CmdStan v", cmdstan_min_version(), " or newer.", + call. = FALSE + ) + .cmdstanr$PATH <- NULL + .cmdstanr$VERSION <- NULL + .cmdstanr$WSL <- FALSE + return(invisible(path)) + } .cmdstanr$PATH <- path - .cmdstanr$VERSION <- read_cmdstan_version(path) + .cmdstanr$VERSION <- version .cmdstanr$WSL <- grepl("//wsl$", path, fixed = TRUE) message("CmdStan path set to: ", path) } else { @@ -88,6 +100,7 @@ cmdstan_version <- function(error_on_NA = TRUE) { .cmdstanr$PATH <- NULL .cmdstanr$VERSION <- NULL .cmdstanr$TEMP_DIR <- NULL +.cmdstanr$WSL <- FALSE # path to temp directory cmdstan_tempdir <- function() { @@ -100,6 +113,21 @@ stop_no_path <- function() { call. = FALSE) } +cmdstan_min_version <- function() { + "2.35.0" +} + +is_supported_cmdstan_version <- function(version) { + if (is.null(version)) { + return(FALSE) + } + cmp <- tryCatch( + suppressWarnings(utils::compareVersion(as.character(version), cmdstan_min_version())), + error = function(e) NA_integer_ + ) + isTRUE(cmp >= 0) +} + #' cmdstan_default_install_path #' #' Path to where [install_cmdstan()] with default settings installs CmdStan. @@ -135,18 +163,20 @@ cmdstan_default_install_path <- function(wsl = FALSE) { cmdstan_default_path <- function(dir = NULL) { if (!is.null(dir)) { installs_path <- dir - } else { - installs_path <- cmdstan_default_install_path() - } - wsl_installed <- wsl_installed() - if (!isTRUE(wsl_installed)) { wsl_installs_path <- NULL wsl_path_exists <- FALSE } else { - wsl_installs_path <- cmdstan_default_install_path(wsl = TRUE) - wsl_path_linux <- gsub(wsl_dir_prefix(wsl = TRUE), "", wsl_installs_path, - fixed=TRUE) - wsl_path_exists <- isTRUE(.wsl_check_exists(wsl_path_linux)) + installs_path <- cmdstan_default_install_path() + wsl_installed <- wsl_installed() + if (!isTRUE(wsl_installed)) { + wsl_installs_path <- NULL + wsl_path_exists <- FALSE + } else { + wsl_installs_path <- cmdstan_default_install_path(wsl = TRUE) + wsl_path_linux <- gsub(wsl_dir_prefix(wsl = TRUE), "", wsl_installs_path, + fixed=TRUE) + wsl_path_exists <- isTRUE(.wsl_check_exists(wsl_path_linux)) + } } if (dir.exists(installs_path) || wsl_path_exists) { latest_cmdstan <- ifelse(dir.exists(installs_path), @@ -224,6 +254,7 @@ is_release_candidate <- function(path) { unset_cmdstan_path <- function() { .cmdstanr$PATH <- NULL .cmdstanr$VERSION <- NULL + .cmdstanr$WSL <- FALSE } # fake a cmdstan version (only used in tests) diff --git a/R/utils.R b/R/utils.R index 94a531373..8cf555c6e 100644 --- a/R/utils.R +++ b/R/utils.R @@ -58,18 +58,6 @@ os_is_linux <- function() { isTRUE(Sys.info()[["sysname"]] == "Linux") } -is_rtools43_toolchain <- function() { - os_is_windows() && R.version$major == "4" && R.version$minor >= "3.0" -} - -is_rtools42_toolchain <- function() { - os_is_windows() && R.version$major == "4" && R.version$minor >= "2.0" && R.version$minor < "3.0" -} - -is_rtools40_toolchain <- function() { - os_is_windows() && R.version$major == "4" && R.version$minor < "2.0" -} - is_ucrt_toolchain <- function() { os_is_windows() && R.version$major == "4" && R.version$minor >= "2.0" } @@ -94,16 +82,33 @@ arch_is_aarch64 <- function() { # Returns the type of make command to use to compile depending on the OS # First checks if $MAKE is set, otherwise falls back to system-specific default make_cmd <- function() { + warn_if_ignored_msys_toolchain_env() if (Sys.getenv("MAKE") != "") { Sys.getenv("MAKE") - } else if (os_is_windows() && !os_is_wsl() && - (Sys.getenv("CMDSTANR_USE_MSYS_TOOLCHAIN") != "" || isTRUE(cmdstan_version(error_on_NA=FALSE) < "2.35.0"))) { - "mingw32-make.exe" } else { "make" } } +warn_if_ignored_msys_toolchain_env <- function() { + if (Sys.getenv("CMDSTANR_USE_MSYS_TOOLCHAIN") == "") { + return(invisible(NULL)) + } + # Keep this warning to once per R session because this helper is called + # from high-frequency internal paths (e.g., make/toolchain resolution). + if (isTRUE(.cmdstanr$WARNED_IGNORED_MSYS_TOOLCHAIN)) { + return(invisible(NULL)) + } + warning( + "Environment variable 'CMDSTANR_USE_MSYS_TOOLCHAIN' is deprecated and ignored. ", + "cmdstanr now requires CmdStan v", cmdstan_min_version(), " or newer.\n", + "If you need legacy MSYS toolchain support, use an older cmdstanr release.", + call. = FALSE + ) + .cmdstanr$WARNED_IGNORED_MSYS_TOOLCHAIN <- TRUE + invisible(NULL) +} + # Returns the stanc exe path depending on the OS stanc_cmd <- function() { if (os_is_windows() && !os_is_wsl()) { diff --git a/R/zzz.R b/R/zzz.R index 945ca50b3..68d59245f 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -51,6 +51,8 @@ cmdstanr_initialize <- function() { call. = FALSE ) .cmdstanr$PATH <- NULL + .cmdstanr$VERSION <- NULL + .cmdstanr$WSL <- FALSE } else { set_cmdstan_path(path) } @@ -62,6 +64,8 @@ cmdstanr_initialize <- function() { call. = FALSE ) .cmdstanr$PATH <- NULL + .cmdstanr$VERSION <- NULL + .cmdstanr$WSL <- FALSE } } else { # environment variable not found @@ -71,11 +75,7 @@ cmdstanr_initialize <- function() { } } - if (getRversion() < "3.5.0") { - .cmdstanr$TEMP_DIR <- tempdir() - } else { - .cmdstanr$TEMP_DIR <- tempdir(check = TRUE) - } + .cmdstanr$TEMP_DIR <- tempdir(check = TRUE) invisible(TRUE) } diff --git a/man/install_cmdstan.Rd b/man/install_cmdstan.Rd index 564ed9e14..fa4157071 100644 --- a/man/install_cmdstan.Rd +++ b/man/install_cmdstan.Rd @@ -64,12 +64,12 @@ is \code{NULL}, which downloads the latest stable release from \item{release_url}{(string) The URL for the specific CmdStan release or release candidate to install. See \url{https://github.com/stan-dev/cmdstan/releases}. The URL should point to the tarball (\code{.tar.gz.} file) itself, e.g., -\code{release_url="https://github.com/stan-dev/cmdstan/releases/download/v2.25.0/cmdstan-2.25.0.tar.gz"}. +\code{release_url="https://github.com/stan-dev/cmdstan/releases/download/v2.35.0/cmdstan-2.35.0.tar.gz"}. If both \code{version} and \code{release_url} are specified then \code{version} will be used.} \item{release_file}{(string) A file path to a CmdStan release tar.gz file downloaded from the releases page: \url{https://github.com/stan-dev/cmdstan/releases}. -For example: \verb{release_file=""./cmdstan-2.33.1.tar.gz"}. If \code{release_file} is +For example: \verb{release_file=""./cmdstan-2.35.0.tar.gz"}. If \code{release_file} is specified then both \code{release_url} and \code{version} will be ignored.} \item{cpp_options}{(list) Any makefile flags/variables to be written to @@ -88,9 +88,8 @@ makefile flags be appended to the end of the existing \code{make/local} file? The default is \code{TRUE}. If \code{FALSE} the file is overwritten.} \item{fix}{For \code{check_cmdstan_toolchain()}, should CmdStanR attempt to fix -any detected toolchain problems? Currently this option is only available on -Windows. The default is \code{FALSE}, in which case problems are only reported -along with suggested fixes.} +any detected toolchain problems? The default is \code{FALSE}. +This argument is currently ignored and retained for compatibility.} } \value{ For \code{cmdstan_make_local()}, if \code{cpp_options=NULL} then the existing @@ -118,16 +117,11 @@ should typically be followed by calling \code{rebuild_cmdstan()}. The \code{check_cmdstan_toolchain()} function attempts to check for the required C++ toolchain. It is called internally by \code{install_cmdstan()} but can also -be called directly by the user. On Windows only, calling the function with -the \code{fix = TRUE} argument will attempt to install the necessary toolchain -components if they are not found. For Windows users with RTools and CmdStan -versions >= 2.35 no additional toolchain configuration is required. - -NOTE: When installing CmdStan on Windows with RTools and CmdStan versions -prior to 2.35.0, the above additional toolchain configuration -is still required. To enable this configuration, set the environment variable -\code{CMDSTANR_USE_MSYS_TOOLCHAIN} to 'true' and call -\code{check_cmdstan_toolchain(fix = TRUE)}. +be called directly by the user. + +\strong{CmdStan versions older than 2.35.0 are no longer supported.} If you need +to work with an older CmdStan version we recommend installing an older +CmdStanR release from GitHub. } \examples{ \dontrun{ diff --git a/tests/testthat/resources/recursive-cmdstan-flags.mk b/tests/testthat/resources/recursive-cmdstan-flags.mk new file mode 100644 index 000000000..76af17e77 --- /dev/null +++ b/tests/testthat/resources/recursive-cmdstan-flags.mk @@ -0,0 +1,3 @@ + +test: + @$(R_HOME)/bin/Rscript --vanilla -e "cat(cmdstanr:::get_cmdstan_flags('STANCFLAGS'))" diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 57d8984b0..7b78c73a2 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -1,7 +1,6 @@ context("fitted-inits") set_cmdstan_path() - data_list_schools <- testing_data("schools") data_list_logistic <- testing_data("logistic") test_inits <- function(mod, fit_init, data_list = NULL) { diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index ad7ad069e..da6d572f9 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -9,11 +9,7 @@ if (!nzchar(cmdstan_test_tarball_url)) { } test_that("install_cmdstan() successfully installs cmdstan", { - if (getRversion() < '3.5.0') { - dir <- tempdir() - } else { - dir <- tempdir(check = TRUE) - } + dir <- tempdir(check = TRUE) expect_message( expect_output( install_cmdstan(dir = dir, cores = CORES, quiet = FALSE, overwrite = TRUE, @@ -44,11 +40,7 @@ test_that("install_cmdstan() errors if installation already exists", { test_that("install_cmdstan() errors if it times out", { skip_if(!is.null(cmdstan_test_tarball_url)) - if (getRversion() < '3.5.0') { - dir <- tempdir() - } else { - dir <- tempdir(check = TRUE) - } + dir <- tempdir(check = TRUE) ver <- latest_released_version() dir_exists <- dir.exists(file.path(dir, paste0("cmdstan-",ver))) # with quiet=TRUE @@ -96,11 +88,7 @@ test_that("install_cmdstan() works with version and release_url", { # this test is irrelevant if tests are using a release candidate tarball URL so skip skip_if(!is.null(cmdstan_test_tarball_url)) - if (getRversion() < '3.5.0') { - dir <- tempdir() - } else { - dir <- tempdir(check = TRUE) - } + dir <- tempdir(check = TRUE) expect_message( expect_output( @@ -158,38 +146,6 @@ test_that("toolchain checks on Unix work", { Sys.setenv("PATH" = path_backup) }) -test_that("toolchain checks on Windows with RTools 3.5 work", { - skip_if_not(os_is_windows()) - skip_if(os_is_wsl()) - skip_if(R.Version()$major > "3") - - path_backup <- Sys.getenv("PATH") - Sys.setenv("PATH" = "") - tmpdir <- tempdir() - tmp_dir1 <- file.path(tmpdir, "dir1") - tmp_dir2 <- file.path(tmpdir, "dir2") - if (dir.exists(tmp_dir1)) unlink(tmp_dir1) - if (dir.exists(tmp_dir2)) unlink(tmp_dir2) - expect_error( - check_rtools35_windows_toolchain(paths= c(tmp_dir1, tmp_dir2)), - "\nA toolchain was not found. Please install RTools 3.5 and run", - fixed = TRUE - ) - if (!dir.exists(tmp_dir1)) dir.create(tmp_dir1) - expect_error( - check_rtools35_windows_toolchain(paths= c(tmp_dir1, tmp_dir2)), - "\nRTools installation found but PATH was not properly set.", - fixed = TRUE - ) - if (!dir.exists(tmp_dir2)) dir.create(tmp_dir2) - expect_error( - check_rtools35_windows_toolchain(paths= c(tmp_dir1, tmp_dir2)), - "\nMultiple RTools 3.5 installations found. Please select the installation to use", - fixed = TRUE - ) - Sys.setenv("PATH" = path_backup) -}) - test_that("clean and rebuild works", { expect_output( rebuild_cmdstan(cores = CORES), @@ -205,12 +161,40 @@ test_that("github_download_url constructs correct url", { ) }) +test_that("extract_cmdstan_version_from_archive_name parses realistic inputs", { + expect_equal( + extract_cmdstan_version_from_archive_name( + "https://github.com/stan-dev/cmdstan/releases/download/v2.36.0/cmdstan-2.36.0.tar.gz" + ), + "2.36.0" + ) + expect_equal( + extract_cmdstan_version_from_archive_name( + "https://github.com/stan-dev/cmdstan/releases/download/v2.36.0/cmdstan-2.36.0-linux-arm64.tar.gz" + ), + "2.36.0" + ) + expect_equal( + extract_cmdstan_version_from_archive_name( + "https://github.com/stan-dev/cmdstan/releases/download/v2.35.0-rc1/cmdstan-2.35.0-rc1.tar.gz?download=1" + ), + "2.35.0-rc1" + ) + expect_equal( + extract_cmdstan_version_from_archive_name( + file.path(tempdir(check = TRUE), "cmdstan-2.35.1-linux-s390x.tar.gz") + ), + "2.35.1" + ) + expect_null( + extract_cmdstan_version_from_archive_name( + "https://github.com/stan-dev/cmdstan/releases/tag/v2.36.0" + ) + ) +}) + test_that("Downloads respect quiet argument", { - if (getRversion() < '3.5.0') { - dir <- tempdir() - } else { - dir <- tempdir(check = TRUE) - } + dir <- tempdir(check = TRUE) version <- latest_released_version() ver_msg <- "trying URL 'https://api.github.com/repos/stan-dev/cmdstan/releases/latest'" @@ -239,11 +223,7 @@ test_that("Download failures return error message", { # GHA fails on Windows old-rel here, but cannot replicate locally skip_if(os_is_windows() && getRversion() < '4.2') - if (getRversion() < '3.5.0') { - dir <- tempdir() - } else { - dir <- tempdir(check = TRUE) - } + dir <- tempdir(check = TRUE) expect_error({ # Use an invalid proxy address to force a download failure @@ -255,11 +235,7 @@ test_that("Download failures return error message", { }) test_that("Install from release file works", { - if (getRversion() < '3.5.0') { - dir <- tempdir() - } else { - dir <- tempdir(check = TRUE) - } + dir <- tempdir(check = TRUE) destfile <- file.path(dir, "cmdstan-2.36.0.tar.gz") @@ -279,3 +255,169 @@ test_that("Install from release file works", { fixed = TRUE ) }) + +test_that("install_cmdstan() errors for unsupported CmdStan versions", { + expect_error( + install_cmdstan(version = "2.34.0", check_toolchain = FALSE, wsl = os_is_wsl()), + "Requested CmdStan version (2.34.0) is unsupported.", + fixed = TRUE + ) + expect_error( + install_cmdstan( + release_url = "https://github.com/stan-dev/cmdstan/releases/download/v2.34.0/cmdstan-2.34.0.tar.gz", + check_toolchain = FALSE, + wsl = os_is_wsl() + ), + "Requested CmdStan release_url/release_file (2.34.0) is unsupported.", + fixed = TRUE + ) + expect_error( + install_cmdstan( + release_file = file.path(tempdir(check = TRUE), "cmdstan-2.34.0.tar.gz"), + check_toolchain = FALSE, + wsl = os_is_wsl() + ), + "Requested CmdStan release_url/release_file (2.34.0) is unsupported.", + fixed = TRUE + ) +}) + +test_that("unsupported release-candidate versions are rejected by the floor check", { + expect_false(is_supported_cmdstan_version("2.34.0-rc1")) + expect_true(is_supported_cmdstan_version("2.35.0-rc1")) + expect_error( + install_cmdstan(version = "2.34.0-rc1", check_toolchain = FALSE, wsl = os_is_wsl()), + "Requested CmdStan version (2.34.0-rc1) is unsupported.", + fixed = TRUE + ) +}) + +test_that("deprecated CMDSTANR_USE_MSYS_TOOLCHAIN is ignored with warning", { + old_flag <- .cmdstanr$WARNED_IGNORED_MSYS_TOOLCHAIN + on.exit(.cmdstanr$WARNED_IGNORED_MSYS_TOOLCHAIN <- old_flag) + + .cmdstanr$WARNED_IGNORED_MSYS_TOOLCHAIN <- FALSE + withr::with_envvar(c(CMDSTANR_USE_MSYS_TOOLCHAIN = "true"), { + expect_warning( + make_cmd(), + "CMDSTANR_USE_MSYS_TOOLCHAIN", + fixed = TRUE + ) + expect_silent(make_cmd()) + }) +}) + +test_that("rtools4x_toolchain_path prefers static-posix when available", { + skip_if(arch_is_aarch64()) + env_var <- paste0( + "RTOOLS", rtools4x_version(), + if (arch_is_aarch64()) "_AARCH64" else "", + "_HOME" + ) + fake_rtools_home <- tempfile(pattern = "rtools-home-pref-", tmpdir = tempdir(check = TRUE)) + on.exit(unlink(fake_rtools_home, recursive = TRUE), add = TRUE) + dir.create(file.path(fake_rtools_home, "x86_64-w64-mingw32.static.posix", "bin"), + recursive = TRUE, showWarnings = FALSE) + dir.create(file.path(fake_rtools_home, "mingw64", "bin"), + recursive = TRUE, showWarnings = FALSE) + file.create(file.path(fake_rtools_home, "x86_64-w64-mingw32.static.posix", "bin", "g++.exe")) + file.create(file.path(fake_rtools_home, "mingw64", "bin", "g++.exe")) + + withr::with_envvar(setNames(fake_rtools_home, env_var), { + expect_equal( + rtools4x_toolchain_path(), + repair_path(file.path(fake_rtools_home, "x86_64-w64-mingw32.static.posix", "bin")) + ) + }) +}) + +test_that("rtools4x_toolchain_path falls back to mingw64 for legacy layouts", { + skip_if(arch_is_aarch64()) + env_var <- paste0( + "RTOOLS", rtools4x_version(), + if (arch_is_aarch64()) "_AARCH64" else "", + "_HOME" + ) + fake_rtools_home <- tempfile(pattern = "rtools-home-fallback-", tmpdir = tempdir(check = TRUE)) + on.exit(unlink(fake_rtools_home, recursive = TRUE), add = TRUE) + dir.create(file.path(fake_rtools_home, "mingw64", "bin"), + recursive = TRUE, showWarnings = FALSE) + file.create(file.path(fake_rtools_home, "mingw64", "bin", "g++.exe")) + + withr::with_envvar(setNames(fake_rtools_home, env_var), { + expect_equal( + rtools4x_toolchain_path(), + repair_path(file.path(fake_rtools_home, "mingw64", "bin")) + ) + }) +}) + +test_that("rtools4x_toolchain_path prefers ABI-compatible legacy fallback", { + skip_if(arch_is_aarch64()) + env_var <- paste0( + "RTOOLS", rtools4x_version(), + if (arch_is_aarch64()) "_AARCH64" else "", + "_HOME" + ) + fake_rtools_home <- tempfile(pattern = "rtools-home-abi-", tmpdir = tempdir(check = TRUE)) + on.exit(unlink(fake_rtools_home, recursive = TRUE), add = TRUE) + dir.create(file.path(fake_rtools_home, "mingw64", "bin"), + recursive = TRUE, showWarnings = FALSE) + dir.create(file.path(fake_rtools_home, "ucrt64", "bin"), + recursive = TRUE, showWarnings = FALSE) + file.create(file.path(fake_rtools_home, "mingw64", "bin", "g++.exe")) + file.create(file.path(fake_rtools_home, "ucrt64", "bin", "g++.exe")) + + withr::with_envvar(setNames(fake_rtools_home, env_var), { + with_mocked_bindings( + { + expect_equal( + rtools4x_toolchain_path(), + repair_path(file.path(fake_rtools_home, "mingw64", "bin")) + ) + }, + is_ucrt_toolchain = function() FALSE + ) + with_mocked_bindings( + { + expect_equal( + rtools4x_toolchain_path(), + repair_path(file.path(fake_rtools_home, "ucrt64", "bin")) + ) + }, + is_ucrt_toolchain = function() TRUE + ) + }) +}) + +test_that("check_rtools4x_windows_toolchain reports checked toolchain paths", { + env_var <- paste0( + "RTOOLS", rtools4x_version(), + if (arch_is_aarch64()) "_AARCH64" else "", + "_HOME" + ) + fake_rtools_home <- tempfile(pattern = "rtools-home-invalid-", tmpdir = tempdir(check = TRUE)) + on.exit(unlink(fake_rtools_home, recursive = TRUE), add = TRUE) + dir.create(file.path(fake_rtools_home, "usr", "bin"), + recursive = TRUE, showWarnings = FALSE) + file.create(file.path(fake_rtools_home, "usr", "bin", "make.exe")) + if (arch_is_aarch64()) { + dir.create(file.path(fake_rtools_home, "aarch64-w64-mingw32.static.posix", "bin"), + recursive = TRUE, showWarnings = FALSE) + } else { + dir.create(file.path(fake_rtools_home, "x86_64-w64-mingw32.static.posix", "bin"), + recursive = TRUE, showWarnings = FALSE) + dir.create(file.path(fake_rtools_home, "ucrt64", "bin"), + recursive = TRUE, showWarnings = FALSE) + dir.create(file.path(fake_rtools_home, "mingw64", "bin"), + recursive = TRUE, showWarnings = FALSE) + } + + withr::with_envvar(setNames(fake_rtools_home, env_var), { + expect_error( + check_rtools4x_windows_toolchain(), + "Checked the following paths:", + fixed = TRUE + ) + }) +}) diff --git a/tests/testthat/test-model-output_dir.R b/tests/testthat/test-model-output_dir.R index cfa6a5ec1..f52848276 100644 --- a/tests/testthat/test-model-output_dir.R +++ b/tests/testthat/test-model-output_dir.R @@ -1,11 +1,7 @@ context("model-output_dir-output-basename") set_cmdstan_path() -if (getRversion() < '3.5.0') { - sandbox <- file.path(tempdir(), "sandbox") -} else { - sandbox <- file.path(tempdir(check = TRUE), "sandbox") -} +sandbox <- file.path(tempdir(check = TRUE), "sandbox") if (!dir.exists(sandbox)) { dir.create(sandbox) on.exit(unlink(sandbox, recursive = TRUE)) diff --git a/tests/testthat/test-path.R b/tests/testthat/test-path.R index 03d9f48d7..575d43d10 100644 --- a/tests/testthat/test-path.R +++ b/tests/testthat/test-path.R @@ -27,11 +27,16 @@ test_that("Setting bad path leads to warning (can't find directory)", { test_that("Setting bad path from env leads to warning (can't find directory)", { unset_cmdstan_path() + .cmdstanr$WSL <- TRUE Sys.setenv(CMDSTAN = "BAD_PATH") expect_warning( cmdstanr_initialize(), "Can't find directory specified by environment variable" ) + expect_null(.cmdstanr$PATH) + expect_null(.cmdstanr$VERSION) + expect_false(isTRUE(.cmdstanr$WSL)) + Sys.unsetenv("CMDSTAN") }) test_that("Setting path from env var is detected", { @@ -44,6 +49,26 @@ test_that("Setting path from env var is detected", { Sys.unsetenv("CMDSTAN") }) +test_that("Unsupported CmdStan path from env var is rejected", { + unset_cmdstan_path() + .cmdstanr$WSL <- TRUE + parent_dir <- file.path(tempdir(check = TRUE), "cmdstan-env-parent") + old_install <- file.path(parent_dir, "cmdstan-2.34.0") + dir.create(old_install, recursive = TRUE, showWarnings = FALSE) + on.exit(unlink(parent_dir, recursive = TRUE), add = TRUE) + on.exit(Sys.unsetenv("CMDSTAN"), add = TRUE) + writeLines("CMDSTAN_VERSION := 2.34.0", con = file.path(old_install, "makefile")) + + Sys.setenv(CMDSTAN = parent_dir) + suppressWarnings(cmdstanr_initialize()) + expect_false(identical(.cmdstanr$PATH, absolute_path(old_install))) + expect_false(identical(.cmdstanr$VERSION, "2.34.0")) + if (!is.null(.cmdstanr$VERSION)) { + expect_true(is_supported_cmdstan_version(.cmdstanr$VERSION)) + } + expect_false(isTRUE(.cmdstanr$WSL)) +}) + test_that("cmdstanr_initialize() also looks for default path", { unset_cmdstan_path() cmdstanr_initialize() @@ -90,6 +115,41 @@ test_that("Warning message is thrown if can't detect version number", { ) }) +test_that("Setting path rejects unsupported CmdStan versions", { + old_path <- .cmdstanr$PATH + old_version <- .cmdstanr$VERSION + old_wsl <- .cmdstanr$WSL + on.exit({ + .cmdstanr$PATH <- old_path + .cmdstanr$VERSION <- old_version + .cmdstanr$WSL <- old_wsl + }) + + path <- file.path(tempdir(check = TRUE), "cmdstan-2.34.0") + dir.create(path, recursive = TRUE, showWarnings = FALSE) + on.exit(unlink(path, recursive = TRUE), add = TRUE) + writeLines("CMDSTAN_VERSION := 2.34.0", con = file.path(path, "makefile")) + + expect_warning( + set_cmdstan_path(path), + "cmdstanr now requires CmdStan v2.35.0 or newer", + fixed = TRUE + ) + expect_null(.cmdstanr$PATH) + expect_null(.cmdstanr$VERSION) + expect_false(isTRUE(.cmdstanr$WSL)) +}) + +test_that("unset_cmdstan_path() also resets WSL state", { + .cmdstanr$PATH <- PATH + .cmdstanr$VERSION <- VERSION + .cmdstanr$WSL <- TRUE + unset_cmdstan_path() + expect_null(.cmdstanr$PATH) + expect_null(.cmdstanr$VERSION) + expect_false(isTRUE(.cmdstanr$WSL)) +}) + test_that("cmdstan_ext() works", { if (os_is_windows() && !os_is_wsl()) { expect_identical(cmdstan_ext(), ".exe") diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 7389e4886..9b35dde4f 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -256,11 +256,28 @@ test_that("as_mcmc.list() works", { }) test_that("get_cmdstan_flags() can be used recursively in `make`", { - mkfile <- normalizePath(test_path("testdata", "Makefile")) + mkfile <- normalizePath(test_path("resources", "recursive-cmdstan-flags.mk")) nonrecursive_flags <- get_cmdstan_flags("STANCFLAGS") - stdo <- processx::run( - command = "make", args = sprintf("--file=%s", mkfile) - )$stdout + recursive_run <- processx::run( + command = "make", + args = sprintf("--file=%s", mkfile), + error_on_status = FALSE + ) + if (recursive_run$status != 0) { + fail( + paste( + "Recursive make failed.", + paste0("status: ", recursive_run$status), + "stdout:", + recursive_run$stdout, + "stderr:", + recursive_run$stderr, + sep = "\n" + ) + ) + return(invisible()) + } + stdo <- recursive_run$stdout recursive_flags <- readLines(textConnection(stdo)) expect_equal(nonrecursive_flags, recursive_flags) }) diff --git a/tests/testthat/testdata/Makefile b/tests/testthat/testdata/Makefile deleted file mode 100644 index 4a58d6544..000000000 --- a/tests/testthat/testdata/Makefile +++ /dev/null @@ -1,3 +0,0 @@ - -test: - @$(R_HOME)/bin/Rscript --vanilla -e 'cat(cmdstanr:::get_cmdstan_flags("STANCFLAGS"))'