☰
CentOS 源码编译安装 ArchR:单细胞 ATAC-seq 环境与排错
2026/10/26 5:47:32 网站建设 项目流程

手里有一台常年跑着 CentOS 的计算节点,硬盘里躺着一堆单细胞 ATAC-seq 的 fragment 文件,这时候你大概率会碰到 ArchR 这个名字。ArchR 是 R 语言里做单细胞染色质可及性分析绕不开的一个工具包,从上游的 Arrow 文件生成、doublet 过滤,到降维聚类、peak 注释、motif 富集、轨迹推断,整条链路它都能接上。问题是它不在 CRAN 上,依赖树又深得吓人,随便一装就是几十个包跟着编译,而 CentOS 特别是 CentOS 7,默认的工具链又老得让人头大。这篇就把我在 CentOS 系列机器上装 ArchR 的完整过程摊开讲一遍:系统层要补什么、GCC 为什么必须换、R 从源码编译时哪些参数真正影响后面的跑分和编译成功率、依赖包按什么顺序装才不容易翻车,以及那些只会出现在日志末尾一两行的报错到底对应什么问题。不管你是第一次在服务器上折腾 R 生信环境,还是已经被had non-zero exit status折磨过几轮,下面这些内容应该都能直接拿去用。

1. 动手之前:CentOS 上装 ArchR 的整体思路

1.1 为什么这台机器上装 ArchR 特别容易卡住

在 Ubuntu 或者 macOS 上装 ArchR,多数人的体验是"装是装上了,就是慢一点"。换到 CentOS,事情的性质就变了。核心矛盾在于工具链的版本落差:CentOS 7 默认的 GCC 是 4.8.5,这个版本对 C++14 的支持都是残缺的,C++17 根本谈不上。而 ArchR 依赖链里的RcppArmadillo、nabor、uwot、motifmatchr这些包,这几年陆续把源码标准提到了 C++14 甚至 C++17。编译器不支持,编译就会在某个.cpp文件的第几百行突然中断,报出来的话往往是error: 'auto' changes meaning in C++11或者干脆一串模板展开失败。

第二个矛盾是系统库的年龄。CentOS 7 的 OpenSSL 停在 1.0.2k,而现在的包管理站点普遍已经不支持这么老的 TLS 版本,install.packages阶段就可能直接卡在下载上,报SSL connect error或者证书校验失败。libgit2 也是同理,CentOS 7 EPEL 里的版本是 0.26,gert包需要 1.x,装devtools的时候如果系统里有老版 libgit2,gert会自作聪明地去链接它,然后编译失败。

第三个矛盾是内存。ArchR 的依赖里有一批重模板的 C++ 包,单个编译单元(一个.cpp文件)在优化编译时峰值内存能吃到 2 到 3 GB。如果你习惯性地设置了MAKEFLAGS=-j16,再加上options(Ncpus = 16),实际并发可能达到几十个编译进程,16 GB 的机器几分钟就被 OOM killer 打成筛子,症状是编译日志戛然而止,末尾跟着一个Killed。

搞清楚这三个矛盾,后面的所有操作其实都是在解决它们。我在第一次装的时候没想明白这层,直接照着网上某一篇 Ubuntu 的教程往下走,结果在RcppArmadillo上卡了整整一个下午。

提示:装 ArchR 之前先执行free -h和df -h。内存低于 16 GB、可用磁盘低于 80 GB 的机器,后面一定要降低并发并挂 swap,否则失败率高得离谱。

1.2 两条路线的取舍:源码编译环境和 Conda 环境

在 CentOS 上搞 ArchR,实际操作中有两条路线可选,选择不同,后面踩的坑完全不一样。

第一条是系统源码编译路线:用 yum 补齐系统库,从源码编译一个新版本的 R,然后在那个 R 里用BiocManager加devtools装 ArchR。这条路的好处是环境干净、路径清晰、出了问题能顺着ldd一路查下去,R 包和系统库的对应关系明确。坏处是准备工作长,光是编译 R 就要二三十分钟,工具链升级也得自己做。

