【问题标题】:Determine minimum R version for all package dependencies确定所有包依赖项的最低 R 版本
【发布时间】:2016-12-05 19:13:06
【问题描述】:

我是包开发人员,想在DESCRIPTION 文件中说明使用我的包所需的最低 R 版本。

available.packages 解析包的说明,但结果不是(很容易)机器可读以查找递归依赖项,因为 Imports 和 Depends 字段是逗号分隔的文本,并且有时包含具有版本要求的包。

中描述的解决方案: Listing R Package Dependencies Without Installing Packages 不是递归解决方案。如果嵌套依赖项需要 R > 3.3,我想知道它。

至少,我希望查看给定 CRAN 包的 R 和导入、链接和依赖包的最低版本。最好列出设置最低 R 或包版本的一个或多个包。

通过消除具有更高版本要求的依赖项,我可以为更多的人提供他们无法修复的旧 R 版本:有些仍然在 R 2.x 上。

【问题讨论】:

  • 我怀疑你会不会对这个项目产生浓厚的兴趣,但一种方法可能是首先检查 R 版本的日期,然后不安装任何较晚日期的包。 (它不会是 R 脚本。)您需要确保为特定版本的 R 和特定操作系统提供适当的构建工具集,因为没有全面的二进制包存储。另一种方法可能是确定用户落后的原因并解决这些问题。
  • 该引用问题的第二个答案确实提供了递归解决方案。所以也许这是重复的(即使选中标记的解决方案完全令人满意)?我会保留标记为重复项,但除非对问题进行编辑以包含对要求的更完整描述,否则我可能会这样做。
  • 不幸的是,这充满了潜在的危险。你有像 rodham 这样的包 声称 >= 2.1.0 但实际上 由于使用了函数 (trimws()) 而需要 >= 3.2.0在 3.2.0 中引入,在 2.1.0 中不可用。由于它是在 3.2.0 发布后引入 CRAN 的,并且 CRAN 测试仅适用于当前和开发版本,它会错过这个依赖错误。我怀疑您最好使用每个 R 版本构建一组 docker 映像并针对它们运行您的 pkg 测试以确定最低版本。非常自动化。
  • 啊,我天真地假设软件包倾向于正确地声明它们的 R 依赖关系,或者可能提供不必要的高版本。

标签: r packages


【解决方案1】:

基于@hrbrmstr 的想法并使用基本函数编写,我现在使用以下函数:

min_r_version <- function(pkg) {
  requireNamespace("tools")
  requireNamespace("utils")
  avail <- utils::available.packages(utils::contrib.url(repo))
  deps <- tools::package_dependencies(pkg, db = avail, recursive = TRUE)
  if (is.null(deps))
    stop("package not found")

  pkgs <- deps[[1]]
  repo = getOption("repo")
  if (is.null(repo))
    repo <- "https://cloud.r-project.org"

  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
}

【讨论】:

  • 很抱歉挖掘这个,但我建议更正这个功能。在 tabula rasa 上运行时,该函数在分配 avail 对象时不知道对象 repo 是什么。
【解决方案2】:
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"

【讨论】:

  • 哇。万分感谢。这是否排除了查询包本身的 R 要求,因为这是我们要更改的变量?
  • 现在,它包含它,因为如果我们排除它,则需要一些if-else 逻辑来处理某些极端情况,但这是该过程的核心逻辑。
  • 更新后的函数支持排除指定的 pkg 并处理所说的边缘情况。可能还有其他我没有想到的极端情况。
猜你喜欢
  • 2015-11-22
  • 2018-02-27
  • 2019-11-27
  • 2017-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-28
  • 1970-01-01
相关资源
最近更新 更多