【问题标题】:When installing an R package, automatically reinstall dependencies when needed安装 R 包时,需要时自动重新安装依赖项
【发布时间】:2019-11-16 16:40:19
【问题描述】:

utils::install.packages 似乎完全能够安装缺少的依赖项。但是,如果由于某种原因(例如,在尝试安装 DiagrammeRError: package ‘igraph’ was installed by an R version with different internals; it needs to be reinstalled for use with this R version 时)已经安装了一个依赖项,但没有正确的版本,那么原来的 install.packages 调用就会停在那里。然后我必须去手动重新安装每个有问题的依赖项。如何实现自动化?

我在 Linux 上运行 R 3.6.1。

【问题讨论】:

  • 这部分是您的“thinko”:您与install.packages() 的“隐含合同”是它可以假设您已安装的软件包是最新的。你也应该运行update.packages(),它会处理这部分。通过了解已安装包的 CRAN 版本,您几乎可以确保要安装的包可以与它们一起使用。 (由于缺少install.packages() 无法控制的系统库或其他工具,安装仍可能失败。)
  • @DirkEddelbuettel 似乎update.packages(你的意思是这个意思吗?)升级了每个安装的包,这可能就足够了,但有点过头了。
  • 这并不过分。这就是系统的设计和工作方式。我基本上每天都在我的机器上运行update.packages(),它只更新少数几个包(并行,使用Ncpus 选项)。我认为您陷入了一种心理障碍,您希望 完全稳定且不进行任何更改,但又要完美安装任何附加软件包。但这不是 CRAN 为您提供的服务保证,因此您必须做其他事情:这是一个方形/圆孔问题。 YMMV。
  • @DirkEddelbuettel 一点也不,我只想要执行我要求的安装或更新所需的更改,仅此而已。
  • 我听到了。我只是试图解释三遍你的假设对这项任务是无效的,所以你碰壁对我来说并不奇怪。退后一步,想想install.packages() 如果您没有安装其他软件包(处于部分陈旧状态),将为您的新软件包做什么。他们对 CRAN 来说都是新鲜的。

标签: r installation dependencies


【解决方案1】:

似乎没有办法在install.packages() 中强制执行此操作。相反,您可以使用pak::pkg_install()。来自pak::pkg_install()“升级”参数:

upgrade 
Whether to upgrade already installed packages to the latest available version. If this is 
FALSE, then only packages that need updates to satisfy version requirements, will be 
updated. If it is TRUE, all specified or dependent packages will be updated to the latest 
available version.

编辑:更仔细地阅读您的问题,听起来您可能正在使用 3.5 和 3.6 的包库。如果是这种情况,我建议在版本之间升级时使用 R 包installr。它可以自动重新安装您在以前版本中拥有的所有软件包。

Edit2:下面的代码将向您展示基于先前版本构建的代码。我会为这些包运行install.packages(built_on_earlier_version, force = TRUE)

installed_packages <- as.data.frame(installed.packages())

installed_packages[as.package_version(installed_packages$Built) < as.package_version("3.6.0"),]

【讨论】:

  • 这个函数听起来会无条件升级所有依赖,不管它们是否需要升级才能使原始包可用,这有点矫枉过正,但总比没有好。
  • @Kodiologist 这不太正确。如果upgrade=FALSE,它只会在必要时更新包依赖项以满足另一个包对该依赖项的版本要求。我相信这正是您所要求的。
  • 不幸的是,看起来pak::pkg_install 所做的所有检查依赖项是否良好,至少对于upgrade = FALSE,都是检查已安装依赖项的版本;它不检查依赖项是否可以实际加载。因此,pak::pkg_install("DiagrammeR") 似乎有效,但随后library(DiagrammeR) 产生Error: package or namespace load failed for ‘DiagrammeR’: package ‘rgexf’ was installed by an R version with different internals; it needs to be reinstalled for use with this R version,就像install.packages("DiagrammeR") 一样。
  • 查看我的答案的编辑。我认为这个问题是由于使用了来自不同版本 R 的包库,即 3.5 而不是 3.6
  • 你是在建议我打电话给installr::updateR吗?我担心这与 Debian 包管理器自己对 R 的管理不能很好地配合。此外,该函数的文档表明它将更新 all 您的包,其中包括我忘记的包并且永远不会再使用。 pak::pkg_install(upgrade = T) 没有那么矫枉过正。
【解决方案2】:

这种方法很粗糙(特别是因为它会很高兴地多次重新下载一个包),但这是我迄今为止想出的最好的方法:

install.rec = function(pkg, repos = 'http://archive.linux.duke.edu/cran')
# Install a package and reinstall any dependencies that need
# to be reinstalled, recursively.
   {while (T)
       {message("INSTALLING: ", pkg)
        out = paste0(collapse = " ",
            system2("Rscript", stdout = T, stderr = T, sprintf(
                "--no-init-file -e \"install.packages('%s', repos='%s')\"",
                pkg, repos)))
        p = regmatches(out, regexec(text = out, perl = T,
            "package ‘(\\S+)’ was installed (?:before R|by an R version)"))[[1]]
        if (length(p))
           {p = p[2]
            message("START RECURSING: ", pkg, " - ", p)
            install.rec(p, repos)
            message("END RECURSING: ", pkg, " - ", p)}
        else
            break}
   message("DONE WITH: ", pkg)}

install.packages 不会引发条件,也不会返回错误,也不会以capture.output 可捕获的方式产生输出,因此我们必须使用系统调用来查看错误消息。创意来自here

【讨论】:

  • 您可以仅使用 base R 做得更好(如果您关心的话)。 installed.packages() 为您提供您所拥有的,available.packages() 为您提供现有的(并且足以让您的安装“整体落后”)。将它与美妙的 tools::CRAN_package_db() 配对以获取依赖信息,然后您就可以绘制图表了。
  • @DirkEddelbuettel 是 install.packages 用来停止“由具有不同内部结构的 R 版本安装”的检查,只是安装包时的主要和次要 R 版本与当前 R 的版本匹配?
  • 我不回答这个问题。它可能在包内部,或者在尝试加载它时出现try/catch 错误答案。我不知道我作为一个包作者如何表示这一点。无论如何,在不伤害你的感情的情况下,我认为你正在重新发明现有的*......你可以随时在 r-devel 上提问。
  • “我认为你正在重新发明现有的*”——我知道;这是令人沮丧的部分。但我想现在已经足够了。
  • 我还注意到您提到了 Debian。那是另一个问题——我还维护了 100 多个 r-cran-* 包,并且已经使用了很长时间。但是我们也没有一个集成的包管理器,因为问题是 hard。对于 Python、Ruby、Julia、Go、Elisp 等,也没有完美的“其他”解决方案......