第二条是Conda 路线:装一个 Miniconda,建一个环境,conda install -c conda-forge r-base=4.3 r-devtools,然后在里面装 ArchR。这条路的好处是省事,conda-forge 提供了预编译好的 R 和一大批二进制包,RcppArmadillo、data.table这些不用自己编,安装时间能压缩到半小时以内。坏处是混装风险:conda 的 R 用的是一套独立的 zlib、openssl、curl,一旦某个包在装的时候走了系统库、另一个走了 conda 库,就会出现运行时找不到符号的问题,排查起来非常折磨人。

我的建议是按机器角色来分。如果这台机器是团队共用的分析服务器,长期要跑各种生信流程,走源码编译路线,一次搞好,后面几年都省心。如果是临时借来的一台机器、或者只是想把某批数据跑完拿到结果,走Conda 路线,快进快出,别在环境上耗时间。

还有个折中方案值得一提:用 conda 只装 R 本身和几个大头依赖(r-rcpparmadillo、r-data.table、r-rhdf5),剩下的 Bioconductor 包还是用BiocManager装。这样既避开了最耗时的编译,又保住了 Bioconductor 生态的完整性。这种混法在实践里成功率不低,前提是把 conda 环境的lib目录加到LD_LIBRARY_PATH里,避免运行时找不到 conda 编译的动态库。

1.3 开装前必须盘清楚的硬件与系统底账

动手之前,先把这台机器的情况摸清楚。跑几条命令,几秒钟的事,但能省下后面几个小时的瞎猜。

cat /etc/redhat-release gcc --version | head -1 free -h df -h / /home /tmp nproc

CentOS 系列的版本差异对 ArchR 安装的影响,大致可以对照下面这张表:

系统版本默认 GCCC++17 支持主要麻烦
CentOS 74.8.5不支持必须升级 devtoolset;glibc 2.17 偏老;OpenSSL 1.0.2
CentOS 88.5支持已停止维护,yum 源要改到归档地址
CentOS Stream 88.5支持源地址需要重新指向,EPEL 配置要跟上
CentOS Stream 911.x支持编译器够新,但默认仓库里的 R 版本偏老

内存方面,ArchR 官方的说法是 16 GB 起步,但这个数字对应的是"能跑起来"。真要做完整的单细胞 ATAC 分析,从 Arrow 文件读取、addDoubletScores、addIterativeLSI,到addPeak2GeneLinks,32 GB 是相对舒服的档位,64 GB 可以做中等规模样本的整合分析。安装阶段的内存需求和运行阶段不一样,安装阶段看重的是单次编译峰值乘以并发数,这个后面细讲。

磁盘方面,R 本身加上全部依赖包,编译过程产生的中间文件加上最终安装体积,大概要 8 到 12 GB。ArchR 的基因组注释包(hg38 一套)下载下来 1.5 GB 左右,解压后更大。原始 fragment 文件和生成的 Arrow 文件才是大头,一个标准的 10x 单细胞 ATAC 样本,Arrow 文件动辄几个 GB。所以预留 100 GB 是比较稳妥的。特别提醒/tmp:从源码编译 R 包时,中间产物默认落在/tmp,如果你的/tmp是挂载的 tmpfs 并且只有 2 GB,编译到一半就会报No space left on device,而这个报错往往被埋在日志中间,很不好找。

2. 系统层准备:编译器、系统库与磁盘

2.1 CentOS 7 的老 GCC 必须先换掉

如果你在 CentOS 7 上,第一条命令不是装包,是换编译器。最省事的做法是用 SCL(Software Collections)提供的 devtoolset,不需要动系统自带的 GCC,装完用scl enable临时切换,互不干扰。

yum install -y centos-release-scl yum install -y devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-gcc-gfortran

devtoolset-9 对应 GCC 9.3,对 C++17 的支持已经够用,而且比 devtoolset-11 的兼容性风险小。装完之后,让它在当前 shell 生效:

scl enable devtoolset-9 bash gcc --version | head -1

如果需要长期生效,可以写进 profile:

echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile

注意:scl enable只影响当前 shell 及其子进程。如果你用nohup或者screen挂后台任务,必须先scl enable再启动,否则后台进程用的还是系统老 GCC,编译结果会非常诡异。我吃过这个亏,同一台机器上前后两次装同一个包,一次成功一次失败,查了半天才发现是启动方式不同。

gfortran这一项千万别漏。ArchR 的依赖链里,TFBSTools依赖DirichletMultinomial,这个包的源码是 Fortran 的,没有 gfortran 会在 configure 阶段直接报Fortran compiler not found。很多人补了gcc和gcc-c++就以为齐了,结果卡在这里,报错信息又藏在00check.log里,不好找。

