Racket/Scheme компилируется в один двоичный файл, без зависимостей? FFI и статическое связывание

Скажем, я создаю приложение в Racket.

И, скажем, в конечном итоге я хочу скомпилировать это приложение в виде единого двоичного файла, который можно было бы распространять среди пользователей без установки Racket или каких-либо других программных библиотек. Я верю, что это возможно, да?

Скажем, в этом приложении я хочу использовать пакет snappy https://docs.racket-lang.org/snappy/, который является оберткой FFI для библиотеки C++.

Я уже столкнулся с небольшой проблемой. Я сделал (require snappy) внутри DrRacket, следуя инструкциям и установив пакет, но получаю сообщение об ошибке:

../../Applications/Racket v7.7/collects/racket/private/kw.rkt:1349:57:
ffi-lib: couldn't open "libsnappy.1.dylib" (dlopen(libsnappy.1.dylib, 6): image not found)

Из этого я могу предположить, что racket-snappy ожидает, что файлы для libsnappy будут находиться на обычном пути unix, но я нахожусь на macos, и мой установлен через Homebrew где-то еще. Я считаю, что ответ на эту проблему здесь https://stackoverflow.com/a/24287418/202168

Меня беспокоит следующее: я не хочу, чтобы пользователям моего приложения приходилось устанавливать эти библиотеки через Homebrew и возиться с путями и т. д.

Я новичок в Racket и практически ничего не знаю о цепочке инструментов компилятора или C / C ++, если на то пошло. Но я считаю, что мне нужно, когда я компилирую свой проект Racket, чтобы иметь возможность raco exe (?) Статически связать libsnappy, который находится в моей системе, и свернуть все в один двоичный файл без каких-либо зависимостей.

Итак, мой вопрос: возможно ли это? Если да, то является ли он простым (то есть управляется с помощью инструментов raco)?

Я предполагаю, что в худшем случае мне придется загрузить все зависимости и собрать их из исходного кода, а также собрать мой проект Racket в виде библиотеки, а затем иметь какой-то каркасный проект C, который объединяет их все в одну вещь. Надеюсь нет.

Я также добавлю ... если это проще в других схемах (курица? Chez? Gambit? Guile?), тогда мне тоже было бы интересно узнать.

Обновление: я нашел эту статью с некоторыми анекдотами годовой давности о том, как кто-то пытался сделать то же самое https://taoofmac.com/space/blog/2019/06/20/2310

Исходя из этого и ответа Райана ниже, raco distribute выглядит многообещающе, и мне действительно нужно попробовать это на себе, чтобы убедиться, что это работает.

Обновите еще раз: Вот еще одна статья, в которой снова подтверждается, что raco distribute следует помещать все в папку без внешних операций https://defn.io/2020/06/28/racket-deployment/ и вот указатель на документы о том, как создать образ .dmg для MacOS: https://docs.racket-lang.org/raco/exe-dist.html#(part._.API_for_.Bundling_.Distributions)


person Anentropic    schedule 18.07.2020    source источник
comment
Я нашел это для Chez github.com/gwatt/chez-exe ... может ли кто-нибудь подтвердить если он делает то, что я прошу? Похоже, что да (но, очевидно, только для Chez Scheme)   -  person Anentropic    schedule 20.07.2020
comment
похоже, здесь описан способ использования Chicken Scheme: foldling.org/scheme. html#2013-07-16   -  person Anentropic    schedule 20.07.2020
comment
кажется, chez-exe не делает то, что я хочу github.com/gwatt/chez-exe/issues /2   -  person Anentropic    schedule 20.07.2020
comment
raco distribute создает версию исполняемого файла (который вы создали с помощью raco exe), который находится в структуре каталогов, где рядом с ним находятся все общие библиотеки racket. Я думаю, что для сторонних общих библиотек вам нужно поместить их в разумное место и соответствующим образом настроить пути, используя config.rktd во время сборки. Если вам действительно нужны статические двоичные файлы, я понятия не имею, но я подозреваю, что это вообще невозможно в OSX.   -  person    schedule 20.07.2020
comment
Я действительно ищу решение для сторонних общих библиотек, типа библиотек C или C++, где часть Racket - это в основном просто привязки FFI.... возможно ли указать путь к файлу .dylib или .so как относительный path и сделать их частью распространяемого пакета?   -  person Anentropic    schedule 20.07.2020
comment
Общие библиотеки, указанные через относительный путь, где я могу поместить все зависимости в один каталог пакета, будут работать для меня... в конце концов, в MacOS то, что выглядит как файл приложения, на самом деле является каталогом. Чего мне нужно избегать, так это наличия каких-либо жестко закодированных абсолютных путей или обращения к пользователю с просьбой настроить пути для использования приложения.   -  person Anentropic    schedule 20.07.2020
comment
Для собственных библиотек Racket, да, я так думаю (одна проблема заключается в том, что единственное, что я собираю таким образом, raco exe теперь создает двоичный файл, который не нуждается ни в каких библиотеках Racket во время выполнения, поэтому я больше не могу сказать). Для сторонних общих библиотек я не уверен, но я ожидаю этого, если вы будете осторожны.   -  person    schedule 20.07.2020


