1

There are some files containing work under progress that I'd like to exclude from the entire build process (checking, installing, even documenting if possible), but not loading (devtools::load_all()).

I always name them dev-{feature}.R so I was about to use a wildcard, but it happened that I was not even able to exclude a single file from the build.

For debugging, I created an R file as "R/dev-myfeature.R" containing only the line stop("TEST"). I then ran usethis::use_build_ignore("R/dev-myfeature.R"), which added ^R/dev-myfeature\.R$ in the .Rbuildignore file.

Whether I run devtools::load_all("."), devtools::check() or even devtools::document(), I get the TEST error, so .Rbuildignore was ignored. Since it works fine with all other files, I guess it has no power inside the R folder.

Is there any way around this limitation?

Otherwise, what would be a good workflow for work-in-progress files that should not be part of the build yet?

kaya3
  • 47,440
  • 4
  • 68
  • 97
Dan Chaltiel
  • 7,811
  • 5
  • 47
  • 92

1 Answers1

3

I think the .Rbuildignore file is useful for bundling of your package, and that's about it. If you want certain functionality in R/*.R files but only on your console when testing, then you may consider structuring the test files with a wrapping if expression.

For example, I'll the existence and true-ness of a variable named MYDEV to declare that I want to load the contents of the file.

if (exists("MYDEV") && MYDEV) {
  stop("TEST")
}

In a default environment (MYDEV not defined), load_all, check, and document do not see the error. The use of exists(...) helps ensure that this will not error (object not found) in a different R instance, either on your computer or elsewhere.

If you set MYDEV <- TRUE, then all the devtools functions will see the stop and error accordingly. I believe there is no (reasonable) way to get the code sourced in load_all and not with document.

One problem you have with not erring with devtools::document is that it calls devtools::load_all internally, as such simple tests do not work. One could adapt a function from https://stackoverflow.com/a/62747050/3358272, with:

search_calling_pkg <- function(pkgs, funcs) {
  # <borrowed from="rlang::trace_back">
  frames <- sys.frames()
  idx <- rlang:::trace_find_bottom(NULL, frames)
  frames <- frames[idx]
  parents <- sys.parents()[idx]
  calls <- as.list(sys.calls()[idx])
  calls <- purrr::map(calls, rlang:::call_fix_car)
  #==# calls <- lapply(calls, rlang:::call_fix_car)
  calls <- rlang:::add_pipe_pointer(calls, frames)
  calls <- purrr::map2(calls, seq_along(calls), rlang:::maybe_add_namespace)
  #==# calls <- Map(rlang:::maybe_add_namespace, calls, seq_along(calls))
  # </borrowed>
  calls_chr <- vapply(calls, function(cl) as.character(cl)[1], character(1))
  ptn <- paste0("^(", paste(pkgs, collapse = "|"), ")::")
  pkgres <- any(grepl(ptn, calls_chr))
  funcres <- !missing(funcs) && length(calls_chr) &&
    any(mapply(grepl, paste0("(^|::)", funcs, "$"), list(calls_chr)))
  return(pkgres && funcres)
}

This function returns true if any of the package/function pairs are found, and this combination will "see" (error on) stop("TEST") with devtools::load_all but not with devtools::document:

if (search_calling_pkg("pkgload", "load_all") &&
      !search_calling_pkg("devtools", "document")) {
  stop("TEST")
}

Yes, this means you need search_calling_pkg somewhere in your environment, whether part of this package or not. Recall that if the .Rbuildignore file includes R/dev-myfeature.R, then package bundles and all down-stream users will never see this code. If you wanted to be even more defensive, you could use

if (exists("search_calling_pkg") &&
      search_calling_pkg("pkgload", "load_all") &&
      !search_calling_pkg("devtools", "document")) {
  stop("TEST")
}
r2evans
  • 141,215
  • 6
  • 77
  • 149