CentOS 8 和 Stream 系列自带的 GCC 8.5 或 11.x 已经支持 C++17,这一节可以跳过。不过还是建议执行一次gcc --version确认,因为有些容器的镜像被裁剪过,只留了gcc没留g++,g++缺失时R CMD INSTALL会报C++ compiler not found,同样是 configure 阶段就挂。

2.2 一份可以直接抄的系统依赖安装清单

下面这份依赖清单是我在几台 CentOS 7 和 Stream 9 上反复验证过的,涵盖 R 编译、常见 R 包编译、ArchR 依赖链需要的全部系统库:

yum install -y epel-release yum install -y \ gcc gcc-c++ gcc-gfortran make cmake git wget bison flex \ zlib-devel bzip2-devel xz-devel pcre-devel pcre2-devel \ libcurl-devel openssl-devel libxml2-devel \ libpng-devel libjpeg-turbo-devel libtiff-devel \ freetype-devel fontconfig-devel harfbuzz-devel fribidi-devel \ cairo-devel readline-devel \ openblas-devel lapack-devel \ libXt-devel

逐项说一下这些包为什么在清单里。zlib-devel、bzip2-devel、xz-devel是 R 自身编译时用来支持压缩格式的,缺了 R 就会以最小功能编译出来,后面Rhdf5lib依赖压缩库时会连锁失败。libcurl-devel是curl包和httr系列的底层依赖,ArchR 安装时会从网上拉数据,缺了之后要么装不上,要么装上了但所有网络函数都报错。libxml2-devel对应XML包,Bioconductor里一堆包间接依赖它。libpng、libjpeg、libtiff、freetype、fontconfig、cairo这一组是绘图相关,ggplot2生态和ragg需要,缺了不会导致安装失败,但会在画图时报no png support。openblas-devel和lapack-devel是矩阵运算的底层,对 ArchR 尤其重要,后面单独讲。readline-devel让 R 交互式终端支持方向键和历史记录,没有也不影响运行,但用起来很难受。

装完之后建议做个简单验证,确认关键库的版本:

rpm -q zlib-devel libcurl-devel openssl-devel openblas-devel

注意:epel-release必须在其他包之前装,否则libgit2-devel、hdf5-devel这些只存在于 EPEL 的包会报No package available。有些精简版镜像连epel-release都装在非标准位置,装完记得yum repolist确认 epel 源已经生效。

2.3 三个最容易埋雷的系统库:HDF5、libgit2、OpenSSL

清单之外,有三个库需要单独拿出来说,因为它们的处理方式反直觉:多数情况下不该装。

先说 HDF5。ArchR 的 Arrow 文件实际上就是 HDF5 格式,读写靠rhdf5,而rhdf5的底层是Rhdf5lib,这个包自带一份 HDF5 源码,安装时会自己编译,跟系统里的 HDF5 完全无关。如果你先yum install hdf5-devel装了系统版本(CentOS 7 上是 1.8.x),再装Rhdf5lib,有时会出现运行期符号冲突,症状是library(rhdf5)报HDF5 library version mismatched error。我遇到过一次,代码里任何h5read都报错,最后用yum remove hdf5-devel卸掉系统库才恢复正常。所以结论是:不要装系统 HDF5,让Rhdf5lib自己管。

再说 libgit2。devtools依赖gert,gert依赖 libgit2。CentOS 7 EPEL 里的libgit2-devel是 0.26,而新版gert要求 1.x。如果系统里有老版本,gert的 configure 脚本会检测到并尝试链接,然后编译失败。正确做法是不装libgit2-devel,让gert走自带的 bundled 版本(需要cmake和openssl-devel,这两个清单里已经有了)。如果你已经装了,yum remove libgit2-devel,然后重装gert。

最后说 OpenSSL。CentOS 7 的 1.0.2k 在访问现代软件源时会遇到 TLS 协商失败,典型报错是SSL connect error、Peer certificate cannot be authenticated with given CA certificates。处理方式有几种:一是尽量用国内镜像源,多数镜像站还保留了 TLS 1.0/1.1 的兼容;二是装openssl11和openssl11-devel,然后在编译某些包时通过环境变量指向它;三是干脆放弃系统 R,走 Conda 路线,Conda 自带一套较新的 OpenSSL,绕开了这个问题。我倾向于第一种加第三种组合,能省事就省事。

