Update a specific R package and its dependencies

user3175783 picture user3175783 · Jan 9, 2014 · Viewed 29.6k times · Source

I have around 4000 R packages installed in my system (a server) and most of them are outdated because they were built before R-3.0.0. Now I know

update.packages(checkBuilt=TRUE, ask=FALSE)

would update all my packages but that's too slow. The thing is the users do not use most of the packages and now and then they ask me to update a package (say fields) they'd use. Now if I run

install.packages("fields")

it would only update the package fields but not the package maps even if fields depends on maps. Thus when I try to load the package fields:

library("fields")

I get an error message

Error: package ‘maps’ was built before R 3.0.0: please re-install it

Is there a way to upgrade fields so that it would also automatically update the packages fields depends on?

Answer

Gavin Simpson picture Gavin Simpson · Jan 9, 2014

As Ben indicated in his comment, you need to get the dependencies for fields, then filter out the packages with Priority "Base" or "Recommended", and then pass that list of package to install.packages() to deal with the installation. Something like:

instPkgPlusDeps <- function(pkg, install = FALSE,
                            which = c("Depends", "Imports", "LinkingTo"),
                            inc.pkg = TRUE) {
  stopifnot(require("tools")) ## load tools
  ap <- available.packages() ## takes a minute on first use
  ## get dependencies for pkg recursively through all dependencies
  deps <- package_dependencies(pkg, db = ap, which = which, recursive = TRUE)
  ## the next line can generate warnings; I think these are harmless
  ## returns the Priority field. `NA` indicates not Base or Recommended
  pri <- sapply(deps[[1]], packageDescription, fields = "Priority")
  ## filter out Base & Recommended pkgs - we want the `NA` entries
  deps <- deps[[1]][is.na(pri)]
  ## install pkg too?
  if (inc.pkg) {
    deps = c(pkg, deps)
  }
  ## are we installing?
  if (install) {
    install.packages(deps)
  }
  deps ## return dependencies
}

This gives:

R> instPkgPlusDeps("fields")
Loading required package: tools
[1] "fields" "spam"   "maps"

which matches with

> packageDescription("fields", fields = "Depends")
[1] "R (>= 2.13), methods, spam, maps"

You get warnings from the sapply() line if a dependency in deps is not actually installed. I think these are harmless as the returned value in that case is NA and we use that to indicate packages we want to install. I doubt it will affect you if you have 4000 packages installed.

The default is not to install packages but just return the list of dependencies. I figured this was safest as you may not realise the chain of dependencies implied and end up installing hundreds of packages accidentally. Pass in install = TRUE if you are happy to install the packages indicated.

Note that I restrict the types of dependencies searched for - things balloon if you use which = "most" - fields has over 300 such dependencies once you recursively resolve those dependences (which include Suggests: fields too). which = "all" will look for everything, including Enhances: which will be a bigger list of packages again. See ?tools::package_dependencies for valid inputs for the which argument.