Rcpp: не удается открыть общий объектный файл

Я пытаюсь разработать пакет R, который использует Arrayfire благодаря библиотеке Rcpp. Я начал писать пример кода (назовем его hello_world.cpp), который выглядит так:

#include <arrayfire.h>

// [[Rcpp::export]]
bool test_array_fire(){
    af::randu(1, 4);    
    return true;
}

Затем я попытался скомпилировать его с помощью функции sourceCpp.

Rcpp::sourceCpp('src/hello_world.cpp')

Моим первым удивлением было то, что мне пришлось установить некоторые флаги вручную (sourceCpp, похоже, игнорирует конфигурацию Makevars при компиляции фрагмента кода C++). Я сделал это с помощью:

Sys.setenv("PKG_CXXFLAGS"="-std=c++11")
Sys.setenv("PKG_CPPFLAGS"="-I/opt/arrayfire/include/")
Sys.setenv("PKG_LIBS"="-L/opt/arrayfire/lib64/ -laf")

Однако код по-прежнему не компилируется должным образом. Каждое испытание заканчивается следующим выводом:

Error in 'dyn.load("/tmp/RtmpHaODIU/sourceCpp-x86_64-pc-linux-gnu-1.0.2/sourcecpp_689c5adb8d/sourceCpp_14.so")':
unable to load shared object '/tmp/RtmpHaODIU/sourceCpp-x86_64-pc-linux-gnu-1.0.2/sourcecpp_689c5adb8d/sourceCpp_14.so':
  libaf.so.3: cannot open shared object file: No such file or directory

К сожалению, я не смог найти решение своей проблемы (даже если некоторые вопросы о переполнении стека поднимают проблемы, которые на первый взгляд более или менее похожи).

Как я могу это исправить?


person KrzJoa    schedule 20.10.2019    source источник


Ответы (2)


Ошибка возникает очень поздно в процессе, когда R пытается загрузить общий объектный файл. Это означает, что компиляция и компоновка работали нормально с помощью установленных вами переменных среды. Но на последнем этапе компоновщик времени выполнения не знает, где находится libaf.so.3. Это конфигурация, которую обычно делают на уровне ОС, например. в моей системе

ralf@barra:~$ /sbin/ldconfig -p | grep libaf
        libafopencl.so.3 (libc6,x86-64) => /lib/libafopencl.so.3
        libafopencl.so (libc6,x86-64) => /lib/libafopencl.so
        libafcpu.so.3 (libc6,x86-64) => /lib/libafcpu.so.3
        libafcpu.so (libc6,x86-64) => /lib/libafcpu.so
        libaf.so.3 (libc6,x86-64) => /lib/libaf.so.3
        libaf.so (libc6,x86-64) => /lib/libaf.so

И если я попробую ваш пример, он без проблем работает с общим объектным файлом, который связан с libaf:

ralf@barra:~$ ldd /tmp/RtmpcjY9dN/sourceCpp-x86_64-pc-linux-gnu-1.0.2/sourcecpp_13d33790279c/sourceCpp_7.so | grep libaf
        libaf.so.3 => /lib/libaf.so.3 (0x00007f21037ed000)

Я ожидаю, что в вашем случае первая команда не даст результата, а вторая (скорректированная) команда приведет к ошибке «файл не найден» (?).

Есть несколько способов сообщить компоновщику времени выполнения о расположении библиотеки:

  • Отредактируйте /etc/ld.so.conf или (лучше) поместите файл в /etc/ld.so.conf.d/, ср. http://arrayfire.org/docs/installing.htm#Linux.
  • Установите LD_LIBRARY_PATH.
  • Добавьте -Wl,-rpath,/opt/arrayfire/lib64/ к PKG_LIBS
  • Установите ArrayFire в каталог, который компоновщик ищет по умолчанию. Это то, что я делаю, так как я компилирую из исходного кода и использую результат Пакеты DEB.

