$ cd ..

Cross-compiling Rust on GitHub Actions

📅 2022-06-28

870 days ago

Lately I’ve been playing around with Oracle’s free-tier of Ampere VM.Standard.A1.Flex machines. Migrating away from x86 Python Docker images was as easy as adding:

platforms: linux/amd64,linux/arm64

to the GitHub Action along with QEMU. Rust, however, needs a bit more work to get running.

What didn’t work

For this project, I attempted to build #f1’s HamVerBot to run on the aarch64 VM.

Building through cargo by adding a target for aarch64-unknown-linux-gnu:

cargo build --release --target aarch64-unknown-linux-gnu

Which disappointingly fails due to:

error occurred: Failed to find tool. Is `aarch64-linux-gnu-gcc` installed?
warning: build failed, waiting for other jobs to finish...
error: failed to run custom build command for `openssl-sys v0.9.72`

Presumably because I ran this command on my local machine, which runs root:xnu-8020.121.3~4/RELEASE_ARM64_T6000 arm64.

Digging a bit more on StackOverflow prompted me to try using cross which claims to be a “Zero setup” cross compilation and “cross testing” of Rust crates. Sounds great!

However, running the following command:

cross build --target aarch64-unknown-linux-gnu

freezes up after a while.

Docker Desktop Screenshot
Frozen + 400% CPU Usage

amd64? Inspecting the Dockerfile shows:

ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER

Hm. Seems like cross runs a amd64 container which then attempts to compile to an aarch64 target. Ouch.

After this point I gave up on trying to build locally and instead worked on getting my CI (GHA) to build my binaries.

What worked

In the GitHub Actions workflow file, it was a simple matter of adding:

- name: Build arm64
  uses: actions-rs/cargo@v1
  with:
    use-cross: true
    command: build
    args: --all --release --target=aarch64-unknown-linux-musl

with the use-cross: true argument being key. I had two of these command blocks since I still did want an x86 binary on the repository.

Sadly,

run pkg_config fail: `"pkg-config" "--libs" "--cflags" "openssl"` did not exit successfully: exit status: 1
error: could not find system library 'openssl' required by the 'openssl-sys' crate

--- stderr
Package openssl was not found in the pkg-config search path.
Perhaps you should add the directory containing `openssl.pc'
to the PKG_CONFIG_PATH environment variable
No package 'openssl' found"

The x86 binary worked as expected, but when building for aarch64 the runner could not find a valid OpenSSL installation:

$HOST = x86_64-unknown-linux-gnu
$TARGET = aarch64-unknown-linux-musl
openssl-sys = 0.9.72

And since ARM support is not on the GitHub Actions Roadmap, I was onto scouring the OpenSSL crate documentation.

The vendored flag stated:

If the vendored Cargo feature is enabled, the openssl-src crate will be used to compile and statically link to a copy of OpenSSL. The build process requires a C compiler, perl (and perl-core), and make. The OpenSSL version will generally track the newest OpenSSL release, and changes to the version are not considered breaking changes.

Perfect! So all we needed to do was add the following to the Config.toml:

[dependencies]
openssl = { version = "0.10", features = ["vendored"] }

Which ended up working exactly how I wanted. 🎉

The entire workflow file is available here: build.yml. My only complaint being that the CI takes north of ~10 minutes to build both the binaries.

GitHub Actions CI time
~11 minutes for 2 binaries +_+

Guess I know what I’m working on next 🙃