Skip to content
This repository was archived by the owner on Dec 16, 2022. It is now read-only.

Optimise target image for cross compilation #13

Merged
merged 5 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ ARG XX_VERSION=1.1.0

FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

FROM golang:${GO_VERSION}-${BASE_VARIANT} as gostable

FROM gostable AS go-linux

FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-deps
FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-base

RUN apk add --no-cache \
bash \
Expand All @@ -26,15 +22,10 @@ RUN apk add --no-cache \

COPY --from=xx / /

ARG TARGETPLATFORM

RUN xx-apk add --no-cache \
xx-c-essentials

RUN xx-apk add --no-cache \
xx-cxx-essentials
FROM build-base AS build-cross

ARG TARGETPLATFORM

RUN xx-apk add --no-cache \
build-base \
pkgconfig \
Expand All @@ -48,7 +39,6 @@ RUN xx-apk add --no-cache \
WORKDIR /build
COPY hack/static.sh .

ARG TARGETPLATFORM
ENV CC=xx-clang
ENV CXX=xx-clang++

Expand All @@ -68,12 +58,25 @@ RUN ./static.sh build_libssh2
RUN ./static.sh build_libgit2


FROM go-${TARGETOS} AS build
# trimmed removes all non necessary files (i.e. openssl binary).
FROM build-cross AS trimmed

# Copy cross-compilation tools
COPY --from=xx / /
# Copy compiled libraries
COPY --from=build-deps /usr/local/ /usr/local/
ARG TARGETPLATFORM
RUN mkdir -p /trimmed/usr/local/$(xx-info triple)/ && \
mkdir -p /trimmed/usr/local/$(xx-info triple)/share

RUN cp -r /usr/local/$(xx-info triple)/lib/ /trimmed/usr/local/$(xx-info triple)/ && \
cp -r /usr/local/$(xx-info triple)/lib64/ /trimmed/usr/local/$(xx-info triple)/ | true && \
cp -r /usr/local/$(xx-info triple)/include/ /trimmed/usr/local/$(xx-info triple)/ && \
cp -r /usr/local/$(xx-info triple)/share/doc/ /trimmed/usr/local/$(xx-info triple)/share/

FROM scratch as libs-arm64
COPY --from=trimmed /trimmed/ /

FROM scratch as libs-amd64
COPY --from=trimmed /trimmed/ /

FROM scratch as libs-armv7
COPY --from=trimmed /trimmed/ /

COPY ./hack/Makefile /Makefile
COPY ./hack/static.sh /static.sh
FROM libs-$TARGETARCH$TARGETVARIANT as libs
107 changes: 26 additions & 81 deletions Dockerfile.test
Original file line number Diff line number Diff line change
@@ -1,107 +1,59 @@
# This Dockerfile tests the hack/Makefile output against git2go.
ARG BASE_VARIANT=alpine
ARG GO_VERSION=1.17.6
ARG GO_VERSION=1.17
ARG XX_VERSION=1.1.0

FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

FROM golang:${GO_VERSION}-${BASE_VARIANT} as gostable

FROM gostable AS go-linux

FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-deps

RUN apk add --no-cache \
bash \
curl \
build-base \
linux-headers \
perl \
cmake \
pkgconfig \
gcc \
musl-dev \
clang \
lld

COPY --from=xx / /
ARG LIBGIT2_IMG
ARG LIBGIT2_TAG

ARG TARGETPLATFORM

RUN xx-apk add --no-cache \
xx-c-essentials

RUN xx-apk add --no-cache \
xx-cxx-essentials

ARG TARGETPLATFORM
RUN xx-apk add --no-cache \
build-base \
pkgconfig \
gcc \
musl-dev \
clang \
lld \
llvm \
linux-headers

WORKDIR /build
COPY hack/static.sh .

ARG TARGETPLATFORM
ENV CC=xx-clang
ENV CXX=xx-clang++
FROM ${LIBGIT2_IMG}:${LIBGIT2_TAG} AS build-deps

RUN CHOST=$(xx-clang --print-target-triple) \
./static.sh build_libz

RUN CHOST=$(xx-clang --print-target-triple) \
./static.sh build_openssl
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

