From 43747c15c7a33a5fe8e750ba452d5b70c64210dd Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 2 Nov 2021 09:57:04 +0100 Subject: [PATCH] Setup cross-compilation when building Docker image --- .github/workflows/check.yaml | 3 -- Dockerfile | 69 ++++++++++++++++++++++++++---- misc/docker-arch-to-rust-target.sh | 15 +++++++ 3 files changed, 75 insertions(+), 12 deletions(-) create mode 100755 misc/docker-arch-to-rust-target.sh diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index ee8cc0de1..41ab8116b 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -332,9 +332,6 @@ jobs: type=semver,pattern={{major}} type=sha - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 diff --git a/Dockerfile b/Dockerfile index a6fa255fa..539baa524 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,73 @@ -ARG RUSTC_VERSION=1.56.0 +# Builds a minimal image with the binary only. It is multi-arch capable, +# cross-building to aarch64 and x86_64. When cross-compiling, Docker sets two +# implicit BUILDARG: BUILDPLATFORM being the host platform and TARGETPLATFORM +# being the platform being built. +# +# Docker platform definitions look like this: linux/arm64 and linux/amd64, so +# there is a small script that translates those platforms to LLVM triples, +# respectively x86-64-unknown-linux-gnu and aarch64-unknown-linux-gnu +ARG RUSTC_VERSION=1.56.1 + +## Base image with cargo-chef and the right cross-compilation toolchain ## # cargo-chef helps with caching dependencies between builds -FROM docker.io/library/rust:${RUSTC_VERSION}-slim AS chef +FROM --platform=${BUILDPLATFORM} docker.io/library/rust:${RUSTC_VERSION}-slim AS chef + +# Install x86_64 and aarch64 cross-compiling stack +RUN apt update && apt install -y --no-install-recommends \ + g++-x86-64-linux-gnu \ + g++-aarch64-linux-gnu \ + libc6-dev-arm64-cross \ + libc6-dev-amd64-cross \ + && rm -rf /var/lib/apt/lists/* + WORKDIR /app RUN cargo install --locked cargo-chef -FROM chef AS planner +ENV \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ + CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \ + CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \ + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc \ + CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc \ + CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++ + +ARG RUSTC_VERSION +ARG TARGETPLATFORM + +# Helper script that transforms docker platforms to LLVM triples +COPY ./misc/docker-arch-to-rust-target.sh / +# Install the right toolchain for cross-compilation +RUN rustup target add `/docker-arch-to-rust-target.sh "${TARGETPLATFORM}"` --toolchain "${RUSTC_VERSION}" + +## Run the planner from cargo-chef ## +FROM --platform=${BUILDPLATFORM} chef AS planner COPY . . RUN cargo chef prepare --recipe-path recipe.json -FROM chef AS builder -COPY --from=planner /app/recipe.json recipe.json +## Actual build stage ## +FROM --platform=${BUILDPLATFORM} chef AS builder + +ARG TARGETPLATFORM + # Build dependencies -RUN cargo chef cook --release --recipe-path recipe.json +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook \ + --release \ + --recipe-path recipe.json \ + --target $(/docker-arch-to-rust-target.sh "${TARGETPLATFORM}") + # Build the rest COPY . . -RUN cargo build --release --bin mas-cli +RUN cargo build \ + --release \ + --bin mas-cli \ + --target $(/docker-arch-to-rust-target.sh "${TARGETPLATFORM}") +# Move the binary to avoid having to guess its name in the next stage +# +RUN mv target/$(/docker-arch-to-rust-target.sh "${TARGETPLATFORM}")/release/mas-cli /mas-cli -FROM gcr.io/distroless/cc -COPY --from=builder /app/target/release/mas-cli /mas-cli +## Runtime stage ## +FROM --platform=${TARGETPLATFORM} gcr.io/distroless/cc +COPY --from=builder /mas-cli /mas-cli ENTRYPOINT ["/mas-cli"] diff --git a/misc/docker-arch-to-rust-target.sh b/misc/docker-arch-to-rust-target.sh new file mode 100755 index 000000000..76bafaa11 --- /dev/null +++ b/misc/docker-arch-to-rust-target.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +if [ "$#" -ne 1 ]; then + echo "usage: $0 [platform]" >&2 + exit 1 +fi + +if [ "$1" = "linux/arm64" ]; then + echo "aarch64-unknown-linux-gnu" +elif [ "$1" = "linux/amd64" ]; then + echo "x86_64-unknown-linux-gnu" +else + echo "unsupported platform $1" >&2 + exit 2 +fi