Cross-compiling using qemu-static

qemu-static allows you to run containers built for other architectures (of course, with a performance penalty). BUT this means you can cross-compile to arm/amd64 quite easily.

Here’s an example:

$ docker run --rm --privileged \
        multiarch/qemu-user-static \
        --reset -p yes

$  uname -a
Linux mindy 6.9.3-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 31 May 2024 15:14:45 +0000 x86_64 GNU/Linux

$ cat Dockerfile
FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine AS base

RUN apk add crystal

$ docker build . --platform=aarch64 -t crystal-arm

$ docker run --platform=aarch64 -ti -v .:/src -w /src crystal-arm crystal build hello.cr -o hello-arm

$ file hello-arm
hello-arm: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, BuildID[sha1]=c10324de8eb05c5ae2729170ee8d0c53583fd7a3, with debug_info, not stripped

You can even create a couple of shell aliases for crystal and shards running in arm and it just works:

$ alias crystal-arm="docker run --platform=aarch64 -ti -v .:/src -w /src crystal-arm crystal"
$ crystal-arm build hello.cr -o hello-arm

This looks (to me) much simpler than the other solutions being discussed. I can expand it to a proper tutorial if there is interest.

2 Likes

I went and wrote it down in some more detail here:

1 Like

Thank you for sharing! This was actually the approach I took for a few years and as you mentioned, performance can be a big issue. As you build bigger and bigger applications, emulating the entire Crystal compiling, linking and everything that Crystal is shelling out to do it’s work can be very expensive.

To be fair, you’re not cross-compiling, but you’re cross-running the compiler :sweat_smile:

As I started to work more and more with aarch64 servers, the build times started to become painful. I tweet about that back in 2022 when decided to build a multi-arch container image for my Crystal development workflow.

That, combined with my need for targeting macOS builds is when I concluded that binfmt/qemu might not solve my problems.

This is a great approach when you don’t want to have the complexity of a cross-compiler/linker and have to deal with other platforms other than Linux.

Thank you again for sharing!
:heart: :heart: :heart:

1 Like

Oh, this is pretty nice!

To improve performance, you can cross-compile the object file on the host, then link it in the qemu VM. That way you don’t have to deal with a cross compiler toolchain on your host or compiling every single library.

2 Likes

Ohhhh that’s a good idea!