#' Show differences between the last snapshot and the library
#'
#' Shows the differences between the project's packrat dependencies, its private
#' package library, and its R scripts.
#'
#' These differences are created when you use the normal R package management
#' commands like \code{\link{install.packages}}, \code{\link{update.packages}},
#' and \code{\link{remove.packages}}. To bring these differences into packrat, you
#' can use \code{\link{snapshot}}.
#'
#' Differences can also arise if one of your collaborators adds or removes
#' packages from the packrat dependencies. In this case, you simply need to tell
#' packrat to update your private package library using \code{\link{restore}}.
#'
#' @param project The directory that contains the R project.
#' @param lib.loc The library to examine. Defaults to the private library
#' associated with the project directory.
#' @param quiet Print detailed information about the packrat status to the console?
#'
#' @return Either \code{NULL} if a \code{packrat} project has not yet been
#' initialized, or a (invisibly) a \code{data.frame} with components:
#' \item{package}{The package name,}
#' \item{packrat.version}{The package version used in the last snapshot,}
#' \item{packrat.source}{The location from which the package was obtained,}
#' \item{library.version}{The package version available in the local library,}
#' \item{currently.used}{Whether the package is used in any of the R code in the current project.}
#'
#' @export
status <- function(project = NULL, lib.loc = libDir(project), quiet = FALSE) {
project <- getProjectDir(project)
stopIfNoLockfile(project)
projectDefault <- identical(project, '.')
project <- normalizePath(project, winslash = '/', mustWork = TRUE)
### Step 1: Collect packages from three sources: packrat.lock, code inspection
### (using lib.loc and getLibPaths() to find packages/dependencies), and by
### enumerating the packages in lib.loc.
## Packages from the lockfile (with their version)
packratPackages <- lockInfo(project, fatal = FALSE)
if (length(packratPackages) == 0) {
initArg <- if (projectDefault) '' else deparse(project)
cat('This directory does not appear to be using packrat.\n',
'Call packrat::init(', initArg, ') to initialize packrat.',
sep = '')
return(invisible())
}
# Get the names, alongside the versions, of packages recorded in the lockfile
packratNames <- getPackageElement(packratPackages, "name")
packratVersions <- getPackageElement(packratPackages, "version")
packratSources <- getPackageElement(packratPackages, "source")
## Packages in the library (with their version)
installedPkgFolders <- list.files(lib.loc, full.names = TRUE)
installedPkgRecords <- lapply(installedPkgFolders, function(path) {
descPath <- file.path(path, "DESCRIPTION")
if (!file.exists(descPath)) {
warning("No DESCRIPTION file for installed package '", basename(path), "'")
return(NULL)
}
DESCRIPTION <- readDcf(descPath, all = TRUE)
list(
name = DESCRIPTION$Package,
source = DESCRIPTION$InstallSource,
version = DESCRIPTION$Version
)
})
installedPkgNames <- unlist(lapply(installedPkgRecords, `[[`, "name"))
names(installedPkgNames) <- installedPkgNames
installedPkgVersions <- unlist(lapply(installedPkgRecords, `[[`, "version"))
names(installedPkgVersions) <- installedPkgNames
# Manually construct package records suitable for later reporting
# Packages inferred from the code
# Don't stop execution if package missing from library; just propagate later
# as information to user
#
# NOTE: We avoid explicitly calling `available.packages()`, just in case we haven't
# yet cached the set of available packages. However, to infer broken dependency chains
# it is in general necessary to have the set of `available.packages()` to fill in
# broken links.
availablePkgs <- if (hasCachedAvailablePackages())
availablePackages()
else
availablePackagesSkeleton()
inferredPkgNames <- appDependencies(
project,
available.packages = availablePkgs
)
# Suppress warnings on 'Suggests', since they may be from non-CRAN repos (e.g. OmegaHat)
suggestedPkgNames <- suppressWarnings(
appDependencies(project,
available.packages = availablePkgs,
fields = "Suggests")
)
# All packages mentioned in one of the three above
allPkgNames <- sort_c(unique(c(
packratNames, installedPkgNames, inferredPkgNames
)))
# Match the above with the set of all package names
.match <- function(what, from = allPkgNames) {
if (is.null(what)) NA
else what[from]
}
packrat.version <- .match(packratVersions)
packrat.source <- .match(packratSources)
library.version <- .match(installedPkgVersions)
currently.used <- allPkgNames %in% c(inferredPkgNames, suggestedPkgNames)
# Generate a table that holds the current overall state
external.packages <- opts$external.packages()
statusTbl <- data.frame(stringsAsFactors = FALSE,
row.names = 1:length(allPkgNames),
package = allPkgNames,
packrat.version = packrat.version,
packrat.source = packrat.source,
library.version = library.version,
currently.used = currently.used,
external.package = allPkgNames %in% external.packages
)
# Only give information on packages not included in external.packages
statusTbl <- statusTbl[!statusTbl$external.package, ]
# Fill the state, according to the different kinds of mismatches there might
# be between packrat.version, library.version, currently.used
if (!quiet) {
# Packages that are only tracked within packrat, but are no longer present
# in the local library nor found in the user's code
onlyPackrat <- with(statusTbl,
!is.na(packrat.version) &
is.na(library.version) &
!currently.used
)
if (any(onlyPackrat)) {
prettyPrint(
searchPackages(packratPackages, statusTbl$package[onlyPackrat]),
header = c("The following packages are tracked by packrat, but are no longer ",
"available in the local library nor present in your code:"),
footer = c("You can call packrat::snapshot() to remove these packages from the lockfile, ",
"or if you intend to use these packages, use packrat::restore() to restore them ",
"to your private library.")
)
}
# Packages that are used in the code, but are not mentioned in either packrat
# or the library
whichUntrackedPackages <- with(statusTbl,
currently.used &
is.na(packrat.version) &
is.na(library.version)
)
pkgNamesUntracked <- statusTbl$package[whichUntrackedPackages]
if (length(pkgNamesUntracked)) {
prettyPrintNames(
pkgNamesUntracked,
c("The following packages are referenced in your code, but are not present\n",
"in your library nor in packrat:\n"),
c("\nYou will need to install these packages manually, then use\n",
"packrat::snapshot() to record these packages in packrat.")
)
}
# Packages that are in the library, currently used, but not tracked by packrat
whichMissingFromPackrat <- with(statusTbl,
currently.used &
is.na(packrat.version) &
(!is.na(library.version))
)
missingFromPackrat <- statusTbl$package[whichMissingFromPackrat]
if (length(missingFromPackrat)) {
prettyPrintPair(
searchPackages(installedPkgRecords, missingFromPackrat),
searchPackages(packratPackages, missingFromPackrat),
"The following packages have been updated in your library, but have not been recorded in packrat:",
"Use packrat::snapshot() to record these packages in packrat.",
"library",
"packrat"
)
}
# Packages that are tracked by packrat, currently used, but out of sync in the library
whichOutOfSync <- with(statusTbl,
currently.used &
!is.na(packrat.version) &
!is.na(library.version) &
packrat.version != library.version)
pkgNamesOutOfSync <- statusTbl$package[whichOutOfSync]
if (length(pkgNamesOutOfSync)) {
prettyPrintPair(
searchPackages(packratPackages, pkgNamesOutOfSync),
searchPackages(installedPkgRecords, pkgNamesOutOfSync),
"The following packages are out of sync between packrat and your current library:",
c("Use packrat::snapshot() to set packrat to use the current library, or use\n",
"packrat::restore() to reset the library to the last snapshot."),
"packrat",
"library"
)
}
# Packages which have been deleted from the library, but are still tracked by packrat,
# and still in use
whichDeletedButStillTracked <- with(statusTbl,
currently.used &
!is.na(packrat.version) &
is.na(library.version))
deletedButStillTracked <- statusTbl$package[whichDeletedButStillTracked]
if (length(deletedButStillTracked)) {
prettyPrintPair(
searchPackages(packratPackages, deletedButStillTracked),
searchPackages(installedPkgRecords, deletedButStillTracked),
"The following packages are used in your code, tracked by packrat, but no longer present in your library:",
c("Use packrat::restore() to restore these libraries.")
)
}
# If everything is in order, let the user know
if (!(any(onlyPackrat) ||
length(missingFromPackrat) ||
length(pkgNamesUntracked) ||
length(pkgNamesOutOfSync) ||
length(deletedButStillTracked) ||
length(missingFromPackrat))) {
message("Up to date.")
}
}
invisible(statusTbl)
}
getPackageElement <- function(package, element) {
setNames(
unlist(lapply(package, "[[", element)),
unlist(lapply(package, "[[", "name"))
)
}
hasCachedAvailablePackages <- function() {
contrib.url <- contrib.url(getOption('repos'))
tempFiles <- list.files(tempdir())
repoNames <- paste("repos_", URLencode(contrib.url, TRUE), ".rds", sep = "")
all(repoNames %in% tempFiles)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.