简体   繁体   中英

how do I make install.packages return an error if an R package cannot be installed?

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?

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.

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.

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

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> 

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.

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)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM