devtools::load_all() 副作用:C++-class 构造函数调用 base::system.file 失败/ returns 空字符串
devtools::load_all() side effects: C++-class constructor calling base::system.file fails/ returns empty string
上下文
我正在(第一次)使用 Rcpp 开发一个 R 包,它实现了另一个程序的接口 (maxima)。此包定义了一个 C++ class,其构造函数需要检索与包一起安装的初始化脚本的路径(包内路径为 inst/extdata/maxima-init.mac
。此脚本的路径随后用作生成运行该程序的子进程的参数。
为了检索 installed 初始化脚本的路径,我从 C++ class 构造函数定义中调用 R 函数 base::system.file
:
...
Environment env("package:base");
Function f = env["system.file"];
fs::path p(Rcpp::as<std::string>(f("extdata", "maxima-init.mac", Named("package") = "rmaxima")));
std::string utilsDir = p.parent_path().string();
...
# spawn child process using path in utilsDir
我的 R/zzz.R
创建了一个 class 的对象,当包被附加时:
loadModule("Maxima", TRUE)
.onAttach <- function(libname, pkgname) {
"package:base" %in% search()
maxima <<- new(RMaxima)
}
问题
我可以 install.packages(rmaxima)
和 library(rmaxima)
就好了,包按预期工作。
我现在想通过使用 devtools::load_all() 来提高我的开发效率,以避免每次我想测试更改时都必须 R CMD build rmaxima
、install.packages(rmaxima)
和 library(rmaxima)
。但是,当调用 devtools::load_all()
(或类似地 devtools::test()
(工作目录设置为包根目录)时,我的实现冻结了,因为变量 utilsDir
为空,因此进程启动不会 return (我猜它一直在等待有效路径)。我最终需要手动终止进程。同样的事情发生在没有设置 .onAttach()
显然 devtools::load_all() 与 R 在重新启动时的默认搜索路径不同。我能做什么?这是问题所在还是我遗漏了什么?
更新
我刚刚在 devtools::load_all()
R 文档文件中遇到以下概念,这可能是正确方向的提示
Shim files:
‘load_all’ also inserts shim functions into the imports environment of
the loaded package. It presently adds a replacement version of
‘system.file’ which returns different paths from ‘base::system.file’.
This is needed because installed and uninstalled package sources have
different directory structures. Note that this is not a perfect
replacement for base::system.file.
我还意识到,devtools::load_all() 只是暂时将我的包安装到,但我的 inst/
中的文件却不知何故没有安装
rcst@Velveeta:~$ ls -1R /tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/:
DESCRIPTION
libs
Meta
NAMESPACE
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/libs:
rmaxima.so
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/Meta:
features.rds
package.rds
事实证明 devtools
提供了完全 这个问题的解决方案。
简而言之:调用 system.file
(即从全局环境并附加 devtools 包)解决了问题。具体修改:
// Environment env("package:base");
// Function f = env["system.file"];
Function f("system.file");
fs::path p(Rcpp::as<std::string>(f("extdata", "maxima-init.mac", Named("package") = "rmaxima")));
std::string utilsDir = p.parent_path().string();
说明
base::system.file(..., mustWork = FALSE)
returns 如果未找到匹配项,则为空字符串。 devtools::load_all()
临时安装 /tmp/
内的包(在我的 linux 机器上)。临时安装的目录结构 不同于常规安装的目录结构 ,即由 install.packages()
创建的目录结构。在我的例子中,最值得注意的是,devtools::load_all()
不会复制包含初始化文件的 inst/
目录。
现在,调用 base::system.file("maxima-init.mac", package="rmaxima", mustWork=FALSE)
自然会失败,因为它会在临时安装中搜索。将 devtools
附加掩码 system.file()
和 devtools::system.file()
,正如上面提到的那样“......意味着拦截对 base::sysem.file()
的调用”并且行为不同于 base::system.file()
。实际上,我认为这意味着它将搜索包的源目录而不是临时安装。
这样,只需从全局环境调用 system.file()
即可自动从 devtools
或 base
调用正确的函数,用于包的开发版本或用户版本。
尽管如此,另外使用 ccache
(感谢@dirk)大大加快了我的开发工作流程。
上下文
我正在(第一次)使用 Rcpp 开发一个 R 包,它实现了另一个程序的接口 (maxima)。此包定义了一个 C++ class,其构造函数需要检索与包一起安装的初始化脚本的路径(包内路径为 inst/extdata/maxima-init.mac
。此脚本的路径随后用作生成运行该程序的子进程的参数。
为了检索 installed 初始化脚本的路径,我从 C++ class 构造函数定义中调用 R 函数 base::system.file
:
...
Environment env("package:base");
Function f = env["system.file"];
fs::path p(Rcpp::as<std::string>(f("extdata", "maxima-init.mac", Named("package") = "rmaxima")));
std::string utilsDir = p.parent_path().string();
...
# spawn child process using path in utilsDir
我的 R/zzz.R
创建了一个 class 的对象,当包被附加时:
loadModule("Maxima", TRUE)
.onAttach <- function(libname, pkgname) {
"package:base" %in% search()
maxima <<- new(RMaxima)
}
问题
我可以 install.packages(rmaxima)
和 library(rmaxima)
就好了,包按预期工作。
我现在想通过使用 devtools::load_all() 来提高我的开发效率,以避免每次我想测试更改时都必须 R CMD build rmaxima
、install.packages(rmaxima)
和 library(rmaxima)
。但是,当调用 devtools::load_all()
(或类似地 devtools::test()
(工作目录设置为包根目录)时,我的实现冻结了,因为变量 utilsDir
为空,因此进程启动不会 return (我猜它一直在等待有效路径)。我最终需要手动终止进程。同样的事情发生在没有设置 .onAttach()
显然 devtools::load_all() 与 R 在重新启动时的默认搜索路径不同。我能做什么?这是问题所在还是我遗漏了什么?
更新
我刚刚在 devtools::load_all()
R 文档文件中遇到以下概念,这可能是正确方向的提示
Shim files:
‘load_all’ also inserts shim functions into the imports environment of the loaded package. It presently adds a replacement version of ‘system.file’ which returns different paths from ‘base::system.file’. This is needed because installed and uninstalled package sources have different directory structures. Note that this is not a perfect replacement for base::system.file.
我还意识到,devtools::load_all() 只是暂时将我的包安装到,但我的 inst/
rcst@Velveeta:~$ ls -1R /tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/:
DESCRIPTION
libs
Meta
NAMESPACE
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/libs:
rmaxima.so
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/Meta:
features.rds
package.rds
事实证明 devtools
提供了完全 这个问题的解决方案。
简而言之:调用 system.file
(即从全局环境并附加 devtools 包)解决了问题。具体修改:
// Environment env("package:base");
// Function f = env["system.file"];
Function f("system.file");
fs::path p(Rcpp::as<std::string>(f("extdata", "maxima-init.mac", Named("package") = "rmaxima")));
std::string utilsDir = p.parent_path().string();
说明
base::system.file(..., mustWork = FALSE)
returns 如果未找到匹配项,则为空字符串。 devtools::load_all()
临时安装 /tmp/
内的包(在我的 linux 机器上)。临时安装的目录结构 不同于常规安装的目录结构 ,即由 install.packages()
创建的目录结构。在我的例子中,最值得注意的是,devtools::load_all()
不会复制包含初始化文件的 inst/
目录。
现在,调用 base::system.file("maxima-init.mac", package="rmaxima", mustWork=FALSE)
自然会失败,因为它会在临时安装中搜索。将 devtools
附加掩码 system.file()
和 devtools::system.file()
,正如上面提到的那样“......意味着拦截对 base::sysem.file()
的调用”并且行为不同于 base::system.file()
。实际上,我认为这意味着它将搜索包的源目录而不是临时安装。
这样,只需从全局环境调用 system.file()
即可自动从 devtools
或 base
调用正确的函数,用于包的开发版本或用户版本。
尽管如此,另外使用 ccache
(感谢@dirk)大大加快了我的开发工作流程。