RUN export LIBRARY_PATH="/usr/local/$(xx-info triple)/lib:/usr/local/$(xx-info triple)/lib64:${LIBRARY_PATH}" && \
export PKG_CONFIG_PATH="/usr/local/$(xx-info triple)/lib/pkgconfig:/usr/local/$(xx-info triple)/lib64/pkgconfig" && \
export OPENSSL_ROOT_DIR="/usr/local/$(xx-info triple)" && \
export OPENSSL_CRYPTO_LIBRARY="/usr/local/$(xx-info triple)/lib64" && \
export OPENSSL_INCLUDE_DIR="/usr/local/$(xx-info triple)/include/openssl"
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} as gostable

RUN ./static.sh build_libssh2
RUN ./static.sh build_libgit2
FROM gostable AS go-linux

# Build-base consists of build platform dependencies and xx.
# These will be used at current arch to yield execute the cross compilations.
FROM go-${TARGETOS} AS build-base

FROM go-${TARGETOS} AS build
RUN apk add clang lld pkgconfig

# Copy cross-compilation tools
COPY --from=xx / /
# Copy compiled libraries
COPY --from=build-deps /usr/local/ /usr/local/

RUN apk add clang lld pkgconfig
# build-go-mod can still be cached at build platform architecture.
FROM build-base as build-go-mod

WORKDIR /root/smoketest
COPY tests/smoketest/go.mod .
COPY tests/smoketest/go.sum .
RUN go mod download

# Build stage install per target platform
# dependency and effectively cross compile the application.
FROM build-go-mod as build

ARG TARGETPLATFORM

# Some dependencies have to installed
# for the target platform: https://github.com/tonistiigi/xx#go--cgo
RUN xx-apk add --no-cache \
musl-dev \
gcc
RUN xx-apk add musl-dev gcc clang lld

WORKDIR /root/smoketest

COPY tests/smoketest/main.go .
COPY --from=build-deps /usr/local/ /usr/local/

ENV CGO_ENABLED=1
RUN export LIBRARY_PATH="/usr/local/$(xx-info triple)/lib:/usr/local/$(xx-info triple)/lib64:${LIBRARY_PATH}" && \
RUN export LIBRARY_PATH="/usr/local/$(xx-info triple):/usr/local/$(xx-info triple)/lib64" && \
export PKG_CONFIG_PATH="/usr/local/$(xx-info triple)/lib/pkgconfig:/usr/local/$(xx-info triple)/lib64/pkgconfig" && \
export FLAGS="$(pkg-config --static --libs --cflags libssh2 openssl libgit2)" && \
CGO_LDFLAGS="${FLAGS} -static" \
xx-go build \
export FLAGS="$(pkg-config --static --libs --cflags libssh2 openssl libgit2)" && \
export CGO_LDFLAGS="${FLAGS} -static" && \
xx-go build \
-ldflags "-s -w" \
-tags 'netgo,osusergo,static_build' \
-o static-test-runner -trimpath main.go;
-o static-test-runner -trimpath main.go


# Ensure that the generated binary is valid for the target platform
RUN xx-verify --static static-test-runner
Expand All @@ -119,11 +71,4 @@ COPY --from=build \

ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

# To do docker run instead, replace the RUN statement with:
# ENTRYPOINT [ "/root/smoketest/static-test-runner" ]

# The approach below was preferred as it provides a way to
# assert the functionality across the supported architectures
# without any extra steps.

RUN /root/smoketest/static-test-runner
61 changes: 58 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,42 @@
IMG ?= hiddeco/golang-with-libgit2
IMG ?= ghcr.io/fluxcd/golang-with-libgit2
TAG ?= latest
STATIC_TEST_TAG := test

PLATFORMS ?= linux/amd64,linux/arm/v7,linux/arm64
BUILD_ARGS ?=

REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel)
TARGET_DIR ?= $(REPOSITORY_ROOT)/build/libgit2
BUILD_ROOT_DIR ?= $(REPOSITORY_ROOT)/build/libgit2-src

LIBGIT2_PATH := $(TARGET_DIR)
LIBGIT2_LIB_PATH := $(LIBGIT2_PATH)/lib
LIBGIT2_LIB64_PATH := $(LIBGIT2_PATH)/lib64
LIBGIT2 := $(LIBGIT2_LIB_PATH)/libgit2.a
MUSL-CC =

export CGO_ENABLED=1
export LIBRARY_PATH=$(LIBGIT2_LIB_PATH):$(LIBGIT2_LIB64_PATH)
export PKG_CONFIG_PATH=$(LIBGIT2_LIB_PATH)/pkgconfig:$(LIBGIT2_LIB64_PATH)/pkgconfig
export CGO_CFLAGS=-I$(LIBGIT2_PATH)/include


