@@ -191,6 +191,12 @@ cmdstan_model <- function(stan_file = NULL, exe_file = NULL, compile = TRUE, ...
191191# ' [`$save_hpp_file()`][model-method-compile] | Save the `.hpp` file containing the generated C++ code. |
192192# ' [`$expose_functions()`][model-method-expose_functions] | Expose Stan functions for use in R. |
193193# '
194+ # ' ## Diagnostics
195+ # '
196+ # ' |**Method**|**Description**|
197+ # ' |:----------|:---------------|
198+ # ' [`$diagnose()`][model-method-diagnose] | Run CmdStan's `"diagnose"` method to test gradients, return [`CmdStanDiagnose`] object. |
199+ # '
194200# ' ## Model fitting
195201# '
196202# ' |**Method**|**Description**|
@@ -231,7 +237,7 @@ CmdStanModel <- R6::R6Class(
231237 self $ functions <- new.env()
232238 self $ functions $ compiled <- FALSE
233239 if (! is.null(stan_file )) {
234- assert_file_exists(stan_file , access = " r" , extension = " stan" )
240+ assert_file_exists(stan_file , access = " r" , extension = c( " stan" , " stanfunctions " ) )
235241 checkmate :: assert_flag(compile )
236242 private $ stan_file_ <- absolute_path(stan_file )
237243 private $ stan_code_ <- readLines(stan_file )
@@ -398,6 +404,8 @@ CmdStanModel <- R6::R6Class(
398404# ' `functions` field in the compiled model object. This can also be done after
399405# ' compilation using the
400406# ' [`$expose_functions()`][model-method-expose_functions] method.
407+ # ' @param dry_run (logical) If `TRUE`, the code will do all checks before compilation,
408+ # ' but skip the actual C++ compilation. Used to speedup tests.
401409# '
402410# ' @param threads Deprecated and will be removed in a future release. Please
403411# ' turn on threading via `cpp_options = list(stan_threads = TRUE)` instead.
@@ -451,8 +459,10 @@ compile <- function(quiet = TRUE,
451459 compile_model_methods = FALSE ,
452460 compile_hessian_method = FALSE ,
453461 compile_standalone = FALSE ,
462+ dry_run = FALSE ,
454463 # deprecated
455464 threads = FALSE ) {
465+
456466 if (length(self $ stan_file()) == 0 ) {
457467 stop(" '$compile()' cannot be used because the 'CmdStanModel' was not created with a Stan file." , call. = FALSE )
458468 }
@@ -501,15 +511,63 @@ compile <- function(quiet = TRUE,
501511 exe <- self $ exe_file()
502512 }
503513
514+ # Resolve stanc and cpp options
515+ if (pedantic ) {
516+ stanc_options [[" warn-pedantic" ]] <- TRUE
517+ }
518+
519+ if (isTRUE(cpp_options $ stan_opencl )) {
520+ stanc_options [[" use-opencl" ]] <- TRUE
521+ }
522+
523+ # Note that unlike cpp_options["USER_HEADER"], the user_header variable is deliberately
524+ # not transformed with wsl_safe_path() as that breaks the check below on WSLv1
525+ if (! is.null(user_header )) {
526+ if (! is.null(cpp_options [[" USER_HEADER" ]]) || ! is.null(cpp_options [[" user_header" ]])) {
527+ warning(" User header specified both via user_header argument and via cpp_options arguments" )
528+ }
529+
530+ cpp_options [[" USER_HEADER" ]] <- wsl_safe_path(absolute_path(user_header ))
531+ stanc_options [[" allow-undefined" ]] <- TRUE
532+ private $ using_user_header_ <- TRUE
533+ }
534+ else if (! is.null(cpp_options [[" USER_HEADER" ]])) {
535+ if (! is.null(cpp_options [[" user_header" ]])) {
536+ warning(' User header specified both via cpp_options[["USER_HEADER"]] and cpp_options[["user_header"]].' , call. = FALSE )
537+ }
538+
539+ user_header <- cpp_options [[" USER_HEADER" ]]
540+ cpp_options [[" USER_HEADER" ]] <- wsl_safe_path(absolute_path(cpp_options [[" USER_HEADER" ]]))
541+ private $ using_user_header_ <- TRUE
542+ }
543+ else if (! is.null(cpp_options [[" user_header" ]])) {
544+ user_header <- cpp_options [[" user_header" ]]
545+ cpp_options [[" user_header" ]] <- wsl_safe_path(absolute_path(cpp_options [[" user_header" ]]))
546+ private $ using_user_header_ <- TRUE
547+ }
548+
549+
550+ if (! is.null(user_header )) {
551+ user_header <- absolute_path(user_header ) # As mentioned above, just absolute, not wsl_safe_path()
552+ if (! file.exists(user_header )) {
553+ stop(paste0(" User header file '" , user_header , " ' does not exist." ), call. = FALSE )
554+ }
555+ }
556+
504557 # compile if:
505558 # - the user forced compilation,
506559 # - the executable does not exist
507560 # - the stan model was changed since last compilation
561+ # - a user header is used and the user header changed since last compilation (#813)
508562 if (! file.exists(exe )) {
509563 force_recompile <- TRUE
510564 } else if (file.exists(self $ stan_file())
511565 && file.mtime(exe ) < file.mtime(self $ stan_file())) {
512566 force_recompile <- TRUE
567+ } else if (! is.null(user_header )
568+ && file.exists(user_header )
569+ && file.mtime(exe ) < file.mtime(user_header )) {
570+ force_recompile <- TRUE
513571 }
514572
515573 if (! force_recompile ) {
@@ -520,6 +578,7 @@ compile <- function(quiet = TRUE,
520578 private $ precompile_cpp_options_ <- NULL
521579 private $ precompile_stanc_options_ <- NULL
522580 private $ precompile_include_paths_ <- NULL
581+ self $ functions $ existing_exe <- TRUE
523582 self $ exe_file(exe )
524583 return (invisible (self ))
525584 } else {
@@ -530,14 +589,14 @@ compile <- function(quiet = TRUE,
530589
531590 if (os_is_wsl() && (compile_model_methods || compile_standalone )) {
532591 warning(" Additional model methods and standalone functions are not " ,
533- " currently available with WSL CmdStan and will not be compiled" ,
592+ " currently available with WSLv1 CmdStan and will not be compiled. " ,
534593 call. = FALSE )
535594 compile_model_methods <- FALSE
536595 compile_standalone <- FALSE
537596 compile_hessian_method <- FALSE
538597 }
539598
540- temp_stan_file <- tempfile(pattern = " model-" , fileext = " .stan " )
599+ temp_stan_file <- tempfile(pattern = " model-" , fileext = paste0( " . " , tools :: file_ext( self $ stan_file())) )
541600 file.copy(self $ stan_file(), temp_stan_file , overwrite = TRUE )
542601 temp_file_no_ext <- strip_ext(temp_stan_file )
543602 tmp_exe <- cmdstan_ext(temp_file_no_ext ) # adds .exe on Windows
@@ -548,23 +607,6 @@ compile <- function(quiet = TRUE,
548607
549608 stancflags_val <- include_paths_stanc3_args(include_paths )
550609
551- if (pedantic ) {
552- stanc_options [[" warn-pedantic" ]] <- TRUE
553- }
554-
555- if (isTRUE(cpp_options $ stan_opencl )) {
556- stanc_options [[" use-opencl" ]] <- TRUE
557- }
558- if (! is.null(user_header )) {
559- cpp_options [[" USER_HEADER" ]] <- wsl_safe_path(user_header )
560- stanc_options [[" allow-undefined" ]] <- TRUE
561- }
562- if (! is.null(cpp_options [[" USER_HEADER" ]])) {
563- cpp_options [[" USER_HEADER" ]] <- wsl_safe_path(absolute_path(cpp_options [[" USER_HEADER" ]]))
564- }
565- if (! is.null(cpp_options [[" user_header" ]])) {
566- cpp_options [[" user_header" ]] <- wsl_safe_path(absolute_path(cpp_options [[" user_header" ]]))
567- }
568610 if (is.null(stanc_options [[" name" ]])) {
569611 stanc_options [[" name" ]] <- paste0(self $ model_name(), " _model" )
570612 }
@@ -587,10 +629,18 @@ compile <- function(quiet = TRUE,
587629 stancflags_standalone <- c(" --standalone-functions" , stancflags_val , stancflags_combined )
588630 self $ functions $ hpp_code <- get_standalone_hpp(temp_stan_file , stancflags_standalone )
589631 self $ functions $ external <- ! is.null(user_header )
632+ self $ functions $ existing_exe <- FALSE
633+
634+ stancflags_val <- paste0(" STANCFLAGS += " , stancflags_val , paste0(" " , stancflags_combined , collapse = " " ))
635+
636+ if (dry_run ) {
637+ return (invisible (self ))
638+ }
639+
590640 if (compile_standalone ) {
591641 expose_stan_functions(self $ functions , ! quiet )
592642 }
593- stancflags_val <- paste0( " STANCFLAGS += " , stancflags_val , paste0( " " , stancflags_combined , collapse = " " ))
643+
594644 withr :: with_path(
595645 c(
596646 toolchain_PATH_env_var(),
0 commit comments