2.4 swap 与临时目录:编译期内存的真正兜底

前面提过,ArchR 依赖链里有几个包的编译峰值内存很高。RcppArmadillo的arma模板在-O2优化下,单个文件编译能吃到 2 GB 以上;motifmatchr链接阶段的峰值更高;uwot里的RcppAnnoy也不省油。如果并发开得多,实际占用是峰值的倍数。

我的经验值是:16 GB 内存的机器,编译 ArchR 依赖时并发不要超过 3;32 GB 可以开到 6;64 GB 以上随便开。这个数字比多数人想象的保守,但 OOM 一次重来的代价远高于多等二十分钟。

光靠降并发还不够,swap 是必须挂的兜底。CentOS 默认不启用 swap 分区的情况很常见(尤其是云主机),手动加一个:

fallocate -l 32G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo "/swapfile swap swap defaults 0 0" >> /etc/fstab free -h

32 GB 的 swapfile 在 SSD 上创建大概十几秒。虽然 swap 的速度比内存差一个数量级,但编译这种场景下,进程偶尔溢出到 swap 只是慢一点,不会直接被杀掉。相比之下,没有 swap 时 OOM killer 是直接SIGKILL,编译日志断在一个莫名其妙的位置,你还得从头再来。

临时目录的问题同样值得提前处理。R 包编译时,R CMD INSTALL会在TMPDIR指向的目录下解压源码、存放.o中间文件。如果/tmp空间不足,或者被挂成了小容量 tmpfs,编译就会中途失败。做法是显式指定一个大盘目录:

mkdir -p /data/tmp echo 'export TMPDIR=/data/tmp' >> ~/.bashrc source ~/.bashrc

顺便把ulimit -s放开一点,某些深度递归的模板代码在默认 8 MB 栈上会栈溢出:

echo 'ulimit -s unlimited' >> ~/.bashrc

这两条看起来不起眼,但在装RcppArmadillo这类包时,能实实在在减少一类很难定位的失败。

3. R 本身的安装与关键参数调优

3.1 从源码编 R 的完整命令与参数解释

CentOS 仓库里的 R 版本普遍偏老。CentOS 7 的 EPEL 提供的是 R 3.5 或 3.6,CentOS Stream 9 的r包大概是 4.1 左右。而 ArchR 要求 R 至少 4.1,配合的 Bioconductor 版本也要对得上(Bioconductor 3.16 对应 R 4.2,3.17 对应 R 4.3,3.18 对应 R 4.3,3.19 对应 R 4.4)。版本对不上,BiocManager::install第一步就会拒绝执行,报Bioconductor version is too old for this R。

所以要么装 conda 的 R,要么从源码编。源码编的完整流程如下,以 R 4.3.2 为例:

cd /opt wget https://cran.r-project.org/src/base/R-4/R-4.3.2.tar.gz tar -xzf R-4.3.2.tar.gz cd R-4.3.2 ./configure \ --prefix=/opt/R/4.3.2 \ --enable-R-shlib \ --enable-memory-profiling \ --with-blas="-lopenblas" \ --with-lapack \ --with-x=no \ --with-cairo \ --with-libpng \ --with-jpeglib \ --with-libtiff \ --with-readline make -j4 make install

参数逐条解释。--prefix指定安装位置,建议放在/opt/R/<版本号>这种带版本号的路径下,将来装第二个版本的 R 时互不干扰,切换只需要改PATH。--enable-R-shlib生成libR.so,这是很多需要嵌入 R 的工具(包括某些 IDE 和rpy2)的前提,虽然 ArchR 本身不需要,但顺手开了省事。--with-blas="-lopenblas"是关键项,见下一节。--with-x=no关掉 X11 支持,服务器上没有图形界面,开了只会增加编译依赖和失败概率。--with-cairo配合后面的几个图像库,保证png()、jpeg()、tiff()这些设备可用,ArchR 出图会用到。--with-readline让交互式终端好用。

编译本身在 8 核机器上大概 8 到 12 分钟,make -j4的并发数根据核数调整,但别开到-j$(nproc),R 的某些编译单元内存占用也不小。

装完之后配置环境变量:

