Building shared libraries in subdirectories

后端 未结 2 1907
梦谈多话
梦谈多话 2021-02-07 07:25

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

相关标签:
2条回答
  • 2021-02-07 08:16

    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.

    0 讨论(0)
  • 2021-02-07 08:20

    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.

    Directory structure

    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/.

    Writing the source code

    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;
    }
    

    Compiling the library

    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
    

    Compiling the program

    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
    

    Controlling the build

    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
    

    Testing it out

    Run:

    $ autoreconf -i
    $ ./configure
    $ make
    $ B/foo
    

    You should see the required output: "Hello world!"

    0 讨论(0)
提交回复
热议问题