Skip to content

Commit 77cd247

Browse files
committed
Set up ARM builds with QEMU and Docker
This commit allows us to provide both 32-bit and 64-bit ARM binaries for our users.
1 parent 6c5950e commit 77cd247

11 files changed

+241
-187
lines changed

.github/workflows/main.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ jobs:
66
build-and-test:
77
strategy:
88
matrix:
9-
ARCH: [x86_64, i386]
9+
ARCH: [x86_64, i386, aarch64, armhf]
10+
UPDATE: ["1"]
1011
fail-fast: false
1112

1213
name: ${{ matrix.ARCH }}
@@ -23,6 +24,9 @@ jobs:
2324
with:
2425
submodules: recursive
2526

27+
- name: Set up QEMU integration for Docker
28+
run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
29+
2630
- name: Build and test AppImage
2731
run: bash ci/build-in-docker.sh
2832

ci/Dockerfile

-26
This file was deleted.

ci/build-in-docker.sh

+99-11
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,120 @@
11
#! /bin/bash
22

3-
set -exo pipefail
3+
log_message() {
4+
color="$1"
5+
shift
6+
if [ -t 0 ]; then tput setaf "$color"; fi
7+
if [ -t 0 ]; then tput bold; fi
8+
echo "$@"
9+
if [ -t 0 ]; then tput sgr0; fi
10+
}
11+
info() {
12+
log_message 2 "[info] $*"
13+
}
14+
warning() {
15+
log_message 3 "[warning] $*"
16+
}
17+
error() {
18+
log_message 1 "[error] $*"
19+
}
20+
21+
if [[ "$ARCH" == "" ]]; then
22+
error "Usage: env ARCH=... bash $0"
23+
exit 2
24+
fi
25+
26+
set -euo pipefail
27+
28+
this_dir="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")"
429

530
case "$ARCH" in
631
x86_64)
7-
base_image=debian:latest
32+
docker_arch=amd64
833
;;
934
i386)
10-
base_image=i386/debian:latest
35+
docker_arch=i386
36+
;;
37+
armhf)
38+
docker_arch=arm32v7
39+
;;
40+
aarch64)
41+
docker_arch=arm64v8
1142
;;
1243
*)
13-
echo "Usage: env ARCH=[x86_64|i386] $0"
14-
exit 2
44+
echo "Unsupported \$ARCH: $ARCH"
45+
exit 3
46+
;;
1547
esac
1648

17-
here="$(readlink -f "$(dirname "$0")")"
18-
cd "$here"
49+
# first, we need to build the image
50+
# we always attempt to build it, it will only be rebuilt if Docker detects changes
51+
# optionally, we'll pull the base image beforehand
52+
info "Building Docker image for $ARCH (Docker arch: $docker_arch)"
53+
54+
build_args=()
55+
if [[ "${UPDATE:-}" == "" ]]; then
56+
warning "\$UPDATE not set, base image will not be pulled!"
57+
else
58+
build_args+=("--pull")
59+
fi
1960

2061
docker_image=linuxdeploy-plugin-qt-build:"$ARCH"
2162

22-
docker build -t "$docker_image" --build-arg base_image="$base_image" .
63+
docker build \
64+
--build-arg ARCH="$ARCH" \
65+
--build-arg docker_arch="$docker_arch" \
66+
"${build_args[@]}" \
67+
-t "$docker_image" \
68+
"$this_dir"/docker
2369

24-
if isatty &>/dev/null; then
25-
extra_args=("-t")
70+
docker_run_args=()
71+
72+
# only if there's more than 1G of free space in RAM, we can build in a RAM disk
73+
if [[ "${GITHUB_ACTIONS:-}" != "" ]]; then
74+
warning "Building on GitHub actions, which does not support --tmpfs flag -> building on regular disk"
75+
elif [[ "$(env LC_ALL=C free -m | grep "Mem:" | awk '{print $4}')" -gt 1024 ]]; then
76+
info "Host system has enough free memory -> building in RAM disk"
77+
docker_run_args+=(
78+
"--tmpfs"
79+
"/docker-ramdisk:exec,mode=777"
80+
)
81+
else
82+
warning "Host system does not have enough free memory -> building on regular disk"
2683
fi
2784