echo 'export R_HOME=/opt/R/4.3.2/lib/R' >> /etc/profile echo 'export PATH=/opt/R/4.3.2/bin:$PATH' >> /etc/profile source /etc/profile R --version

注意:/opt/R/4.3.2/lib/R这个路径在不同 configure 参数下可能不同,装完先find /opt/R -name R_HOME确认一下。有些 R 包安装时会读R_HOME环境变量,指错了会在R CMD INSTALL时报cannot find R home directory。

如果机器上已经有一个系统 R,装新版本后要注意PATH的优先级。which R确认当前生效的是哪一个,Rscript -e 'R.version.string'再三确认。我曾因为PATH顺序问题,装了半天发现一直在用系统的 R 3.5,所有包都装到了错误的 library 路径下。

3.2 BLAS/LAPACK 的选择对 ArchR 的影响

这一节值得单独讲,因为它直接影响 ArchR 跑起来之后的速度,而且是安装阶段一次性决定、后面很难改的。

R 默认自带的 BLAS 是参考实现(reference BLAS),单线程、无优化,矩阵乘法的性能大概只有 OpenBLAS 多线程版本的十分之一到五分之一。ArchR 的分析流程里,addIterativeLSI要做大规模稀疏矩阵的 SVD,addClusters涉及距离矩阵计算,getCoAccessibility要算相关性矩阵,这些操作全都是 BLAS 的重度用户。用参考 BLAS 跑一个中等规模的样本,LSI 那一步可能要跑四十分钟;换成 OpenBLAS 多线程,能压到十分钟以内。

所以--with-blas="-lopenblas"这个参数一定要加。装完后验证:

R CMD config BLAS_LIBS

输出应该包含-lopenblas。再做个性能对照,建一个 2000×2000 的随机矩阵做乘法,看耗时:

n <- 2000 a <- matrix(rnorm(n*n), n, n) system.time(b <- a %*% a)

在 8 核机器上,OpenBLAS 多线程版本大约 0.5 到 1 秒,参考 BLAS 要 6 到 10 秒。差一个数量级。

线程数可以通过环境变量控制:

export OPENBLAS_NUM_THREADS=8

这里有个容易踩的坑:ArchR 本身是多线程的(addArchRThreads()),OpenBLAS 也是多线程的。如果两边都开满,实际线程数会相乘,超订之后性能反而下降,甚至因为内存争抢导致进程被杀。我的做法是把 OpenBLAS 的线程数限制在物理核数的一半左右,给 ArchR 的并行留出空间。比如 16 核机器,OPENBLAS_NUM_THREADS=8,addArchRThreads(threads = 8),两边加起来基本打满但不至于打架。

提示:OPENBLAS_NUM_THREADS这个变量在提交作业的脚本里也要设一遍。很多人只在交互 shell 里设了,写成qsub或sbatch脚本提交之后,作业进程读不到这个变量,又退回默认的全核模式,跑起来比预期慢或者直接把节点拖垮。

3.3 .Rprofile 与 .R/Makevars 的配置模板

R 装好之后,先别急着装包。把用户级的配置文件写好,能避免后面一大批重复的、烦人的问题。

~/.Rprofile里放这几行:

options(repos = c(CRAN = "https://cloud.r-project.org")) options(Ncpus = 4) options(timeout = 600) options(warn = 1) Sys.setenv(MAKEFLAGS = "-j2")

repos指定 CRAN 源,如果你的网络访问官方源慢,把地址换成常用的国内镜像站即可,格式一样。Ncpus控制install.packages同时安装多个包的并发数。timeout默认是 60 秒,装大包(比如BSgenome.Hsapiens.UCSC.hg38有几百 MB)时经常超时,调到 600 秒很必要。MAKEFLAGS控制单个包内部make的并发。

这里有个关键点要说清楚:Ncpus和MAKEFLAGS的并发是相乘的。如果Ncpus = 8且MAKEFLAGS = "-j8",理论上最多会有 64 个编译进程同时跑,内存瞬间打满。我建议的做法是:Ncpus设小一点(4 左右),让多个包并行;MAKEFLAGS设成 2,控制单个包内部的并发。这样总的并发峰值大约 8,16 GB 内存能扛住。

~/.R/Makevars里放编译器和优化选项:

