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-msvcWindows 32-bit
- name: win-i686
  runner: windows-latest
  target: i686-pc-windows-msvcLinux 64-bit
- name: linux-amd64
  runner: ubuntu-latest
  target: x86_64-unknown-linux-gnuLinux 32-bit
- name: linux-i686
  runner: ubuntu-latest
  target: i686-unknown-linux-gnuLinux ARM 64-bit
- name: linux-arm64
  runner: ubuntu-latest
  target: aarch64-unknown-linux-gnuMacOS 64-bit
- name: macos-amd64
  runner: macos-latest
  target: x86_64-apple-darwinMacOS ARM 64-bit
- name: macos-arm64
  runner: macos-latest
  target: aarch64-apple-darwinCompiling 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