85+
if [ -t 1 ]; then
86+
# needed on unixoid platforms to properly terminate the docker run process with Ctrl-C
87+
docker_run_args+=("-t")
88+
fi
89+
90+
# fix for https://stackoverflow.com/questions/51195528/rcc-error-in-resource-qrc-cannot-find-file-png
91+
if [ "${CI:-}" != "" ]; then
92+
docker_args+=(
93+
"--security-opt"
94+
"seccomp:unconfined"
95+
)
96+
fi
97+
98+
uid="${UID:-"$(id -u)"}"
99+
info "Running build with uid $uid"
100+
28101
run_in_docker() {
29-
docker run -e ARCH --rm -i "${extra_args[@]}" --init -w /ws -v "$(readlink -f "$here"/..)":/ws --user "$(id -u)" "$docker_image" "$@"
102+
# run the build with the current user to
103+
# a) make sure root is not required for builds
104+
# b) allow the build scripts to "mv" the binaries into the /out directory
105+
docker run \
106+
--rm \
107+
-i \
108+
--init \
109+
-e GITHUB_RUN_NUMBER \
110+
-e ARCH \
111+
-e CI \
112+
--user "$uid" \
113+
"${docker_args[@]}" \
114+
-v "$(readlink -f "$this_dir"/..):/ws" \
115+
-w /ws \
116+
"$docker_image" \
117+
"$@"
30118
}
31119

32120
run_in_docker bash ci/build.sh

ci/build-static-patchelf.sh

-62
This file was deleted.

ci/build.sh

+8-14
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ if [ "$ARCH" == "" ]; then
99
fi
1010

1111
# use RAM disk if possible
12-
if [ "$CI" == "" ] && [ -d /dev/shm ]; then
13-
TEMP_BASE=/dev/shm
12+
if [ "$CI" == "" ] && [ -d /docker-ramdisk ]; then
13+
TEMP_BASE=/docker-ramdisk
1414
else
1515
TEMP_BASE=/tmp
1616
fi
@@ -31,30 +31,24 @@ OLD_CWD="$(readlink -f .)"
3131

3232
pushd "$BUILD_DIR"
3333

34-
if [ "$ARCH" == "i386" ]; then
35-
EXTRA_CMAKE_ARGS=("-DCMAKE_TOOLCHAIN_FILE=$REPO_ROOT/cmake/toolchains/i386-linux-gnu.cmake" "-DUSE_SYSTEM_CIMG=OFF")
36-
fi
37-
38-
cmake "$REPO_ROOT" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo "${EXTRA_CMAKE_ARGS[@]}" -DBUILD_TESTING=On -DSTATIC_BUILD=On
34+
cmake "$REPO_ROOT" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=ON -DSTATIC_BUILD=ON
3935

4036
make -j"$(nproc)"
4137

4238
ctest -V --no-tests=error
4339

4440
make install DESTDIR=AppDir
4541

46-
# build patchelf
47-
"$REPO_ROOT"/ci/build-static-patchelf.sh "$(readlink -f out/)"
48-
patchelf_path="$(readlink -f out/usr/bin/patchelf)"
49-
50-
# build custom strip
51-
"$REPO_ROOT"/ci/build-static-binutils.sh "$(readlink -f out/)"
52-
strip_path="$(readlink -f out/usr/bin/strip)"
42+
patchelf_path="$(which patchelf)"
43+
strip_path="$(which strip)"
5344

5445
export UPD_INFO="gh-releases-zsync|linuxdeploy|linuxdeploy-plugin-qt|continuous|linuxdeploy-plugin-qt-$ARCH.AppImage"
5546

5647
wget "https://github.com/TheAssassin/linuxdeploy/releases/download/continuous/linuxdeploy-$ARCH.AppImage"
48+
# qemu is not happy about the AppImage type 2 magic bytes, so we need to "fix" that
49+
dd if=/dev/zero bs=1 count=3 seek=8 conv=notrunc of=linuxdeploy-"$ARCH".AppImage
5750
chmod +x linuxdeploy*.AppImage
51+
5852
./linuxdeploy-"$ARCH".AppImage --appdir AppDir \
5953
-d "$REPO_ROOT"/resources/linuxdeploy-plugin-qt.desktop \
6054
-i "$REPO_ROOT"/resources/linuxdeploy-plugin-qt.svg \