CC = gcc CXX = g++ CXX11 = g++ -std=gnu++11 CXX14 = g++ -std=gnu++14 CXX17 = g++ -std=gnu++17 CXXFLAGS = -O2 -fno-var-tracking -fno-var-tracking-assignments CFLAGS = -O2 -fno-var-tracking -fno-var-tracking-assignments FFLAGS = -O2 FCFLAGS = -O2

CXX11、CXX14、CXX17这几行是给那些在DESCRIPTION里声明了SystemRequirements: C++17的包用的,R 会自动调用对应的编译器命令。如果你的 devtoolset 是 9 及以上,这里的g++就是新版;如果没切 devtoolset,这几行等于白写,编译出来的还是 4.8.5 的标准,报错照旧。

-fno-var-tracking这两个选项在 CentOS 7 上尤其有用。老版本 GCC 在-g调试信息下编译大模板代码时,变量跟踪会吃掉大量内存和时间,关掉之后编译内存峰值能降 20% 左右,编译速度也有提升。

4. ArchR 及依赖包的实际安装过程

4.1 依赖包的安装顺序为什么重要

很多人装 ArchR 的方式是直接一句devtools::install_github("..."),然后看着它一个个往下拉。这种方式在 Ubuntu 上偶尔能成,在 CentOS 上成功率很低,原因在于 ArchR 的DESCRIPTION里列出的依赖不一定覆盖完整,有些是运行期才用到的,有些是间接依赖但需要特定版本。直接装主体,往往在某个间接依赖上崩掉,前面的编译成果白费。

我的做法是分层安装,按依赖的深度分三批走。

第一批是基础工具链和底层包,这一批装完,后面的很多问题会自动消失:

install.packages(c("devtools", "BiocManager", "remotes", "data.table", "Matrix"))

第二批是 Bioconductor 的框架包和核心数据结构包:

BiocManager::install(version = "3.18") BiocManager::install(c( "S4Vectors", "IRanges", "GenomicRanges", "GenomeInfoDb", "Biostrings", "BSgenome", "Rsamtools", "GenomicAlignments", "BiocParallel", "rtracklayer", "SummarizedExperiment" ), update = FALSE, ask = FALSE)

第三批是 ArchR 特有的分析依赖,这批是重灾区:

BiocManager::install(c( "rhdf5", "Rhdf5lib", "Rcpp", "RcppArmadillo", "chromVAR", "motifmatchr", "TFBSTools", "Seurat", "SeuratObject", "hexbin", "smooth" ), update = FALSE, ask = FALSE)

分层的价值在于失败范围可控。第二批挂了,你只需要重跑那一条命令,不用从头再来。而且每一层装完可以做一次library()验证,确认这个包真的能加载,而不是装上了但动态库链接有问题。library()验证这一步很多人跳过,结果装完 ArchR 之后在library(ArchR)时报一个unable to load shared object,回头一个个查哪个包出了问题。

update = FALSE这个参数也很关键。默认情况下 BiocManager 会检查所有依赖的版本,遇到"已安装但版本偏旧"的包就想升级。升级意味着重新编译,一处升级可能引发连锁反应,一晚上都装不完。锁定为不升级,用现有版本,多数情况下 ArchR 能正常工作。

4.2 主体安装命令与分阶段验证

依赖装完之后,装 ArchR 主体:

devtools::install_github( "GreenleafLab/ArchR", ref = "master", repos = BiocManager::repositories() )

ref = "master"指定主分支。ArchR 的迭代节奏在这两年放缓了不少,master 分支长期维持在 1.0.x 这条线上,相对成熟。如果你需要某个特定版本,可以改成具体的 tag。

repos = BiocManager::repositories()这一句的作用是把 Bioconductor 的仓库地址一并传给install_github,让它解析依赖时能同时看到 CRAN 和 Bioconductor。少了这一句,遇到 Bioconductor 包会报package 'xxx' is not available。

安装过程可能持续二十分钟到一个小时,取决于有多少包需要现场编译。日志会一直往下刷,中间夹杂大量警告,不用太在意。真正需要关注的是最后几十行,如果有ERROR或者non-zero exit status,那就是真的失败了。

装完之后的验证分三步走,一步比一步深:

# 第一步:能不能加载 library(ArchR) # 第二步:线程设置能不能生效 addArchRThreads(threads = 4) # 第三步:基因组对象能不能构建 addArchRGenome("hg38")

