Skip to content

Commit

Permalink
support reporting + checking of R system requirements (#2101)
Browse files Browse the repository at this point in the history
* begin supporting R system requirements

* support for redhat

* tweak display

* [WIP]

* tweaks

* add some tests

* update NEWS

* simplify a bit

* tweak resolution of sysreqs

* more refinement

* [WIP]

* more work

* resolve package aliases

* tweaks

* try to resolve packages

* check system requirements for other distros

* rename

* move sysreqs to single file

* tidy up

* add collapse arg

* add to pkgdown

* formatting

* try to resolve virtual packages on debian systems

* allow alias to accept vector text

* handle virtual packages on rpm in a similar way

* fixes

* tweaks
  • Loading branch information
kevinushey authored Mar 2, 2025
1 parent e7a42df commit c7dbbc5
Show file tree
Hide file tree
Showing 46 changed files with 14,402 additions and 84 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export(scaffold)
export(settings)
export(snapshot)
export(status)
export(sysreqs)
export(update)
export(upgrade)
export(use)
Expand Down
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@

# renv (development version)

* `renv` gains the `sysreqs()` function, which can be used to query the system
packages required by a set of R packages. Functionality is currently available
for Debian-based distributions, as well as Red Hat distributions.

* On Linux, `renv` now uses the database from
<https://github.com/rstudio/r-system-requirements> when determining if
an R package's required system libraries are installed, and notifies
the user which packages (if any) are missing during install / restore.

* Fixed an issue where `renv` could fail to retrieve credentials registered
for 'github.com' when querying URLs at 'api.github.com'.

Expand Down
4 changes: 2 additions & 2 deletions R/abi.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ renv_abi_check <- function(packages = NULL,
reasons <- unique(tbl$reason)
if ("Rcpp_precious_list" %in% reasons) {
packages <- sort(unique(tbl$package[tbl$reason == "Rcpp_precious_list"]))
caution_bullets(
bulletin(
"The following packages were built against a newer version of Rcpp than is currently available:",
packages,
c(
Expand All @@ -61,7 +61,7 @@ renv_abi_check <- function(packages = NULL,
if ("missing" %in% reasons) {

missing <- tbl[tbl$reason == "missing", ]
caution_bullets(
bulletin(
"The following required system libraries are unavailable:",
unique(missing$dependency),
c(
Expand Down
8 changes: 5 additions & 3 deletions R/aliases.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

# aliases used primarily for nicer / normalized text output
the$aliases <- list(
the$aliases <- c(
bioc = "Bioconductor",
bioconductor = "Bioconductor",
bitbucket = "Bitbucket",
Expand All @@ -16,6 +16,8 @@ the$aliases <- list(
xgit = "Git"
)

alias <- function(text) {
the$aliases[[text]] %||% text
alias <- function(text, aliases = the$aliases) {
matches <- text %in% names(aliases)
text[matches] <- aliases[text[matches]]
text
}
2 changes: 1 addition & 1 deletion R/available-packages.R
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ renv_available_packages_query <- function(type, repos, quiet = FALSE) {
paste(c(header(url), msgs, ""), collapse = "\n")
})

caution_bullets(header, msgs)
bulletin(header, msgs)
filter(dbs, Negate(is.null))

}
Expand Down
12 changes: 6 additions & 6 deletions R/cache.R
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ renv_cache_diagnose_corrupt_metadata <- function(paths, problems, verbose) {

# nocov start
if (verbose) {
caution_bullets(
bulletin(
"The following package(s) are missing 'Meta/package.rds':",
renv_cache_format_path(bad),
"These packages should be purged and reinstalled."
Expand Down Expand Up @@ -304,7 +304,7 @@ renv_cache_diagnose_corrupt_metadata <- function(paths, problems, verbose) {

# nocov start
if (verbose) {
caution_bullets(
bulletin(
"The following package(s) have corrupt 'Meta/package.rds' files:",
renv_cache_format_path(bad),
"These packages should be purged and reinstalled."
Expand Down Expand Up @@ -335,7 +335,7 @@ renv_cache_diagnose_missing_descriptions <- function(paths, problems, verbose) {

# nocov start
if (verbose) {
caution_bullets(
bulletin(
"The following packages are missing DESCRIPTION files in the cache:",
renv_cache_format_path(bad),
"These packages should be purged and reinstalled."
Expand Down Expand Up @@ -369,7 +369,7 @@ renv_cache_diagnose_bad_hash <- function(paths, problems, verbose) {
fmt <- "%s %s [Hash: %s != %s]"
entries <- sprintf(fmt, lhs$Package, lhs$Version, lhs$Hash, rhs$Hash)

caution_bullets(
bulletin(
"The following packages have incorrect hashes:",
entries,
"Consider using `renv::rehash()` to re-hash these packages."
Expand Down Expand Up @@ -412,7 +412,7 @@ renv_cache_diagnose_wrong_built_version <- function(paths, problems, verbose) {
# nocov start
if (verbose) {

caution_bullets(
bulletin(
"The following packages have no 'Built' field recorded in their DESCRIPTION file:",
paths[isna],
"renv is unable to validate the version of R this package was built for."
Expand Down Expand Up @@ -450,7 +450,7 @@ renv_cache_diagnose_wrong_built_version <- function(paths, problems, verbose) {
# nocov start
if (verbose) {

caution_bullets(
bulletin(
"The following packages in the cache were built for a different version of R:",
renv_cache_format_path(paths[wrong]),
"These packages will need to be purged and reinstalled."
Expand Down
14 changes: 7 additions & 7 deletions R/caution.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ caution <- function(fmt = "", ..., con = stdout()) {
writeLines(sprintf(fmt, ...), con = con)
}

caution_bullets <- function(preamble = NULL,
values = NULL,
postamble = NULL,
...,
bullets = TRUE,
emitter = NULL)
bulletin <- function(preamble = NULL,
values = NULL,
postamble = NULL,
...,
bullets = TRUE,
emitter = NULL)
{
if (empty(values))
return(invisible())

renv_dots_check(...)

lines <- c(
if (length(preamble)) paste(preamble, collapse = "\n"),
if (length(preamble)) paste(preamble, collapse = "\n"),
if (bullets)
paste("-", values, collapse = "\n")
else
Expand Down
12 changes: 6 additions & 6 deletions R/clean.R
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ renv_clean_library_tempdirs <- function(project, prompt) {
# nocov start
if (prompt || renv_verbose()) {

caution_bullets("The following directories will be removed:", bad)
bulletin("The following directories will be removed:", bad)

if (prompt && !proceed())
cancel()
Expand Down Expand Up @@ -177,7 +177,7 @@ renv_clean_system_library <- function(project, prompt) {
# nocov start
if (prompt || renv_verbose()) {

caution_bullets(
bulletin(
"The following non-system packages are installed in the system library:",
packages,
c(
Expand Down Expand Up @@ -227,7 +227,7 @@ renv_clean_unused_packages <- function(project, prompt) {
# nocov start
if (prompt || renv_verbose()) {

caution_bullets(
bulletin(
c(
"The following packages are installed in the project library,",
"but appear to be no longer used in your project."
Expand Down Expand Up @@ -272,7 +272,7 @@ renv_clean_package_locks <- function(project, prompt) {
# nocov start
if (prompt || renv_verbose()) {

caution_bullets(
bulletin(
"The following stale package locks were discovered in your library:",
basename(old),
"These locks will be removed."
Expand Down Expand Up @@ -306,7 +306,7 @@ renv_clean_cache <- function(project, prompt) {
missing <- !file.exists(projlist)
if (any(missing)) {

caution_bullets(
bulletin(
"The following projects are monitored by renv, but no longer exist:",
projlist[missing],
"These projects will be removed from renv's project list."
Expand Down Expand Up @@ -342,7 +342,7 @@ renv_clean_cache <- function(project, prompt) {

if (prompt || renv_verbose()) {

caution_bullets(
bulletin(
"The following packages are installed in the cache but no longer used:",
renv_cache_format_path(diff),
"These packages will be removed."
Expand Down
9 changes: 9 additions & 0 deletions R/config-defaults.R
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,15 @@ config <- list(
)
},

sysreqs.check = function(..., default = TRUE) {
renv_config_get(
name = "sysreqs.check",
type = "logical[1]",
default = default,
args = list(...)
)
},

updates.check = function(..., default = FALSE) {
renv_config_get(
name = "updates.check",
Expand Down
2 changes: 1 addition & 1 deletion R/dependencies.R
Original file line number Diff line number Diff line change
Expand Up @@ -1883,7 +1883,7 @@ renv_dependencies_report <- function(errors) {
paste(c(header(file), messages, ""), collapse = "\n")
})

caution_bullets(
bulletin(
"WARNING: One or more problems were discovered while enumerating dependencies.",
c("", lines),
"Please see `?renv::dependencies` for more information.",
Expand Down
16 changes: 16 additions & 0 deletions R/diagnostics.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ diagnostics <- function(project = NULL) {
renv_diagnostics_project,
renv_diagnostics_status,
renv_diagnostics_packages,
renv_diagnostics_sysreqs,
renv_diagnostics_abi,
renv_diagnostics_profile,
renv_diagnostics_settings,
Expand Down Expand Up @@ -145,6 +146,21 @@ renv_diagnostics_packages <- function(project) {

}

renv_diagnostics_sysreqs <- function(project) {

if (!renv_platform_linux())
return()

writef(header("R System Requirements"))

lockfile <- renv_lockfile_create(project)
records <- renv_lockfile_records(lockfile)
sysreqs <- map(records, `[[`, "SystemRequirements")
ok <- renv_sysreqs_check(sysreqs, prompt = FALSE)
invisible(ok)

}

renv_diagnostics_packages_version <- function(lockfile, all) {

data <- rep.int(NA_character_, length(all))
Expand Down
4 changes: 2 additions & 2 deletions R/equip-macos.R
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ renv_equip_macos_toolchain <- function() {
return(TRUE)

command <- paste("sudo /usr/sbin/installer -pkg", shQuote(destfile), "-target /")
caution_bullets(
bulletin(
"The R LLVM toolchain has been successfully downloaded. Please execute:",
command,
"in a separate terminal to complete installation."
Expand Down Expand Up @@ -123,7 +123,7 @@ renv_equip_macos_rstudio <- function(spec, destfile) {
if (!installed)
return(FALSE)

caution_bullets(
bulletin(
"The R LLVM toolchain has been downloaded and installed to:",
spec$dst,
"This toolchain will be used by renv when installing packages from source."
Expand Down
4 changes: 2 additions & 2 deletions R/extsoft.R
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ renv_extsoft_install <- function(quiet = FALSE) {

if (interactive()) {

caution_bullets(
bulletin(
"The following external software tools will be installed:",
files,
sprintf("Tools will be installed into %s.", renv_path_pretty(extsoft))
Expand Down Expand Up @@ -135,7 +135,7 @@ renv_extsoft_use <- function(quiet = FALSE) {

if (interactive()) {

caution_bullets(
bulletin(
"The following entries will be added to ~/.R/Makevars:",
c(localsoft, libxml, localcpp, locallibs),
"These tools will be used when compiling R packages from source."
Expand Down
4 changes: 2 additions & 2 deletions R/hydrate.R
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ renv_hydrate_resolve_missing <- function(project, library, remotes, missing) {
sprintf("[%s]: %s", package, short)
})

caution_bullets(
bulletin(
"The following package(s) were not installed successfully:",
text,
"You may need to manually download and install these packages."
Expand Down Expand Up @@ -416,7 +416,7 @@ renv_hydrate_report <- function(packages, na, linkable) {
}

if (length(na)) {
caution_bullets(
bulletin(
"The following packages are used in this project, but not available locally:",
csort(names(na)),
"renv will attempt to download and install these packages."
Expand Down
14 changes: 11 additions & 3 deletions R/install.R
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@ install <- function(packages = NULL,
cancel_if(prompt && !proceed())
}

# check for installed dependencies
if (config$sysreqs.check(default = renv_platform_linux())) {
paths <- map(records, `[[`, "Path")
sysreqs <- map(paths, renv_sysreqs_read)
renv_sysreqs_check(sysreqs, prompt = prompt)
}


# install retrieved records
before <- Sys.time()
renv_install_impl(records)
Expand Down Expand Up @@ -766,7 +774,7 @@ renv_install_preflight_requirements <- function(records) {
fmt <- "Package '%s' requires '%s', but '%s' will be installed"
text <- sprintf(fmt, format(package), format(requires), format(actual))
if (renv_verbose()) {
caution_bullets(
bulletin(
"The following issues were discovered while preparing for installation:",
text,
"Installation of these packages may not succeed."
Expand All @@ -789,7 +797,7 @@ renv_install_postamble <- function(packages) {
installed <- map_chr(packages, renv_package_version)
loaded <- map_chr(packages, renv_namespace_version)

caution_bullets(
bulletin(
c("", "The following loaded package(s) have been updated:"),
packages[installed != loaded],
"Restart your R session to use the new versions."
Expand Down Expand Up @@ -826,7 +834,7 @@ renv_install_preflight_permissions <- function(library) {
postamble <- sprintf(fmt, info$effective_user %||% info$user)

# print it
caution_bullets(
bulletin(
preamble = preamble,
values = library,
postamble = postamble
Expand Down
2 changes: 1 addition & 1 deletion R/library.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ renv_library_diagnose <- function(project, libpath) {
# if only some symlinks are broken, report to user
if (any(missing)) {

caution_bullets(
bulletin(
"The following package(s) are missing entries in the cache:",
basename(children[missing]),
"These packages will need to be reinstalled."
Expand Down
2 changes: 1 addition & 1 deletion R/load.R
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ renv_load_check_description <- function(project) {

values <- sprintf("[line %i is blank]", bad)

caution_bullets(
bulletin(
sprintf("%s contains blank lines:", renv_path_pretty(descpath)),
values,
c(
Expand Down
2 changes: 1 addition & 1 deletion R/lockfile-read.R
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ renv_lockfile_read_preflight <- function(contents) {

all <- unlist(parts, recursive = TRUE, use.names = FALSE)

caution_bullets(
bulletin(
"The lockfile contains one or more merge conflict markers:",
head(all, n = -1L),
"You will need to resolve these merge conflicts before the file can be read."
Expand Down
2 changes: 1 addition & 1 deletion R/migrate.R
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ renv_migrate_packrat_cache_impl <- function(targets) {
if (nrow(bad) == 0)
return(TRUE)

caution_bullets(
bulletin(
"The following packages could not be copied from the Packrat cache:",
with(bad, sprintf("%s [%s]", format(source), reason)),
"These packages may need to be reinstalled and re-cached."
Expand Down
Loading

0 comments on commit c7dbbc5

Please sign in to comment.