我有一个简单的通用lisp服务器程序,该程序使用osicat库与posix文件系统接口。我需要执行此操作,因为系统创建了文件的符号链接,并使用POSIX stat元数据,而在可移植Lisp中,这些操作都不是直接可以做的。
我正在使用quicklisp管理依赖项,而我将所有这些都固定在一个有效的发行版中。该应用程序可在CCL和SBCL之间移植,我倾向于在前者中构建它并使用后者进行部署。我使用asdf defsystem
声明了应用程序的依赖关系,并且可以使用quicklisp加载它,以便从本地项目轻松开发。
对于部署,我只是使用一些有趣的剧本,它们在远程复制了开发人员环境(例如,设置quicklisp,将代码推送到本地项目中,用完用户主目录),但确实很麻烦。最近,随着它变得更加稳定,我一直在使用sb-ext:save-lisp-and-die
和一个简单的编译脚本来对其进行编译。这意味着我得到了一个可执行文件,它可以像服务器一样运行,具有服务管理脚本和一个匿名用户帐户。
这一直很好,所以我最近将这一步移到了下一个级别,并且我正在使用编译脚本来构建.deb软件包,因此我可以将所有内容捆绑到可重定位的二进制文件中。这也可以完成,但是无法从原始构建主机中重定位生成的二进制文件。他们拒绝启动,并且似乎试图动态加载osicat的共享库
Unhandled SIMPLE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING
Mar 15 12:47:14 annie [479]: {10005C05B3}>:
Mar 15 12:47:14 annie [479]: Error opening shared object "libosicat.so":
Mar 15 12:47:14 annie [479]: libosicat.so: cannot open shared object file: No such file or directory.
该图像似乎希望在原始构建树的quicklisp档案中找到它
(ERROR "Error opening ~:[runtime~;shared object ~:*~S~]:~% ~A." "/home/builder/buil...quicklisp/dists/quicklisp/software/osicat-20180228-git/posix/libosicat.so
(SB-SYS:DLOPEN-OR-LOSE #S(SB-ALIEN::SHARED-OBJECT :PATHNAME #P"
因此在源代码中四处寻找,我意识到,当quicklisp获取osicat并执行其构建操作时,它会编译该DLL来包装它与系统库的接口,而不仅仅是直接将ffi封装给它们-可能是因为它使用的是cffi groveller,我对cffi尚不甚了解(尚未)。很好,但是不是使用系统链接程序链接到.so,而是尝试从固定路径dlopen
进行save-image
的访问,该路径不是很可移植,并且会破坏source: Hello World
target: lo
Hello World
lo // No match: He != lo
Hello World
lo // No match: el != lo
Hello World
lo // No match: ll != lo
Hello World
lo // Match: lo != lo
的用途>
在这一点上我有些困惑,但是在我进一步深入研究QL和cffi构建之前,我想知道是否缺少一些构建或编译配置,这会使它在更静态的环境中进行引导时尚或影响包装图书馆的生产。理想情况下,我只希望可以将一个blob包裹在安装程序中,然后将其链接到系统库,但是如果我必须部署一些其他工件,那么可能就可以了。我不知道如何使自动生成的共享对象出现在受控制的路径上。
尽管如此,我还是可以为posix调用编写一个.so,并将其分发到应用程序中,然后尝试直接使用FFI。那会有点痛苦,所以我宁愿不这样做。
答案 0 :(得分:2)
也许您可以在SBCL上使用sb-posix:symlink
和sb-posix:fstat
,通过功能切换来消除osicat依赖性。
答案 1 :(得分:2)
是的,当转储的映像启动时,它正在尝试重新加载共享库。如您所见,如果映像不是在转储映像的计算机上启动的,则该映像不起作用。
这几乎是static-program-op要解决的问题。像这样的简单系统定义应该可以帮助您编译静态程序:
(defsystem "foo"
:defsystem-depends-on ("cffi-grovel")
:build-operation "static-program-op" ; "asdf" package is implied
:build-pathname "foo" ; path of the generated binary
:entry-point "foo:main" ; function to use as the entry point
;; ... everything else ...
)
如果您的系统依赖于grovel文件(由:cffi-wrapper-file
,:c-file
或:o-file
定义),例如osicat提供的文件,则它将这些文件静态链接到转储的映像
但是,这并不完美。
从本质上讲,仍然存在一些问题。有些是由CFFI本身固定在上游的(例如,不重新加载静态嵌入式库的共享库),有些则有些困难。 (例如,SBCL的默认编译选项默认情况下不允许您使用static-program-op
。这在SBCL的Debian版本中已得到修复,但其他发行版的响应速度较慢。)
这显然是整个社区都遇到的一个问题,并且有一些图书馆可以为您提供帮助:
static-program-op
的方法,但是需要您构建自定义的SBCL。但是,它生成.deb
和.rpm
之类的分发软件包,以便能够指定系统共享库的依赖项(例如,如果您依赖sqlite,它将找出提供它的软件包并添加它作为.deb
中的依赖项。我强烈建议您查看.gitlab-ci.yml
作为示例。我建议阅读这两个库的网页以做出选择,它们都有其优点和缺点。 <joke>
显然,linux-packaging是更好的选择。</joke>