第一步失败,多半是某个动态库没找到,用ldd查具体是哪个包:先library(那个包)看报错,报错信息里的.so路径拿去ldd一下,就能看出缺了哪个系统库。

第二步失败很罕见,基本不会。

第三步是真正的考验。addArchRGenome("hg38")会从网上下载 hg38 的全套注释文件,包括BSgenome、TxDb、org.Hs.eg.db打包成的 ArchR 专用格式,体积在 1.5 GB 上下,解压后写入当前工作目录下的一个子目录。这一步会同时检验网络、磁盘和几个注释包是否装好,是"装完能跑"的最终判定。

注意:addArchRGenome的下载缓存落在当前工作目录下,不是固定的全局路径。这意味着你在不同目录下跑addArchRGenome("hg38"),会重复下载多份。我建议在固定的项目根目录下先跑一次,之后都setwd()过去,或者用getArchRGenome()查一下当前的路径设置。磁盘紧张的时候,重复下载几份注释能把盘塞满。

4.3 基因组注释包与离线机器的搬运方法

有些分析机器是完全隔离的,没有外网。这种情况下addArchRGenome会卡住或者报错,需要手动搬运。

第一步是在一台能上网的机器上,把注释文件准备好。最直接的方式是在能上网的机器上跑一次addArchRGenome("hg38"),然后把生成的那个目录整个打包:

tar -czf archr_hg38_annotations.tar.gz ArchR/

第二步是搬到隔离机器上,解压到与源机器相同的相对路径下。ArchR 查找注释时依赖相对路径和工作目录,挪位置容易找不到。

第三步是验证:

library(ArchR) addArchRThreads(threads = 4) genome <- getArchRGenome() hg38 <- createGenomeAnnotation(genome = genome)

如果第二节的txdb部分报could not find a TxDb,说明注释包没装好。可以在能上网的机器上把TxDb.Hsapiens.UCSC.hg38.knownGene、org.Hs.eg.db、BSgenome.Hsapiens.UCSC.hg38这三个 R 包单独下载下来,用install.packages("/path/to/pkg.tar.gz", repos = NULL)离线安装。这三个包加起来大概 800 MB,比下载整个注释集省事。

还有个小细节:org.Hs.eg.db这类注释包在不同 Bioconductor 版本之间不兼容,一定要保证离线包和在线机器的 Bioconductor 版本一致,否则装上了也可能读不出数据。用BiocManager::version()两边对一下,版本号必须完全一致。

5. 踩坑实录:典型报错与排查路径

5.1 编译期被 Killed:内存与并行的平衡

症状很典型:终端里日志刷到一半突然停住,最后一行是某个.cpp文件名,后面紧跟着Killed,或者gcc: internal compiler error: Killed (program cc1plus)。没有明确的错误信息,没有行号,就这么没了。新手容易误以为是代码有 bug,反复重试,结果每次都死在同一个位置。

这个就是 OOM killer 干的。系统内存不够,内核挑了个占内存最大的进程直接杀掉,杀的就是cc1plus(GCC 的 C++ 前端)。验证方法是看系统日志:

dmesg | grep -i "killed process"

输出里能看到被杀进程的 PID 和占用的内存量。如果看到cc1plus吃了 3 GB 以上被杀,那就确诊了。

解决思路有三条,我通常三条一起上:

一是降并发。把MAKEFLAGS从-j8降到-j1或-j2,options(Ncpus = 4)降到2。这样峰值内存直接降一个数量级,代价是时间变长。

二是加 swap。前面 2.4 节讲过,32 GB 的 swapfile 能兜住大部分溢出场景。有 swap 之后,即使并发开得稍微大一点,进程也只是变慢而不会被杀。

三是单独处理重包。ArchR 依赖里最耗内存的几个包是RcppArmadillo、TFBSTools、motifmatchr、uwot、nabor。这几个可以单独用最低并发装:

MAKEFLAGS=-j1 R CMD INSTALL --no-test-load RcppArmadillo_x.y.z.tar.gz

--no-test-load跳过安装后的加载测试,能省一点内存和时间。装完这几个大头,剩下的包再用正常并发跑,整体效率反而更高。

