Подтвердить что ты не робот

"Лучшая практика" для библиотеки clojure, которая использует собственные библиотеки?

Хотя это может показаться субъективным, есть конкретный пример, который я хотел бы помочь разрешить. Это связано с проблемой с библиотекой Overtone Clojure https://github.com/overtone/overtone/issues/274, которая, похоже, должна быть "Лучшей практикой" для Leiningen и применяться к большему количеству библиотек, чем просто Overtone.

Overtone - это библиотека Clojure, предназначенная для использования в других проектах. Overtone требует, чтобы родные библиотеки работали, поэтому он использует :native-path "native" в project.clj https://github.com/overtone/overtone/blob/master/project.clj#L69, чтобы получить правильный путь для родных scsynth-библиотек [overtone/scsynth "3.5.7.0"] которые используются.

Однако я считаю, что это сбрасывает входящий путь из проекта, который зависит от библиотеки Overtone. См. Проблему для некоторого фона, но в основном после зависимости от [overtone "0.9.1"] в project.clj (System/getProperty "java.library.path") возвращается только текущий собственный путь, а проект с использованием Overtone не может проходить по пути к любым локальным библиотекам.

Итак, вопрос в том, как может зависимый проект смешивать локальные родные библиотеки с Overtone? Должен ли Overtone или зависимый проект корректировать настройки project.clj? Как?

4b9b3361

Ответ 1

Я не знаю о "лучших", но вот практика, которая успешно сработала для меня, по крайней мере, в четырех проектах, три из которых были "настоящими", коммерческими. Хотя я обнаружил только конкретный случай для ZeroMQ, я считаю, что принципы носят общий характер и должны работать для любых родных библиотек. Большая часть кода может быть легко использована повторно, и она лицензируется в Eclipse, поэтому не стесняйтесь принимать ее, если хотите. У меня не было необходимости в более общей версии встроенной библиотеки, но я считаю, что ее можно легко извлечь.

Проблема, с которой я столкнулась со стандартными решениями (lein: native-path, JVM args и т.д.), заключалась в том, что я хотел иметь портативное решение для дистрибутива uberjar , которое не требует от пользователя установки чего-либо еще, поэтому такие инструкции, как "загрузить uberjar, установить libzmq-dev из вашего диспетчера пакетов, а затем запустить uberjar", не могут быть и речи.

Принцип довольно прост: я собираю собственные библиотеки в библиотечном банке для всех поддерживаемых платформ. Таким образом:

  • Я, как автор библиотеки, контролирую, какие именно версии используются. Никаких сюрпризов нет.
  • Включение библиотеки из другого проекта может быть сделано без какого-либо осознания того, что библиотека опирается на собственные библиотеки.
  • Точно так же распределение uberjar, будь то от leiningen или maven, работает без проблем.

Я не полагаюсь ни на одну из стратегий связывания ОС, так как мне было трудно заставить их работать надежно. Так что я сделал, что я включил все необходимые родные библиотеки, которые не гарантированно присутствуют в запущенной системе в банке, а затем во время выполнения библиотека:

  • динамически обнаруживает, на какой платформе он работает;
  • извлекает собственные библиотеки из собственного архива в системный каталог temp, если он еще не существует (на основе SHA, это было необходимо, чтобы избежать накопления на платформах, которые неправильно очищают свои временные директории - это, Windows);
  • "вручную" загружает собственные зависимости в правильном порядке, поэтому динамическая компоновка не пытается разрешить библиотеки на пути к хост-системе.

Есть, конечно, недостатки этого подхода:

  • Поскольку процесс сборки для одного артефакта включает в себя три разных ОС (я поддерживаю Linux, Windows и OSX) и 2 архитектуры (i386 и x86_64), процесс сборки трудно автоматизировать и требует доступа к соответствующим машинам;
  • В связи с тем, что конкретные версии родных библиотек объединены, пользователи библиотеки не могут их обновлять; в уязвимых средах это может быть неприемлемым;
  • Как пользователь этой библиотеки, вы должны доверять мне, что вложенные собственные библиотеки действительно являются тем, что я утверждаю, что они есть, и действительно были построены в процессе, который я описываю в исходном коде, без каких-либо дополнительных вмешательств;
  • Поскольку я практически ничего не знаю о связи динамических библиотек, могут быть другие проблемы с этим подходом, хотя они еще не укусили меня.

Ответ 2

Я выпустил clj-nativedep через Clojars, который может помочь с этой проблемой. Библиотека предоставляет возможность быстро идентифицировать нормализованное имя для текущей системной архитектуры и может загружать любой выбранный ресурс (внутри jar или путь к классам) в среду выполнения.

Смотрите: https://github.com/rritoch/clj-nativedep

Эта система была специально создана для моего проекта WarpCTL, который использует большое количество собственного кода, созданного с помощью swig. Из-за того, как обрабатывается загрузка класса Clojure, нативные зависимости нужно загружать через статический класс-конструктор, вы можете увидеть пример в https://github.com/rritoch/WarpCTL/blob/master/extra/JADL-SDK/build/java/src/com/vnetpublishing/swig/adl/jadl_sdk.java#L13, Для этого проекта я создаю Java-код в JAR и добавляю clj-nativedep и jar в качестве зависимости. Должна быть возможность загружать ресурсы таким образом из чистых приложений Clojure, но для лучшей производительности он должен быть загружен из статического конструктора классов.

Ответ 3

У меня также была эта проблема один раз, поэтому я сделал неортодоксальный плагин lein, который решает именно это раз и навсегда с несколькими строками, добавленными в ваш project.clj: https://bitbucket.org/noncom/nativot

ПРЕДУПРЕЖДЕНИЕ: он чрезвычайно не приветствуется способом Clojure, так как он нарушает всю концепцию повторяемости и т.д., позволяя вам упаковывать произвольные баночки, ресурсы и другие файлы в полученную банку и просто заставляет ее работать.