cross 是由 Rust Tools Team 维护的交叉编译工具,主要的卖点是 zero setup cross compilation
。使用起来也特别简单,cross
内部直接调用了 cargo
,所以我们可以直接使用那些在 cargo
上能用的参数:
比如说构建
cross build --target aarch64-unknown-linux-gnu
或者测试:
cross test --target mips64-unknown-linux-gnuabi64
但是我们都知道,复杂度只能被转移而不能被消灭。Databend 项目最近就遇到了 cross 在构建 aarch64-unknown-linux-gnu
target 时找不到依赖库的问题。
TL;DR
相信发行版的维护者,正确安装 arm64 的依赖:
FROM rustembedded/cross:aarch64-unknown-linux-gnu
RUN dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install --assume-yes libssl-dev libssl-dev:arm64 zlib1g-dev zlib1g-dev:arm64
然后配置 Cross.toml
使得 cross
使用我们自己构建的镜像运行即可:
[target.aarch64-unknown-linux-gnu]
image = "<your-org>/build-tool:aarch64-unknown-linux-gnu"
现象
使用 cross v0.2.1
构建 aarch64-unknown-linux-gnu
会如下报错(此时使用的是旧版的 0.1.16
对应的镜像):
= note: /usr/bin/ld: cannot find -lz
error: build failed
在升级 cross 使用的 image 之后,则会出现 OpenSSL 相关的报错:
running: "aarch64-linux-gnu-gcc" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-I" "/usr/include" "-Wall" "-Wextra" "-E" "build/expando.c"
cargo:warning=build/expando.c:2:33: fatal error: openssl/opensslconf.h: No such file or directory
cargo:warning=compilation terminated.
exit status: 1
--- stderr
thread 'main' panicked at '
Header expansion error:
Error { kind: ToolExecError, message: "Command \"aarch64-linux-gnu-gcc\" \"-O0\" \"-ffunction-sections\" \"-fdata-sections\" \"-fPIC\" \"-g\" \"-fno-omit-frame-pointer\" \"-I\" \"/usr/include\" \"-Wall\" \"-Wextra\" \"-E\" \"build/expando.c\" with args \"aarch64-linux-gnu-gcc\" did not execute successfully (status code exit status: 1)." }
Failed to find OpenSSL development headers.
You can try fixing this setting the `OPENSSL_DIR` environment variable
pointing to your OpenSSL installation or installing OpenSSL headers package
specific to your distribution:
# On Ubuntu
sudo apt-get install libssl-dev
# On Arch Linux
sudo pacman -S openssl
# On Fedora
sudo dnf install openssl-devel
See rust-openssl README for more information:
https://github.com/sfackler/rust-openssl#linux
', /cargo/registry/src/github.com-1ecc6299db9ec823/openssl-sys-0.9.71/build/main.rs:162:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: build failed
原因
出现这种诡异情况的原因是 cross 本身的逻辑跟其使用的 image 是高度耦合的,而 databend 的 workflow 中使用 cargo install --version 0.1.16 cross
将版本锁定在了最后一个可用的旧版本上,在升级 cross 之后就 break 了。一个简单的方案是将 cross
rollback 回去,但是这并没有解决根本问题:我们需要搞清楚 cross
升级之后破坏了什么,然后想出一个彻底的解决方案。
通过搜索不难知道 PR Remove OpenSSL #332 去除了 cross 对 OpenSSL 的支持,主要的讨论发生在 Issue Remove OpenSSL #229 :维护者认为对 cross 来说,OpenSSL 的支持已经超出了这个工具目标,而提供这项支持消耗了社区大量的精力来维护 OpenSSL 相关的脚本,但还是引起了非常多的问题。根据 PR 中删除的内容我们会发现 cross 确实被迫做了很多工作:他们被迫维护了自己编译 openssl 的脚本,为每个不同的架构修改 OPENSSL_DIR
等环境变量。在删除 OpenSSL 之后,cross
的维护变得容易了很多。
但是我们确实需要 OpenSSL,在不修改项目依赖的前提下,社区被迫使用将 PR #322 中删除的内容自行添加回来的方案:
FROM rustembedded/cross:aarch64-unknown-linux-musl-0.2.1
COPY openssl.sh /
RUN bash /openssl.sh linux-aarch64 aarch64-linux-musl-
ENV OPENSSL_DIR=/openssl \
OPENSSL_INCLUDE_DIR=/openssl/include \
OPENSSL_LIB_DIR=/openssl/lib \
看起来越来越奇怪了:在各个发行版交叉编译已经非常成熟的现在,我们还需要自己编译 OpenSSL 吗?
当然不需要。我们找不到这些库,是因为我们根本就没有安装他们。
根据 Ubuntu 的文档 MultiarchSpec,如果要安装 arm64 版本的依赖库,我们需要这样做:
dpkg --add-architecture arm64
apt-get install --assume-yes libssl-dev:arm64
查看一下这个包中的文件列表:
/usr/include/aarch64-linux-gnu/openssl/opensslconf.h
...
/usr/lib/aarch64-linux-gnu/libcrypto.a
/usr/lib/aarch64-linux-gnu/libcrypto.so
/usr/lib/aarch64-linux-gnu/libssl.a
/usr/lib/aarch64-linux-gnu/libssl.so
/usr/lib/aarch64-linux-gnu/pkgconfig/libcrypto.pc
/usr/lib/aarch64-linux-gnu/pkgconfig/libssl.pc
/usr/lib/aarch64-linux-gnu/pkgconfig/openssl.pc
我们会发现 Ubuntu 的维护者们已经帮我们把这些工作都做好了,不需要自行编译 OpenSSL,不需要修改 PKG_CONFIG_PATH,也不需要自己修改 OPENSSL_DIR。安装好自己需要的包,it just works!
解决方案
在找到根本原因之后,我们的解决方案就非常简单了:build: Install arm64 version of libssl and zlib1g
FROM rustembedded/cross:aarch64-unknown-linux-gnu
RUN dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install --assume-yes libssl-dev libssl-dev:arm64 zlib1g-dev zlib1g-dev:arm64
相信上游的维护者,安装好自己需要的包即可。
尾声
最后我将这个解决方案反馈给了上游,并且跟他们讨论将 OpenSSL 支持添加回来的方案,希望可以解决 cross
的用户遇到的类似问题。
总结
这个问题启发了我几个思考:
信息鸿沟
Ubuntu 维护者一秒钟能解决的问题,一群资深的 Rust 开发者在五年内陆陆续续的持续修复都没有解决。
路径依赖
cross 在 initial commit 中引入了 openssl.sh
作为 OpenSSL 支持的解决方案,从那之后所有的开发者都在这个路径上修修补补,没有彻底地解决这个问题。
不太清楚当初
cross
项目为什么坚持使用自行编译 openssl 的方案,了解详情的话欢迎在评论指出来~
上游维护者
感谢 Ubuntu 的维护者。