My answer builds on Gavin's answer... Note that the original poster, user3175783, asked for a more intelligent version of update.packages()
. That function skips installing packages that are already up-to-date. But Gavin's solution installs a package and all its dependencies, whether they are up-to-date or not. I used Gavin's tip of skipping base packages (which are not actually installable), and coded up a solution which also skips up-to-date packages.
The main function is installPackages()
. This function and its helpers perform a topological-sort of the dependency tree rooted at a given set of packages. The packages in the resulting list are checked for staleness and installed one by one. Here's some example output:
> remove.packages("tibble")
Removing package from ‘/home/frederik/.local/lib/x86_64/R/packages’
(as ‘lib’ is unspecified)
> installPackages(c("ggplot2","stringr","Rcpp"), dry_run=T)
## Package digest is out of date ( 0.6.9 < 0.6.10 )
Would have installed package digest
## Package gtable is up to date ( 0.2.0 )
## Package MASS is up to date ( 7.3.45 )
## Package Rcpp is out of date ( 0.12.5 < 0.12.8 )
Would have installed package Rcpp
## Package plyr is out of date ( 1.8.3 < 1.8.4 )
Would have installed package plyr
## Package stringi is out of date ( 1.0.1 < 1.1.2 )
Would have installed package stringi
## Package magrittr is up to date ( 1.5 )
## Package stringr is out of date ( 1.0.0 < 1.1.0 )
Would have installed package stringr
...
## Package lazyeval is out of date ( 0.1.10 < 0.2.0 )
Would have installed package lazyeval
## Package tibble is not currently installed, installing
Would have installed package tibble
## Package ggplot2 is out of date ( 2.1.0 < 2.2.0 )
Would have installed package ggplot2
Here's the code, sorry about the length:
library(tools)
# Helper: a "functional" interface depth-first-search
fdfs = function(get.children) {
rec = function(root) {
cs = get.children(root);
out = c();
for(c in cs) {
l = rec(c);
out = c(out, setdiff(l, out));
}
c(out, root);
}
rec
}
# Entries in the package "Priority" field which indicate the
# package can't be upgraded. Not sure why we would exclude
# recommended packages, since they can be upgraded...
#excl_prio = c("base","recommended")
excl_prio = c("base")
# Find the non-"base" dependencies of a package.
nonBaseDeps = function(packages,
ap=available.packages(),
ip=installed.packages(), recursive=T) {
stopifnot(is.character(packages));
all_deps = c();
for(p in packages) {
# Get package dependencies. Note we are ignoring version
# information
deps = package_dependencies(p, db = ap, recursive = recursive)[[1]];
ipdeps = match(deps,ip[,"Package"])
# We want dependencies which are either not installed, or not part
# of Base (e.g. not installed with R)
deps = deps[is.na(ipdeps) | !(ip[ipdeps,"Priority"] %in% excl_prio)];
# Now check that these are in the "available.packages()" database
apdeps = match(deps,ap[,"Package"])
notfound = is.na(apdeps)
if(any(notfound)) {
notfound=deps[notfound]
stop("Package ",p," has dependencies not in database: ",paste(notfound,collapse=" "));
}
all_deps = union(deps,all_deps);
}
all_deps
}
# Return a topologically-sorted list of dependencies for a given list
# of packages. The output vector contains the "packages" argument, and
# recursive dependencies, with each dependency occurring before any
# package depending on it.
packageOrderedDeps = function(packages, ap=available.packages()) {
# get ordered dependencies
odeps = sapply(packages,
fdfs(function(p){nonBaseDeps(p,ap=ap,recursive=F)}))
# "unique" preserves the order of its input
odeps = unique(unlist(odeps));
# sanity checks
stopifnot(length(setdiff(packages,odeps))==0);
seen = list();
for(d in odeps) {
ddeps = nonBaseDeps(d,ap=ap,recursive=F)
stopifnot(all(ddeps %in% seen));
seen = c(seen,d);
}
as.vector(odeps)
}
# Checks if a package is up-to-date.
isPackageCurrent = function(p,
ap=available.packages(),
ip=installed.packages(),
verbose=T) {
if(verbose) msg = function(...) cat("## ",...)
else msg = function(...) NULL;
aprow = match(p, ap[,"Package"]);
iprow = match(p, ip[,"Package"]);
if(!is.na(iprow) && (ip[iprow,"Priority"] %in% excl_prio)) {
msg("Package ",p," is a ",ip[iprow,"Priority"]," package\n");
return(T);
}
if(is.na(aprow)) {
stop("Couldn't find package ",p," among available packages");
}
if(is.na(iprow)) {
msg("Package ",p," is not currently installed, installing\n");
F;
} else {
iv = package_version(ip[iprow,"Version"]);
av = package_version(ap[aprow,"Version"]);
if(iv < av) {
msg("Package ",p," is out of date (",
as.character(iv),"<",as.character(av),")\n");
F;
} else {
msg("Package ",p," is up to date (",
as.character(iv),")\n");
T;
}
}
}
# Like install.packages, but skips packages which are already
# up-to-date. Specify dry_run=T to just see what would be done.
installPackages =
function(packages,
ap=available.packages(), dry_run=F,
want_deps=T) {
stopifnot(is.character(packages));
ap=tools:::.remove_stale_dups(ap)
ip=installed.packages();
ip=tools:::.remove_stale_dups(ip)
if(want_deps) {
packages = packageOrderedDeps(packages, ap);
}
for(p in packages) {
curr = isPackageCurrent(p,ap,ip);
if(!curr) {
if(dry_run) {
cat("Would have installed package ",p,"\n");
} else {
install.packages(p,dependencies=F);
}
}
}
}
# Convenience function to make sure all the libraries we have loaded
# in the current R session are up-to-date (and to update them if they
# are not)
updateAttachedLibraries = function(dry_run=F) {
s=search();
s=s[grep("^package:",s)];
s=gsub("^package:","",s)
installPackages(s,dry_run=dry_run);
}
update.packages(checkBuilt=TRUE, ask=FALSE)
. – Elbowap <- available.packages(); pkgs <- tools::package_dependencies("fields",db=ap,recursive=TRUE)
. Then you need to filter out built-in and recommended packages, and install the rest. (This doesn't deal with the order in which the dependency graph goes, but it might work for your case.) – Auntie>
and you should be using the code/pre markdown which is to indent by 4 spaces. – Callowwhich
argument. If I dowhich = "most"
with fields you'll need to install almost 400 packages! For some more popular packages you could end up installing big chunks of CRAN, in which case you might just update all from CRAN at the weekend. – Callowupdate.packages()
function, with all its limitations? Why do you consider writing new package installation functions to be an attempt to "re-engineer or re-write R's package system"? Isn't this rather an attempt to improve R's package system? And haven't the functions we use, likepackage_dependencies()
andinstalled.packages()
, been made available for just this purpose? – Pseudaxis