Ответы (1)


Существует частичное решение с использованием комбинации raco distribute и define-runtime-path.

Предположим, у вас есть программа, использующая libzmq, которая, как вы знаете, установлена ​​в вашей системе сборки по адресу /usr/lib/x86_64-linux-gnu/libzmq.so.5. Вы можете использовать define-runtime-path для создания ссылки на этот файл и указать raco distribute скопировать его в каталог дистрибутива. Например, предположим, что my-app.rkt выглядит следующим образом:

#lang racket/base
(require racket/runtime-path)
(define-runtime-path zmq "/usr/lib/x86_64-linux-gnu/libzmq.so.5")
(printf "zmq = ~e\n" zmq)

Когда вы запускаете программу с помощью racket my-app.rkt, она печатает

zmq = <path:/usr/lib/x86_64-linux-gnu/libzmq.so.5>

Но если вы запустите raco exe my-app.rkt, а затем raco distribute MyApp my-app, то каталог MyApp будет содержать копию libzmq.so.5:

$ find MyApp/ -type f 
MyApp/lib/plt/my-app/exts/ert/r0/libzmq.so.5
MyApp/lib/plt/racket3m-7.7
MyApp/bin/my-app

и если вы запустите ./MyApp/bin/my-app, он напечатает

zmq = #<path:/PATH/TO/HERE/./MyApp/bin/../lib/plt/my-app/exts/ert/r0/libzmq.so.5>

Вы можете использовать (ffi-lib zmq) для загрузки общей библиотеки. К сожалению, этот каталог не находится в пути поиска, который приложение будет использовать для загрузки общих библиотек, поэтому существующие библиотеки Racket, которые просто пытаются загрузить (ffi-lib "libzmq" '("5")), не найдут копию приложения.

Есть еще один способ использования define-runtime-path специально для разделяемых библиотек, и я думал, что это решит эту проблему, но, похоже, это не так. Мне это кажется ошибкой, поэтому я отправлю отчет об ошибке.

Обновление: я подал отчет об ошибке о том, что общая библиотека define-runtime-path ('so) режим заставляет raco distribute копировать общую библиотеку вне пути поиска библиотеки приложения.

person Ryan Culpepper    schedule 21.07.2020
comment
Спасибо за ваш ответ, это похоже на информацию, которая мне нужна. Правильно ли я понимаю последние абзацы: если я напишу свою собственную привязку Racket FFI к C-библиотеке, все будет работать (потому что define-runtime-path есть в моем проекте), но если я использую сторонние привязки Racket к C-библиотеке, то это может не работать? - person Anentropic; 21.07.2020
comment
Правильно. В частности, ваша собственная привязка FFI будет работать, если вы используете переменную, определенную define-runtime-path, для загрузки внешней библиотеки. Сторонние привязки FFI, вероятно, не будут работать, потому что они будут использовать обычный путь поиска библиотеки Racket+OS. - person Ryan Culpepper; 21.07.2020
comment
Что насчет того, если я вручную создам символическую ссылку на файлы библиотеки по пути, ожидаемому сторонними привязками на моей машине... тогда мне не понадобится define-runtime-path... следует raco distribute затем успешно скопировать их в папку dist? - person Anentropic; 21.07.2020
comment
Нет, потому что raco distribute не будет знать о зависимости, потому что ffi-lib не регистрирует зависимость, как это делает define-runtime-path. И в общем случае это невозможно, потому что его аргументы могут быть вычислены динамически, и потому что код может иметь несколько вызовов ffi-lib, но хочет использовать только первый успешный, и так далее. - person Ryan Culpepper; 21.07.2020
comment
Хм, похоже, мне нужно проверить каждую стороннюю библиотеку, которая предоставляет привязки FFI, чтобы узнать, совместимы ли они с raco distribute тогда? Если они используют define-runtime-path, все должно быть в порядке, если у них жестко запрограммированы пути, то мой дистрибутив не будет автономным? - person Anentropic; 21.07.2020
comment
Вот еще одна идея: я думаю, вы могли бы обойти проблему, вручную скопировав общие библиотеки в соответствующий каталог подкаталога, созданный raco distribute. Я не пробовал, автоматизировать было бы неудобно и неудобно, но должно работать. В Racket 7.7, основываясь на одном из моих экспериментов, правый подкаталог выглядит как lib/plt/<appname>/lib. Если вы вручную скопируете общие библиотеки в этот каталог, приложение (и любые сторонние библиотеки Racket, которые оно использует) должно найти их. - person Ryan Culpepper; 21.07.2020
comment
Большое спасибо за Вашу помощь. Я обновил вопрос с помощью нескольких статей, которые я нашел, похоже, что raco distribute обычно должен с этим справляться, например. defn.io/2020/06/28/racket-deployment показывает это успешное добавление копий libssl.1.1.dylib и libcrypto.1.1.dylib в папку dist/lib/plt/.... Я думаю, что я должен попробовать это сам и посмотреть. Мой вопрос заключался в том, чтобы изучить заранее, на случай, если это будет совершенно невозможно, но я чувствую, что Racket, вероятно, подойдет для этого ???? Я думаю, мне понравится этот язык... - person Anentropic; 21.07.2020