An aport, the units, and everything a phone needs to come up with a working sensor without a single command being run by hand. Verified on the dev phone across two reboots: modules-load.d loads qcomtee, tmpfiles builds the SFS root, the mount unit brings up persist, and the daemon is ready 51 seconds into the boot, owning net.reactivated.Fprint with the enrolled finger visible. The packaging shape is the one imsd uses for 81voltd. A versioned provides="fprintd=..." satisfies plasma-workspace -- its Users KCM is the enrolment UI and speaks exactly this bus name -- and excludes the real fprintd, which is not tidiness: fprintd is D-Bus-activatable, so a client call would otherwise start it and fight us for the name. The cost is the fprintd-* CLIs, which go with the package. fprintd-pam is an install_if subpackage pinned to the exact fprintd version, so the provides breaks its condition and apk purges it -- taking pam_fprintd, which is the entire point of the daemon, with it. Depending on it explicitly is what keeps it, and it has no dependency on fprintd itself. Two things the packaging exposed in the daemon: The transcript is for experiments, not for a shipped daemon. A file per start in an unrotated directory, recording the time of every unlock, to say what the journal already has. It is now opt-in behind --log-dir, which is what deploy-dev.sh passes since fplearn.sh reads it. Taking it off the daemon path also took away the setvbuf it was doing as a side effect of dup2'ing fd 1, and under systemd stdout is a pipe, which means full buffering: the daemon started, worked, answered D-Bus calls, and printed nothing. A working daemon that looks hung. stdout is now line-buffered from the first line of main. The config ships as generated by fp6fpcfg.py --daemon --verbose, sha256 b205c756914a66f1, because that is the file every accuracy number was measured on. The quieter variant is untested and switching is a measurement. The trustlet is not here and never will be: focal64.mbn is a proprietary OEM-signed blob, and the unit's ConditionPathExists is what keeps the package inert without it -- as it does on a kernel with no CONFIG_QCOMTEE.
114 lines
5.3 KiB
Shell
Executable file
114 lines
5.3 KiB
Shell
Executable file
#!/bin/sh -eu
|
|
# SPDX-License-Identifier: GPL-3.0-only
|
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
# CI package build: cross-compile fingerprintd for aarch64 with crafter-build
|
|
# (the README's "Cross-compiling" flow), run the test suite natively, and
|
|
# package the result via packaging/APKBUILD. 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.
|
|
#
|
|
# Built packages land in /home/build/.local/share/abuild/*/aarch64/fingerprintd*.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}
|
|
|
|
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
|
|
"$@" && return 0
|
|
echo "$_desc failed (attempt $_i/3), retrying in 15s..." >&2
|
|
sleep 15
|
|
done
|
|
echo "$_desc failed after 3 attempts" >&2
|
|
return 1
|
|
}
|
|
|
|
# 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 fingerprintd $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"
|
|
|
|
# --- aarch64 Alpine sysroot (unprivileged: apk.static --usermode)
|
|
SYSROOT="$HOME/.cache/fingerprintd/sysroot-aarch64-alpine"
|
|
retry "make sysroot" "$SRC/packaging/make-sysroot.sh" "$SYSROOT"
|
|
|
|
# --- libqcomtee: Qualcomm's BSD-3 QTEE client (quic-teec, pinned commit),
|
|
# which is not in Alpine and is not vendored here. crafter-build looks for it
|
|
# under ~/.cache/fingerprintd/libqcomtee-<target>, which is this script's
|
|
# default output dir. Needs git, installed above.
|
|
retry "make libqcomtee" "$SRC/packaging/make-libqcomtee.sh" \
|
|
--target=aarch64-alpine-linux-musl --sysroot="$SYSROOT" --march=armv8-a
|
|
|
|
# --- cross-compile the daemon; run the suites natively. The core is portable
|
|
# by construction (no GLib, no libqcomtee, no system headers), which is what
|
|
# lets the wire formats and state machines be tested on the build host at all.
|
|
cd "$SRC"
|
|
XTARGET="--target=aarch64-alpine-linux-musl --sysroot=$SYSROOT --march=armv8-a --mtune=generic"
|
|
crafter-build -- $XTARGET
|
|
crafter-build test
|
|
|
|
# --- bundle + package
|
|
./packaging/make-bin-tarball.sh "$VER"
|
|
|
|
PKG="$HOME/pkg"
|
|
rm -rf "$PKG"
|
|
mkdir -p "$PKG"
|
|
cp "$SRC/packaging/APKBUILD" "$PKG/APKBUILD"
|
|
mv "fingerprintd-$VER.tar.gz" "$PKG/"
|
|
sed -i "s/^pkgver=.*/pkgver=$VER/" "$PKG/APKBUILD"
|
|
# a throwaway signing key: phones trust the registry-signed APKINDEX, not
|
|
# per-package keys (same situation as fp6-img's pmbootstrap-built packages).
|
|
# abuild >= 3.18 keeps keys under ~/.config/abuild and output under
|
|
# ~/.local/share/abuild (REPODEST default).
|
|
abuild-keygen -a -n >/dev/null 2>&1
|
|
sudo cp "$HOME"/.config/abuild/*.rsa.pub /etc/apk/keys/
|
|
# CHOST puts abuild in cross mode so arch="aarch64" packages on this x86_64
|
|
# host. -d skips dependency handling entirely: nothing compiles under abuild
|
|
# (with -r, cross mode would try to install a nonexistent build-base-aarch64
|
|
# plus the runtime depends); !tracedeps in the APKBUILD keeps abuild from
|
|
# resolving the aarch64 ELF NEEDED entries against this x86_64 host.
|
|
cd "$PKG"
|
|
abuild checksum
|
|
CHOST=aarch64 abuild -d
|
|
|
|
echo "=== built packages ==="
|
|
ls -la "$HOME"/.local/share/abuild/*/aarch64/fingerprintd*.apk
|