tl;dr: you can install cross-compiler toolchains to compile C/C++ for Windows or Linux from macOS with these two Homebrew Formulas.
brew install FiloSottile/musl-cross/musl-cross
brew install mingw-w64
Cross-compiling C and C++ is dreadful.
While in Go you just need to set an environment variable, for C you need a whole separate toolchain, that might require an intermediate toolchain to build, and you need to know what you are targeting very well.
musl-cross-make
Thankfully, Rich Felker built a Makefile set to build musl-based cross-compilers, musl-cross-make. It took a few patches, but it runs well on macOS.
musl-cross-make builds beautifully self-contained cross-compilers, so you don't have to worry about pointing to the right libraries path or about where you keep the toolchain. Also, it can target Linux running on a number of different architectures.
Maybe most importantly, it's based on the musl C standard library. This means that the binaries will only run on a musl-based system, like Alpine. However, if you build them as static binaries by passing -static
as a LDFLAG they will run anywhere, including in scratch Docker containers. musl is specifically engineered to support fully static binaries, which is not recommended with glibc.
homebrew-musl-cross
Still, I'm a big Homebrew fan. It lets you build software in a well defined sandbox, and only the binaries are linked into your PATH, GNU Stow style. Also, it manages resources and offers powerful dev tools.
So, I wrapped up musl-cross-make in a Homebrew Formula, FiloSottile/homebrew-musl-cross. It takes a long time to build, but it generates a full cross-compiler toolchain, and links into /usr/local/bin
just the prefixed binaries, like x86_64-linux-musl-gcc
.
brew install FiloSottile/musl-cross/musl-cross
It comes with a precompiled Homebrew Bottle for High Sierra, so if you want to build everything from source use brew install --build-from-source
.
Other architectures are supported. For example to get a Raspberry Pi cross-compiler use:
brew install FiloSottile/musl-cross/musl-cross --without-x86_64 --with-arm-hf
You can also use --with-i486
(x86 32-bit), --with-aarch64
(ARM 64-bit), --with-arm
(ARM soft-float) and --with-mips
.
Using this with Go and Rust
To cross-compile cgo projects you can set the CC and CXX environment flags when building to x86_64-linux-musl-gcc
and x86_64-linux-musl-g++
(or corresponding), on top of the usual GOOS
and GOARCH
.
To use this toolchain as the target linker for Rust cross-compilation, add lines like these to your .cargo/config
:
[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"
A more complete guide to Rust cross-compilation is here.
mingw-w64
For Windows, there is now a Mingw-w64 Formula directly in homebrew-core, so you can install it simply with brew install mingw-w64
.
The resulting GCC toolchain has prefixes x86_64-w64-mingw32-
and i686-w64-mingw32-
.
If you find cross-compilation more fun than it probably is, you might want to follow me on Twitter.