diff --git a/.github/workflows/R-CMD-check-wsl.yaml b/.github/workflows/R-CMD-check-wsl.yaml index 2d6475c3f..699958a91 100644 --- a/.github/workflows/R-CMD-check-wsl.yaml +++ b/.github/workflows/R-CMD-check-wsl.yaml @@ -30,11 +30,6 @@ jobs: CMDSTANR_OPENCL_TESTS: true steps: - - name: cmdstan env vars - run: | - echo "CMDSTAN_PATH=${HOME}/.cmdstan" >> $GITHUB_ENV - shell: bash - - uses: actions/checkout@v6 - uses: r-lib/actions/setup-r@v2 @@ -44,7 +39,7 @@ jobs: with: extra-packages: any::rcmdcheck, local::. - - uses: Vampire/setup-wsl@v6 + - uses: Vampire/setup-wsl@v7 with: distribution: Ubuntu-22.04 wsl-version: 2 @@ -55,12 +50,18 @@ jobs: run: | sudo apt-get update sudo apt-get install -y build-essential libopenmpi-dev ocl-icd-opencl-dev pocl-opencl-icd + sudo chmod 755 /root shell: wsl-bash {0} - name: Install cmdstan run: | cmdstanr::check_cmdstan_toolchain() cmdstanr::install_cmdstan(cores = 2, wsl = TRUE, overwrite = TRUE) + cat( + paste0("CMDSTAN=", cmdstanr::cmdstan_path(), "\n"), + file = Sys.getenv("GITHUB_ENV"), + append = TRUE + ) shell: Rscript {0} - name: Session info diff --git a/R/path.R b/R/path.R index 3e4fecc15..0507d9622 100644 --- a/R/path.R +++ b/R/path.R @@ -9,7 +9,8 @@ #' @export #' #' @param path (string) The full file path to the CmdStan installation. If -#' `NULL` (the default) then the path is set to the default path used by +#' `NULL` (the default) then the path is set using the `"CMDSTAN"` +#' environment variable when available, otherwise the default path used by #' [install_cmdstan()] if it exists. #' @return A string. Either the file path to the CmdStan installation or the #' CmdStan version number. @@ -39,7 +40,8 @@ #' set_cmdstan_path <- function(path = NULL) { if (is.null(path)) { - path <- cmdstan_default_path() + env_path <- Sys.getenv("CMDSTAN") + path <- if (nzchar(env_path)) env_path else cmdstan_default_path() } if (dir.exists(path)) { path <- absolute_path(path) @@ -219,6 +221,25 @@ cmdstan_default_path <- function(dir = NULL) { latest_cmdstan } +is_wsl_unc_path <- function(path) { + is.character(path) && + length(path) == 1 && + !is.na(path) && + startsWith(repair_path(path), "//wsl$/") +} + +cmdstan_version_from_path <- function(path) { + path <- repair_path(path) + match <- regmatches( + path, + regexpr("cmdstan-[0-9]+\\.[0-9]+\\.[0-9]+(?:-rc[0-9]+)?$", path) + ) + if (!length(match) || is.na(match) || !nzchar(match)) { + return(NULL) + } + sub("^cmdstan-", "", match) +} + #' Find the version of CmdStan from makefile #' @noRd @@ -226,7 +247,23 @@ cmdstan_default_path <- function(dir = NULL) { #' @return Version number as a string. read_cmdstan_version <- function(path) { makefile_path <- file.path(path, "makefile") - if (!file.exists(makefile_path)) { + if (is_wsl_unc_path(path)) { + makefile <- tryCatch( + suppressWarnings(readLines(makefile_path, warn = FALSE)), + error = function(e) NULL + ) + } else if (file.exists(makefile_path)) { + makefile <- readLines(makefile_path) + } else { + makefile <- NULL + } + if (is.null(makefile)) { + if (is_wsl_unc_path(path)) { + version_from_path <- cmdstan_version_from_path(path) + if (!is.null(version_from_path)) { + return(version_from_path) + } + } warning( "Can't find CmdStan makefile to detect version number. ", "Path may not point to valid installation.", @@ -234,7 +271,6 @@ read_cmdstan_version <- function(path) { ) return(NULL) } - makefile <- readLines(makefile_path) version_line <- grep("^CMDSTAN_VERSION :=", makefile, value = TRUE) if (length(version_line) == 0) { stop("CmdStan makefile is missing a version number.", call. = FALSE) diff --git a/man/set_cmdstan_path.Rd b/man/set_cmdstan_path.Rd index b803cdd8e..fe77e09c3 100644 --- a/man/set_cmdstan_path.Rd +++ b/man/set_cmdstan_path.Rd @@ -14,7 +14,8 @@ cmdstan_version(error_on_NA = TRUE) } \arguments{ \item{path}{(string) The full file path to the CmdStan installation. If -\code{NULL} (the default) then the path is set to the default path used by +\code{NULL} (the default) then the path is set using the \code{"CMDSTAN"} +environment variable when available, otherwise the default path used by \code{\link[=install_cmdstan]{install_cmdstan()}} if it exists.} \item{error_on_NA}{(logical) Should an error be thrown if CmdStan is not diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index ccb781cc1..243b803fd 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -117,7 +117,7 @@ test_that("install_cmdstan() works with version and release_url", { fixed = TRUE ) expect_true(dir.exists(file.path(dir, "cmdstan-2.36.0"))) - set_cmdstan_path(cmdstan_default_path()) + set_cmdstan_path() }) test_that("toolchain checks on Unix work", { @@ -143,6 +143,7 @@ test_that("toolchain checks on Unix work", { }) test_that("clean and rebuild works", { + set_cmdstan_path() expect_output( rebuild_cmdstan(cores = CORES), paste0("CmdStan v", cmdstan_version(), " built"), diff --git a/tests/testthat/test-path.R b/tests/testthat/test-path.R index 5bc258191..a06af62f5 100644 --- a/tests/testthat/test-path.R +++ b/tests/testthat/test-path.R @@ -45,6 +45,17 @@ test_that("Setting path from env var is detected", { expect_false(is.null(.cmdstanr$VERSION)) }) +test_that("set_cmdstan_path() uses CMDSTAN env var when path is omitted", { + unset_cmdstan_path() + withr::local_envvar(c(CMDSTAN = PATH)) + expect_message( + set_cmdstan_path(), + paste("CmdStan path set to:", PATH), + fixed = TRUE + ) + expect_equal(cmdstan_path(), PATH) +}) + test_that("Unsupported CmdStan path from env var is rejected", { unset_cmdstan_path() .cmdstanr$WSL <- TRUE @@ -184,6 +195,16 @@ test_that("CmdStan version helpers handle invalid inputs", { expect_false(is_supported_cmdstan_version("not-a-version")) }) +test_that("CmdStan version can be recovered from WSL UNC install path", { + wsl_path <- "//wsl$/Ubuntu-22.04/root/.cmdstan/cmdstan-2.38.0" + + expect_true(is_wsl_unc_path(wsl_path)) + expect_equal(cmdstan_version_from_path(wsl_path), "2.38.0") + expect_equal(cmdstan_version_from_path(paste0(wsl_path, "/")), "2.38.0") + expect_equal(suppressWarnings(read_cmdstan_version(wsl_path)), "2.38.0") + expect_null(cmdstan_version_from_path("//wsl$/Ubuntu-22.04/root/.cmdstan/not-cmdstan")) +}) + test_that("cmdstan_ext() works", { if (os_is_windows() && !os_is_wsl()) { expect_identical(cmdstan_ext(), ".exe")