静态链接 Haskell 程序
ghc
默认会静态链接 Haskell 依赖,动态链接其他语言的依赖(比如 C 库)。这样在部署的时候就会非常非常麻烦。编译出来的二进制程序可能在一个发型版上能工作,但是在其他发行版就挂了(因为缺动态链接库)。库的版本也是个头疼的问题。
一个很显然的 use case 就是 AWS Lambda,你对机器没有控制权,自然也没有权限安装这些库。这时候静态链接就很有必要了。
网络上有甚多关于 crt
和 opal-static
/fPIC
的 hack。他们都是过时的,不需要。
我们使用 Stack build system。它太好用了,不熟悉的可以想象成 Haskell 的 pipenv
。直接用 cabal
也是一样的,不过我更喜欢 Stack。
举个例子。我有一个项目使用了 text-icu
这个依赖。它是一个 icu
(C library) 到 Haskell 的 binding。如果在 Debian 上直接编译然后到 Ubuntu 上使用,就会出现找不到动态链接库的错误:
$ ./MainParser: error while loading shared libraries: libicuuc.so.57: cannot open shared object file: No such file or directory
但是我其实是有这个库安装的,不过版本太新了
$ ls /usr/lib/x86_64-linux-gnu/ | grep libicuuc
libicuuc.a
libicuuc.so
libicuuc.so.60
libicuuc.so.60.2
ldd 一下会发现更多的依赖:
$ ldd MainParser
linux-vdso.so.1 (0x00007fff1d19f000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f92fac94000)
libicuuc.so.57 => not found
libicui18n.so.57 => not found
libicudata.so.57 => not found
libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f92faa6a000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f92fa862000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f92fa65f000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f92fa45b000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f92fa23c000)
libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007f92f9fbb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f92f9bca000)
/lib64/ld-linux-x86-64.so.2 (0x00007f92fb032000)
部署实在是很痛苦。我们来静态编译来解决这个问题。
在 package.yaml
的 executables
里加入如下几行:
ghc-options: -Wall -O2 -static -threaded
cc-options: -static
ld-options: -static -pthread
在最外层声明用到的库:
extra-libraries: icuuc icudata icui18n stdc++
stack build
编译,会发现如下警告:
warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
不用管他,我们几乎不可能会遇到缺这种情况。我用 alpine
试了一下,musl
是可以正常运行的,看起来不一定需要 glibc
。
file
一下,编译的二进制果然是静态的:
$ file ~/.local/bin/out-exe
/root/.local/bin/out-exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=60b77954d518087e2421560cbfcef152d71ab337, stripped
当然了,这样一来程序的体积会非常大。不过这也是没有办法的事情。