ifeq ($(shell uname -s),Linux)
export CGO_LDFLAGS=$(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs --static --cflags libssh2 openssl libgit2) -static
else
export CGO_LDFLAGS=$(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs --static --cflags libssh2 openssl libgit2) -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-allow-shlib-undefined -static
endif

ifeq ($(shell uname -s),Linux)
MUSL-PREFIX=$(REPOSITORY_ROOT)/build/musl/$(shell uname -m)-linux-musl-native/bin/$(shell uname -m)-linux-musl
MUSL-CC=$(MUSL-PREFIX)-gcc
export CC=$(MUSL-PREFIX)-gcc
export CXX=$(MUSL-PREFIX)-g++
export AR=$(MUSL-PREFIX)-ar
endif

GO_STATIC_FLAGS=-tags 'netgo,osusergo,static_build'


.PHONY: build
build:
docker buildx build \
Expand All @@ -17,7 +49,9 @@ build:
test:
docker buildx build \
--platform=$(PLATFORMS) \
--tag $(IMG):$(TAG) \
--tag $(IMG):$(TAG)-test \
--build-arg LIBGIT2_IMG=$(IMG) \
--build-arg LIBGIT2_TAG=$(TAG) \
--file Dockerfile.test \
$(BUILD_ARGS) .

Expand All @@ -33,3 +67,24 @@ builder:
--use
# install qemu emulators
docker run -it --rm --privileged tonistiigi/binfmt --install all


$(LIBGIT2): $(MUSL-CC)
ifeq ($(shell uname -s),Darwin)
TARGET_DIR=$(TARGET_DIR) BUILD_ROOT_DIR=$(BUILD_ROOT_DIR) \
./hack/static.sh all
else
IMG_TAG=$(IMG):$(TAG) ./hack/extract-libraries.sh
endif

$(MUSL-CC):
ifneq ($(shell uname -s),Darwin)
./hack/download-musl.sh
endif


# dev-test is a smoke test for development environment
# consuming the libraries generated by this project.
dev-test: $(LIBGIT2)
cd tests/smoketest; go vet $(GO_STATIC_FLAGS) ./...
cd tests/smoketest; go run $(GO_STATIC_FLAGS) main.go
36 changes: 36 additions & 0 deletions hack/download-musl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash

set -eoux pipefail

MUSL_X86_64_FILENAME=x86_64-linux-musl-native.tgz
MUSL_X86_64_SHA512=44d441ad9aa11a06feddf3daa4c9f53ad7d9ca37af1f5a61379aca07793703d179410cea723c1b7fca94c4de19a321228bdb3656bc5cbdb5e3bea8e2d6dac6c7
MUSL_AARCH64_FILENAME=aarch64-linux-musl-native.tgz
MUSL_AARCH64_SHA512=16d544e09845c9dbba50f29e0cb04dd661e17eb63c56acad6a67fd2a78aa7596b792477c7177d3cd56d408a27dc291a90507df882f2b099c0f25511ce08fd3b5

MUSL_FILENAME="${MUSL_X86_64_FILENAME}"
MUSL_SHA512="${MUSL_X86_64_SHA512}"
if [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then
MUSL_FILENAME="${MUSL_AARCH64_FILENAME}"
MUSL_SHA512="${MUSL_AARCH64_SHA512}"
fi

MUSL_AARCH64_URL="https://more.musl.cc/11.2.1/x86_64-linux-musl/${MUSL_FILENAME}"

ROOT_DIR="$(git rev-parse --show-toplevel)"
MUSL_DIR="${ROOT_DIR}/build/musl"

if [ ! -f "${MUSL_DIR}/bin" ]; then
TARGET_FILE="${MUSL_DIR}/${MUSL_FILENAME}"
mkdir -p "${MUSL_DIR}"

echo "${MUSL_SHA512} ${TARGET_FILE}"
curl -o "${TARGET_FILE}" -LO "${MUSL_AARCH64_URL}"
if ! echo "${MUSL_SHA512} ${TARGET_FILE}" | sha512sum --check; then
echo "Checksum failed for ${MUSL_FILENAME}."
rm -rf "${MUSL_DIR}"
exit 1
fi

tar xzf "${TARGET_FILE}" -C "${MUSL_DIR}"
rm "${TARGET_FILE}"
fi
Loading