Reputation: 12707
install.packages()
returns a warning if a package cannot be installed (for instance, if it is unavailable); for example:
install.packages("notapackage")
(EDIT: I'd like to throw an error regardless of the reason the package cannot be installed, not just this example case of a missing package).
I am running the install.packages
command in a script, and I would like it to trigger a proper error and exit execution. I don't see an obvious option inside install.packages
for handling this behavior. Any suggestions?
Upvotes: 17
Views: 5898
Reputation: 1279
I've added another version that essentially tweaks @mnl's solution (which is in turn inspired by @dirk-eddelbuettel's install_packages2
function) to collect and report all of the package build failures.
# `install.packages` unfortunately does not throw an error if package
# installation fails but rather a warning. So we check the warnings and promote
# the appropriate ones to errors. The regex conditions are taken from the
# `install_packages2` function in
# https://github.com/eddelbuettel/littler/blob/master/inst/examples/install2.r
warns <- character(0L)
withCallingHandlers(
expr = {
install.packages(c("pk1", "pkg2", "etc"))
}, warning = function(w) {
catch <- (
grepl("download of package .* failed", w$message)
|| grepl("(dependenc|package).*(is|are) not available", w$message)
|| grepl("installation of package.*had non-zero exit status", w$message)
|| grepl("installation of one or more packages failed", w$message)
)
if (catch) {
warns <<- c(warns, w$message)
invokeRestart("muffleWarning")
}
}
)
if (length(warns) >= 1L) {
msg <- paste(warns, collapse = "\n")
stop(msg)
}
Upvotes: 0
Reputation: 997
try to install then look for warnings to stop the execution and return an error. also calls library() , just in case !
install_or_fail <- function(package_name){
tryCatch({install.packages(package_name, dependencies = TRUE)
library(package_name)},
error = function(e){ print(e) },
warning = function(w){
catch <-
grepl("download of package .* failed", w$message) ||
grepl("(dependenc|package).*(is|are) not available", w$message) ||
grepl("installation of package.*had non-zero exit status", w$message) ||
grepl("installation of one or more packages failed", w$message)
if(catch){ print(w$message)
stop(paste("installation failed for:",package_name ))}}
)
}
inspired by : https://github.com/eddelbuettel/littler/blob/master/inst/examples/install2.r
Upvotes: 3
Reputation: 401
Building off of Cameron Kerr's answer, here's another solution that would work in a Dockerfile
(or at a unix command line) without needing to add an additional file/layer:
RUN R -e " \
install_packages_or_die <- function (pkgs, repos='http://cran.rstudio.com/') { \
for (l in pkgs) { install.packages(l, dependencies=TRUE, repos=repos); \
if ( ! library(l, character.only=TRUE, logical.return=TRUE) ) { \
stop ('ERROR: failed installing requested package \'',l,'\'') } } } ; \
install_packages_or_die (pkgs= c('mime')); "
RUN R -e " \
install_packages_or_die <- function (pkgs, repos='http://cran.rstudio.com/') { \
for (l in pkgs) { install.packages(l, dependencies=TRUE, repos=repos); \
if ( ! library(l, character.only=TRUE, logical.return=TRUE) ) { \
stop ('ERROR: failed installing requested package \'',l,'\'') } } } ; \
install_packages_or_die (pkgs= c('packagedoesnotexist')); "
Note: sometimes packages install dependencies after the requested package failed, and you'll still have to search the log to see what the actual error was.
Upvotes: 0
Reputation: 1875
Having just solved this for myself, I noted that install.packages() results in calling library(thepackage) to see it its usable.
I made a R script that installs the given packages, uses library
on each to see if its loadable, and if not calls quit
with a non-0 status.
I call it install_packages_or_die.R
#!/usr/bin/env Rscript
packages = commandArgs(trailingOnly=TRUE)
for (l in packages) {
install.packages(l, dependencies=TRUE, repos='https://cran.rstudio.com/');
if ( ! library(l, character.only=TRUE, logical.return=TRUE) ) {
quit(status=1, save='no')
}
}
Use it like this in your Dockerfile. I'm grouping them in useful chunks to try and make reasonable use of Docker build cache.
ADD install_packages_or_die.R /
RUN Rscript --no-save install_packages_or_die.R profvis devtools memoise
RUN Rscript --no-save install_packages_or_die.R memoise nosuchpackage
Disclaimer: I do not consider myself an R programmer at all currently, so there may very well be better ways of doing this.
Upvotes: 16
Reputation: 12707
The R function WithCallingHandlers()
lets us handle any warnings with an explicitly defined function. For instance, we can tell the R to stop if it receives any warnings (and return the warning message as an error message).
withCallingHandlers(install.packages("notapackage"),
warning = function(w) stop(w))
I am guessing that this is not ideal, since presumably a package could install successfully but still throw a warning; but haven't encountered that case. As Dirk suggests, testing require
for the package is probably more robust.
Upvotes: 6
Reputation: 368399
Expanding on the quick comment:
R> AP <- available.packages()
R> "notapackage" %in% AP[,1] # expected to yield FALSE
[1] FALSE
R> "digest" %in% AP[,1] # whereas this should be TRUE
[1] TRUE
R>
Upvotes: 0