Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create multiarch image by cross-compiling Swift package to arm64 #125

Merged
merged 67 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
08d4155
Run Docker workflow on all 'docker/'-prefixed branches
fwcd Dec 29, 2022
956005d
Use 2-space indent in scripts
fwcd Dec 29, 2022
0011fc5
Extract arch-aware build scripts from Dockerfile
fwcd Dec 29, 2022
96c18d1
Build arm64 image in CI again
fwcd Dec 29, 2022
c4eac68
Force scripts to run in bash
fwcd Dec 29, 2022
7d73cea
Pass TARGETOS and TARGETARCH from Dockerfile
fwcd Dec 29, 2022
3b6f508
Declare TARGETOS and TARGETARCH as ARGs
fwcd Dec 29, 2022
105873a
Make sure .build is always created
fwcd Dec 29, 2022
9329e12
Set up include and linker paths for crosscompiling
fwcd Dec 29, 2022
7460baf
Use single-line if for installing crossbuild sysroot
fwcd Dec 29, 2022
1a68836
Move cross-compilation setup into dependencies script
fwcd Dec 29, 2022
006e1f0
Install the native target toolchain to get the Swift libraries
fwcd Dec 29, 2022
56f4e6b
Fix ARG syntax
fwcd Dec 29, 2022
ed4807c
Renew ARG for second stage
fwcd Dec 29, 2022
388ad62
Add unified script for setting up cross sysroot with qemu-debootstrap
fwcd Dec 30, 2022
83b946c
Fix typo
fwcd Dec 30, 2022
08a770a
Set up schroot configuration
fwcd Dec 30, 2022
85f1ff9
Chroot directly into sysroot
fwcd Dec 30, 2022
4b6703a
Output the new sysroot for debugging
fwcd Dec 30, 2022
40e3664
Script chroot properly
fwcd Dec 30, 2022
8483e74
Remove unused DEBIAN=ON flag
fwcd Dec 30, 2022
c7d881e
Enable repositories in install-build-dependencies-apt
fwcd Dec 30, 2022
2089be7
Install add-apt-repository before using it
fwcd Dec 30, 2022
e82b04c
Fix sysroot path
fwcd Dec 30, 2022
9a11317
Set cross compile sysroot paths manually again
fwcd Dec 30, 2022
2caeeeb
Add build-essential to APT dependencies
fwcd Dec 30, 2022
532c477
Output extra flags
fwcd Dec 30, 2022
ca5de65
Set up registry caching for CI-built images
fwcd Dec 30, 2022
478e4fe
Consolidate install and sysroot into single step
fwcd Dec 30, 2022
38ac19a
Fix typo
fwcd Dec 30, 2022
e69a774
Fix double equals operator in Dockerfile
fwcd Dec 30, 2022
3c6f2ae
Pass cross-compile include path to C++ compiler too
fwcd Jan 1, 2023
662c62f
Add OS check to install-cross-compilation-sysroot
fwcd Jan 2, 2023
b3f459b
Use debootstrap directly instead of qemu-debootstrap
fwcd Jan 2, 2023
f9ebc4e
Add script for mapping arch names
fwcd Jan 2, 2023
deb61d1
Perform second-stage bootstrap too
fwcd Jan 2, 2023
01ca499
Copy get-linux-arch-name to image
fwcd Jan 2, 2023
aa3f4e1
Add pull request trigger to Docker workflow (without push)
fwcd Feb 21, 2023
3502fdb
Merge branch 'main' into docker/cross-compile-image
fwcd Jul 6, 2023
a3841aa
Merge branch 'main' into docker/cross-compile-image
fwcd Jul 30, 2023
e588b87
Bump to 5.8.1-jammy
fwcd Jul 30, 2023
d021efb
Copy get-linux-arch-name in earlier step already
fwcd Jul 30, 2023
92bf511
Set workdir earlier
fwcd Jul 30, 2023
be190dd
Use multi-stage build to create sysroot
fwcd Jul 31, 2023
974cd92
Remove legacy cross-compile sysroot scripts
fwcd Jul 31, 2023
8c07d20
Add $arch_name-linux-gnu to linker path
fwcd Jul 31, 2023
7e0dcf2
Work around glibc header issue
fwcd Aug 1, 2023
abdafad
Work around <tgmath.h> issue
fwcd Aug 1, 2023
684ba14
Install libstdc++-12-dev as build dependency
fwcd Aug 1, 2023
6a15b90
Use lld as linker
fwcd Aug 1, 2023
51e6b57
Include standard headers from target sysroot
fwcd Aug 1, 2023
3cd4021
Place target sysroot under /usr/local
fwcd Aug 1, 2023
80e2a07
Check for $TARGETSYSROOT variable
fwcd Aug 1, 2023
8863b3a
Install cross-GCC instead of installing it natively to the sysroot
fwcd Aug 1, 2023
4abaabe
Install native GCC if not cross-compiling
fwcd Aug 1, 2023
ebed54f
Symlink libc into /lib/$arch_name-linux-gnu
fwcd Aug 1, 2023
f0d75ed
Fix typo
fwcd Aug 1, 2023
f0f441d
Install native GCC into target sysroot again
fwcd Aug 1, 2023
fda56af
Remove libc workaround again
fwcd Aug 1, 2023
5e09f49
Only deploy on main
fwcd Aug 1, 2023
abf64e0
Reorder -tools-directory
fwcd Aug 1, 2023
36c223b
Install native GCC only if not cross-compiling
fwcd Aug 1, 2023
486dfe9
Link libstdc++ from target sysroot rather than cross-GCC
fwcd Aug 1, 2023
052c44e
Symlink sysroot libs into cross-GCC libs
fwcd Aug 1, 2023
5b79129
Link target sysroot in runner image
fwcd Aug 1, 2023
8bf4abb
Remove native GCC from sysroot again
fwcd Aug 1, 2023
e59ede9
Disable build cache
fwcd Aug 1, 2023
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
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
jobs:
deploy:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Set up Kubernetes CLI
Expand Down
11 changes: 4 additions & 7 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
Expand All @@ -25,11 +26,7 @@ jobs:
uses: docker/build-push-action@v4
with:
context: .
# TODO: Ideally we could just build an aarch64 image too (under QEMU user-mode emulation) by using
#
# platforms: linux/amd64,linux/arm64/v8
#
# See https://github.com/fwcd/d2/issues/124
platforms: linux/amd64
push: true
platforms: linux/amd64,linux/arm64/v8
push: true # TODO: Only push on main
tags: ghcr.io/fwcd/d2:latest

