1 2#' Run an `R CMD check` process in the background 3#' 4#' rcmdcheck_process is an R6 class, that extends the 5#' [callr::rcmd_process] class (which in turn extends [processx::process]. 6#' 7#' @section Usage: 8#' ``` 9#' cp <- rcmdcheck_process$new(path = ".", args = character(), 10#' build_args = character(), check_dir = NULL, 11#' libpath = .libPaths(), repos = getOption("repos")) 12#' 13#' cp$parse_results() 14#' ``` 15#' 16#' Other methods are inherited from [callr::rcmd_process] and 17#' [processx::process]. 18#' 19#' Note that you calling the `get_output_connection` and 20#' `get_error_connection` method on this is not a good idea, because 21#' then the stdout and/or stderr of the process will not be collected 22#' for `parse_results()`. 23#' 24#' You can still use the `read_output_lines()` and `read_error_lines()` 25#' methods to read the standard output and error, `parse_results()` is 26#' not affected by that. 27#' 28#' @section Arguments: 29#' * `cp`: A new rcmdcheck_process object. 30#' * `path`: Path to a package tree or a package archive file. This is the 31#' package to check. 32#' * `args`: Command line arguments to `R CMD check`. 33#' * `build_args`: Command line arguments to `R CMD build`. 34#' * `check_dir`: Directory for the results. 35#' * `libpath`: The library path to set for the check. 36#' * `repos`: The `repos` option to set for the check. 37#' This is needed for cyclic dependency checks if you use the 38#' `--as-cran` argument. The default uses the current value. 39#' 40#' @section Details: 41#' Most methods are inherited from [callr::rcmd_process] and 42#' [processx::process]. 43#' 44#' `cp$parse_results()` parses the results, and returns an S3 object with 45#' fields `errors`, `warnnigs` and `notes`, just like [rcmdcheck()]. It 46#' is an error to call it before the process has finished. Use the 47#' `wait()` method to wait for the check to finish, or the `is_alive()` 48#' method to check if it is still running. 49#' 50#' @importFrom R6 R6Class 51#' @name rcmdcheck_process 52NULL 53 54#' @export 55 56rcmdcheck_process <- R6Class( 57 "rcmdcheck_process", 58 inherit = callr::rcmd_process, 59 60 public = list( 61 62 initialize = function(path = ".", args = character(), 63 build_args = character(), check_dir = NULL, libpath = .libPaths(), 64 repos = getOption("repos")) 65 rcc_init(self, private, super, path, args, build_args, check_dir, 66 libpath, repos), 67 68 parse_results = function() 69 rcc_parse_results(self, private), 70 71 read_output_lines = function(...) { 72 l <- super$read_output_lines(...) 73 private$cstdout <- c(private$cstdout, paste0(l, "\n")) 74 l 75 }, 76 77 read_error_lines = function(...) { 78 l <- super$read_error_lines(...) 79 private$cstderr <- c(private$cstderr, paste0(l, "\n")) 80 l 81 }, 82 83 read_output = function(...) { 84 l <- super$read_output(...) 85 private$cstdout <- c(private$cstdout, l) 86 l 87 }, 88 89 read_error = function(...) { 90 l <- super$read_error(...) 91 private$cstderr <- c(private$cstderr, l) 92 l 93 }, 94 95 kill = function(...) { 96 private$killed <- TRUE 97 super$kill(...) 98 } 99 100 ), 101 private = list( 102 path = NULL, 103 check_dir = NULL, 104 targz = NULL, 105 description = NULL, 106 cstdout = character(), 107 cstderr = character(), 108 killed = FALSE, 109 session_output = NULL, 110 tempfiles = character() 111 ) 112) 113 114#' @importFrom callr rcmd_process rcmd_process_options 115#' @importFrom desc desc 116 117rcc_init <- function(self, private, super, path, args, build_args, 118 check_dir, libpath, repos) { 119 120 if (file.info(path)$isdir) { 121 path <- find_package_root_file(path = path) 122 } else { 123 path <- normalizePath(path) 124 } 125 126 if (is.null(check_dir)) { 127 check_dir <- tempfile() 128 cleanup <- TRUE 129 } else { 130 cleanup <- FALSE 131 } 132 133 targz <- build_package(path, check_dir, build_args = build_args, 134 libpath = libpath, quiet = TRUE) 135 136 private$description <- desc(path) 137 private$path <- path 138 private$check_dir <- check_dir 139 private$targz <- targz 140 141 private$session_output <- tempfile() 142 profile <- make_fake_profile(session_output = private$session_output) 143 private$tempfiles <- c(private$session_output, profile) 144 145 options <- rcmd_process_options( 146 cmd = "check", 147 cmdargs = c(basename(targz), args), 148 libpath = libpath, 149 repos = repos, 150 user_profile = TRUE 151 ) 152 153 with_envvar( 154 c(R_PROFILE_USER = profile, 155 R_LIBS_USER = paste(libpath, collapse = .Platform$path.sep)), 156 with_dir( 157 dirname(targz), 158 super$initialize(options) 159 ) 160 ) 161 162 invisible(self) 163} 164 165rcc_parse_results <- function(self, private) { 166 if (self$is_alive()) stop("Process still alive") 167 168 ## Make sure all output is read out 169 self$read_output_lines() 170 self$read_error_lines() 171 172 on.exit(unlink(private$tempfiles, recursive = TRUE), add = TRUE) 173 174 new_rcmdcheck( 175 stdout = paste(win2unix(private$cstdout), collapse = ""), 176 stderr = paste(win2unix(private$cstderr), collapse = ""), 177 description = private$description, 178 status = self$get_exit_status(), 179 duration = duration(self$get_start_time()), 180 timeout = private$killed, 181 session_info = private$session_output 182 ) 183} 184