Cross Compile a Go Project With CGO Dependencies Using Zig

Cross Compile a Go Project With CGO Dependencies Using Zig

March 19, 2022

Cross compiling with Go is usually straighforward: set GOOS and GOARCH environement variables and then go build. Unfortunately for projects that uses CGO dependencies things can be harder. Depending on the target architecture it requires to install a C compiler like gcc, clang or x86_64-w64-mingw64-gcc and configure additional environment variables like CC along with the CGO_ENABLED=1 one.

In this post we’ll see how to cross-compile the Paw project for linux, macOS and Windows using Zig without the need to install additional complilers.

Paw is a cross platform application to manage passwords and identities securely, it is written in Go and uses Fyne toolkit for the GUI and golang.design/x/clipboard library for the CLI that have CGO dependencies.

Zig is a general-purpose programming language and toolchain that provides a zero-dependency, drop-in C/C++ compiler that supports cross-compilation out-of-the-box.

Requirements

  • debian/ubuntu based distro
  • go v1.18.0
  • zig v0.9.1

Download the source code

  git clone https://github.com/lucor/paw.git

Compile for target linux amd64

# install the GUI and CLI dependencies
apt-get update
apt-get install -y -q --no-install-recommends \
    libgl-dev \
    libx11-dev \
    libxrandr-dev \
    libxxf86vm-dev \
    libxi-dev \
    libxcursor-dev \
    libxinerama-dev

# create dist dir
mkdir -p dist/linux-amd64

# build
CGO_ENABLED=1 \
GOOS=linux \
GOARCH=amd64 \
CC="zig cc -target x86_64-linux-gnu -isystem /usr/include -L/usr/lib/x86_64-linux-gnu" \
CXX="zig c++ -target x86_64-linux-gnu -isystem /usr/include -L/usr/lib/x86_64-linux-gnu" \
go build -trimpath -o dist/linux-amd64 ./cmd/...

Compile for target linux arm64

# install the GUI and CLI dependencies
dpkg --add-architecture arm64
apt-get update
apt-get install -y -q --no-install-recommends \
    libgl-dev:arm64 \
    libx11-dev:arm64 \
    libxrandr-dev:arm64 \
    libxxf86vm-dev:arm64 \
    libxi-dev:arm64 \
    libxcursor-dev:arm64 \
    libxinerama-dev:arm64

# create dist dir
mkdir -p dist/linux-arm64

# build
CGO_ENABLED=1 \
GOOS=linux \
GOARCH=arm64 \
PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig \
CC="zig cc -target aarch64-linux-gnu -isystem /usr/include -L/usr/lib/aarch64-linux-gnu" \
CXX="zig c++ -target aarch64-linux-gnu -isystem /usr/include -L/usr/lib/aarch64-linux-gnu" \
go build -trimpath -o dist/linux-arm64 ./cmd/...

Compile for target windows amd64

# create dist dir
mkdir -p dist/windows-amd64

# build
CGO_ENABLED=1 \
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 ./cmd/...

Compile for target macOS amd64

export MACOS_MIN_VER=10.14
export MACOS_SDK_PATH="/path/to/macOS/sdk"

# create dist dir
mkdir -p dist/darwin-amd64

# build
CGO_ENABLED=1 \
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 -trimpath -buildmode=pie -o dist/darwin-amd64  ./cmd/...

Compile for target macOS arm64

export MACOS_MIN_VER=11.1
export MACOS_SDK_PATH="/path/to/macOS/sdk"

# create dist dir
mkdir -p dist/darwin-arm64

# build
CGO_ENABLED=1 \
GOOS=darwin \
GOARCH=arm64 \
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 aarch64-macos-gnu -isysroot ${MACOS_SDK_PATH} -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks" \
CXX="zig c++ -mmacosx-version-min=${MACOS_MIN_VER} -target aarch64-macos-gnu -isysroot ${MACOS_SDK_PATH} -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks" \
go build -ldflags "-s -w" -buildmode=pie -trimpath -o dist/darwin-arm64 ./cmd/...

References: