构建 vignettes 时突然出现 'arch = x64' 错误
结论
问题出在项目里的空目录 inst/libs。日志已经给出了线索:
Removed empty directory 'my_pkg/inst/libs'
构建 vignettes 时,R CMD build 会先临时安装源码目录。此时 inst/libs 还没有被清理,会一并复制到临时安装结果中。在 Windows 下,已安装 package 中存在 libs 目录,通常表示其中包含与架构相关的本机库。R 会按当前架构查找 x64 对应的内容,但这个目录是空的,因此报错:
Error : package 'my_pkg' is not installed for 'arch = x64'
删除空的 inst/libs 即可解决。这个问题与 vignette 内容、pkgbuild 调用栈或 RStudio 的 PackageInstallArgs 基本无关。
为什么只有构建 vignettes 时失败
处理 vignettes 时,R CMD build 需要临时安装 package,供构建期间加载:
installing the package to build vignettes
...
testing if installed package can be loaded from temporary location
临时安装使用的是尚未完成清理的源码树,因此会包含空的 inst/libs。
不构建 vignettes 时,构建流程会先整理源码包并删除空目录:
Removed empty directory 'my_pkg/inst/libs'
之后从生成的 .tar.gz 安装时,inst/libs 已经不存在。package 会按普通的纯 R package 处理,i386 和 x64 的加载测试都能通过。
devtools::build_vignettes() 能够成功,是因为它主要负责渲染 .Rmd,生成 doc/、Meta/ 和索引,并不会完整执行 R CMD build 针对临时安装结果所做的架构加载检查。
修复方法
先在 package 根目录检查 inst/libs 是否存在以及是否为空:
dir.exists("inst/libs")
list.files(
"inst/libs",
all.files = TRUE,
recursive = TRUE,
full.names = TRUE
)
如果结果只有 .、..,或者没有任何文件,就可以删除该目录:
unlink("inst/libs", recursive = TRUE, force = TRUE)
也可以在 PowerShell 中执行:
Remove-Item -Recurse -Force .\inst\libs
删除前要确认目录确实为空,以免误删需要保留的 DLL 或其他二进制文件。
随后重启 R session,并依次验证:
devtools::build()
devtools::install(build_vignettes = TRUE)
devtools::check()
也可以直接测试基础构建流程:
R CMD build C:/Users/greg/Workspace/R/Packages/my_pkg
R CMD check my_pkg_0.0.0.9000.tar.gz
修复后,构建日志不应再出现清理空 inst/libs 的提示,临时安装的加载测试也应正常完成。
inst/libs 为什么会影响架构判断
安装 package 时,inst/ 下的内容会复制到 package 根目录。例如:
inst/libs
安装后会变成:
my_pkg/libs
libs 不是普通的数据目录,通常用于存放 package 编译后的本机库。在 Windows 下,它还可能按架构划分:
libs/i386/
libs/x64/
如果 libs 是空的,package 就会处于不完整状态。这个目录暗示 package 可能包含本机库,但当前 x64 架构下又没有可加载的内容,因此会出现 “未针对 x64 安装” 的错误,即使 package 实际上完全由 R 代码组成。
纯 R package 不需要手工创建 inst/libs。如果 package 包含 C、C++ 或 Fortran 源码,通常应把源码放在 src/ 中,由 R CMD INSTALL 将编译结果安装到正确的 libs 位置。
为什么切换 Git branch 也没有效果
Git 默认不跟踪空目录。即使切换到以前能够通过检查的 commit 或 branch,工作区中的空 inst/libs 仍可能继续存在。重新安装 R、RStudio、Rtools 或 devtools 也不会清理项目目录里的这个本地文件夹。
如果删除后目录再次出现,应检查创建它的脚本、IDE 操作或构建工具。每次构建前可以快速检查:
if (dir.exists("inst/libs")) {
print(list.files("inst/libs", all.files = TRUE, recursive = TRUE))
}
不建议只用 --no-multiarch 绕过错误。空的 inst/libs 本身就不符合正确的 package 布局,单架构安装仍可能在加载检查时失败。
PackageInstallArgs 和调用栈是否有关
.Rproj 中的配置:
PackageInstallArgs: --no-multiarch --with-keep.source
主要影响由 RStudio 发起的安装操作,无法解释为什么从命令行执行 R CMD build 时也会出现相同错误。基础命令同样能够复现,说明问题并非 devtools、pkgbuild 或 RStudio 的独有行为。
.Last.error.trace 中出现 pkgbuild::build()、callr::rcmd_safe() 和 processx::run(),是因为 devtools 通过这些组件启动了外部 Rcmd.exe。实际失败的是子进程中的临时 package 加载测试。调用栈只记录了错误传回当前 R session 的路径,不能据此认定 pkgbuild 是根因。
关于 RStudio autocompletion
构建失败后,my_pkg:: 显示内部对象,并不表示 package 已经 “安装了一半”。RStudio 生成补全结果时,可能同时使用项目源码索引、之前的 namespace 缓存和 devtools 加载过的开发环境。构建失败后,这些状态可能暂时不一致。
删除空目录并重新安装成功后,可以重启 R session,再检查实际导出的接口:
getNamespaceExports("my_pkg")
getNamespaceInfo(asNamespace("my_pkg"), "path")
find.package("my_pkg")
与编辑器的 autocompletion 相比,这些结果更适合用来确认当前加载的是哪个安装副本,以及 namespace 实际导出了哪些对象。
备注:内容仅供参考。
