高版本R安装低版本R包不兼容问题的解决思路探索

前些天,在电脑上安装R包时碰见了一个问题:

1
2
3
4
5
6
> install.packages("FastEPRR_2.0.zip")
inferring 'repos = NULL' from 'pkgs'
package 'FastEPRR' successfully unpacked and MD5 sums checked
> library(FastEPRR)
Error: package or namespace load failed for 'FastEPRR':
package 'FastEPRR' was installed before R 4.0.0: please re-install it

根据报错提示,应该是我们用的R包版本比较老所导致的不兼容问题。这个R包并非官方CRAN上的包,而是实验室自己开发并封装的计算工具包,因此只有离线安装这一种安装方法,并且这个包的版本只与R 3.x.x版本兼容,低于系统里装的R 4.3.1版本。

下面是一些探索。

一、R包的种类

参考:

《R包开发》# 第四章 程序包结构与状态

R包总共有五种状态:

  • 源代码包(source):R包源代码,包括R包文档以及实现这个包的功能的R代码、C++代码(可能有)、附带数据(可能有)。
  • 捆绑包(bundled):打包压缩为.tar.gz格式的源代码包。
  • 二进制包(binary):专为Windows平台设计。将源代码包预先编译为二进制格式,然后打包为.zip压缩文件。
  • 已安装的(installed):使用install.package()函数安装后的包。通常位于$R_HOME/library目录下。
  • 载入内存中的(in-memory):使用library()函数加载的包,可以在程序中进一步调用。

其中,我们最常接触到的R包安装包属于第二种(捆绑包)和第三种(二进制包)。

理论上说,捆绑包是跨平台的,因为其中存储的是R包源代码,可以在不同平台上使用各自的编译工具进行编译安装。然而实际情况是Linux平台通常会自带C++编译工具,但Windows不会自带,即使在Windows平台上安装了第三方的C++编译工具,在打包阶段和安装阶段调用的C++编译工具也是不同的。因此在Windows平台上安装捆绑包(尤其是含C++代码的捆绑包)远比在Linux上要麻烦的多。

这次我们遇到的问题也是如此。实验室提供了这个包的捆绑包格式和二进制包格式,出问题的是二进制包格式。在Linux环境中安装上述R包,一切是正常的:

1
2
3
install.packages("FastEPRR_2.0.tar.gz",repo=NULL,type="source")
library(FastEPRR)
# 一切正常无报错

然而有时候我们需要在Windows上使用这个包(甚至是安装其他包时也可能出现这个问题),因此有必要探索一下Windows上如何解决。

二、一种思路

虽然在Windows平台上安装捆绑包很麻烦,但如果把编译的步骤放到打包阶段,问题就变得好解决的多了。我们可以在R 4.x.x版本的开发工具中重新编译FastEPRR的二进制包,使其能够在高版本R上运行起来。

首先,我们准备一下Windows平台上的编译工具链。这里推荐安装 Rtools 。根据电脑上的R语言版本选择合适的Rtools(R 4.2.x选择RTools 4.2 ,R 4.3.x选择RTools 4.3)。建议安装到一个盘符的根目录中以便调用(例如 D:\rtools43 这样的位置)。

理论上说,Rtools安装完后会自动将编译工具链添加到系统的环境变量。因此不用管太多,接着左下面的操作即可。

前面提到,实验室提供了的这个包的捆绑包格式,因此将其解压缩就能获得源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
D:.
│ DESCRIPTION
│ NAMESPACE

├─man
│ FastEPRR-package.Rd
│ FastEPRR.Rd

├─R
│ alignEsRho.R
│ FastEPRR_ALN.R
│ FastEPRR_rho2r.R
│ FastEPRR_VCF_step1.R
│ FastEPRR_VCF_step2.R
│ FastEPRR_VCF_step3.R
│ util.R
│ VariableRecomRate.R
│ vcfEsRho.R

└─src
ms.c
ms.h
rand2.c
streec.c

既然Windows二进制包是从源代码包编译过来的,我们也就可以从上面这一套源代码中自己攒一个二进制包。

根据 《R · R 包开发 | 保姆级教程》 这篇文章,我们建立一个R包的开发环境:

1
2
3
4
5
6
7
8
9
# 安装并加载开发包所需包
install.packages("usethis", "devtools", "roxygen2")
library(usethis)
library(devtools)
library(roxygen2)

# 检查
has_devel()
# output: Your system is ready to build packages!

接下来,我们新建一个R包的开发目录,命名无所谓(因为最后打包的时候会用源码里面定义的名称来命名)

1
2
3
usethis::create_package("fastEPRR2.1") # 建立开发目录
system("copy D:/linux/R/FastEPRR/* .") # 调用系统指令,将解压缩后的捆绑包的内容全部复制到开发目录中。这一步也可也手动进行。
devtools::document() # 更新一下文件

由于我们只是想用高版本R重新编译二进制包,因此做到这里就可以了,不需要其他更多的工作。下一步是进行打包:

1
devtools::build(binary = TRUE) # 参数`binary=TRUE`代表构建为二进制包。默认情况下构建的是捆绑包格式。

这一步会经历一些编译的过程,时间不长大概半分钟左右就完了。

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> devtools::build(binary = TRUE)
── R CMD INSTALL ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
─ installing to library 'C:/Users/ab124/AppData/Local/Temp/RtmpozCnQV/temp_libpath6e41f9d73a5'
─ installing *source* package 'FastEPRR' ...
** using staged installation
** libs
using C compiler: 'gcc.exe (GCC) 12.3.0'
make: Nothing to be done for 'all'.
installing to C:/Users/ab124/AppData/Local/Temp/RtmpozCnQV/temp_libpath6e41f9d73a5/00LOCK-fastEPRR2.1/00new/FastEPRR/libs/x64
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
─ MD5 sums
packaged installation of 'FastEPRR' as FastEPRR_2.0.zip
─ DONE (FastEPRR)
[1] "H:/temp/temp-2024-2-20/FastEPRR_2.0.zip"

最终生成的二进制包文件名为FastEPRR_2.0.zip,它是由源代码确定的,并不受 create_package() 建立的目录名称的影响。下面我们加载一下这个文件,看看是否正常:

1
2
install.packages("FastEPRR_2.0.zip")
library(FastEPRR)

输出:

1
2
3
4
5
> install.packages("FastEPRR_2.0.zip")
inferring 'repos = NULL' from 'pkgs'
package 'FastEPRR' successfully unpacked and MD5 sums checked
> library(FastEPRR)
>

没有报错。说明这回的包是可以正确运行的!

参考文献