I’ve been working on a GUI app that uses the Fyne1 framework and Go. An interesting thing I thought I’ll tackle was to have a keyboard shortcut registered system-wide that would pop the app whenever you pressed it. Furthermore, it should work on Linux, Windows and Mac.

The main options that I found for cross-compiling:

  • use go build - if you don’t depend on external C libraries
  • use fyne-cross
  • use zig

Note: When compiling for darwin, you will need the MacOS SDK on your VM.

You can get the MacOS SDK:

# programatically
ENV OSX_SDK="MacOSX11.3.sdk"
ENV OSX_SDK_URL="https://github.com/joseluisq/macosx-sdks/releases/download/11.3/${OSX_SDK}.tar.xz"

RUN curl -sSL "$OSX_SDK_URL" -o "/$OSX_SDK.tar.xz"
RUN mkdir /osxsdk && tar -xf "/$OSX_SDK.tar.xz" -C "/osxsdk"

From these options, the problem that I usually encountered after building the binaries was related to the hotkeys. They were not registered correctly. In the go files, I used the go:build tag in order to target different systems. The configuration was not properly taken into account when using fyne-cross. Therefore, I had to resort to another option.

Zig worked brilliantly2. You could easily put a Zig application into a Dockerfile and directly use FROM scratch. Really portable that way. That’s because it provides a zero-dependency, drop-in C/C++ compiler that supports cross-compilation out-of-the-box.

# darwin
    GOOS=darwin \
    GOARCH=amd64 \
    CGO_LDFLAGS="-mmacosx-version-min=${MACOS_MIN_VER} --sysroot ${MACOS_SDK_PATH} -F/System/Library/Frameworks -L/usr/lib" \
    CC="zig cc -mmacosx-version-min=${MACOS_MIN_VER} -target x86_64-macos-gnu -isysroot ${MACOS_SDK_PATH} -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks" \
    CXX="zig c++ -mmacosx-version-min=${MACOS_MIN_VER} -target x86_64-macos-gnu -isysroot ${MACOS_SDK_PATH} -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks" \
    go build -ldflags="-w -buildmode=pie" -trimpath -o dist/darwin-amd64/macaw-darwin-amd64 ./cmd/macaw/main.go
# windows
    GOOS=windows \
    GOARCH=amd64 \
    CC="zig cc -target x86_64-windows-gnu" \
    CXX="zig c++ -target x86_64-windows-gnu" \
    go build -trimpath -ldflags='-H=windowsgui' -o dist/windows-amd64/macaw-windows-amd64.exe ./cmd/macaw/main.go
# linux
    GOOS=linux \
    GOARCH=amd64 \
    go build -o dist/linux-amd64/macaw-linux-amd64 ./cmd/macaw/main.go