Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine minimum R version for all package dependencies

I am a package developer and want to state the minimum R version required to use my package in the DESCRIPTION file.

available.packages parses DESCRIPTIONS of packages, but the result is not (easily) machine readable to lookup recursive dependencies, since the Imports and Depends fields are comma separated text, and sometimes contain packages have version requirements.

The solution described in: Listing R Package Dependencies Without Installing Packages is not a recursive solution. If a nested dependency needed R > 3.3, I want to know about it.

At a minimum, I would like to see the minimum version of R and imported, linked, and depends packages for a given CRAN package. Better still would be to list the package or packages which set the minimum R or package version.

By eliminating dependencies which have higher version requirements, I can serve more people with institutionally old R versions they can't fix: some are still on R 2.x.

like image 397
Jack Wasey Avatar asked Jul 31 '16 17:07

Jack Wasey


2 Answers

min_r_version <- function(package="ggplot2", exclude_main_pkg=TRUE) {

  purrr::walk(c("tools", "purrr", "devtools", "stringi", "tidyr", "dplyr"), 
              require, character.only=TRUE)

  deps <- package_dependencies(package, recursive=TRUE)

  if (exclude_main_pkg) {
    pkgs <- deps[[1]]
  } else {
    pkgs <- c(package, deps[[1]])
  }

  available.packages() %>% 
    as_data_frame() %>% 
    filter(Package %in% pkgs) %>% 
    select(Depends)  %>% 
    unlist() -> pkg_list

  # if main pkg only relied on core R packages (i.e. pkgs that aren't in CRAN) and we 
  # excluded the pkg itself from the min version calculation, this is an edge case we need
  # to handle.

  if (length(pkg_list) == 0) return("Unspecified")

  stri_split_regex(pkg_list, "[,]") %>%
    unlist() %>%
    trimws() %>%
    stri_match_all_regex(c("^R$|^R \\(.*\\)$")) %>%
    unlist() %>%
    discard(is.na(.)) %>%
    unique() %>%
    stri_replace_all_regex("[R >=\\(\\)]", "") %>%
    data_frame(vs=.) %>%
    separate(vs, c("a", "b", "c"), fill="right") %>%
    mutate(c=ifelse(is.na(c), 0, c)) %>%
    arrange(a, b, c) %>%
    tail(1) %>%
    unite(min, a:c, sep=".") -> vs

  return(vs$min)

}

# did we handle the edge cases well enought?
base <- c("base", "compiler", "datasets", "grDevices", "graphics", "grid", "methods", "parallel", "profile", "splines", "stats", "stats4", "tcltk", "tools", "translations")
(base_reqs <- purrr::map_chr(base, min_r_version))
##  [1] "Unspecified" "Unspecified" "Unspecified" "Unspecified" "Unspecified"
##  [6] "Unspecified" "Unspecified" "Unspecified" "Unspecified" "Unspecified"
## [11] "Unspecified" "Unspecified" "Unspecified" "Unspecified" "Unspecified"

# a few of the "core" contributed pkgs rely on a pkg or two outside of base
# but many only rely on base packages, to this is another gd edge case to
# text for.
contrib <- c("KernSmooth", "MASS", "Matrix", "boot", "class", "cluster", "codetools", "foreign", "lattice", "mgcv", "nlme", "nnet", "rpart", "spatial", "survival")
contrib_reqs <- purrr::map_chr(contrib, min_r_version)
##  [1] "Unspecified" "Unspecified" "3.0.0"       "Unspecified" "3.1.0"      
##  [6] "Unspecified" "Unspecified" "Unspecified" "Unspecified" "3.0.2"      
## [11] "3.0.0"       "Unspecified" "Unspecified" "Unspecified" "3.0.1"      

# See what the min version of R shld be for some of my pkgs
min_r_version("ggalt") # I claim R (>= 3.0.0) in DESCRIPTION
## [1] "3.1.2"

min_r_version("curlconverter") # I claim R (>= 3.0.0) in DESCRIPTION
## [1] "3.1.2"

min_r_version("iptools") # I claim R (>= 3.0.0) in DESCRIPTION
## [1] "3.0.0"
like image 59
hrbrmstr Avatar answered Nov 11 '22 12:11

hrbrmstr


Based on ideas from @hrbrmstr and written with base functions, I'm now using the following function:

min_r_version <- function(pkg, repo = "http://cloud.r-project.org") {
  requireNamespace("tools")
  requireNamespace("utils")
  if (is.null(repo))
    repo <- "https://cloud.r-project.org"
  repo <- getOption("repos", default = repo)
  avail <- utils::available.packages(utils::contrib.url(repos = repo))
  deps <- tools::package_dependencies(pkg, db = avail, recursive = TRUE)
  if (is.null(deps))
    stop("package not found")
  
  pkgs <- deps[[1]]
  
  matches <- avail[ , "Package"] %in% pkgs
  pkg_list <- avail[matches, "Depends"]
  vers <- grep("^R$|^R \\(.*\\)$", pkg_list, value = TRUE)
  vers <- gsub("[^0-9.]", "", vers)
  if (length(vers) == 0)
    return("Not specified")
  
  max_ver = vers[1]
  if (length(vers) == 1)
    return(max_ver)
  
  for (v in 2:length(vers))
    if (utils::compareVersion(vers[v], max_ver) > 0)
      max_ver <- vers[v]
  
  max_ver
}
like image 1
2 revs, 2 users 82% Avatar answered Nov 11 '22 13:11

2 revs, 2 users 82%