Reemus Icon

Rust Cross-Compilation With GitHub Actions

👍🔥❤️😂😢😕
views
comments

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}"
yaml-icon

Be sure to replace the following variables with your own values:

  • <insert-name> - The name of your project as specified in your Cargo.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
yaml-icon

Windows 32-bit

- name: win-i686
  runner: windows-latest
  target: i686-pc-windows-msvc
yaml-icon

Linux 64-bit

- name: linux-amd64
  runner: ubuntu-latest
  target: x86_64-unknown-linux-gnu
yaml-icon

Linux 32-bit

- name: linux-i686
  runner: ubuntu-latest
  target: i686-unknown-linux-gnu
yaml-icon

Linux ARM 64-bit

- name: linux-arm64
  runner: ubuntu-latest
  target: aarch64-unknown-linux-gnu
yaml-icon

MacOS 64-bit

- name: macos-amd64
  runner: macos-latest
  target: x86_64-apple-darwin
yaml-icon

MacOS ARM 64-bit

- name: macos-arm64
  runner: macos-latest
  target: aarch64-apple-darwin
yaml-icon

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 }}
yaml-icon

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"
]
toml-icon

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.

👍🔥❤️😂😢😕

Comments

...

Your name will be displayed publicly

Loading comments...