packaging, ci: build the apks with the Crafter.Build Flatpak
Crafter.Build's CI publishes a Flatpak and nothing else since 2026-10-02: the rolling `latest` release's `crafter-build-linux-x86_64-musl-v2.tar.gz` that build-package.sh fetched is gone (404 since 2026-10-03), so a push of a release would fail at `fetch crafter-build` and publish nothing. Install the Flatpak (`net.catcrafts.CrafterBuild//stable` from flatpak.catcrafts.net) in the job container and run it as crafter-build. bubblewrap inside the container needs a privileged runner, so the job runs on the `flatpak` label. The sysroot stays Alpine edge and gains llvm-runtimes, the package with the std module source of its own libc++ (libc++-dev ships none). make-sysroot.sh adds its list with --upgrade: run over a sysroot from before this change (libc++ 22.1.8, no std module source) a plain add installed llvm-runtimes 23.1.2 alone, and its std.cppm did not compile against the 22.1.8 headers (`'text_encoding' file not found`); with --upgrade libc++, libc++-dev and llvm-runtimes came out at 23.1.2 and it compiled. AGENTS.md §4.1, §4.3, §8.3 and §9.4 describe the route as it runs, and the README's Building section carries the install lines. Three comments in the files this touches said what is not so and are corrected: package.yml had fp6-img install the package by a pinned version and sha256 (it takes the newest in the registry), build-package.sh had fp6-img's image build run `make check` on the aarch64 binaries (it builds no imsd: no suite runs on them), and make-sysroot.sh's usage line lacked the --march without which the cross build fails on an x86 host. Tested: build-package.sh run end to end on this commit in a privileged alpine:edge container (podman) on the workstation.
This commit is contained in:
parent
828b0af95b
commit
a625248e18
5 changed files with 129 additions and 66 deletions
|
|
@ -8,16 +8,17 @@ name: package
|
|||
# Build: crafter-build cross-compiles against an Alpine aarch64 sysroot
|
||||
# (packaging/build-package.sh — AGENTS.md §4.3's cross-compiling flow), the
|
||||
# test suite runs natively, and packaging/APKBUILD.binary wraps the result.
|
||||
# This needs no privileged runner: it runs in an alpine:edge container on
|
||||
# the ordinary arch-latest runner.
|
||||
# crafter-build is the Crafter.Build Flatpak, and bubblewrap inside the job
|
||||
# container needs a privileged runner: the job runs on the `flatpak` label
|
||||
# (a privileged runner registered to this repository), in an alpine:edge
|
||||
# container.
|
||||
#
|
||||
# Release gating is the version: pkgver comes from implementations/main.cpp,
|
||||
# the registry answers 409 for an already-published version, and the publish
|
||||
# step treats that as "nothing to do" — so pushes only release when the
|
||||
# Version constant bumps. This is the only producer of the imsd apk: fp6-img
|
||||
# images install the published package from the registry (pinned version,
|
||||
# pinned sha256) instead of building their own, so image and 'apk upgrade'
|
||||
# carry the same binary.
|
||||
# images install the published package from the registry instead of
|
||||
# building their own, so image and 'apk upgrade' carry the same binary.
|
||||
#
|
||||
# Publishing needs PACKAGE_TOKEN (catbot account, package:write scope) — an
|
||||
# org-level secret on Catcrafts since 2026-09-02, shared with fp6-img;
|
||||
|
|
@ -30,7 +31,7 @@ on:
|
|||
|
||||
jobs:
|
||||
package:
|
||||
runs-on: arch-latest
|
||||
runs-on: flatpak
|
||||
container:
|
||||
image: alpine:edge
|
||||
timeout-minutes: 90
|
||||
|
|
|
|||
63
AGENTS.md
63
AGENTS.md
|
|
@ -105,8 +105,24 @@ pdn/boot match, defaults for absent keys — proven on the phone only, captures
|
|||
|
||||
### 4.1 Native, with crafter-build
|
||||
|
||||
Prerequisites: crafter-build, clang with libc++, lld, and the gio-2.0 headers
|
||||
(`glib2` on Arch, `glib-dev` on Alpine).
|
||||
crafter-build is the Crafter.Build Flatpak. It carries its own clang, lld and
|
||||
libc++, and its SDK (org.freedesktop.Sdk, from Flathub) has the gio-2.0
|
||||
headers, so the host needs Flatpak and nothing else:
|
||||
|
||||
```sh
|
||||
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo
|
||||
flatpak install catcrafts net.catcrafts.CrafterBuild//stable
|
||||
mkdir -p ~/.local/bin
|
||||
printf '#!/bin/sh\nexec flatpak run net.catcrafts.CrafterBuild//stable "$@"\n' > ~/.local/bin/crafter-build
|
||||
chmod +x ~/.local/bin/crafter-build
|
||||
```
|
||||
|
||||
`crafter-build` is a script and not an alias so that the name also resolves in
|
||||
a shell that has not defined the alias (a fresh `bash -c`, a CI step);
|
||||
`~/.local/bin` has to be on `PATH` (any directory on it will do). The
|
||||
sandbox does not see the host's `/tmp`: keep the checkout under `/home`, or
|
||||
grant its directory with `flatpak run --filesystem=<dir>`.
|
||||
|
||||
```sh
|
||||
crafter-build # bin/imsd-<target>-<march>-<mtune>-<id>/imsd
|
||||
|
|
@ -120,6 +136,14 @@ crafter-build lint # the house-style rules of lint-rules.h
|
|||
Output directories under `bin/` are named `<product>-<target>-<march>-<mtune>-<id>`;
|
||||
the `./bin/imsd-*/imsd` globs in §8.3 rely on that.
|
||||
|
||||
A host-native binary built this way links the Flatpak's libc++ through an
|
||||
rpath into `/app/lib`: run it inside the sandbox (`flatpak run --command=sh
|
||||
net.catcrafts.CrafterBuild//stable`, with `--socket=session-bus` for §8.3's
|
||||
daemons — the sandbox's own session bus is a filter that refuses their names;
|
||||
`crafter-build test` runs the suites there
|
||||
anyway), or build with `make` (§4.2) for one that runs on the host. A sysroot
|
||||
build (§4.3) gets no such rpath — it links the sysroot's libc++.
|
||||
|
||||
### 4.2 With make (distro packaging)
|
||||
|
||||
GNU make, `clang++` with libc++ and the libc++ std module sources (`std.cppm`:
|
||||
|
|
@ -152,22 +176,30 @@ the `80-imsd-ofono.preset`.
|
|||
### 4.3 Cross-compiling for a phone (aarch64 Alpine / postmarketOS)
|
||||
|
||||
```sh
|
||||
packaging/make-sysroot.sh # one-time: Alpine aarch64 sysroot from the CDN
|
||||
packaging/make-sysroot.sh # one-time: Alpine aarch64 sysroot from the CDN (edge)
|
||||
crafter-build -- --target=aarch64-alpine-linux-musl \
|
||||
--sysroot=$HOME/.cache/imsd/sysroot-aarch64-alpine \
|
||||
--march=armv8.6-a+fp16fml+aes+sha3+sm4 --mtune=cortex-a720
|
||||
# the same flags after `crafter-build test --target=aarch64-alpine-linux-musl`
|
||||
# run the suite under qemu-aarch64 against the sysroot
|
||||
# `crafter-build test` with the same flags fails: its runner is qemu-aarch64,
|
||||
# which the Flatpak's sandbox does not have. Test host-native (§4.1).
|
||||
```
|
||||
|
||||
`make-sysroot.sh` needs no root, qemu or device: it downloads
|
||||
`apk-tools-static` and the Alpine keys and populates a minimal rootfs (musl,
|
||||
libc++, glib and their headers, compiler-rt, libunwind, gcc for its
|
||||
libc++ with its std module source, glib and their headers, compiler-rt,
|
||||
libunwind, gcc for its
|
||||
`crtbeginS.o`/libgcc, which clang's `*-alpine-linux-musl` driver links
|
||||
against). In a cross build `project.cpp` does not ask the host's pkg-config
|
||||
(it would answer for the wrong architecture); it adds `-I=/usr/include/glib-2.0`
|
||||
and `-I=/usr/lib/glib-2.0/include`, which clang resolves inside the sysroot.
|
||||
This is the flow CI runs (§9.4).
|
||||
crafter-build compiles the std module against the sysroot's libc++ headers
|
||||
from the sysroot's own source — Alpine's `llvm-runtimes`, the same release
|
||||
as its libc++, which `make-sysroot.sh` installs (`libc++-dev` ships none);
|
||||
run over an existing sysroot the script upgrades its packages (`apk add
|
||||
--upgrade`).
|
||||
Without that source it falls back to the toolchain's source for the same libc++
|
||||
major release, then to the sources Crafter.Build carries for other
|
||||
releases. This is the flow CI runs (§9.4).
|
||||
|
||||
## 5. Tests
|
||||
|
||||
|
|
@ -791,7 +823,9 @@ Known gaps, all inherited rather than introduced:
|
|||
(`imsd-ofono-restart-calls.sh`) restarts the Calls daemon of every user
|
||||
running one. The first start since boot leaves a running Calls alone.
|
||||
|
||||
Development without a phone or root:
|
||||
Development without a phone or root (crafter-build's binaries inside the
|
||||
sandbox, the `make` build's `build/make/imsd` and `build/make/imsd-ofonod` on
|
||||
the host: §4.1):
|
||||
|
||||
```sh
|
||||
./bin/imsd-*/imsd --session & # imsd's ABI on the session bus
|
||||
|
|
@ -857,10 +891,15 @@ install finds no unit active and marks nothing. `imsd-systemd` marks
|
|||
### 9.4 CI and what a push means
|
||||
|
||||
`.forgejo/workflows/package.yml` runs on every push to `main` (and on
|
||||
dispatch): in an `alpine:edge` container, `packaging/build-package.sh`
|
||||
provisions clang/lld/llvm-runtimes/glib-dev/abuild, fetches the musl build of
|
||||
crafter-build, builds the sysroot, cross-compiles the four products, runs the
|
||||
suite natively, wraps the result with `APKBUILD.binary`, and the publish step
|
||||
dispatch): on the `flatpak` runner label (a privileged runner registered to
|
||||
this repository — bubblewrap inside the job container needs the privilege), in an
|
||||
`alpine:edge` container, `packaging/build-package.sh` installs the
|
||||
Crafter.Build Flatpak (`net.catcrafts.CrafterBuild//stable` from
|
||||
flatpak.catcrafts.net — the branch Crafter.Build publishes from its release
|
||||
tags, where `beta` follows every push to its master; the build log names its
|
||||
commit) and llvm/abuild, builds
|
||||
the sysroot, cross-compiles the four products, runs the suite natively inside
|
||||
the Flatpak, wraps the result with `APKBUILD.binary`, and the publish step
|
||||
uploads the apks to the Forgejo Alpine registry
|
||||
(`…/api/packages/Catcrafts/alpine/edge/fp6`) with `PACKAGE_TOKEN`. The
|
||||
registry answers 409 for an already-published version and the step treats
|
||||
|
|
|
|||
26
README.md
26
README.md
|
|
@ -42,11 +42,11 @@ your carrier's P-CSCF address.
|
|||
|
||||
On a Catcrafts Fairphone 6 image imsd is installed from the project's apk
|
||||
registry and upgrades with `apk upgrade` (an upgrade restarts the daemon; a
|
||||
call in progress ends). Elsewhere, build it (below), `make install`, and
|
||||
enable the units yourself — `systemctl enable imsd`, and `systemctl enable
|
||||
--now imsd-ofonod` for GNOME Calls: imsd's preset, the
|
||||
`multi-user.target.wants` link and the `/etc/imsd.env` condition below come
|
||||
with the apk, not with `make install`.
|
||||
call in progress ends). Elsewhere, build it with `make` (below), run
|
||||
`make install`, and enable the units yourself — `systemctl enable imsd`,
|
||||
and `systemctl enable --now imsd-ofonod` for GNOME Calls: imsd's preset,
|
||||
the `multi-user.target.wants` link and the `/etc/imsd.env` condition
|
||||
below come with the apk, not with `make install`.
|
||||
|
||||
Configuration is environment variables in `/etc/imsd.env`; the one required
|
||||
setting is the P-CSCF:
|
||||
|
|
@ -102,13 +102,23 @@ session and root may place, answer and end calls; an ssh login may not.
|
|||
|
||||
## Building
|
||||
|
||||
[Crafter Build](https://forgejo.catcrafts.net/Catcrafts/Crafter.Build) plus
|
||||
clang/libc++/lld and the gio-2.0 headers:
|
||||
With the [Crafter Build](https://forgejo.catcrafts.net/Catcrafts/Crafter.Build)
|
||||
Flatpak, which carries the compiler:
|
||||
|
||||
```sh
|
||||
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo
|
||||
flatpak install catcrafts net.catcrafts.CrafterBuild//stable
|
||||
alias crafter-build='flatpak run net.catcrafts.CrafterBuild//stable'
|
||||
crafter-build # the daemon; -- --product=media|dialerd|ofonod for the others
|
||||
crafter-build test # the seven unit-test suites
|
||||
make && make check # the same, without crafter-build (distro packaging)
|
||||
```
|
||||
|
||||
Or without crafter-build (distro packaging), with clang/libc++/lld and the
|
||||
gio-2.0 headers:
|
||||
|
||||
```sh
|
||||
make && make check
|
||||
```
|
||||
|
||||
Cross-compiling for a phone, the Makefile's requirements, the package layout
|
||||
|
|
|
|||
|
|
@ -5,44 +5,28 @@
|
|||
# cross-compiling flow of AGENTS.md §4.3), run the test suite natively, and package
|
||||
# the result via packaging/APKBUILD.binary. Expects an x86_64 Alpine
|
||||
# environment with root — the workflow runs it in an alpine:edge job
|
||||
# container on an ordinary runner. Root only installs packages and hands off
|
||||
# to a scratch user: the sysroot is built with apk.static --usermode (which
|
||||
# refuses root) and abuild wants a user too.
|
||||
# container on the privileged `flatpak` runner: crafter-build is the
|
||||
# Crafter.Build Flatpak, and bubblewrap inside the container needs the
|
||||
# privilege. Root only installs packages and hands off to a scratch user: the
|
||||
# sysroot is built with apk.static --usermode (which refuses root) and abuild
|
||||
# wants a user too.
|
||||
#
|
||||
# Built packages land in /home/build/.local/share/abuild/*/aarch64/imsd*.apk;
|
||||
# the workflow's publish step uploads them to the Forgejo Alpine registry.
|
||||
|
||||
set -eu
|
||||
|
||||
# The musl build of crafter-build (Crafter.Build CI's release-musl job):
|
||||
# this container is Alpine, and the glibc launcher cannot run on musl. v2 =
|
||||
# SSE4.2 baseline: the CI box is an Intel N5105 (no AVX). Overridable for
|
||||
# local rehearsals (file:// works).
|
||||
CRAFTER_URL=${CRAFTER_URL:-https://forgejo.catcrafts.net/Catcrafts/Crafter.Build/releases/download/latest/crafter-build-linux-x86_64-musl-v2.tar.gz}
|
||||
# crafter-build is the Crafter.Build Flatpak: it carries its own clang, lld
|
||||
# and libc++, and its SDK has glib, so nothing that compiles is installed
|
||||
# here. `stable` is the branch Crafter.Build publishes from its release tags
|
||||
# (`beta` follows every push to its master), so the toolchain of a release
|
||||
# moves only with a release of the toolchain. The branch is overridable
|
||||
# for a rehearsal against another one.
|
||||
CRAFTER_REMOTE=${CRAFTER_REMOTE:-https://flatpak.catcrafts.net/catcrafts.flatpakrepo}
|
||||
CRAFTER_REF=${CRAFTER_REF:-net.catcrafts.CrafterBuild//stable}
|
||||
|
||||
SRC=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
|
||||
# clang cross-targets aarch64 natively and the target's libc++/glib come from
|
||||
# the sysroot; llvm-runtimes/libc++-dev/glib-dev here serve the NATIVE
|
||||
# test-suite run. build-base = Alpine's standard build environment (the one
|
||||
# abuild implies): binutils' ld/ar for clang's default link driver, gcc's
|
||||
# libgcc_s/crt objects the musl clang driver links against.
|
||||
if [ "$(id -u)" = 0 ]; then
|
||||
apk add -q git curl tar clang lld llvm llvm-runtimes libc++-dev llvm-libunwind-dev glib-dev \
|
||||
build-base abuild sudo
|
||||
id build >/dev/null 2>&1 || adduser -D build
|
||||
addgroup build abuild 2>/dev/null || true
|
||||
echo 'build ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/build
|
||||
# abuild in cross mode strips with $CHOST-strip; llvm-strip handles any
|
||||
# ELF arch, so give it that name
|
||||
ln -sf "$(command -v llvm-strip)" /usr/local/bin/aarch64-alpine-linux-musl-strip
|
||||
# the CI checkout arrives root-owned; crafter-build writes bin/ into it
|
||||
chown -R build "$SRC"
|
||||
# -l: a login shell, so HOME really is /home/build (abuild keys + output);
|
||||
# it scrubs the environment, so carry the one knob that matters across
|
||||
exec su -l build -c "CRAFTER_URL='${CRAFTER_URL:-}' sh -eu '$SRC/packaging/build-package.sh'"
|
||||
fi
|
||||
|
||||
retry() { # retry <description> <cmd...>
|
||||
_desc=$1; shift
|
||||
for _i in 1 2 3; do
|
||||
|
|
@ -54,26 +38,49 @@ retry() { # retry <description> <cmd...>
|
|||
return 1
|
||||
}
|
||||
|
||||
# llvm for llvm-strip (abuild in cross mode strips with $CHOST-strip); the
|
||||
# compiler is the Flatpak's.
|
||||
if [ "$(id -u)" = 0 ]; then
|
||||
apk add -q git curl tar flatpak llvm abuild sudo
|
||||
# the Flatpak as a system installation the build user runs; its SDK
|
||||
# (org.freedesktop.Sdk) comes from Flathub
|
||||
flatpak remote-add --if-not-exists catcrafts "$CRAFTER_REMOTE"
|
||||
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
retry "install crafter-build" flatpak install -y --noninteractive catcrafts "$CRAFTER_REF"
|
||||
id build >/dev/null 2>&1 || adduser -D build
|
||||
addgroup build abuild 2>/dev/null || true
|
||||
echo 'build ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/build
|
||||
# abuild in cross mode strips with $CHOST-strip; llvm-strip handles any
|
||||
# ELF arch, so give it that name
|
||||
ln -sf "$(command -v llvm-strip)" /usr/local/bin/aarch64-alpine-linux-musl-strip
|
||||
# the CI checkout arrives root-owned; crafter-build writes bin/ into it
|
||||
chown -R build "$SRC"
|
||||
# -l: a login shell, so HOME really is /home/build (abuild keys + output);
|
||||
# it scrubs the environment, so carry the one knob that matters across
|
||||
exec su -l build -c "CRAFTER_REF='$CRAFTER_REF' sh -eu '$SRC/packaging/build-package.sh'"
|
||||
fi
|
||||
|
||||
# implementations/main.cpp is the version's single source of truth (same
|
||||
# derivation as make-bin-tarball.sh)
|
||||
VER=$(sed -n 's/.*char\* Version = "\(.*\)".*/\1/p' "$SRC/implementations/main.cpp")
|
||||
[ -n "$VER" ] || { echo "cannot read Version from implementations/main.cpp" >&2; exit 1; }
|
||||
echo ">> packaging imsd $VER"
|
||||
|
||||
# --- crafter-build: static launcher from the rolling release
|
||||
mkdir -p "$HOME/crafter-build"
|
||||
retry "fetch crafter-build" \
|
||||
sh -c "curl -fsSL '$CRAFTER_URL' | tar -xz -C '$HOME/crafter-build'"
|
||||
PATH="$HOME/crafter-build/bin:$PATH"
|
||||
export CRAFTER_BUILD_HOME="$HOME/crafter-build/share/crafter-build"
|
||||
# --- crafter-build: the Flatpak, under the name the rest of this file uses.
|
||||
# The build log names the build it ran with.
|
||||
mkdir -p "$HOME/bin"
|
||||
printf '#!/bin/sh\nexec flatpak run %s "$@"\n' "$CRAFTER_REF" > "$HOME/bin/crafter-build"
|
||||
chmod +x "$HOME/bin/crafter-build"
|
||||
PATH="$HOME/bin:$PATH"
|
||||
flatpak info "$CRAFTER_REF" | sed -n 's/^ *\(Version\|Commit\|Date\): */crafter-build \1: /p'
|
||||
crafter-build --version
|
||||
|
||||
# --- aarch64 Alpine sysroot (unprivileged: apk.static --usermode)
|
||||
SYSROOT="$HOME/.cache/imsd/sysroot-aarch64-alpine"
|
||||
retry "make sysroot" "$SRC/packaging/make-sysroot.sh" "$SYSROOT"
|
||||
|
||||
# --- cross-compile the products; run the suites natively (same sources,
|
||||
# host arch — the aarch64 binaries are additionally exercised by fp6-img's
|
||||
# chroot `make check` whenever an image builds)
|
||||
# --- cross-compile the products; run the suites natively, inside the Flatpak
|
||||
# (same sources, host arch: no suite runs on the aarch64 binaries)
|
||||
cd "$SRC"
|
||||
# The phone is one SoC, 4x Cortex-A520 + 4x Cortex-A720, so build for it.
|
||||
# armv8.6-a is the highest level whose mandatory set it exposes (8.7 would
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
# clang can use via --sysroot. No root, no qemu, no device needed:
|
||||
#
|
||||
# packaging/make-sysroot.sh [dir] # default: ~/.cache/imsd/sysroot-aarch64-alpine
|
||||
# crafter-build --target=aarch64-alpine-linux-musl --sysroot=<dir>
|
||||
# crafter-build -- --target=aarch64-alpine-linux-musl --sysroot=<dir> \
|
||||
# --march=… --mtune=… # the phone's values: AGENTS.md §4.3
|
||||
set -eu
|
||||
|
||||
SYSROOT="${1:-$HOME/.cache/imsd/sysroot-aarch64-alpine}"
|
||||
|
|
@ -16,8 +17,13 @@ MIRROR="${MIRROR:-https://dl-cdn.alpinelinux.org/alpine/edge}"
|
|||
ARCH=aarch64
|
||||
# gcc is in the list for its crtbeginS.o/libgcc: clang's driver for
|
||||
# *-alpine-linux-musl links against the GCC runtime it detects under
|
||||
# <sysroot>/usr/lib/gcc/<triple>/
|
||||
PKGS="musl-dev libc++ libc++-dev glib-dev compiler-rt llvm-libunwind-dev gcc"
|
||||
# <sysroot>/usr/lib/gcc/<triple>/. llvm-runtimes is in it for the std module
|
||||
# source of this very libc++ (usr/share/libc++/v1/std.cppm; libc++-dev ships
|
||||
# none): crafter-build compiles the sysroot's own copy first. --upgrade below
|
||||
# is for a run over an existing sysroot: added alone beside libc++ 22,
|
||||
# llvm-runtimes 23's source did not compile against its headers; upgraded
|
||||
# with it, it did.
|
||||
PKGS="musl-dev libc++ libc++-dev llvm-runtimes glib-dev compiler-rt llvm-libunwind-dev gcc"
|
||||
|
||||
work=$(mktemp -d)
|
||||
trap 'rm -rf "$work"' EXIT
|
||||
|
|
@ -40,6 +46,6 @@ mkdir -p "$SYSROOT"
|
|||
--arch "$ARCH" --root "$SYSROOT" --initdb --no-scripts --no-cache \
|
||||
--usermode --keys-dir "$work/usr/share/apk/keys/$ARCH" \
|
||||
-X "$MIRROR/main" -X "$MIRROR/community" \
|
||||
add $PKGS
|
||||
add --upgrade $PKGS
|
||||
|
||||
echo ">> done: $SYSROOT"
|
||||
|
|
|
|||
Loading…
Reference in a new issue