Rust 交叉编译

6 min read

事情的起因要从一个叫 ffsend 的软件说起。ffsend 是一个用来操作 Firefox Send 的命令行工具,而 Firefox Send 是 Mozilla 推出的一款带有“阅后即焚”功能的文件分享工具;换句话说,用 ffsend 可以很方便的在命令行中分享 2.5GB 以下的任意文件。 我希望能在 Android 手机上通过 Termux 来使用 ffsend,不过 ffsend 的 GitHub Releases 中并没有提供任何 ARM 架构的二进制可执行文件。

GitHub Releases

“源码都给你了,没有 ARM 架构的版本自己编译一个不就有了?”

话是这么说,不过在 Termux 中编译并不见得是个好的方案。首先,你必须要安装一个大概 200M 的 rust,然后你还要下载各种编译时依赖,大概 160M;这一切就仅仅是为了一个 13.3M 大小的小应用。幸好还只是小应用,如果是更大的项目,已手机 CPU 的那点性能可能要编译一天。

交叉编译正好可以解决这一问题。得益于 gcc 等跨平台编译器 (链接器) 和 rust 的发展,我们可以很轻松的通过交叉编译的方式在电脑上编译出能在手机上运行的版本。

前期准备

首先,我们需要下载编译能够在 ARM 平台运行的软件所必须的运行库,也就是 target

在命令行中运行

$ rustup target list
aarch64-apple-ios
aarch64-fuchsia
aarch64-linux-android (installed)
aarch64-unknown-cloudabi
aarch64-unknown-linux-gnu
aarch64-unknown-linux-musl
arm-linux-androideabi
arm-unknown-linux-gnueabi
arm-unknown-linux-gnueabihf
...

这会为我们列出已经安装的和可以被安装运行库。

选择我们所需要的 aarch64-linux-android,使用以下命令安装

rustup target add aarch64-linux-android

然后我们还需要安装合适的链接器来配合 target 工作。对于 rust 这类比较贴近于硬件的语言,Google 为开发者们提供了一套完整的开发包,也就是 Android NDK;不过 Google 并没有提供独立的交叉编译工具链,但是我们可以自己从 Android NDK 中编译一份出来 (参考这篇文档)。

这是我从 Android NDK 中编译出来的一份用于交叉编译的工具链,目前只支持 Linux:GitHub Releases

我手机的架构是 aarch64 (arm64),所以我应该下载的工具链是 android-toolchain-linux-x86_64-arm64.tgz

用 Chrome 浏览器进行下载会报错,推荐在命令行中用 wgetcurl 等工具下载

$ wget https://github.com/Mogeko/android-toolchain/releases/download/4.9/android-toolchain-linux-x86_64-arm64.tgz

下载好后将其解压,然后将 android-toolchain/<arch>/bin 放入 $PATH 中。

至此,前期的准备工作就完成了。

实验交叉编译

我们需要先确保我们安装的 rust 运行库和编译工具链能够正常工作,顺便也可以讲解以下使用 rust 交叉编译的具体流程。

我们先用 cargo,新建一个最简单的项目

cargo new hello

这个项目的作用就是输出 Hello, World (使用 cargo 新建二进制项目时的示例代码)

然后,进入目录 hello/src,也就是项目源码的目录。使用 rustc 进行编译

rustc -C linker="aarch64-linux-android-gcc" --target="aarch64-linux-android" main.rs

我们可以看到一个被编译好的二进制文件 main,将其拷贝到手机上,便可以用 Termux 运行了。

这里的命令应该很好理解,相当于告诉 rustcaarch64-linux-android-gcc 作为链接器,用 aarch64-linux-android 作为运行库来编译 main.rs,这样编译出来的程序自然是能在 aarch64 (arm64) 架构下运行了。

不过一般人都不会直接用 rustc 来编译程序,而是用 cargo 来管理 rust 项目。我们也可以使用 cargo 来进行交叉编译,只需要在项目的根目录中新建文件 .cargo/config,填写上如下的内容

[target.aarch64-linux-android]
linker = "aarch64-linux-android-gcc"
ar = "aarch64-linux-android-ar"

然后使用以下命令进行编译

cargo build --target="aarch64-linux-android"

编译好的二进制文件在 hello/target/aarch64-linux-android/debug 里。

将其拷贝到 Android 手机上用 Termux 运行

Run with Termux

我们可以在 .cargo/config 中添加多条不同 target 的配置,然后在编译时通过 --target= 参数传递给 cargo 来实现灵活的跨平台编译。

例如,这条配置可以编译能在 Windows 中运行的程序 (工具链需要单独下载)

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-ar"
cargo build --target="x86_64-pc-windows-gnu"

编译 ffsend

首先去 ffsend 的 GitHub Release 下载最新版 ffsend 的源码,并解压

GitHub Releases

wget https://github.com/timvisee/ffsend/archive/v0.2.44.tar.gz
tar xvzf v0.2.44.tar.gz

进入文件夹 ffsend-0.2.44,新建文件 .cargo/config,配置 target 和工具链。

[target.aarch64-linux-android]
linker = "aarch64-linux-android-gcc"
ar = "aarch64-linux-android-ar"

安装编译所需要的依赖

sudo apt install build-essential cmake pkg-config libssl-dev

使用 cargo 进行交叉编译

cargo build --target="aarch64-linux-android" --release

将编译好的文件 (ffsend-0.2.44/target/aarch64-linux-android/) 拷贝到 Android 手机上运行即可

Run with Termux