2

I would prefer to have a config file and list the packages within it which are needed for the project, rather than relying on renv::init() to scrape the project and find all which I need (it often can't).

So my question is - how do I explicitly tell renv which packages are required for a project, an example would be appreciated.

baxx
  • 3,956
  • 6
  • 37
  • 75
  • It's not clear from your question exactly how much you want `renv::init()` to do. Do you want it to set up the project-local library, just not do the discovery? If you don't want that part, isn't `renv::init(bare = TRUE)` enough to do everything else, and your own function could install necessary packages. – user2554330 Oct 31 '20 at 14:35
  • @user2554330 thanks - "your own function", so from this I should create some `setup.R` which installs the required packages into the renv? I'm finding it hard to tell, the closer it is to what python does (requirements files) the more it makes sense for me though. I'll have a look at `bare=TRUE`, though I'm not sure what's considered best practice once that setting is set – baxx Oct 31 '20 at 15:57

2 Answers2

2

The renv package does all sorts of fancy things: installing from several different locations, setting up a project-specific library so that you can control the versions for a project, etc. If you need that stuff, I think you're out of luck. As far as I can see it has no way to pass in a list of dependencies, it needs to scan your source to find them. I suppose you could include a function like

loadPackages <- function() {
  requireNamespace("foo")
  requireNamespace("bar")
   ...
}

to make it easier for renv to find your required packages, but if it's failing in some other way (e.g. you have incomplete files that don't parse properly), this won't help.

If you don't need all that fancy stuff, you could use the function below:

needsPackages <- function(pkgs, install = TRUE, update = FALSE, 
              load = FALSE, attach = FALSE) {
  missing <- c()
  for (p in pkgs) {
    if (!nchar(system.file(package = p)))
      missing <- c(missing, p)
  }
  if (length(missing)) {
    missing <- unique(missing)
    if (any(install)) {
      toinstall <- intersect(missing, pkgs[install])
      install.packages(toinstall)
      for (p in missing)
        if (!nchar(system.file(package = p)))
          stop("Did not install: ", p)
    } else
      stop("Missing packages: ", paste(missing, collapse = ", "))
  }
  if (any(update))
    update.packages(oldPkgs = pkgs[update], ask = FALSE, checkBuilt = TRUE)
  for (p in pkgs[load])
    loadNamespace(p)
  for (p in pkgs[attach])
    library(p, character.only = TRUE)
}

which is what I've used in one project. You call it as

needsPackages(c("foo", "bar"))

and it installs the missing ones. It can also update, load, or attach them. It's just using the standard function install.packages to install from CRAN, no fancy selection of install locations, or maintenance of particular package versions. If you do use something simple like this, you should run sessionInfo() afterwards to record package version numbers, in case you need to return to the same state later. (Though returning to that state will probably be painful!)

user2554330
  • 37,248
  • 4
  • 43
  • 90
2

There are two possible ways forward here:

  1. Configure renv to use "explicit" snapshots, as described in https://rstudio.github.io/renv/reference/snapshot.html#snapshot-type -- this workflow requires that you list your package requirements in your DESCRIPTION file;

  2. Manually use renv::init(bare = TRUE) + renv::install(<packages>) (or your own package installation functions) to install the packages you need for your project, building the list of <packages> from some separate source that you maintain.

If you have specific workflow that you wish renv would make possible, then you could consider filing a feature request at https://github.com/rstudio/renv/issues.

Kevin Ushey
  • 20,530
  • 5
  • 56
  • 88