diff --git a/Dockerfile b/Dockerfile index b2c1a028a..1cae15c74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,20 @@ -# Container for building Go binary. -FROM golang:1.27.3-trixie AS builder -# Install dependencies -RUN apt-get update && apt-get install -y --no-install-recommends build-essential git +# Container for building Go binary. It always runs on the build platform and +# cross-compiles for the target platform, so multi-platform builds don't compile +# under QEMU emulation. +FROM --platform=$BUILDPLATFORM golang:1.27.3-trixie AS builder +ARG BUILDARCH +ARG TARGETOS +ARG TARGETARCH +# Install dependencies, plus a cgo cross compiler when the target arch differs +# from the build arch (herumi bls requires cgo). +RUN apt-get update && apt-get install -y --no-install-recommends build-essential git \ + && if [ "$TARGETARCH" != "$BUILDARCH" ]; then \ + case "$TARGETARCH" in \ + arm64) apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross ;; \ + amd64) apt-get install -y --no-install-recommends gcc-x86-64-linux-gnu libc6-dev-amd64-cross ;; \ + *) echo "unsupported TARGETARCH=$TARGETARCH" && exit 1 ;; \ + esac; \ + fi # Prep and copy source WORKDIR /app/charon @@ -11,14 +24,20 @@ COPY . . # Populate GO_BUILD_FLAG with a build arg to provide an optional go build flag. ARG GO_BUILD_FLAG ENV GO_BUILD_FLAG=${GO_BUILD_FLAG} -RUN echo "Building with GO_BUILD_FLAG='${GO_BUILD_FLAG}'" +RUN echo "Building for ${TARGETOS}/${TARGETARCH} on ${BUILDARCH} with GO_BUILD_FLAG='${GO_BUILD_FLAG}'" -# Build with Go module and Go build caches. +# Build with Go module and Go build caches (the build cache keys entries by +# GOARCH, so both target arches can share it). RUN \ --mount=type=cache,target=/go/pkg \ --mount=type=cache,target=/root/.cache/go-build \ - go build -o charon "${GO_BUILD_FLAG}" . -RUN echo "Built charon version=$(./charon version)" + if [ "$TARGETARCH" != "$BUILDARCH" ]; then \ + case "$TARGETARCH" in \ + arm64) export CC=aarch64-linux-gnu-gcc ;; \ + amd64) export CC=x86_64-linux-gnu-gcc ;; \ + esac; \ + fi \ + && CGO_ENABLED=1 GOOS="$TARGETOS" GOARCH="$TARGETARCH" go build -o charon "${GO_BUILD_FLAG}" . # Copy final binary into light stage. FROM debian:trixie-slim @@ -33,6 +52,9 @@ ARG GITHUB_SHA=local ENV GITHUB_SHA=${GITHUB_SHA} COPY --from=builder /app/charon/charon /usr/local/bin/ +# Sanity check the binary on the target platform (the builder can't run a +# cross-compiled binary). +RUN version="$(/usr/local/bin/charon version)" && echo "Built charon version=${version}" # Don't run container as root ENV USER=charon diff --git a/testutil/promrated/Dockerfile b/testutil/promrated/Dockerfile index 361867069..3d48e8442 100644 --- a/testutil/promrated/Dockerfile +++ b/testutil/promrated/Dockerfile @@ -1,14 +1,23 @@ -FROM golang:1.27.3-alpine AS builder +# Build on the build platform and cross-compile for the target platform, so +# multi-platform builds don't compile under QEMU emulation. promrated has no cgo +# dependencies, so a pure Go build cross-compiles without a C toolchain. +FROM --platform=$BUILDPLATFORM golang:1.27.3-alpine AS builder -# Install dependencies -RUN apk add --no-cache build-base git +# Install dependencies (git for VCS build info) +RUN apk add --no-cache git WORKDIR /app COPY . . -# Build with Go module and Go build caches. -RUN go build -o promrated github.com/obolnetwork/charon/testutil/promrated/promrated +ARG TARGETOS +ARG TARGETARCH +RUN CGO_ENABLED=0 GOOS="$TARGETOS" GOARCH="$TARGETARCH" \ + go build -o promrated github.com/obolnetwork/charon/testutil/promrated/promrated + +FROM alpine:3.24 + +COPY --from=builder /app/promrated /app/promrated # Don't run container as root ENV USER=promrated