Создание общих библиотек в подкаталогах - программирование
Подтвердить что ты не робот

Создание общих библиотек в подкаталогах

Я пытаюсь создать R-пакет, который использует некоторый C-код. У меня есть библиотека C, которая скомпилирована в исполняемый файл, который можно вызвать из командной строки. С ним связан файл Makefile.

Я пытаюсь получить информацию здесь, где говорится

Если вы хотите создать и затем подключиться к библиотеке, скажем, используя код в подкаталог, используйте что-то вроде

 .PHONY: all mylibs

 all: $(SHLIB)
 $(SHLIB): mylibs

 mylibs:
         (cd subdir; make) 

Будьте осторожны, чтобы создать все необходимые зависимости, так как нет гарантировать, что зависимости всех будет выполняться в определенном порядке (и некоторые из машин сборки CRAN использование нескольких процессоров и параллельных операций).

Если я создаю новый подкаталог папки src в моем пакете, называемый someLibrary, с кодом и Makefile без изменений и, в свою очередь, в исходном файле Makevars для моего пакета, я добавлю код выше без изменений, то я смогу построить эту общую библиотеку для экспорта с помощью useDynLib?


Изменить 1:

Следующая информация здесь, я изменил Makefile, чтобы создать общую библиотеку, добавив

CFLAG = -fPIC -g -O3 
LDFLAGS= -shared

Однако это приводит к тому, что файл .so не экспортируется непосредственно в каталог libs пакета. Если я жестко закодирую путь в целевой, то файл отправляется в каталог libs пакета (все это путем вызова R CMD INSTALL myPackage).


Изменить 2:

Наконец, я хотел бы знать, как делать вызовы в общей библиотеке, учитывая, что у него есть метод main(), который я мог бы вызвать из исполняемого файла командной строки.

Какова процедура, чтобы разоблачить это для R NAMESPACE, чтобы его можно было вызвать через .Call?

PS. Пожалуйста, дайте мне знать, если я должен сделать последний бит отдельным вопросом.

4b9b3361

Ответ 1

В комментариях к вопросу автор вопроса задал вопрос об использовании 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 script.

AC_INIT([libhelloworld], 1, [email protected])

# 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

Вы должны увидеть требуемый вывод: "Привет, мир!"

Ответ 2

callable.c

#include <stdio.h>

int main(int argc, char **argv) {
    printf("Hello World\n");
    return 0;
}

Чтобы получить файл .so из файла C. callable.c используйте

R CMD SHLIB callable.c

Итак, теперь мы вызываем .so

R обычно требует, чтобы все аргументы C были указателями, но если ваш основной метод игнорирует argc, тогда мы можем обойти это. Поэтому для вызова main из callable.so из R мы можем написать такой метод.

main <- function() {
    dyn.load("callable.so")
    out <- .C("main", argc=0, argv="")
}

Вызов основной функции будет выполнять основную функцию C.