Skip to content

Commit 923d6c2

Browse files
committed
Merge branch 'release/samedec-0.4.1'
2 parents 4396489 + 51d563a commit 923d6c2

24 files changed

Lines changed: 1393 additions & 770 deletions

File tree

.github/container/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Rust Cross-Compiling Containers
2+
3+
[cross-rs](https://github.com/cross-rs/cross) splits the build environment into:
4+
5+
1. An "outer" build environment, which contains `cargo` and a toolchain.
6+
7+
2. An "inner" build environment, which includes a gcc cross-compiler, foreign-architecture runtime libraries, and qemu.
8+
9+
If the outer build environment is containerized, this introduces problems. The inner build environment (2) needs access to the host's Docker or podman socket. This can be done, but it does not mesh well with Github Actions' `jobs:*:container` option.
10+
11+
Further, with containers, we want all parts of the build environment:
12+
13+
* Included and ready-to-use
14+
* Fully-defined by the `Containerfile` and the image's `sha256:` hash
15+
16+
Depending on an external container at runtime defeats some of these design goals.
17+
18+
Instead of using cross-rs, we create our own environment that includes all of these tools. See
19+
20+
```
21+
./build.sh
22+
```
23+
24+
to build it. The build instructions are heavily influenced by the cross-rs project but do not depend on it.
25+
26+
You can use any of these containers offline to build samedec. Define the following volumes:
27+
28+
* `/src`: the repository root of a Rust project
29+
* `/install`: destination for binaries installed with `cargo install`
30+
* `/src/target` (**optional**): a build directory.
31+
* `/cargohome` (**optional**): a persistent `$CARGO_HOME` directory
32+
33+
Example:
34+
35+
```bash
36+
mkdir -p out/aarch64-unknown-linux-gnu
37+
38+
podman run \
39+
--security-opt label=disable \
40+
--userns=keep-id:uid=1001,gid=1001 \
41+
--rm -it \
42+
--volume .:/src:ro \
43+
--volume ./target:/src/target:rw \
44+
--volume ./out/aarch64-unknown-linux-gnu:/install \
45+
ghcr.io/cbs228/sameold/builder/aarch64-unknown-linux-gnu \
46+
cargo install --path crates/samedec
47+
```
48+
49+
You should run your build as UID 1001 within the container. The above `--userns` mapping will accomplish this.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# Debian base image, with arm64 toolchains
3+
#
4+
5+
FROM ghcr.io/cbs228/sameold/builder/rust:latest
6+
7+
ARG SOURCE_DATE_EPOCH
8+
9+
RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
10+
--mount=target=/var/cache/apt,type=cache,sharing=locked \
11+
--mount=destination=/var/log,type=tmpfs \
12+
[ "$(dpkg --print-architecture)" != arm64 ] || exit 0; \
13+
set -eux; \
14+
apt-get update; \
15+
apt-get install -y \
16+
binutils-aarch64-linux-gnu \
17+
gcc-aarch64-linux-gnu \
18+
libc6-dev-arm64-cross \
19+
libc6:arm64 \
20+
; \
21+
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache
22+
23+
ARG RUST_TARGET="aarch64-unknown-linux-gnu"
24+
25+
RUN set -eux; \
26+
export CARGO_HOME=/usr/local/cargo; \
27+
rustup target add "$RUST_TARGET";
28+
29+
ENV CARGO_BUILD_TARGET="$RUST_TARGET" \
30+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
31+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-run-maybe
32+
33+
USER builder
34+
35+
LABEL org.opencontainers.image.description="A Debian-based Rust cross-compiling environment."
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# Debian base image, with armhf toolchains
3+
#
4+
5+
FROM ghcr.io/cbs228/sameold/builder/rust:latest
6+
7+
ARG SOURCE_DATE_EPOCH
8+
9+
RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
10+
--mount=target=/var/cache/apt,type=cache,sharing=locked \
11+
--mount=destination=/var/log,type=tmpfs \
12+
[ "$(dpkg --print-architecture)" != armhf ] || exit 0; \
13+
set -eux; \
14+
apt-get update; \
15+
apt-get install -y \
16+
binutils-arm-linux-gnueabi \
17+
gcc-arm-linux-gnueabihf \
18+
libc6-dev-armhf-cross \
19+
libc6:armhf \
20+
; \
21+
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache
22+
23+
ARG RUST_TARGET="armv7-unknown-linux-gnueabihf"
24+
25+
RUN set -eux; \
26+
export CARGO_HOME=/usr/local/cargo; \
27+
rustup target add "$RUST_TARGET";
28+
29+
ENV CARGO_BUILD_TARGET="$RUST_TARGET" \
30+
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
31+
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-run-maybe
32+
33+
USER builder
34+
35+
LABEL org.opencontainers.image.description="A Debian-based Rust cross-compiling environment."

.github/container/base/Dockerfile

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#
2+
# Debian base image
3+
#
4+
5+
# or buster-20240612-slim for a snapshot-capable, reproducible image
6+
ARG DEBIAN_TAG=buster-slim
7+
8+
FROM docker.io/library/debian:${DEBIAN_TAG}
9+
10+
ARG DEBIAN_TAG
11+
ARG SOURCE_DATE_EPOCH
12+
13+
# Create a non-root user for running builds
14+
# GHA wants uid 1001
15+
ARG UID=1001
16+
17+
RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
18+
--mount=target=/var/cache/apt,type=cache,sharing=locked \
19+
--mount=destination=/var/log,type=tmpfs \
20+
set -eux; \
21+
# create an unprivileged user for builds
22+
groupadd builder -g "$UID"; \
23+
useradd builder -u "$UID" -g "$UID" --create-home; \
24+
# build directories for any user
25+
mkdir -m 1777 /src /install /cargo; \
26+
# re-enable apt caching (we have a cache mount)
27+
rm -f /etc/apt/apt.conf.d/docker-clean; \
28+
# if tag contains numerics, like buster-20240612-slim, use the
29+
# snapshot URL that's baked into the image
30+
if echo "${DEBIAN_TAG}" | grep -q "[0-9]"; then \
31+
sed -i -r \
32+
-e 's/^deb/# deb/' \
33+
-e 's|^#\s*(.*http://snapshot\.)|\1|' \
34+
/etc/apt/sources.list; \
35+
cat >&2 /etc/apt/sources.list; \
36+
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/use-snapshot.conf; \
37+
echo 'Acquire::Retries "10";' >> /etc/apt/apt.conf.d/use-snapshot.conf; \
38+
echo 'Acquire::Retries::Delay::Maximum "600";' >> /etc/apt/apt.conf.d/use-snapshot.conf; \
39+
fi; \
40+
# enable packages from multiple architectures
41+
dpkg --add-architecture amd64; \
42+
dpkg --add-architecture armhf; \
43+
dpkg --add-architecture arm64; \
44+
dpkg --add-architecture i386; \
45+
# install native C compilers, CI utilities, and qemu
46+
apt-get update; \
47+
apt-get install -y --no-install-recommends \
48+
build-essential \
49+
ca-certificates \
50+
curl \
51+
gcc \
52+
git \
53+
libc6-dev \
54+
qemu-user \
55+
qemu-user-binfmt \
56+
tar \
57+
zstd; \
58+
git config --system --add safe.directory '*'; \
59+
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache
60+
61+
# Directories for sources and installed binaries
62+
VOLUME ["/src", "/install"]
63+
64+
LABEL org.opencontainers.image.source="https://github.com/cbs228/sameold"
65+
LABEL org.opencontainers.image.description="A minimal debian cross-compiling environment with good glibc compatibility."
66+
67+
WORKDIR "/src"

.github/container/build.sh

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#!/usr/bin/env bash
2+
#
3+
# (re)build the CI container image
4+
#
5+
# run with --push to push the images.
6+
7+
# set to disable cache
8+
NO_CACHE="${NO_CACHE:-}"
9+
10+
DEBIAN_TAG="buster-20240612-slim"
11+
CONTAINER_PREFIX="ghcr.io/cbs228"
12+
CONTAINER_FQNAME="${CONTAINER_PREFIX}/sameold/builder/%s"
13+
14+
RUST_VERSIONS=("1.84.0")
15+
16+
usage() {
17+
cat <<EOF
18+
Usage: $0 [--push]
19+
20+
Build container images for the CI environment. To select
21+
a particular container platform tool like podman, set
22+
23+
BUILDER=podman
24+
25+
Prior to pushing, make sure to
26+
27+
podman login "$CONTAINER_PREFIX"
28+
29+
or equivalent.
30+
EOF
31+
}
32+
33+
run() {
34+
# Echo and run
35+
echo >&2 "$@"
36+
"$@"
37+
}
38+
39+
container_builder() {
40+
# Usage: automatically detect container platform command
41+
42+
if [ -x "${BUILDER:-}" ]; then
43+
echo "${BUILDER}"
44+
elif [ -n "${BUILDER:-}" ]; then
45+
command -v "${BUILDER}"
46+
else
47+
{
48+
command -v podman || \
49+
command -v docker
50+
} 2>/dev/null || {
51+
echo >&2 "FATAL: container platform tools not found"
52+
return 1;
53+
}
54+
fi
55+
}
56+
57+
container_name() {
58+
# Usage: container_name SUFFIX
59+
60+
#shellcheck disable=SC2059
61+
printf "$CONTAINER_FQNAME" "$1"
62+
}
63+
64+
buildcontainer() {
65+
# Usage: buildcontainer ARGS
66+
#
67+
# Run the $BUILDER to build a container image. Some standardized
68+
# arguments are passed to every build.
69+
70+
if [[ $BUILDER =~ podman$ ]]; then
71+
run "$BUILDER" build \
72+
--build-arg SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
73+
--timestamp "$SOURCE_DATE_EPOCH" \
74+
${NO_CACHE:+--no-cache} \
75+
"$@"
76+
else
77+
# docker mode; 100% untested
78+
run "$BUILDER" buildx build \
79+
--build-arg SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
80+
--output type=docker,rewrite-timestamp=true \
81+
${NO_CACHE:+--no-cache} \
82+
"$@"
83+
fi
84+
}
85+
86+
tagall() {
87+
# Usage: tagall SHORTNAME TAG0 TAG1 ...
88+
#
89+
# Apply all tags to the given image, which must already be tagged as
90+
# "current/container/prefix/$SHORTNAME:$TAG0"
91+
92+
local prefix
93+
prefix="$(container_name "${1?}")"
94+
shift
95+
96+
run "$BUILDER" tag "${@/#/"$prefix:"}"
97+
}
98+
99+
pushall() {
100+
# Usage: pushall SHORTNAME TAG0 TAG1 ...
101+
#
102+
# Push the given image "current/container/prefix/$SHORTNAME" and
103+
# all the tags specified on the command line. The pushes are not
104+
# atomic and will happen sequentially.
105+
106+
local prefix
107+
prefix="$(container_name "${1?}")"
108+
shift
109+
110+
local tag
111+
for tag in "$@"; do
112+
run "$BUILDER" push "${prefix}:${tag}"
113+
done
114+
}
115+
116+
# return if sourced
117+
(return 0 2>/dev/null) && return 0
118+
119+
set -euo pipefail
120+
121+
if ! options="$(getopt -o 'hp' --long help,push -- "$@")"; then
122+
usage >&2
123+
exit 1
124+
fi
125+
126+
eval set -- "${options:-}"
127+
push_images=''
128+
while true; do
129+
case "${1:-}" in
130+
(-h | --help)
131+
usage
132+
exit 0 ;;
133+
(-p | --push)
134+
push_images=y ;;
135+
('') ;;
136+
esac
137+
shift || break
138+
done
139+
140+
BUILDER="$(container_builder)"
141+
selfdir="$(dirname "$(realpath -e "${0?}")")"
142+
143+
# set SOURCE_DATE_EPOCH if possible
144+
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git log -1 --pretty=%ct -- "$selfdir" || date +%s)}"
145+
export SOURCE_DATE_EPOCH
146+
147+
# tag with "latest" and SOURCE_DATE_EPOCH
148+
CONTAINER_TAGS=("latest" "$(date --date '@'"$SOURCE_DATE_EPOCH" +'%Y-%m-%d')")
149+
150+
# build the base image
151+
base_tag="$(container_name base):${CONTAINER_TAGS[0]}"
152+
153+
buildcontainer \
154+
--build-arg DEBIAN_TAG="$DEBIAN_TAG" \
155+
--tag "$base_tag" \
156+
"${selfdir?}/base"
157+
158+
# add rust to the base image
159+
rust_tag="$(container_name rust):${CONTAINER_TAGS[0]}"
160+
161+
buildcontainer \
162+
--from "$base_tag" \
163+
--build-arg RUST_VERSIONS="${RUST_VERSIONS[*]}" \
164+
--tag "$rust_tag" \
165+
"${selfdir?}/rust"
166+
167+
# build architecture-specific images
168+
for containerdir in "${selfdir?}/"*-*-*; do
169+
[ -d "$containerdir" ] || continue
170+
171+
platform_triple="$(basename ${containerdir})"
172+
cur_tag="$(container_name "$platform_triple"):${CONTAINER_TAGS[0]}"
173+
174+
buildcontainer \
175+
--from "$rust_tag" \
176+
--tag "${cur_tag}" \
177+
"${containerdir}"
178+
done
179+
180+
# if all builds succeed, apply remaining tags...
181+
tagall base "${CONTAINER_TAGS[@]}"
182+
tagall rust "${CONTAINER_TAGS[@]}"
183+
for containerdir in "${selfdir?}/"*-*-*; do
184+
[ -d "$containerdir" ] || continue
185+
186+
tagall "$(basename "$containerdir")" "${CONTAINER_TAGS[@]}"
187+
done
188+
189+
[ -n "${push_images:-}" ] || exit 0
190+
191+
# ... and push
192+
for containerdir in "${selfdir?}/"*; do
193+
[ -d "$containerdir" ] || continue
194+
195+
pushall "$(basename "$containerdir")" "${CONTAINER_TAGS[@]}"
196+
done

0 commit comments

Comments
 (0)