Nothing
#' Clean a project
#'
#' Clean up a project and its associated \R libraries.
#'
#' # Actions
#'
#' The following clean actions are available:
#'
#' \describe{
#'
#' \item{`package.locks`}{
#'
#' During package installation, \R will create package locks in the
#' library path, typically named `00LOCK-<package>`. On occasion, if package
#' installation fails or \R is terminated while installing a package, these
#' locks can be left behind and will inhibit future attempts to reinstall
#' that package. Use this action to remove such left-over package locks.
#'
#' }
#'
#' \item{`library.tempdirs`}{
#'
#' During package installation, \R may create temporary directories with
#' names of the form `file\w{12}`, and on occasion those files can be
#' left behind even after they are no longer in use. Use this action to
#' remove such left-over directories.
#' }
#'
#' \item{`system.library`}{
#'
#' In general, it is recommended that only packages distributed with \R
#' are installed into the default library (the library path referred to
#' by `.Library`). Use this action to remove any user-installed packages
#' that have been installed to the system library.
#'
#' Because this action is destructive, it is by default never run -- it
#' must be explicitly requested by the user.
#'
#' }
#'
#' \item{`unused.packages`}{
#'
#' Remove packages that are installed in the project library, but no longer
#' appear to be used in the project sources.
#'
#' Because this action is destructive, it is by default only run in
#' interactive sessions when prompting is enabled.
#'
#' }
#'
#' }
#'
#'
#' @inherit renv-params
#'
#' @param actions The set of clean actions to take. See the documentation in
#' **Actions** for a list of available actions, and the default actions
#' taken when no actions are supplied.
#'
#' @export
#'
#' @examples
#' \dontrun{
#'
#' # clean the current project
#' renv::clean()
#'
#' }
clean <- function(project = NULL,
...,
actions = NULL,
prompt = interactive())
{
renv_scope_error_handler()
renv_dots_check(...)
project <- renv_project_resolve(project)
renv_project_lock(project = project)
renv_scope_verbose_if(prompt)
renv_activate_prompt("clean", NULL, prompt, project)
actions <- actions %||% renv_clean_actions(prompt)
all <- list(
package.locks = renv_clean_package_locks,
library.tempdirs = renv_clean_library_tempdirs,
system.library = renv_clean_system_library,
unused.packages = renv_clean_unused_packages
)
methods <- all[actions]
for (method in methods)
tryCatch(method(project, prompt), error = warnify)
writef("- The project has been cleaned.")
invisible(project)
}
renv_clean_actions <- function(prompt) {
default <- c(
"package.locks",
"library.tempdirs"
)
unsafe <- c(
# "system.library",
"unused.packages"
)
c(default, if (prompt) unsafe)
}
renv_clean_library_tempdirs <- function(project, prompt) {
ntd <- function() {
writef("- No temporary directories were found in the project library.")
FALSE
}
library <- renv_paths_library(project = project)
children <- list.files(library, full.names = TRUE)
bad <- grep("/file\\w{12}$", children, value = TRUE)
if (empty(bad))
return(ntd())
# nocov start
if (prompt || renv_verbose()) {
caution_bullets("The following directories will be removed:", bad)
if (prompt && !proceed())
cancel()
}
# nocov end
unlink(bad, recursive = TRUE)
TRUE
}
# remove user packages in system library
renv_clean_system_library <- function(project, prompt) {
ntd <- function() {
writef("- No non-system packages were discovered in the system library.")
FALSE
}
# explicitly query for packages
syslib <- renv_path_normalize(renv_libpaths_system())
db <- installed_packages(lib.loc = syslib, priority = "NA")
packages <- setdiff(db$Package, "translations")
# also look for leftover package folders
# (primarily for Windows, where .dlls from old packages can be left behind)
# nocov start
if (renv_platform_windows()) {
folders <- list.files(syslib, full.names = TRUE)
descpaths <- file.path(folders, "DESCRIPTION")
missing <- !file.exists(descpaths)
packages <- union(packages, basename(folders)[missing])
}
# nocov end
# check for any packages needing removal
if (empty(packages))
return(ntd())
# nocov start
if (prompt || renv_verbose()) {
caution_bullets(
"The following non-system packages are installed in the system library:",
packages,
c(
"Normally, only packages distributed with R should be installed in the system library.",
"These packages will be removed.",
"If necessary, consider reinstalling these packages in your site library."
)
)
if (prompt && !proceed())
cancel()
}
# nocov end
remove(packages, library = syslib)
TRUE
}
renv_clean_unused_packages <- function(project, prompt) {
ntd <- function() {
writef("- No unused packages were found in the project library.")
FALSE
}
# find packages installed in the project library
library <- renv_paths_library(project = project)
installed <- list.files(library)
if (empty(installed))
return(ntd())
# find packages used in the project and their recursive dependencies
packages <- renv_snapshot_dependencies(project, dev = TRUE)
paths <- renv_package_dependencies(packages, project = project)
packages <- names(paths)
# figure out which packages aren't needed
removable <- renv_vector_diff(installed, packages)
if (empty(removable))
return(ntd())
# nocov start
if (prompt || renv_verbose()) {
caution_bullets(
c(
"The following packages are installed in the project library,",
"but appear to be no longer used in your project."
),
removable,
"These packages will be removed."
)
if (prompt && !proceed())
cancel()
}
# nocov end
remove(removable, library = library)
return(TRUE)
}
renv_clean_package_locks <- function(project, prompt) {
ntd <- function() {
writef("- No stale package locks were found.")
FALSE
}
# find 00LOCK directories in library
library <- renv_paths_library(project = project)
lock <- list.files(path = library, pattern = "^00LOCK", full.names = TRUE)
if (empty(lock))
return(ntd())
# check to see which are old
now <- Sys.time()
mtime <- file.mtime(lock)
mtime[is.na(mtime)] <- now
diff <- difftime(now, mtime, units = "secs")
old <- lock[diff > 120]
if (empty(old))
return(ntd())
# nocov start
if (prompt || renv_verbose()) {
caution_bullets(
"The following stale package locks were discovered in your library:",
basename(old),
"These locks will be removed."
)
if (prompt && !proceed())
cancel()
}
# nocov end
unlink(old, recursive = TRUE)
TRUE
}
# nocov start
renv_clean_cache <- function(project, prompt) {
ntd <- function() {
writef("- No unused packages were found in the renv cache.")
FALSE
}
# find projects monitored by renv
projects <- renv_paths_root("projects")
projlist <- character()
if (file.exists(projects))
projlist <- readLines(projects, warn = FALSE, encoding = "UTF-8")
# inform user if any projects are missing
missing <- !file.exists(projlist)
if (any(missing)) {
caution_bullets(
"The following projects are monitored by renv, but no longer exist:",
projlist[missing],
"These projects will be removed from renv's project list."
)
if (prompt && !proceed())
cancel()
writeLines(projlist[!missing], con = projects, useBytes = TRUE)
}
action <- function(project) {
library <- renv_paths_library(project = project)
packages <- list.files(library, full.names = TRUE)
descs <- file.path(packages, "DESCRIPTION")
existing <- file.exists(descs)
map_chr(descs[existing], renv_cache_path, USE.NAMES = FALSE)
}
# for each project, find packages used in their renv private library,
# and look for entries in the cache
projlist <- projlist[!missing]
callback <- renv_progress_callback(action, length(projlist))
used <- uapply(projlist, callback)
# check what packages are actually available in the cache
available <- renv_cache_list()
diff <- renv_vector_diff(available, used)
if (empty(diff))
return(ntd())
if (prompt || renv_verbose()) {
caution_bullets(
"The following packages are installed in the cache but no longer used:",
renv_cache_format_path(diff),
"These packages will be removed."
)
if (prompt && !proceed())
cancel()
}
# remove the directories
unlink(diff, recursive = TRUE)
renv_cache_clean_empty()
writef("- %i package(s) have been removed.", length(diff))
TRUE
}
# nocov end
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.