Rust and Go natively support cross-compilation.
We can combine several snapcraft directives to build snaps for multiple platforms, on the same host.
Plarforms
With the platforms (or architectures) directive, we can tell snapcraft to build a snap for a platform different from the host platform, for example:
platforms:
amd64:
arm64:
build-on: [amd64, arm64]
build-for: [arm64]
This obviously will work only if the build product itself is in fact dedicated to the target (build-for
) platform. With rust, go or scripts, this is very handy.
Build details
With the on
directive, we can tell snapcraft to do different things per platform. This is quite convenient. Without it, we would need to map from arm64
to aarch64-unknown-linux-gnu
etc. in an override-build
script ourselves.
For example:
build-packages:
- on amd64 to arm64:
- gcc-aarch64-linux-gnu
- linux-libc-dev-arm64-cross
- libc6-dev-arm64-cross
build-environment:
- to amd64:
- target: x86_64-unknown-linux-gnu
- on amd64 to arm64:
- CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
- target: aarch64-unknown-linux-gnu
or, for golang:
build-environment:
- to arm64:
- GOOS: linux
- GOARCH: arm64
- CC: /usr/bin/aarch64-linux-gnu-gcc
Override build
Next, we just pass the target to the compiler:
cargo build --release --target=$target
CI
Using a platform matrix, because:
- Packing amd64 and arm64 snaps without
snapcraft clean
in between fills up github runner’s disk space. - The
snapcore/action-publish
action takes only one snap at a time anyway.
jobs:
release:
name: Release
runs-on: ubuntu-24.04
strategy:
matrix:
platform:
- amd64
- arm64
The main thing is to filter the snapcraft build, because that github workflow’s matrix directive already takes care of that:
- name: Build snap
uses: snapcore/action-build@v1
id: build
env:
CRAFT_BUILD_FOR: ${{ matrix.platform }}
If you’re using snapcraft directly, then
snapcraft pack --build-for=${{ matrix.platform }}
Examples
- nushell-snap (rust)
- grafana-agent-snap (go)