Racket/Scheme 编译为单个二进制文件,没有依赖项? FFI 和静态链接

Racket/Scheme compile to single binary, no dependencies? FFI and static linking

假设我在 Racket 中构建应用程序。

并说最终我想将该应用程序编译为可以分发给用户的单个二进制文件,而无需他们安装 Racket 或任何其他软件库。我相信这是可能的,是吗?

在那个应用程序中说我想使用 snappy 包 https://docs.racket-lang.org/snappy/,它是一些围绕 C++ 库的 FFI 包装器。

我已经运行陷入了一个小问题。我在 DrRacket 中做了 (require snappy) 并按照提示安装了软件包,但我收到错误消息:

../../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 在其他地方安装的。我相信这个问题的答案就在这里

我担心的是:我不希望我的应用程序的用户必须通过 Homebrew 和 fiddle 使用路径等安装这些库

我是一个 Racket 菜鸟,对编​​译器工具链或 C/C++ 基本上一无所知。但我相信我需要的是当我编译我的 Racket 项目时能够让 raco exe(?) “静态地 link” 我系统上的 libsnappy 并将所有内容整合到一个文件中没有依赖关系的二进制文件。

所以我的问题是:这可能吗?如果是这样,它是否简单明了(即通过 raco 工具管理)?

我想象在最坏的情况下,我必须下载所有依赖项并从源代码构建它们,并将我的 Racket 项目也构建为一个库,然后有某种骨架 C 项目将它们全部拉入一个事物。我希望不会。

我还会添加...如果这在其他方案中更容易(Chicken?Chez?Gambit?Guile?)那么我也有兴趣知道。

更新: 我在这篇文章中发现了一些关于有人尝试同样事情的多年轶事 https://taoofmac.com/space/blog/2019/06/20/2310

基于此,以及下面 Ryan 的回答,raco distribute 看起来很有希望,我真的需要亲自尝试一下以确认有效。

再次更新:这是另一篇文章再次确认 raco distribute 应该将所有内容放入没有外部依赖的文件夹中 https://defn.io/2020/06/28/racket-deployment/ and here is a pointer to the docs for how to build a .dmg image for MacOS: https://docs.racket-lang.org/raco/exe-dist.html#(part._.A.P.I_for_.Bundling_.Distributions)

有一个使用 raco distributedefine-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 的方法专门用于共享库,我认为它可以解决该问题,但似乎没有。这对我来说似乎是一个错误,所以我会提交错误报告。

更新:我已经提交了 bug report 关于 define-runtime-path 的共享库 ('so) 模式导致 raco distribute 复制的事实应用程序库搜索路径之外的共享库。