ci/docker/Dockerfile

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# generic Dockerfile for all architectures
2+
# used to "cache" prebuilt binaries of tools we use internally and installed dependencies
3+
# needs to be re-run in CI every time as we cannot store auto-built Docker images due to GitHub's strict quota
4+
# it will save a lot of time in local development environments, though
5+
6+
ARG docker_arch
7+
8+
# we'll just use Debian as a base image for now, mainly because it produces less headache than Ubuntu with arm
9+
# a big pro is that they ship an up to date CMake in their stable distribution
10+
# also, they still provide IA-32 builds for some reason...
11+
# some people in the AppImage community do not (want to) realize i386 is dead for good
12+
# we are going to drop i686 in the future!
13+
FROM ${docker_arch}/debian:stable
14+
15+
# variables that need to be availabe during build and runtime must(!) be repeated after FROM
16+
ARG ARCH
17+
18+
19+
SHELL ["bash", "-x", "-c"]
20+
21+
RUN export DEBIAN_FRONTEND=noninteractive && \
22+
apt-get update && \
23+
apt-get install -y build-essential cmake git gcovr patchelf wget \
24+
libmagic-dev libjpeg-dev libpng-dev libboost-filesystem-dev libboost-regex-dev \
25+
cimg-dev qtbase5-dev qtdeclarative5-dev-tools qml-module-qtquick2 qtdeclarative5-dev \
26+
googletest google-mock nlohmann-json3-dev autoconf libtool nano qtwebengine5-dev gdb && \
27+
apt-get autoremove --purge -y && \
28+
apt-get clean -y
29+
30+
# install into separate destdir to avoid polluting the $PATH with tools like ld that will break things
31+
ENV TOOLS_DIR=/tools
32+
33+
COPY install-static-binutils.sh /
34+
RUN bash /install-static-binutils.sh
35+
36+
COPY install-static-patchelf.sh /
37+
RUN bash /install-static-patchelf.sh
38+
39+
# make patchelf and strip available in $PATH
40+
# they are static binaries, so we can just copy them
41+
RUN cp "$TOOLS_DIR"/usr/bin/patchelf /usr/local/bin && \
42+
cp "$TOOLS_DIR"/usr/bin/strip /usr/local/bin
43+
44+
ENV CI=1
45+
46+
# in case AppImageLauncher is installed on the host, this little snippet will make AppImages launch normally
47+
#RUN echo -e '#! /bin/bash\nset -exo pipefail\nexec "$@"' > /usr/bin/AppImageLauncher && \
48+
# chmod +x /usr/bin/AppImageLauncher
49+
50+
# we need to configure some Qt tools, therefore we use /tmp as temporary home
51+
ENV HOME=/tmp
52+
53+
# make sure all AppImages can run in Docker
54+
ENV APPIMAGE_EXTRACT_AND_RUN=1
55+
56+
# seems necessary to make test.sh run on qemu/arm
57+
RUN apt-get update && \
58+
apt-get install -y qemu-user-static && \
59+
apt-get clean -y

ci/build-static-binutils.sh renamed to ci/docker/install-static-binutils.sh

+2-30
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,6 @@
33
set -e
44
set -x
55

6-
INSTALL_DESTDIR="$1"
7-
8-
if [[ "$INSTALL_DESTDIR" == "" ]]; then
9-
echo "Error: build dir $BUILD_DIR does not exist" 1>&2
10-
exit 1
11-
fi
12-
13-
# support cross-compilation for 32-bit ISAs
14-
case "$ARCH" in
15-
"x86_64"|"amd64")
16-
;;
17-
"i386"|"i586"|"i686")
18-
export CFLAGS="-m32"
19-
export CXXFLAGS="-m32"
20-
;;
21-
*)
22-
echo "Error: unsupported architecture: $ARCH"
23-
exit 1
24-
;;
25-
esac
26-
27-
# use RAM disk if possible
28-
if [ "$CI" == "" ] && [ -d /dev/shm ]; then
29-
TEMP_BASE=/dev/shm
30-
else
31-
TEMP_BASE=/tmp
32-
fi
33-
346
cleanup () {
357
if [ -d "$BUILD_DIR" ]; then
368
rm -rf "$BUILD_DIR"
@@ -55,5 +27,5 @@ make -j "$(nproc)"
5527
make clean
5628
make -j "$(nproc)" LDFLAGS="-all-static"
5729

58-
# install into user-specified destdir
59-
make install DESTDIR="$(readlink -f "$INSTALL_DESTDIR")"
30+
# install into separate destdir to avoid polluting the $PATH with tools like ld that will break things
31+
make install DESTDIR="${TOOLS_DIR}"

0 commit comments

Comments
 (0)