Cross-compiling Rust Code using GitLab CI
I've been excited to learn the Rust language for two years now since it compiles the performance properties of statically-compiled languages with modern language ergonomics (and allows you to parallelize things you normally wouldn't dare to thanks to its safety model). Another nice, less highlighted aspect of Rust is that it supports compiling to many different platforms including Windows, Linux and MacOS but also to the Web via WebAssembly - and cross-compilation is very straightforward, too.
Since I've recently been playing a lot with GitLab and especially it's wonderful built-in CI/CD platform based on Docker, I wanted to use Gitlab CI/CD to automatically compile a Rust command line application from the same code base for Windows an Linux every time that the pipeline is triggered.
TL;DR: Check out the CI workflow in spai and the statically compiled binaries for Windows and Linux that it produces.
A setup for cross-compiling to Windows
First, we need a way to cross-compile our rust code to Windows.
rustup target list shows the target
x86_64-pc-windows-gnu which uses MinGW to compile. When installing the target, however, the MinGW compiler toolchain is not installed. We could get it from the distribution's package manager, or we could take advantage of docker to make the compiler setup more portable!
The rust-musl-builder project is a Docker image that can be used to quickly set up all dependencies for statically compiling Linux Rust binaries using the musl libc. It simply starts with a Ubuntu base image and adds the Rust toolchain plus the tools and libraries needed to compile to the
Additions for compiling with MinGW
From such an ubuntu base system, it is easy to additionally install MinGW and add the additional
$ sudo apt-get update && sudo apt-get install -y gcc-mingw-w64 $ rustup target add x86_64-pc-windows-gnu
Additional configurations are necessary to correctly configure
cargo to find the right linker and
[target.x86_64-pc-windows-gnu] linker = "/usr/bin/x86_64-w64-mingw32-gcc" ar = "/usr/x86_64-w64-mingw32/bin/ar" [target.i686-pc-windows-gnu] linker = "/usr/bin/i686-w64-mingw32-gcc" ar = "/usr/i686-w64-mingw32/bin/ar"
I've packaged the changes in the above section into the rust-musl-builder-mingw project and associated docker image. Thanks to this, you can simply use an alias to cross-compile to windows from any (Linux) machine running Docker:
alias rust-musl-mingw-builder='docker run --rm -it -v "$(pwd)":/home/rust/src sseemayer/rust-musl-builder-mingw'
We can now use the above docker image to build a Gitlab CI/CD pipeline that will rebuild Windows and Linux binaries everytime the pipeline is triggered:
image: "sseemayer/rust-musl-builder-mingw:latest" stages: - test - build test: stage: test script: - rustc --version && cargo --version - cargo test --all --verbose linux-musl: stage: build artifacts: paths: - target/x86_64-unknown-linux-musl/release/spai script: - cargo build --release --target x86_64-unknown-linux-musl windows-mingw: stage: build artifacts: paths: - target/x86_64-pc-windows-gnu/release/spai.exe script: - cargo build --release --target x86_64-pc-windows-gnu
artifacts property defines which files to keep as the output of the build operation. These files can then be reached directly from a link:
In our example,
<ref> could be
<path> could be
<job_name> could be
The Final Result
This is the main idea behind how spai, my cross-platform tool for monitoring a folder for changes and posting them to an HTTP URL, is compiled. You can follow the link to see all of the sourcecode and give it a try.