@@ -397,6 +397,8 @@ CmdStanModel <- R6::R6Class(
397397# ' `functions` field in the compiled model object. This can also be done after
398398# ' compilation using the
399399# ' [`$expose_functions()`][model-method-expose_functions] method.
400+ # ' @param dry_run (logical) If `TRUE`, the code will do all checks before compilation,
401+ # ' but skip the actual C++ compilation. Used to speedup tests.
400402# '
401403# ' @param threads Deprecated and will be removed in a future release. Please
402404# ' turn on threading via `cpp_options = list(stan_threads = TRUE)` instead.
@@ -450,8 +452,10 @@ compile <- function(quiet = TRUE,
450452 compile_model_methods = FALSE ,
451453 compile_hessian_method = FALSE ,
452454 compile_standalone = FALSE ,
455+ dry_run = FALSE ,
453456 # deprecated
454457 threads = FALSE ) {
458+
455459 if (length(self $ stan_file()) == 0 ) {
456460 stop(" '$compile()' cannot be used because the 'CmdStanModel' was not created with a Stan file." , call. = FALSE )
457461 }
@@ -500,15 +504,63 @@ compile <- function(quiet = TRUE,
500504 exe <- self $ exe_file()
501505 }
502506
507+ # Resolve stanc and cpp options
508+ if (pedantic ) {
509+ stanc_options [[" warn-pedantic" ]] <- TRUE
510+ }
511+
512+ if (isTRUE(cpp_options $ stan_opencl )) {
513+ stanc_options [[" use-opencl" ]] <- TRUE
514+ }
515+
516+ # Note that unlike cpp_options["USER_HEADER"], the user_header variable is deliberately
517+ # not transformed with wsl_safe_path() as that breaks the check below on WSLv1
518+ if (! is.null(user_header )) {
519+ if (! is.null(cpp_options [[" USER_HEADER" ]]) || ! is.null(cpp_options [[" user_header" ]])) {
520+ warning(" User header specified both via user_header argument and via cpp_options arguments" )
521+ }
522+
523+ cpp_options [[" USER_HEADER" ]] <- wsl_safe_path(absolute_path(user_header ))
524+ stanc_options [[" allow-undefined" ]] <- TRUE
525+ private $ using_user_header_ <- TRUE
526+ }
527+ else if (! is.null(cpp_options [[" USER_HEADER" ]])) {
528+ if (! is.null(cpp_options [[" user_header" ]])) {
529+ warning(' User header specified both via cpp_options[["USER_HEADER"]] and cpp_options[["user_header"]].' , call. = FALSE )
530+ }
531+
532+ user_header <- cpp_options [[" USER_HEADER" ]]
533+ cpp_options [[" USER_HEADER" ]] <- wsl_safe_path(absolute_path(cpp_options [[" USER_HEADER" ]]))
534+ private $ using_user_header_ <- TRUE
535+ }
536+ else if (! is.null(cpp_options [[" user_header" ]])) {
537+ user_header <- cpp_options [[" user_header" ]]
538+ cpp_options [[" user_header" ]] <- wsl_safe_path(absolute_path(cpp_options [[" user_header" ]]))
539+ private $ using_user_header_ <- TRUE
540+ }
541+
542+
543+ if (! is.null(user_header )) {
544+ user_header <- absolute_path(user_header ) # As mentioned above, just absolute, not wsl_safe_path()
545+ if (! file.exists(user_header )) {
546+ stop(paste0(" User header file '" , user_header , " ' does not exist." ), call. = FALSE )
547+ }
548+ }
549+
503550 # compile if:
504551 # - the user forced compilation,
505552 # - the executable does not exist
506553 # - the stan model was changed since last compilation
554+ # - a user header is used and the user header changed since last compilation (#813)
507555 if (! file.exists(exe )) {
508556 force_recompile <- TRUE
509557 } else if (file.exists(self $ stan_file())
510558 && file.mtime(exe ) < file.mtime(self $ stan_file())) {
511559 force_recompile <- TRUE
560+ } else if (! is.null(user_header )
561+ && file.exists(user_header )
562+ && file.mtime(exe ) < file.mtime(user_header )) {
563+ force_recompile <- TRUE
512564 }
513565
514566 if (! force_recompile ) {
@@ -530,7 +582,7 @@ compile <- function(quiet = TRUE,
530582
531583 if (os_is_wsl() && (compile_model_methods || compile_standalone )) {
532584 warning(" Additional model methods and standalone functions are not " ,
533- " currently available with WSL CmdStan and will not be compiled" ,
585+ " currently available with WSLv1 CmdStan and will not be compiled. " ,
534586 call. = FALSE )
535587 compile_model_methods <- FALSE
536588 compile_standalone <- FALSE
@@ -548,23 +600,6 @@ compile <- function(quiet = TRUE,
548600
549601 stancflags_val <- include_paths_stanc3_args(include_paths )
550602
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- }
568603 if (is.null(stanc_options [[" name" ]])) {
569604 stanc_options [[" name" ]] <- paste0(self $ model_name(), " _model" )
570605 }
@@ -588,10 +623,17 @@ compile <- function(quiet = TRUE,
588623 self $ functions $ hpp_code <- get_standalone_hpp(temp_stan_file , stancflags_standalone )
589624 self $ functions $ external <- ! is.null(user_header )
590625 self $ functions $ existing_exe <- FALSE
626+
627+ stancflags_val <- paste0(" STANCFLAGS += " , stancflags_val , paste0(" " , stancflags_combined , collapse = " " ))
628+
629+ if (dry_run ) {
630+ return (invisible (self ))
631+ }
632+
591633 if (compile_standalone ) {
592634 expose_stan_functions(self $ functions , ! quiet )
593635 }
594- stancflags_val <- paste0( " STANCFLAGS += " , stancflags_val , paste0( " " , stancflags_combined , collapse = " " ))
636+
595637 withr :: with_path(
596638 c(
597639 toolchain_PATH_env_var(),
0 commit comments