Проблема со связанными сторонними библиотеками заключается в том, что вам нужно будет указать места для их поиска. Аддоны узла представляют собой динамически подключаемые общие библиотеки, поэтому всякий раз, когда вам требуется аддон, соответствующий динамический c загрузчик должен будет загрузить все необходимые библиотеки.
Как мы можем проверить, какие библиотеки нам нужны?
- macOS:
otool -L your_addon.node
- Linux:
ldd you_addon.node
Для загрузки требуемых библиотек наш загрузчик будет искать в нескольких местах, например, во всех путях, перечисленных в LD_LIBRARY_PATH
на Linux.
Но мы поставляем нашу собственную библиотеку, которой нет ни в одном из стандартных путей поиска?
Это возможно для библиотеку или исполняемый файл (формат ELF в Linux или формат Mach-O, например, macOS), чтобы указать путь к загрузчику времени выполнения . Эти пути жестко запрограммированы в двоичный файл и могут указывать дополнительные пути для поиска библиотек.
Как мы можем получить двоичные файлы RPATH
?
- macOS:
otool -l your_addon.node | grep RPATH -A2
- Linux:
objdump -x your_addon.node | grep RPATH
Хорошо, rpath
это так!
Мы можем настроить наш rpath
через флаги компоновщика:
"conditions": [
["OS==\"mac\"",
{
"link_settings": {
"libraries": [
"-Wl,-rpath,@loader_path",
"-Wl,-rpath,@loader_path/..",
],
}
}
],
["OS==\"linux\"",
{
"link_settings": {
"libraries": [
"-Wl,-rpath,'$$ORIGIN'",
"-Wl,-rpath,'$$ORIGIN'/.."
],
}
}
]
],
$$ORIGIN
? @loader_path
? Что это?
rpath
записи жестко запрограммированы, поэтому исправление, например, /home/youruser/libs/foo/bar/
сломается, как только вы попытаетесь использовать свой аддон на другом компьютере.
Оба $ORIGIN
и @loader_path
- это токены, которые наш динамический c загрузчик собирается заменить на каталог, содержащий наш двоичный файл. Таким образом, независимо от того, где будет установлена наша библиотека, если мы укажем пути относительно расположения наших двоичных файлов, загрузчик Dynami c сможет ее найти. ('$$ORIGIN'
- это всего лишь небольшой обходной путь, поэтому node-gyp
не испортит ситуацию при попытке подстановки значений)
Хорошее прочтение по этому топу c - эта статья
Пример
.
├── build
│ └── Release
│ └── addon.node
├── index.js
├── lib
│ └── my_library.dylib
├── package-lock.json
└── package.json
Наша сборка генерирует файл ./build/Release/addon.node
, который связывается с ./lib/my_library.dylib
во время сборки.
Используя @loader_path
, теперь мы можем указать rpath
относительно местоположения наших двоичных файлов, поскольку @loader_path
будет заменено на /whatever/path/to/our/package/build/Release
.
{
"link_settings": {
"libraries": [
"-Wl,-rpath,@loader_path/../../lib",
],
}
}
Во время выполнения это приведет к /whatever/path/to/our/package/build/Release/../../lib
, где именно находится наша библиотека.
А как насчет Windows?
Windows двоичные файлы не имеют / не используют свойство rpath
.
Однако порядок поиска DLL начнет поиск в каталоге, из которого загружено приложение.
Межплатформенный подход
Мой подход к доставке необходимых библиотек выглядит следующим образом:
- Свяжите ваши библиотеки во время сборки
- Скопируйте ваши библиотеки в результирующий выходной директор y, например,
build/Release
- Установите для
rpath
значение $ORIGIN
или @loader_path
на Linux и macOS
Таким образом, мы даем команду динамическому c загрузчик в Linux и macOS для поиска нашей библиотеки в той же папке, что и наш результирующий двоичный файл, что является поведением по умолчанию для Windows.
Копирование файлов может выполняться через дополнительную цель в наш файл gyp:
{
"target_name": "action_before_build",
"type": "none",
"copies": [{
"files": [ "/your/lib.dylib" ],
"destination": "<(PRODUCT_DIR)"
}]
}