实测下来,16 GB 内存的机器按上面这套组合拳,装 ArchR 全套依赖大约需要 50 分钟到 1 小时 20 分钟,中途不会被 kill。如果只降并发不加 swap,也基本能过,但遇到TFBSTools这种吃内存的包还是有可能挂,加 swap 更保险。

5.2 动态库找不到与版本冲突

装是装上了,一library()就报unable to load shared object ... undefined symbol: xxx,这类问题的排查路径和编译期错误完全不同,得从动态链接的角度看。

第一步,定位是哪个包:

library(出问题的包名)

报错信息里会包含.so文件的完整路径,记下来。

第二步,查这个.so的依赖:

ldd /path/to/包名.so | grep "not found"

not found的那几行就是缺的库。常见的有libcurl.so.4、libssl.so.1.1、libhdf5.so.xxx。缺哪个装哪个,或者确认路径是否在LD_LIBRARY_PATH里。

第三步,如果所有库都能找到,但报的是undefined symbol,那就是版本冲突:找到的库版本太老,没有那个符号。用nm查一下符号在不在:

nm -D /path/to/libxxx.so | grep 符号名

或者用strings看版本号。这类问题在混装 Conda 和系统库的环境里特别常见,因为LD_LIBRARY_PATH里 conda 的路径和系统路径混在一起,加载顺序不确定。

ArchR 生态里最容易出的两个具体冲突:

其一,HDF5 冲突。前面提过,系统装了hdf5-devel会和Rhdf5lib打架。症状是library(rhdf5)报HDF5 library version mismatched error。解决就是yum remove hdf5-devel,然后重新装Rhdf5lib:

install.packages("BiocManager") BiocManager::install("Rhdf5lib", force = TRUE)

其二,OpenSSL 冲突。CentOS 7 的 1.0.2 和某些包要求的 1.1.1 不兼容。curl包能装上但网络请求全失败,报SSL connect error。这种场景下最省事的办法是换一个不需要新版 TLS 的源,或者装openssl11后在编译curl时指定:

export PKG_CONFIG_PATH=/usr/lib64/openssl11/pkgconfig:$PKG_CONFIG_PATH R CMD INSTALL --configure-vars='INCLUDE_DIR=/usr/include/openssl11 LIB_DIR=/usr/lib64/openssl11' curl_x.y.z.tar.gz

这套配置有点绕,如果只是想让 ArchR 装上、不需要从网上下数据,可以先忽略这个问题,装完之后导入本地准备好的注释文件即可。

5.3 常见报错速查表

把这几台机器上遇到过的报错整理成一张表,遇到类似情况可以先对照着看。

报错片段根因处理方式
error: 'auto' changes meaning in C++11GCC 太老,不支持 C++11 语义切换 devtoolset-9 及以上
error: unrecognized command line option '-std=gnu++17'GCC 4.x 不认识 C++17 参数同上,必须换编译器
Fortran compiler not found缺 gfortranyum install gcc-gfortran
C++ compiler not found缺 g++yum install gcc-c++
Killed/internal compiler error: Killed编译内存溢出降并发、加 swap
No space left on device/tmp或工作盘满了设TMPDIR到大盘
cannot find -lcurl缺 libcurl-develyum install libcurl-devel
cannot find -lxml2缺 libxml2-develyum install libxml2-devel
libgit2 version is too old系统 libgit2 版本低卸掉libgit2-devel,让 gert 自带编译
HDF5 library version mismatched系统 HDF5 与 Rhdf5lib 冲突卸掉系统hdf5-devel,重装 Rhdf5lib
SSL connect error老 OpenSSL 与站点 TLS 不匹配换源,或升级 openssl11
Bioconductor version ... too oldR 版本与 Bioc 版本对不上升级 R 或指定匹配的 Bioc 版本
package 'xxx' is not available仓库源没配全BiocManager::repositories()传参
there is no package called 'xxx'依赖没装全就装主体按分层顺序补装
had non-zero exit status编译失败,需向上翻日志找根因用install.packages(..., verbose=TRUE)定位
RcppArmadillo 编译超时timeout太短options(timeout = 1200)

表格里最后一条值得多说一句。had non-zero exit status这个提示本身不包含任何有效信息,它只告诉你"某个包编译失败了"。真正的错误在它上面几十行的地方。用verbose = TRUE重跑,或者把安装日志重定向到文件再搜ERROR:

install.packages("出问题的包

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询