I am trying to build an R package that uses some C code. I have a C library that is compiled into an executable, that can be called from the command line. There is a Makefile as
callable.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello World\n");
return 0;
}
To get a .so file from the C file callable.c use
R CMD SHLIB callable.c
So now we have callable.so
R normally requires that all C arguments be pointers, but if your main method ignores argc, then we can get around this. So to call main from callable.so from R we can write a method like so.
main <- function() {
dyn.load("callable.so")
out <- .C("main", argc=0, argv="")
}
Calling that main function will run the C main function.
The original question author asked, in the comments on the question, for an example of using Automake, Libtool and LDADD
to link a program compiled in one directory with a shared library compiled in a second directory. This is a complete, standalone, fully worked example of how to compile a library and program in separate directories of the same source tree using GNU Autotools.
We need to set up a directory structure as follows:
├ A/
│ ├ Makefile.am
│ ├ helloworld.c
│ └ helloworld.h
├ B/
│ ├ Makefile.am
│ └ foo.c
├ configure.ac
└ Makefile.am
The shared library will be compiled in directory A/
, and the program that uses it in directory B/
.
There are three source files.
A/helloworld.c
is the source code of the library. It exports one procedure, say_hello()
, which prints the message "Hello world!" to standard output.
#include <stdio.h>
#include "helloworld.h"
void
say_hello (void)
{
printf ("Hello world!\n");
}
A/helloworld.h
is the header file that contains the declaration of the say_hello()
function. It only has one line:
void say_hello (void);
Finally, B/foo.c
is the source code of the program that uses the shared library. It includes the library's header file, and calls say_hello()
.
#include <helloworld.h>
int
main (int argc, char **argv)
{
say_hello ();
return 0;
}
We will use Automake and Libtool to compile the shared library. Both of these tools are very powerful, and actually remarkably well-documented. The manuals (Automake, Libtool) are definitely worth reading.
The A/Makefile.am
file is used by automake
to control the compilation of the library.
# 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
The B/Makefile.am
file controls compilation of the library. We need to use the LDADD variable to tell automake
to link against the library we compiled earlier.
# 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
Finally, we need a top level Makefile.am
to tell Automake how to build the project, and a configure.ac
file to tell Autoconf how to find the required tools.
The top-level Makefile.am
is fairly straightforward:
# 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
Finally, the configure.ac
file tells Autoconf how to create the configure
script.
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
Run:
$ autoreconf -i
$ ./configure
$ make
$ B/foo
You should see the required output: "Hello world!"