Rust Cross Compile Linux to macOS using GitHub Actions

It’s easy enough to add extra targets using the cargo command when building your Rust project. However, the Rust cross compile process gets a little tricker when linking is done for platforms different to the host platform.

I wanted to setup a GitHub Actions workflow that would build binaries for different platforms from the same actions runner.

While it might be possible to use GitHub Actions Matrix to run a build across multiple operating systems and install Rust / rustup / cargo on each, performing the build in each place, I opted for a different strategy.

Using a base Rust musl build container, on top of Debian Buster, I’ve added osxcross and the required build tools. This supports building and linking macOS binaries from the Linux container.

Rust Cross Compile GitHub Action

I’ve built a Docker image for GitHub Actions to use. It is based on the popular rustup image. Currently I’ve built a musl-1.0.53 variant. My version copies in and sets up osxcross. This bakes all the heavy lifting into the image so that GitHub actions can quickly build targets for linux and macOS (x86).

You can find the action on the Rust Cross Compile GitHub Action here.

Usage example

Set up a .cargo/config file to designate the target to linker mapping. For example macOS x86:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[target.x86_64-apple-darwin]
linker = "x86_64-apple-darwin14-clang"
ar = "x86_64-apple-darwin14-ar"
[target.x86_64-apple-darwin] linker = "x86_64-apple-darwin14-clang" ar = "x86_64-apple-darwin14-ar"
[target.x86_64-apple-darwin]
linker = "x86_64-apple-darwin14-clang"
ar = "x86_64-apple-darwin14-ar"

Add a GitHub Actions workflow:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
name: Rust static build macOS and Linux
on:
push:
branches:
- main
jobs:
build:
name: build for all platforms
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
BINARY_NAME: rust-test1
steps:
- uses: actions/checkout@v2
- name: Build-musl macOS x86
uses: Shogan/rust-musl-action@v1.0.2
with:
args: cargo build --target x86_64-apple-darwin --release
- name: Build-musl Linux x86
uses: Shogan/rust-musl-action@v1.0.2
with:
args: cargo build --target x86_64-unknown-linux-musl --release
name: Rust static build macOS and Linux on: push: branches: - main jobs: build: name: build for all platforms runs-on: ubuntu-latest env: CARGO_TERM_COLOR: always BINARY_NAME: rust-test1 steps: - uses: actions/checkout@v2 - name: Build-musl macOS x86 uses: Shogan/rust-musl-action@v1.0.2 with: args: cargo build --target x86_64-apple-darwin --release - name: Build-musl Linux x86 uses: Shogan/rust-musl-action@v1.0.2 with: args: cargo build --target x86_64-unknown-linux-musl --release
name: Rust static build macOS and Linux
on:
  push:
    branches:
      - main
jobs:
  build:
    name: build for all platforms
    runs-on: ubuntu-latest
    env:
      CARGO_TERM_COLOR: always
      BINARY_NAME: rust-test1
    steps:
    - uses: actions/checkout@v2
    - name: Build-musl macOS x86
      uses: Shogan/rust-musl-action@v1.0.2
      with:
        args: cargo build --target x86_64-apple-darwin --release
    - name: Build-musl Linux x86
      uses: Shogan/rust-musl-action@v1.0.2
      with:
        args: cargo build --target x86_64-unknown-linux-musl --release

Release binaries can now easily be built from a single ubuntu linux GitHub actions runner. For example, get the Cargo.toml version and create a release with the built binaries by adding a couple of extra steps:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
steps:
- uses: actions/checkout@v2
- name: Set build version
id: version
shell: bash
run: |
VERSION="$(cat Cargo.toml | grep 'version =' -m 1 | sed 's@version =@@' | xargs)"
echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV
echo "::notice::publish build version $VERSION"
- name: Upload macOS x86 binary to release
uses: Spikatrix/upload-release-action@b713c4b73f0a8ddda515820c124efc6538685492
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: target/x86_64-apple-darwin/release/${{ env.BINARY_NAME }}
asset_name: ${{ env.BINARY_NAME }}-macos-x86
target_commit: ${{ github.sha }}
tag: v${{ env.RELEASE_VERSION }}
release_name: v${{ env.RELEASE_VERSION }}
prerelease: false
overwrite: true
body: ${{ env.BINARY_NAME }} release
steps: - uses: actions/checkout@v2 - name: Set build version id: version shell: bash run: | VERSION="$(cat Cargo.toml | grep 'version =' -m 1 | sed 's@version =@@' | xargs)" echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV echo "::notice::publish build version $VERSION" - name: Upload macOS x86 binary to release uses: Spikatrix/upload-release-action@b713c4b73f0a8ddda515820c124efc6538685492 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: target/x86_64-apple-darwin/release/${{ env.BINARY_NAME }} asset_name: ${{ env.BINARY_NAME }}-macos-x86 target_commit: ${{ github.sha }} tag: v${{ env.RELEASE_VERSION }} release_name: v${{ env.RELEASE_VERSION }} prerelease: false overwrite: true body: ${{ env.BINARY_NAME }} release
steps:
    - uses: actions/checkout@v2
    - name: Set build version
      id: version
      shell: bash
      run: |
        VERSION="$(cat Cargo.toml | grep 'version =' -m 1 | sed 's@version =@@' | xargs)"
        echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV
        echo "::notice::publish build version $VERSION"
    - name: Upload macOS x86 binary to release
      uses: Spikatrix/upload-release-action@b713c4b73f0a8ddda515820c124efc6538685492
      with:
        repo_token: ${{ secrets.GITHUB_TOKEN }}
        file: target/x86_64-apple-darwin/release/${{ env.BINARY_NAME }}
        asset_name: ${{ env.BINARY_NAME }}-macos-x86
        target_commit: ${{ github.sha }}
        tag: v${{ env.RELEASE_VERSION }}
        release_name: v${{ env.RELEASE_VERSION }}
        prerelease: false
        overwrite: true
        body: ${{ env.BINARY_NAME }} release

There are many ways to achieve an automated CI process that can do Rust cross compile and linking. It was an interesting investigation into custom Docker containers for GitHub actions and the Rust tool chain setting up this GitHub Action package.

Feel free to contribute or improve the GitHub Action by sending a pull request on GitHub.

Leave a Comment