49 changes: 43 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,46 @@
FROM swift:5.7 as builder
ARG SWIFTVERSION=5.8.1
ARG UBUNTUDISTRO=jammy

# Install native dependencies
FROM swift:${SWIFTVERSION}-${UBUNTUDISTRO} AS sysroot

WORKDIR /opt/d2

# Install build dependencies into target sysroot
COPY Scripts/install-build-dependencies-apt Scripts/
RUN Scripts/install-build-dependencies-apt && rm -rf /var/lib/apt/lists/*

# Build
FROM --platform=$BUILDPLATFORM swift:${SWIFTVERSION}-${UBUNTUDISTRO} AS builder

ARG SWIFTVERSION
ARG UBUNTUDISTRO
ARG BUILDARCH
ARG TARGETARCH

ARG TARGETSYSROOT=/usr/local/${TARGETARCH}-ubuntu-${UBUNTUDISTRO}

WORKDIR /opt/d2

# Copy target sysroot into builder
# TODO: Only copy stuff that we need for compilation (/usr/lib, /usr/include etc.)
COPY --from=sysroot / ${TARGETSYSROOT}

# Install (cross-)GCC and patch some paths
COPY Scripts/prepare-docker-buildroot Scripts/get-linux-arch-name Scripts/
RUN Scripts/prepare-docker-buildroot

# (Cross-)compile D2
COPY Sources Sources
COPY Tests Tests
COPY Package.swift Package.resolved ./
RUN swift build -c release
COPY Scripts/build-release Scripts/
RUN Scripts/build-release

FROM swift:${SWIFTVERSION}-${UBUNTUDISTRO}-slim AS runner

FROM swift:5.7-slim as runner
ARG TARGETARCH
ARG UBUNTUDISTRO

ARG TARGETSYSROOT=/usr/local/${TARGETARCH}-ubuntu-${UBUNTUDISTRO}

# Install Curl, add-apt-repository and node package repository
RUN apt-get update && apt-get install -y curl software-properties-common && rm -rf /var/lib/apt/lists/*
Expand All @@ -21,6 +50,10 @@ RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
COPY Scripts/install-runtime-dependencies-apt Scripts/
RUN Scripts/install-runtime-dependencies-apt && rm -rf /var/lib/apt/lists/*

# Link 'sysroot' to / to make sure D2 can find the Swift stdlibs
# (the runpath within the D2 executable still points to its /usr/lib/swift)
RUN ln -s / ${TARGETSYSROOT}

WORKDIR /opt/d2

# Install Node dependencies
Expand All @@ -32,9 +65,13 @@ RUN Scripts/install-node-dependencies
COPY Resources Resources
COPY LICENSE README.md ./

ARG TARGETOS
ARG TARGETARCH

# Set up .build folder in runner
WORKDIR /opt/d2/.build
RUN mkdir -p x86_64-unknown-linux-gnu/release && ln -s x86_64-unknown-linux-gnu/release release
COPY Scripts/setup-dotbuild-tree Scripts/
RUN Scripts/setup-dotbuild-tree

# Copy font used by swiftplot to the correct path
COPY --from=builder \
Expand Down
30 changes: 30 additions & 0 deletions Scripts/build-release
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

# (Cross-)compile D2

set -e
cd "$(dirname $0)/.."

extra_flags=()

if [ -n "$TARGETARCH" ] && [ -n "$TARGETSYSROOT" ]; then
echo "==> Setting flags for target arch '$TARGETARCH'"
arch_name="$(Scripts/get-linux-arch-name $TARGETARCH)"

extra_flags+=(
--arch "$arch_name"
--sdk "$TARGETSYSROOT"
-Xswiftc -use-ld=lld
-Xswiftc -resource-dir -Xswiftc "$TARGETSYSROOT/usr/lib/swift"
-Xswiftc -tools-directory -Xswiftc "/usr/$arch_name-linux-gnu/bin"
-Xcc -I"$TARGETSYSROOT/usr/include"
-Xcc -I"$TARGETSYSROOT/usr/include/freetype2"
-Xcc -I"$TARGETSYSROOT/usr/include/$arch_name-linux-gnu"
-Xcc -I"$TARGETSYSROOT/usr/include/cairo"
-Xlinker -L"$TARGETSYSROOT/usr/lib"
-Xlinker -L"$TARGETSYSROOT/usr/lib/$arch_name-linux-gnu" # libtesseract installs there
)
fi

echo "==> Building D2 with flags ${extra_flags[@]}"
exec swift build -c release "${extra_flags[@]}"
18 changes: 18 additions & 0 deletions Scripts/get-linux-arch-name
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

# Unfortunately, Docker uses a different name for some CPU architectures than some
# Linux tools (arm64 vs aarch64, amd64 vs x86_64, ...), therefore we use this script
# to perform this mapping once and for all.

set -e

if [ $# -lt 1 ]; then
echo "Usage: $0 [arch name]"
exit 1
fi

case "$1" in
amd64) echo "x86_64";;
arm64) echo "aarch64";;
*) echo "$1";;
esac
27 changes: 20 additions & 7 deletions Scripts/install-build-dependencies-apt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,25 @@
set -e
cd "$(dirname $0)/.."

apt-get update
apt-get install -y \
libcairo2-dev \
libsqlite3-dev \
libleptonica-dev \
libtesseract-dev \
dependencies=(
libcairo2-dev
libsqlite3-dev
libleptonica-dev
libtesseract-dev
libgraphviz-dev
libstdc++-12-dev
# TODO: poppler-utils and libssl1.1 needed?
)

echo "==> Installing add-apt-repository"
apt-get update
apt-get install -y software-properties-common

# TODO: poppler-utils and libssl1.1 needed?
echo "==> Enabling all repositories"
for repo in main universe multiverse; do
add-apt-repository $repo
done

echo "==> Installing ${dependencies[@]}"
apt-get update
apt-get install -y "${dependencies[@]}"
16 changes: 9 additions & 7 deletions Scripts/install-runtime-dependencies-apt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
set -e
cd "$(dirname $0)/.."

apt-get update
apt-get install -y \
libcairo2 \
libsqlite3-0 \
tesseract-ocr \
graphviz \
dependencies=(
libcairo2
libsqlite3-0
tesseract-ocr
graphviz
nodejs
# TODO: poppler-utils and libssl1.1 needed?
)

# TODO: poppler-utils and libssl1.1 needed?
apt-get update
apt-get install -y "${dependencies[@]}"
63 changes: 63 additions & 0 deletions Scripts/prepare-docker-buildroot
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash

# Prepares the build stage during the Docker image build. This assumes
# that the target sysroot has already been installed to $TARGETSYSROOT.

set -e
cd "$(dirname $0)/.."

if [ -z "$BUILDARCH" ]; then
echo "Please make sure to specify BUILDARCH!"
exit 1
fi

if [ -z "$TARGETARCH" ]; then
echo "Please make sure to specify TARGETARCH!"
exit 1
fi

if [ -z "$TARGETSYSROOT" ]; then
echo "Please make sure to specify TARGETSYSROOT!"
exit 1
fi

arch_name="$(Scripts/get-linux-arch-name $TARGETARCH)"

echo "==> Updating apt..."
apt-get update

if [ "$BUILDARCH" == "$TARGETARCH" ]; then
echo "==> Installing build essentials"
apt-get install -y build-essential
else
# Apparently, we need to symlink libstdc++ manually
if [ ! -f "$TARGETSYSROOT/usr/lib/$arch_name-linux-gnu/libstdc++.so" ]; then
echo "==> Symlinking libstdc++"
ln -s "$TARGETSYSROOT/usr/lib/$arch_name-linux-gnu/libstdc++.so"{.6,}
fi

# Symlinking the sysroot libs into the cross-GCC libs
# fixes a very strange linking issue where the linker would
# try resolving libc in /lib/$arch_name-linux-gnu. This also
# needs to happen before installing the cross-GCC package.
echo "==> Symlinking sysroot libs into cross-GCC libs"
mkdir -p "/usr/$arch_name-linux-gnu"
ln -s "$TARGETSYSROOT/usr/lib/$arch_name-linux-gnu" "/usr/$arch_name-linux-gnu/lib"

echo "==> Installing cross-GCC"
apt-get install -y gcc-$arch_name-linux-gnu
fi

# Workaround for https://github.com/stephencelis/CSQLite/pull/1
if [ ! -f /usr/include/sqlite3.h ]; then
echo "==> Patching sqlite3.h"
ln -s {"$TARGETSYSROOT",}/usr/include/sqlite3.h
fi

# There seem to be some strange errors with <tgmath.h> (glibc)
# on host + target x86_64. Since this header seems to be provided
# elsewhere, we can remove it from the target sysroot as a workaround.
if [ "$BUILDARCH" = amd64 ] && [ "$TARGETARCH" = amd64 ] && [ -f "$TARGETSYSROOT/usr/include/tgmath.h" ]; then
echo "==> Patching tgmath.h"
mv "$TARGETSYSROOT/usr/include/tgmath.h"{,.backup}
fi
41 changes: 41 additions & 0 deletions Scripts/setup-dotbuild-tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

set -e
cd "$(dirname $0)/.."

if [ -n "$TARGETOS" ]; then
case "$TARGETOS" in
linux)
vendor=unknown
os=linux-gnu
;;
*)
echo "Unsupported target os '$TARGETOS'"
exit 1
;;
esac
else
echo "Please specify the target OS by setting the TARGETOS variable!"
exit 1
fi

if [ -n "$TARGETARCH" ]; then
case "$TARGETARCH" in
amd64) arch=x86_64;;
arm64) arch=arm64;;
*)
echo "Unsupported target arch '${D2TARGETARCH[@]}'"
exit 1
;;
esac
else
echo "Please specify the target arch by setting the TARGETARCH variable!"
exit 1
fi

config=release

mkdir -p .build/"$arch-$vendor-$os"/"$config"

cd .build
ln -s "$config" "$arch-$vendor-$os"/"$config"
Loading