Rust Cross-Compilation With GitHubย Actions
Building your Rust project for different target operating-systems and architectures is rather easy with GitHub actions. The process goes like this:
- Clone your repository
- Install the Rust toolchain
- Set up the Rust cache (optional)
- Build application binaries
- Upload binaries to an accessible location
Basic build workflow
Let's say we want to build for the following platforms:
- Windows (64-bit)
- Linux (64-bit)
- Mac (ARM & 64-bit)
The following workflow will do exactly that.
name: Build
on: [push]
env:
# The project name specified in your Cargo.toml
PROJECT_NAME: <insert-name>
jobs:
build:
# Set the job to run on the platform specified by the matrix below
runs-on: ${{ matrix.runner }}
# Define the build matrix for cross-compilation
strategy:
matrix:
include:
- name: linux-amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
- name: win-amd64
runner: windows-latest
target: x86_64-pc-windows-msvc
- name: macos-amd64
runner: macos-latest
target: x86_64-apple-darwin
- name: macos-arm64
runner: macos-latest
target: aarch64-apple-darwin
# The steps to run for each matrix item
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: "${{ matrix.target }}"
- name: Setup Cache
uses: Swatinem/rust-cache@v2
- name: Build Binary
run: cargo build --verbose --locked --release --target ${{ matrix.target }}
- name: Release Binary
shell: bash
run: |
BIN_SUFFIX=""
if [[ "${{ matrix.runner }}" == "windows-latest" ]]; then
BIN_SUFFIX=".exe"
fi
# The built binary output location
BIN_OUTPUT="target/${{ matrix.target }}/release/${PROJECT_NAME}${BIN_SUFFIX}"
# Define a better name for the final binary
BIN_RELEASE="${PROJECT_NAME}-${{ matrix.name }}${BIN_SUFFIX}"
BIN_RELEASE_VERSIONED="${PROJECT_NAME}-${{ github.ref_name }}-${{ matrix.name }}${BIN_SUFFIX}"
# Move the built binary where you want it
mv "${BIN_OUTPUT}" "./<your-destination>/${BIN_RELEASE}"
Be sure to replace the following variables with your own values:
<insert-name>
- The name of your project as specified in yourCargo.toml
<your-destination>
- The destination directory where you want to store the built binaries
Compiling for other architectures
Here is a non-exhaustive list of configs you can include in your workflow matrix.include
section to compile your Rust project for different operating systems and architectures.
Windows 64-bit
- name: win-amd64
runner: windows-latest
target: x86_64-pc-windows-msvc
Windows 32-bit
- name: win-i686
runner: windows-latest
target: i686-pc-windows-msvc
Linux 64-bit
- name: linux-amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
Linux 32-bit
- name: linux-i686
runner: ubuntu-latest
target: i686-unknown-linux-gnu
Linux ARM 64-bit
- name: linux-arm64
runner: ubuntu-latest
target: aarch64-unknown-linux-gnu
MacOS 64-bit
- name: macos-amd64
runner: macos-latest
target: x86_64-apple-darwin
MacOS ARM 64-bit
- name: macos-arm64
runner: macos-latest
target: aarch64-apple-darwin
Compiling With Cross
The cross project is a great tool for cross-compiling Rust projects without needing to worry about the toolchain or other potential dependencies.
Personally I feel it's a better alternative than using the native cargo build
command if you are building for more exotic targets such as Android or you don't have access to the target platform directly.
You can easily mix and match the use of cross
together with cargo
in your GitHub Actions workflow. Here's a simple example to demonstrate building for Linux 64-bit and Android ARM 64-bit.
name: Build
on: [push]
jobs:
build:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
# Linux config
- name: linux-amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
command: cargo
# Android config
- name: android-arm
runner: ubuntu-latest
target: aarch64-linux-android
command: cross
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
# Only install cross if we need it
# Install via cargo-binstall which I found faster
- name: Install Cross
if: matrix.command == 'cross'
shell: bash
run: |
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo binstall --no-confirm cross
- name: Build Binary
run: ${{ matrix.command }} build --verbose --locked --release --target ${{ matrix.target }}
Be sure to read the documentation for cross to understand how to configure it. You may need to customize the build images cross uses with additional dependencies required by your project.
For example if you need OpenSSL
to compile your project, you will need to add the following config to your Cargo.toml
.
[target.<architecture>]
pre-build = [
"dpkg --add-architecture $CROSS_DEB_ARCH",
"apt-get update && apt-get --assume-yes install libssl-dev:$CROSS_DEB_ARCH"
]
Make sure you replace <architecture>
with your target architecture.
Viewing possible build targets
You can find a list of possible build targets for Rust here. Alternatively you can use the command rustup target list
.
Useful Resources
Here are a bunch of useful resources I explored which you might find helpful.
- cardinal.rs - Example GitHub release workflow
- cross - Zero setup cross compilation for Rust
- rust-android-gradle - Mozilla GitHub project
- Packaging and distributing a rust tool - Rust CLI book
- Cross-compilation - rustup Book
- Cross-compilation in Rust - Kerkour's Blog
Comments