我正在尝试构建一个使用一些C代码的R包。我有一个C库,它被编译成可执行文件,可以从命令行调用。有一个与之关联的Makefile。
我正在尝试查看信息here,其中包含
如果您想创建然后链接到库,请说使用代码 子目录,使用类似
的东西
.PHONY: all mylibs
all: $(SHLIB)
$(SHLIB): mylibs
mylibs:
(cd subdir; make)
小心创建所有必需的依赖项,因为没有 保证所有的依赖关系 将以特定顺序(以及一些CRAN构建机器)运行 使用多个CPU并行并行。)
如果我在我的包中创建一个src
文件夹的新子目录,名为someLibrary
,代码和Makefile保持不变,反过来,在原始的Makevars
文件中为我包我添加上面的代码不变,那么我将能够使用useDynLib
构建要导出的共享库吗?
以下信息here,我通过添加
更改了Makefile
以创建共享库
CFLAG = -fPIC -g -O3
LDFLAGS= -shared
但是,这会导致.so
文件不直接导出到包的libs
目录的问题。如果我将路径硬编码到目标中,那么文件将被发送到包的libs
目录(这完全取决于对R CMD INSTALL myPackage
的调用)。
最后,我想知道如何调用共享库,因为它有一个main()
方法,我可以从命令行可执行文件调用。
将此公开给R NAMESPACE
的程序是什么,以便可以通过.Call
调用?
PS。如果我将最后一点作为一个单独的问题,请告诉我。
答案 0 :(得分:13)
原始问题作者在问题评论中询问了使用 Automake , Libtool 和LDADD
链接编译的程序的示例一个目录,其中包含在第二个目录中编译的共享库。 这是一个完整,独立,完整的示例,说明如何使用GNU Autotools在同一源代码树的不同目录中编译库和程序。
我们需要按如下方式设置目录结构:
├ A/
│ ├ Makefile.am
│ ├ helloworld.c
│ └ helloworld.h
├ B/
│ ├ Makefile.am
│ └ foo.c
├ configure.ac
└ Makefile.am
共享库将在目录A/
中编译,以及在目录B/
中使用它的程序。
有三个源文件。
A/helloworld.c
是该库的源代码。它导出一个过程say_hello()
,它打印消息“Hello world!”到标准输出。
#include <stdio.h>
#include "helloworld.h"
void
say_hello (void)
{
printf ("Hello world!\n");
}
A/helloworld.h
是包含say_hello()
函数声明的头文件。它只有一行:
void say_hello (void);
最后,B/foo.c
是使用共享库的程序的源代码。它包含库的头文件,并调用say_hello()
。
#include <helloworld.h>
int
main (int argc, char **argv)
{
say_hello ();
return 0;
}
我们将使用Automake和Libtool来编译共享库。这两种工具都非常强大,而且实际上有很好的文档记录。手册(Automake,Libtool)绝对值得一读。
A/Makefile.am
automake
文件用于控制库的编译。
# We're going to compile one libtool library, installed to ${libdir},
# and named libhelloworld.
lib_LTLIBRARIES = libhelloworld.la
# List the source files used by libhelloworld.
libhelloworld_la_SOURCES = helloworld.c
# We install a single header file to ${includedir}
include_HEADERS = helloworld.h
B/Makefile.am
文件控制库的编译。我们需要使用LDADD
变量告诉automake
链接我们之前编译的库。
# Compile one program, called foo, and installed to ${bindir}, with a single C
# source file.
bin_PROGRAMS = foo
foo_SOURCES = foo.c
# Link against our uninstalled copy of libhelloworld.
LDADD = $(top_builddir)/A/libhelloworld.la
# Make sure we can find the uninstalled header file.
AM_CPPFLAGS = -I$(top_srcdir)/A
最后,我们需要一个顶级Makefile.am
告诉Automake如何构建项目,以及configure.ac
文件告诉Autoconf如何找到所需的工具。
顶级Makefile.am
非常简单:
# Compile two subdirectories. We need to compile A/ first so the shared library is
# available to link against.
SUBDIRS = A B
# libtool requires some M4 scripts to be added to the source tree. Make sure that
# Autoconf knows where to find them.
ACLOCAL_AMFLAGS = -I m4
最后,configure.ac
文件告诉Autoconf如何创建configure
脚本。
AC_INIT([libhelloworld], 1, peter@peter-b.co.uk)
# This is used to help configure check whether the source code is actually present, and
# that it isn't being run from some random directory.
AC_CONFIG_SRCDIR([A/helloworld.c])
# Put M4 macros in the m4/ subdirectory.
AC_CONFIG_MACRO_DIR([m4])
# We're using automake, but we want to turn off complaints about missing README files
# etc., so we need the "foreign" option.
AM_INIT_AUTOMAKE([foreign])
# We need a C compiler
AC_PROG_CC
# Find the tools etc. needed by libtool
AC_PROG_LIBTOOL
# configure needs to generate three Makefiles.
AC_CONFIG_FILES([A/Makefile
B/Makefile
Makefile])
AC_OUTPUT
执行命令
$ autoreconf -i
$ ./configure
$ make
$ B/foo
您应该看到所需的输出:“Hello world!”
答案 1 :(得分:2)
callable.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello World\n");
return 0;
}
要从C文件callable.c获取.so文件,请使用
R CMD SHLIB callable.c
所以现在我们有callable.so
R通常要求所有C参数都是指针,但如果你的main方法忽略了argc,那么我们可以解决这个问题。因此,要从R调用main来自callable.so,我们可以编写一个类似的方法。
main <- function() {
dyn.load("callable.so")
out <- .C("main", argc=0, argv="")
}
调用main函数将运行C main函数。