R 包与 Rcpp 的链接错误:"undefined symbol: LAPACKE_dgels"
Linking error of R package with Rcpp: "undefined symbol: LAPACKE_dgels"
我正在创建一个 R 包 'lapacker' 以使用 R API 头文件 'lapacker' 为 R 提供和使用的内部 LAPACK 库提供 C 接口(仅具有双精度和双复数)"R_ext/Lapack.h"。源代码:
https://github.com/ypan1988/lapacker/
项目结构:
/lapacker
/inst
/include
/lapacke.h
/someother header files
/R
/zzz.R
/src
/lapacke_dgetrf.c
/lapacke_dgetrf_work.c
/loads of other utility functions provided by LAPACKE
/rcpp_hello.cpp
DESCRIPTION
NAMESPACE
在项目中,我在 rcpp_hello.cpp 文件中尝试了一个测试函数(注意这个例子来自 https://www.netlib.org/lapack/lapacke.html#_calling_code_dgels_code):
//'@export
// [[Rcpp::export]]
void example_lapacke_dgels()
{
double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
lapack_int info,m,n,lda,ldb,nrhs;
int i,j;
m = 5;
n = 3;
nrhs = 2;
lda = 3;
ldb = 2;
info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);
for(i=0;i<n;i++)
{
for(j=0;j<nrhs;j++)
{
printf("%lf ",b[i][j]);
}
printf("\n");
}
}
整个包可以正常编译,没有错误,在R中给出正确答案(表示可以找到符号LAPACKE_dgels):
> example_lapacke_dgels()
2.000000 1.000000
1.000000 1.000000
1.000000 2.000000
然而,当我创建一个单独的 C++ 文件时,说 demo3.cpp 具有完全相同的功能,
#include <Rcpp.h>
#include <lapacke.h>
// [[Rcpp::depends(lapacker)]]
// [[Rcpp::export]]
void lapacke_dgels_test()
{
double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
lapack_int info,m,n,lda,ldb,nrhs;
int i,j;
m = 5;
n = 3;
nrhs = 2;
lda = 3;
ldb = 2;
info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);
for(i=0;i<n;i++)
{
for(j=0;j<nrhs;j++)
{
printf("%lf ",b[i][j]);
}
printf("\n");
}
}
它不再正确编译(实际上我在 macOS 和 ubuntu 下都尝试过,同样的链接问题),并给出链接错误消息(找不到符号 LAPACKE_dgels):
> Rcpp::sourceCpp("~/Desktop/demo3.cpp", showOutput = TRUE)
/usr/lib/R/bin/R CMD SHLIB -o 'sourceCpp_6.so' 'demo3.cpp'
g++ -I/usr/share/R/include -DNDEBUG -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/Rcpp/include" -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/lapacker/include" -I"/home/yipan/Desktop" -fpic -g -O2 -fdebug-prefix-map=/build/r-base-AitvI6/r-base-3.4.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g -c demo3.cpp -o demo3.o
g++ -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o sourceCpp_6.so demo3.o -L/usr/lib/R/lib -lR
Error in dyn.load("/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so") :
unable to load shared object '/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so':
/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so: undefined symbol: LAPACKE_dgels
我也查看了/R/x86_64-pc-linux-gnu-library/3.4/lapacker/libs下的lapacker.so,发现:
000000000000c6b0 g DF .text 00000000000001bf Base LAPACKE_dgels
我是否遗漏了一些让 demo3.cpp 正确编译的东西?非常感谢您的耐心和时间!
您遇到了一个棘手的问题。您尝试解析的符号 LAPACKE_dgels
是 lapacker.so
的一部分,在软件包安装期间构建。问题是,R 包的库不适合 linking。相反,它们由 R 在 run-time 处动态加载。基本上,我看到四种可能性:
- 将
lapacke
转换为仅 header 的库并将其安装在 inst/include
中(c.f。RcppArmadillo
)。
- Link 系统安装
lapacke
(容易 Linux ...)
- 用R注册所有函数,使用R提供的方法link给它们(c.f。WRE和
nloptr
)。
- 编译一个用于 linking 的库并使用 R 包安装它。你仍然需要一个插件才能工作,因为你必须添加
-L<path/to/lib> -l<libname> ....
到 PKG_LIBS
。
我确信 CRAN 上有使用方法 4 的示例,但现在想到 none。但是,作为 "code kata" 我已经将我最近的一个测试包转换为使用这种结构,c.f。 https://github.com/rstub/levmaR/tree/static.
(原始不完整答案。)
在src/Makevars
你有
PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
通过Rcpp 属性编译cpp 文件时需要模拟设置。实现此目的的最佳方法是使用 Rcpp 插件 c.f。 RcppArmadillo's solution(调整未经测试!):
inlineCxxPlugin <- function(...) {
plugin <-
Rcpp::Rcpp.plugin.maker(
include.before = "#include <lapacke.h>",
libs = "$(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)",
package = "lapacker"
)
settings <- plugin()
settings$env$PKG_CPPFLAGS <- "-I../inst/include"
settings
}
顺便说一句,为什么你想直接与 LAPACK 接口,而 RcppArmadillo 已经这样做了?
我正在创建一个 R 包 'lapacker' 以使用 R API 头文件 'lapacker' 为 R 提供和使用的内部 LAPACK 库提供 C 接口(仅具有双精度和双复数)"R_ext/Lapack.h"。源代码: https://github.com/ypan1988/lapacker/
项目结构:
/lapacker
/inst
/include
/lapacke.h
/someother header files
/R
/zzz.R
/src
/lapacke_dgetrf.c
/lapacke_dgetrf_work.c
/loads of other utility functions provided by LAPACKE
/rcpp_hello.cpp
DESCRIPTION
NAMESPACE
在项目中,我在 rcpp_hello.cpp 文件中尝试了一个测试函数(注意这个例子来自 https://www.netlib.org/lapack/lapacke.html#_calling_code_dgels_code):
//'@export
// [[Rcpp::export]]
void example_lapacke_dgels()
{
double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
lapack_int info,m,n,lda,ldb,nrhs;
int i,j;
m = 5;
n = 3;
nrhs = 2;
lda = 3;
ldb = 2;
info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);
for(i=0;i<n;i++)
{
for(j=0;j<nrhs;j++)
{
printf("%lf ",b[i][j]);
}
printf("\n");
}
}
整个包可以正常编译,没有错误,在R中给出正确答案(表示可以找到符号LAPACKE_dgels):
> example_lapacke_dgels()
2.000000 1.000000
1.000000 1.000000
1.000000 2.000000
然而,当我创建一个单独的 C++ 文件时,说 demo3.cpp 具有完全相同的功能,
#include <Rcpp.h>
#include <lapacke.h>
// [[Rcpp::depends(lapacker)]]
// [[Rcpp::export]]
void lapacke_dgels_test()
{
double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
lapack_int info,m,n,lda,ldb,nrhs;
int i,j;
m = 5;
n = 3;
nrhs = 2;
lda = 3;
ldb = 2;
info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);
for(i=0;i<n;i++)
{
for(j=0;j<nrhs;j++)
{
printf("%lf ",b[i][j]);
}
printf("\n");
}
}
它不再正确编译(实际上我在 macOS 和 ubuntu 下都尝试过,同样的链接问题),并给出链接错误消息(找不到符号 LAPACKE_dgels):
> Rcpp::sourceCpp("~/Desktop/demo3.cpp", showOutput = TRUE)
/usr/lib/R/bin/R CMD SHLIB -o 'sourceCpp_6.so' 'demo3.cpp'
g++ -I/usr/share/R/include -DNDEBUG -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/Rcpp/include" -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/lapacker/include" -I"/home/yipan/Desktop" -fpic -g -O2 -fdebug-prefix-map=/build/r-base-AitvI6/r-base-3.4.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g -c demo3.cpp -o demo3.o
g++ -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o sourceCpp_6.so demo3.o -L/usr/lib/R/lib -lR
Error in dyn.load("/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so") :
unable to load shared object '/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so':
/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so: undefined symbol: LAPACKE_dgels
我也查看了/R/x86_64-pc-linux-gnu-library/3.4/lapacker/libs下的lapacker.so,发现:
000000000000c6b0 g DF .text 00000000000001bf Base LAPACKE_dgels
我是否遗漏了一些让 demo3.cpp 正确编译的东西?非常感谢您的耐心和时间!
您遇到了一个棘手的问题。您尝试解析的符号 LAPACKE_dgels
是 lapacker.so
的一部分,在软件包安装期间构建。问题是,R 包的库不适合 linking。相反,它们由 R 在 run-time 处动态加载。基本上,我看到四种可能性:
- 将
lapacke
转换为仅 header 的库并将其安装在inst/include
中(c.f。RcppArmadillo
)。 - Link 系统安装
lapacke
(容易 Linux ...) - 用R注册所有函数,使用R提供的方法link给它们(c.f。WRE和
nloptr
)。 - 编译一个用于 linking 的库并使用 R 包安装它。你仍然需要一个插件才能工作,因为你必须添加
-L<path/to/lib> -l<libname> ....
到PKG_LIBS
。
我确信 CRAN 上有使用方法 4 的示例,但现在想到 none。但是,作为 "code kata" 我已经将我最近的一个测试包转换为使用这种结构,c.f。 https://github.com/rstub/levmaR/tree/static.
(原始不完整答案。)
在src/Makevars
你有
PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
通过Rcpp 属性编译cpp 文件时需要模拟设置。实现此目的的最佳方法是使用 Rcpp 插件 c.f。 RcppArmadillo's solution(调整未经测试!):
inlineCxxPlugin <- function(...) {
plugin <-
Rcpp::Rcpp.plugin.maker(
include.before = "#include <lapacke.h>",
libs = "$(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)",
package = "lapacker"
)
settings <- plugin()
settings$env$PKG_CPPFLAGS <- "-I../inst/include"
settings
}
顺便说一句,为什么你想直接与 LAPACK 接口,而 RcppArmadillo 已经这样做了?