xx-go: zig support

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2025-09-12 15:03:27 +02:00
parent f074957406
commit 9fb3908307
4 changed files with 258 additions and 15 deletions

View File

@@ -342,6 +342,28 @@ RUN go build -o hello hello.go && \
xx-verify hello
```
`xx-go` can also drive [Zig](https://ziglang.org/) as the underlying C
toolchain for CGo builds. This can be useful when you want a single
cross-compiling toolchain that works across multiple platforms, or when you
already rely on Zig in your build pipeline.
Zig support is **opt-in** and is enabled by setting the `XX_GO_PREFER_C_COMPILER`
environment variable to `zig`. When this variable is set and the `zig` binary
is available in `PATH`, `xx-go` will configure CGo to use Zig as the C compiler
for the current target.
```dockerfile
FROM --platform=$BUILDPLATFORM golang:alpine
RUN apk add zig
# ...
ARG TARGETPLATFORM
RUN xx-apk add musl-dev
ENV CGO_ENABLED=1
ENV XX_GO_PREFER_C_COMPILER=zig
RUN xx-go build -o hello ./hello.go && \
xx-verify hello
```
## Rust
Building Rust can be achieved with the `xx-cargo` wrapper that automatically

View File

@@ -511,6 +511,96 @@ testHelloCGO() {
assert_output --partial "PKG_CONFIG=$(xx-info triple)-pkg-config"
}
testHelloCGOZig() {
if ! supportZig; then
skip "Zig not supported"
fi
export CGO_ENABLED=1
export XX_GO_PREFER_C_COMPILER=zig
add zig
run xx-go build -x -o /tmp/a.out ./fixtures/hello_cgo.go
assert_success
run xx-verify /tmp/a.out
assert_success
if ! xx-info is-cross; then
run /tmp/a.out
assert_success
assert_output "hello cgo"
fi
}
@test "native-hellocgo-zig" {
unset TARGETARCH
testHelloCGOZig
}
@test "amd64-hellocgo-zig" {
export TARGETARCH=amd64
testHelloCGOZig
}
@test "arm64-hellocgo-zig" {
export TARGETARCH=arm64
testHelloCGOZig
}
@test "arm-hellocgo-zig" {
export TARGETARCH=arm
testHelloCGOZig
}
@test "ppc64le-hellocgo-zig" {
export TARGETARCH=ppc64le
testHelloCGOZig
}
@test "riscv64-hellocgo-zig" {
if ! supportRiscVCGo; then
skip "RISC-V CGO not supported"
fi
export TARGETARCH=riscv64
testHelloCGOZig
}
@test "loong64-hellocgo-zig" {
if ! supportLoong64CGo; then
skip "LOONGARCH64 not supported"
fi
if [ -f /etc/alpine-release ]; then
# FIXME: loong64-hellocgo issue on alpine < 3.21
# ld.lld: error: unknown emulation: elf64loongarch
# ld.lld: error: /loongarch64-alpine-linux-musl/usr/lib/gcc/loongarch64-alpine-linux-musl/14.2.0/crtbeginS.o:(.text+0x0): unknown relocation (102) against symbol
# error: unknown target triple 'loongarch64-alpine-linux-musl', please use -triple or -arch
alpineRelease=$(cat /etc/alpine-release)
if ! grep PRETTY_NAME /etc/os-release | cut -d '=' -f 2 | tr -d '"' | grep -q "edge$" || [ "$(semver compare "$alpineRelease" "3.21.0")" -lt 0 ]; then
skip
fi
fi
export TARGETARCH=loong64
testHelloCGOZig
}
@test "386-hellocgo-zig" {
export TARGETARCH=386
testHelloCGOZig
}
@test "arm64-cgoenv-zig" {
if ! supportZig; then
skip "Zig not supported"
fi
export TARGETARCH=arm64
export XX_GO_PREFER_C_COMPILER=zig
export CGO_ENABLED=1
add zig
# single/double quotes changed in between go versions
run sh -c "xx-go env | sed 's/[\"'\'']//g'"
assert_success
assert_output --partial "CC=zig cc -target"
assert_output --partial "CXX=zig c++ -target"
}
@test "wrap-unwrap" {
target="arm64"
if [ "$(xx-info arch)" = "arm64" ]; then target="amd64"; fi
@@ -535,3 +625,10 @@ testHelloCGO() {
assert_success
assert_output "$nativeArch"
}
@test "clean-packages" {
if ! supportZig; then
skip "Zig not supported"
fi
del zig
}

View File

@@ -142,3 +142,7 @@ supportLoong64CGo() {
supportRC() {
command -v llvm-rc >/dev/null 2>&1
}
supportZig() {
[ -f /etc/alpine-release ] && versionGTE "$(xx-info os-version | cut -d'.' -f1-2)" "3.20"
}

150
src/xx-go
View File

@@ -6,6 +6,9 @@ for l in $(xx-info env); do
export "${l?}"
done
: "${XX_GO_PREFER_C_COMPILER=clang}"
: "${XX_ZIG_TRIPLE=unknown-unknown-none}"
export GOOS="${TARGETOS}"
export GOARCH="${TARGETARCH}"
@@ -24,6 +27,28 @@ case "$TARGETARCH" in
;;
esac
fi
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="x86_64-linux-musl"
else
XX_ZIG_TRIPLE="x86_64-linux-gnu"
fi
if [ "$TARGETOS" = "darwin" ]; then
XX_ZIG_TRIPLE="x86_64-macos-none"
elif [ "$TARGETOS" = "windows" ]; then
XX_ZIG_TRIPLE="x86_64-windows-gnu"
fi
;;
"arm64")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="aarch64-linux-musl"
else
XX_ZIG_TRIPLE="aarch64-linux-gnu"
fi
if [ "$TARGETOS" = "darwin" ]; then
XX_ZIG_TRIPLE="aarch64-macos-none"
elif [ "$TARGETOS" = "windows" ]; then
XX_ZIG_TRIPLE="aarch64-windows-gnu"
fi
;;
"arm")
if [ -z "$GOARM" ]; then
@@ -39,6 +64,94 @@ case "$TARGETARCH" in
;;
esac
fi
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="arm-linux-musleabihf"
else
XX_ZIG_TRIPLE="arm-linux-gnueabihf"
fi
if [ "$TARGETVARIANT" = "v6" ]; then
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="arm-linux-musleabi"
else
XX_ZIG_TRIPLE="arm-linux-gnueabi"
fi
fi
if [ "$TARGETVARIANT" = "v5" ]; then
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="arm-linux-musleabi"
else
XX_ZIG_TRIPLE="arm-linux-gnueabi"
fi
fi
if [ "$TARGETOS" = "windows" ]; then
XX_ZIG_TRIPLE="arm-windows-gnu"
fi
;;
"riscv64")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="riscv64-linux-musl"
else
XX_ZIG_TRIPLE="riscv64-linux-gnu"
fi
;;
"ppc64le")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="powerpc64le-linux-musl"
else
XX_ZIG_TRIPLE="powerpc64le-linux-gnu"
fi
;;
"s390x")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="s390x-linux-musl"
else
XX_ZIG_TRIPLE="s390x-linux-gnu"
fi
;;
"loong64")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="loongarch64-linux-musl"
else
XX_ZIG_TRIPLE="loongarch64-linux-gnu"
fi
;;
"386")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="x86-linux-musl"
else
XX_ZIG_TRIPLE="x86-linux-gnu"
fi
if [ "$TARGETOS" = "windows" ]; then
XX_ZIG_TRIPLE="x86-windows-gnu"
fi
;;
"mips")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="mips-linux-musl"
else
XX_ZIG_TRIPLE="mips-linux-gnueabi"
fi
;;
"mipsle")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="mipsel-linux-musl"
else
XX_ZIG_TRIPLE="mipsel-linux-gnueabi"
fi
;;
"mips64")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="mips64-linux-musl"
else
XX_ZIG_TRIPLE="mips64-linux-gnuabi64"
fi
;;
"mips64le")
if [ "$XX_LIBC" = "musl" ]; then
XX_ZIG_TRIPLE="mips64el-linux-musl"
else
XX_ZIG_TRIPLE="mips64el-linux-gnuabi64"
fi
;;
esac
@@ -76,26 +189,33 @@ if command -v "$XX_TRIPLE-g++" >/dev/null 2>/dev/null; then
cxx_set=1
fi
if command -v clang >/dev/null 2>/dev/null; then
triple=$(xx-clang --print-target-triple || true)
if [ -n "$triple" ]; then
export CC="$triple-clang"
export CXX="$triple-clang++"
if [ "$XX_GO_PREFER_C_COMPILER" = "clang" ]; then
if command -v clang >/dev/null 2>/dev/null; then
triple=$(xx-clang --print-target-triple || true)
if [ -n "$triple" ]; then
export CC="$triple-clang"
export CXX="$triple-clang++"
c_set=1
cxx_set=1
fi
fi
if command -v "$XX_TRIPLE-ar" >/dev/null 2>/dev/null; then
export AR="$XX_TRIPLE-ar"
ar_set=1
fi
if command -v "$XX_TRIPLE-pkg-config" >/dev/null 2>/dev/null; then
export PKG_CONFIG="$XX_TRIPLE-pkg-config"
pkgconfig_set=1
fi
elif [ "$XX_GO_PREFER_C_COMPILER" = "zig" ]; then
if command -v "zig" >/dev/null 2>/dev/null; then
export CC="zig cc -target $XX_ZIG_TRIPLE"
c_set=1
export CXX="zig c++ -target $XX_ZIG_TRIPLE"
cxx_set=1
fi
fi
if command -v "$XX_TRIPLE-ar" >/dev/null 2>/dev/null; then
export AR="$XX_TRIPLE-ar"
ar_set=1
fi
if command -v "$XX_TRIPLE-pkg-config" >/dev/null 2>/dev/null; then
export PKG_CONFIG="$XX_TRIPLE-pkg-config"
pkgconfig_set=1
fi
if [ -z "$GOBIN" ] && [ -n "$GOPATH" ] && [ -n "$GOARCH" ] && [ -n "$GOOS" ]; then
export PATH="${GOPATH}/bin/${GOOS}_${GOARCH}:${PATH}"
fi