Что касается Rcpp::sourceCpp несоблюдения файла Makevars: проблема в том, что файл C++, который вы пишете, нельзя использовать напрямую. Вместо атрибутов Rcpp приходится создавать дополнительные функции-оболочки, что делается во временном каталоге. Теперь в принципе можно было бы скопировать файл Makevars в этот каталог. Однако обычно такие переменные задаются с помощью атрибутов Rcpp::plugins и Rcpp::depends. Например, включение C++11 осуществляется с помощью // [[Rcpp::plugins(cpp11)]]. Для других переменных вы можете написать свой собственный плагин или использовать тот, который предоставлен моим RcppArrayFire.

Тем не менее, я предлагаю вам начать с пакета, если это ваша цель. Rcpp::sourceCpp отлично подходит для многих вещей, но взаимодействие с установленной системой библиотекой без помощи пакета R не входит в их число.

person Ralf Stubner    schedule 21.10.2019
comment
Ok. Я хотел рассматривать этот простой пример с sourceCpp как разминку перед разработкой пакета. - person KrzJoa; 22.10.2019
comment
@KrzJoa Можно использовать sourceCpp таким образом. Сработало ли изменение конфигурации компоновщика времени выполнения? Относительно существующих пакетов: arrayfire-r делает большое подмножество API доступным на уровне R, но оно не является полным. RcppArrayFire не пытается сделать API доступным на уровне R. Вместо этого он упрощает переход на C++, где, конечно же, доступен полный API. Это похоже на такие пакеты, как PcppArmadillo, Rcppeigen и RcppGSL. - person Ralf Stubner; 22.10.2019

Несколько вещей, быстро:

  • когда вы используете sourceCpp(), вы не используете пакет
  • случайное отбрасывание переменной, используемой с пакетом, не помогает
  • на самом деле есть пакет от Ральфа, который, вероятно, подойдет
  • и у нас есть статья об этом здесь, в Rcpp Gallery

Поэтому я бы, вероятно, начал с переделки/перестройки примеров из Rcpp Gallery статьи о RcppArrayFire.

person Dirk Eddelbuettel    schedule 20.10.2019
comment
Итак, подведем итог: 1) sourceCpp не использует Makevars, и мы должны установить флаги с помощью Sys.setenv 2) Вы рекомендуете использовать RcppArrayFire вместо того, чтобы компоновать библиотеку C++ ArrayFire самостоятельно. Кстати, я знаю пакет RcppArrayFire и видел статью в галерее Rcpp. Однако я пытался составить Rcpp с C++ ArrayFire lib более простым способом (то есть без использования каких-либо дополнительных оболочек Rcpp). Я просто пытался изучить Rcpp, найдя более универсальное решение, которое облегчает добавление произвольной библиотеки C++ (не только тех, которые уже обернуты Rcpp). - person KrzJoa; 21.10.2019
comment
@KrzJoa Мне любопытно, что вы считаете более простым. Если у вас есть какие-либо предложения, не стесняйтесь заявить о проблеме. Кстати, вы знакомы с github.com/arrayfire/arrayfire-r? - person Ralf Stubner; 21.10.2019
comment
@KrzJoa: даже в качестве непакетной альтернативы для более простых задач sourceCpp действительно отражает переменные среды. См. виньетку Rcpp Attributes для получения дополнительной информации. Это. А также посмотрите десятки других ответов здесь от меня и других, говорящих: используйте пакет, когда сталкиваетесь с более сложной задачей, например, с системными библиотеками. Как Ральф только что снова сказал. - person Dirk Eddelbuettel; 21.10.2019
comment
Да, я знаю как RcppArrayFire, так и arrayfire-r. Однако я до сих пор не уверен, предлагают ли они доступ к полному ArrayFire API. Вот почему я решил начать с чистой библиотеки C++ вместо использования каких-то удобных (????) оболочек. - person KrzJoa; 22.10.2019