CMake: ошибка времени выполнения (dyld: библиотека не загружена) для динамически связанных ресурсов в MacOS

проблема

В MacOS у меня возникают проблемы со связыванием во время выполнения проекта CMake, который зависит от динамически связанных ресурсов - но только после установки проекта! Проблема не возникает, когда я только собираю бинарный файл без его установки.

$ ./testapp
Hello world!
$ $INSTALLDIR/testapp
dyld: Library not loaded: @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib
  Referenced from: /Users/normanius/workspace/installdir/testapp
  Reason: image not found
[1]    76964 trace trap  /Users/normanius/workspace/installdir/testapp   

Минимальный пример

Я могу воспроизвести проблему в минимальной установке, состоящей из CMakeLists.txt а также main.cpp, Библиотека, на которую я ссылаюсь, называется VTK (v7.1.1), которая была построена с помощью общих библиотек (дополнительные сведения см. Ниже).

# CMakeLists.txt
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(test)

# Test application.
add_executable(testapp
               main.cpp)

# Find vtk (library that has to be linked to dynamically).
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
target_link_libraries(testapp ${VTK_LIBRARIES}) # <---- this causes the problem

# Install instructions.
install(TARGETS testapp DESTINATION "${CMAKE_INSTALL_PREFIX}")

main.cpp даже не использует какие-либо объекты VTK.

// main.cpp
#include <iostream>

int main (int argc, char* argv[])
{
    std::cout << "Hello world!" << std::endl;
    return 0;
}

Я строю проект с помощью следующих команд. Флаг CMAKE_PREFIX_PATH Я решил дать CMake подсказку, где найти библиотеку VTK.

$ INSTALLDIR="path/to/installation"
$ mkdir build && cd build
$ cmake .. -DCMAKE_PREFIX_PATH="$DEVPATH/lib/vtk/cmake" \
           -DCMAKE_BUILD_TYPE=Release \
           -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" 
$ make
$ make install

При выполнении testapp в папке build все выглядит нормально:

$ ./testapp
Hello world!
$ cp testapp $INSTALLDIR/testapp
$ $INSTALLDIR/testapp
Hello world!

Однако, если я запускаю исполняемый файл в INSTALLDIR Я получаю ошибку во время выполнения:

$ $INSTALLDIR/testapp
dyld: Library not loaded: @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib
  Referenced from: /Users/normanius/workspace/installdir/testapp
  Reason: image not found
[1]    76964 trace trap  /Users/normanius/workspace/installdir/testapp    

Естественно, проблема исчезнет, ​​если я уберу target_link_libraries() инструкция в CMakeLists.txt,

Так что же происходит при установке проекта CMake? А что не так в моем случае? Я тестировал разные версии CMake (3.5, 3.9 и 3.10) - но поведение такое же.

подробности

По-видимому, механизм RPATH в MacOS не настроен должным образом для примера.

Это выдержка из структуры ссылок testapp двоичная:

$ otool -L testapp
testapp:
    @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersFlowPaths-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersGeneric-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersHyperTree-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
...

Поскольку он может сыграть роль в создании библиотеки VTK (другого проекта CMake): для поддержки python необходимо установить флаги проекта. VTK_WRAP_PYTHON=ON а также BUILD_SHARED_LIBS=ON, Установочный префикс был установлен на CMAKE_INSTALL_PREFIX="$VTK_INSTALL_DIR", Чтобы убедиться, что ресурсы находятся во время выполнения, необходимо дополнительно включить поддержку RPATH через CMAKE_MACOSX_RPATH=ON а также CMAKE_INSTALL_RPATH="$VTK_INSTALL_DIR/lib",

Заворачивать

Что я концептуально ошибаюсь? Что происходит при установке проекта с make install? Можно ли решить эту проблему в CMake? Или это связано только с ВТК и как были созданы общие библиотеки?

1 ответ

Решение

CMake изменяет RPATH для всех установленных целей при запуске make install,

Представьте себе создание совместно используемой библиотеки и исполняемого файла в рамках одного и того же проекта CMake. Чтобы иметь возможность запускать исполняемый файл, он должен иметь возможность динамически загружать общую библиотеку во время выполнения. Поэтому CMake по умолчанию добавляет полный (абсолютный) путь к динамической библиотеке в дереве сборки в rpath исполняемого файла. Это очень удобно для разработки, так как мы можем запустить исполняемый файл прямо из дерева сборки, но мы, вероятно, не захотим отправлять исполняемый файл таким образом.

Вот почему CMake будет изменять rpath после установки, чтобы он содержал только переносимые пути (т.е. удалял запись, указывающую на дерево сборки). То есть, если вы не поместите вашу разделяемую библиотеку в одно из системных расположений по умолчанию, исполняемый файл не найдет ее после установки.

Однако CMake позволяет вам указать установочный rpath, который заменит удаленную запись дерева сборки на указанную вами. Увидеть INSTALL_RPATH а также INSTALL_RPATH_USE_LINK_PATH целевые свойства для деталей.

Поскольку весь этот rpath-компонент полностью зависит от платформы, OSX поставляется со своими собственными особыми правилами. Довольно подробное объяснение можно найти в (к сожалению, довольно устаревшем) CMake wiki:

В отличие от других UNIX, компоновщик Darwin, dyld, находит зависимые динамические библиотеки, используя полный путь к каждому dylib. Например, в исполняемом файле "foo" записанные полные пути являются именами установки для каждого зависимого dylib. А библиотека "/usr/lib/libSystem.dylib" имеет установочное имя "/usr/lib/libSystem.B.dylib", как указано "otool -D". При связывании с "foo", "foo" зависит от "/usr/lib/libSystem.B.dylib". Эту зависимость можно увидеть с помощью "otool -L foo". Для перемещаемых двоичных файлов доступны для использования @executable_path, @loader_path и @rpath. В примере "foo" @executable_path и @loader_path заменяют расположение "foo". @rpath заменяется на RPATH в "foo", чтобы найти зависимых dylibs. Таким образом, механизм RPATH вступает в игру. Компоновщик будет искать @rpath/ dependencies в следующем порядке:

  • DYLD_LIBRARY_PATH - переменная окружения, которая содержит список каталогов
  • RPATH - список каталогов, который связан с исполняемым файлом. Они могут содержать @loader_path и @executable_path.
  • встроенные каталоги - / lib / usr / lib
  • DYLD_FALLBACK_LIBRARY_PATH - переменная среды, которая содержит список каталогов

Вы должны быть в состоянии решить эту проблему, настроив соответствующие целевые свойства, но это довольно сложно и может быть довольно сложно получить право.

Это исправило проблему для меня:

set(CMAKE_MACOSX_RPATH OFF)

add_library(your-lib SHARED)
Другие вопросы по тегам