commit 1dc19d4a300de03bc7bf0d9472297438e3a0eb54
parent 4efa6149ed102c9dccaa63907a52463d9975e45e
Author: finwo <finwo@pm.me>
Date: Mon, 14 Sep 2026 17:58:49 +0200
Added kernel packages
Diffstat:
11 files changed, 486 insertions(+), 85 deletions(-)
diff --git a/mk/build.sh b/mk/build.sh
@@ -29,6 +29,7 @@ checksum=
depends=
provides=
replaces=
+subpackages=
FILESDIR="${ROOT}/packages/${PKG}/files"
WORK="${ROOT}/build/work/${PKG}"
@@ -36,6 +37,10 @@ SRCDEST="${ROOT}/build/work/sources"
DESTDIR="${WORK}/dest"
WRKSRC=
OUTDIR="${ROOT}/build/repo"
+# shared build logic for templates; set before the template is sourced so
+# templates can `. "${MKDIR}/kernel.inc"` at the top level
+MKDIR="${HERE}"
+export MKDIR
# Build-time sysroot (generated, never committed): libraries that other
# packages build against install twice - once into DESTDIR (the shipped
# .apk) and once into SYSROOT (headers + .so + prefix-fixed .pc files).
@@ -104,7 +109,7 @@ if [ -n "${distfiles}" ]; then
patch -d "${WRKSRC}" -p1 --no-backup-if-mismatch -i "${p}"
done
fi
-export DESTDIR WRKSRC FILESDIR WORK SYSROOT
+export DESTDIR WRKSRC FILESDIR WORK SYSROOT MKDIR
# host-built tools first (muon), target sysroot visible to pkg-config.
# Both are additive only; system cc/patch/curl/pkg-config resolve as before.
export PATH="${ROOT}/build/host/bin:${PATH}"
@@ -127,8 +132,6 @@ do_install
KEY=$(resolve_sign_key) || die "no signing key (mk/keymgmt.sh use)"
# --- emit signed .apk (v2): shell + pax-tar + openssl, no other languages ---
-msg "${pkgname}: packing ${PKGVER}"
-
# fixed timestamp for the whole artifact (reproducible when pinned)
: "${SOURCE_DATE_EPOCH:=$(date +%s)}"
export SOURCE_DATE_EPOCH
@@ -139,65 +142,121 @@ if [ ! -x "${HERE}/pax-tar" ] || [ "${HERE}/pax-tar.c" -nt "${HERE}/pax-tar" ];
cc -std=c99 -O2 -Wall -Wextra -o "${HERE}/pax-tar" "${HERE}/pax-tar.c"
fi
-CTRL="${WORK}/ctrl"
-rm -rf "${CTRL}"
-mkdir -p "${CTRL}"
-
-{
- printf 'pkgname = %s\n' "${pkgname}"
- printf 'pkgver = %s\n' "${PKGVER}"
- printf 'pkgdesc = %s\n' "${short_desc}"
- printf 'url = %s\n' "${homepage}"
- printf 'builddate = %s\n' "${SOURCE_DATE_EPOCH}"
- printf 'packager = %s\n' "UNOS build driver (mk/build.sh)"
- printf 'size = %s\n' "$(find "${DESTDIR}" -type f -printf '%s\n' | awk '{s+=$1} END {print s+0}')"
- printf 'arch = %s\n' "x86_64"
- printf 'origin = %s\n' "${pkgname}"
- printf 'maintainer = %s\n' "${maintainer}"
- printf 'license = %s\n' "${license}"
- for d in ${depends}; do printf 'depend = %s\n' "${d}"; done
- for p in ${provides}; do printf 'provides = %s\n' "${p}"; done
- for r in ${replaces}; do printf 'replaces = %s\n' "${r}"; done
-} > "${CTRL}/.PKGINFO"
-
-for s in pre-install post-install pre-deinstall post-deinstall pre-upgrade post-upgrade trigger; do
- if [ -f "${FILESDIR}/${s}" ]; then
- cp "${FILESDIR}/${s}" "${CTRL}/.${s}"
- chmod 755 "${CTRL}/.${s}"
+# spick <relpath>... : move staged paths from DESTDIR into the current
+# SUBDEST (split staging). Only valid inside a sub_<name>() function.
+spick() {
+ [ -n "${SUBDEST:-}" ] || die "spick outside a subpackage split"
+ [ $# -ge 1 ] || die "spick needs paths"
+ for _p in "$@"; do
+ [ -e "${DESTDIR}/${_p}" ] || die "spick: no such staged path: ${_p}"
+ mkdir -p "${SUBDEST}/$(dirname "${_p}")"
+ mv "${DESTDIR}/${_p}" "${SUBDEST}/${_p}"
+ done
+ # moves leave empty parent dirs behind; prune them so the remainder
+ # assert below sees real leftovers only (never DESTDIR itself)
+ find "${DESTDIR}" -mindepth 1 -depth -type d -empty -delete
+}
+
+# emit_apk <stage> <pkgname> <desc> <depends> <provides> <replaces> <suffix>
+# suffix "" = primary (scripts from FILESDIR/<s>); otherwise per-subpackage
+# scripts from FILESDIR/<s>.<suffix>.
+emit_apk() {
+ _stage=$1; _name=$2; _desc=$3; _depends=$4; _provides=$5; _replaces=$6; _sfx=$7
+ _ctrl="${WORK}/ctrl.${_name}"
+ rm -rf "${_ctrl}"
+ mkdir -p "${_ctrl}"
+
+ {
+ printf 'pkgname = %s\n' "${_name}"
+ printf 'pkgver = %s\n' "${PKGVER}"
+ printf 'pkgdesc = %s\n' "${_desc}"
+ printf 'url = %s\n' "${homepage}"
+ printf 'builddate = %s\n' "${SOURCE_DATE_EPOCH}"
+ printf 'packager = %s\n' "UNOS build driver (mk/build.sh)"
+ printf 'size = %s\n' "$(find "${_stage}" -type f -printf '%s\n' | awk '{s+=$1} END {print s+0}')"
+ printf 'arch = %s\n' "x86_64"
+ printf 'origin = %s\n' "${pkgname}"
+ printf 'maintainer = %s\n' "${maintainer}"
+ printf 'license = %s\n' "${license}"
+ for d in ${_depends}; do printf 'depend = %s\n' "${d}"; done
+ for p in ${_provides}; do printf 'provides = %s\n' "${p}"; done
+ for r in ${_replaces}; do printf 'replaces = %s\n' "${r}"; done
+ } > "${_ctrl}/.PKGINFO"
+
+ for s in pre-install post-install pre-deinstall post-deinstall pre-upgrade post-upgrade trigger; do
+ if [ -z "${_sfx}" ]; then _f="${FILESDIR}/${s}"; else _f="${FILESDIR}/${s}.${_sfx}"; fi
+ if [ -f "${_f}" ]; then
+ cp "${_f}" "${_ctrl}/.${s}"
+ chmod 755 "${_ctrl}/.${s}"
+ fi
+ done
+
+ # data segment (pax-tar: sorted, root-owned, per-file SHA1 headers).
+ # The data tar KEEPS its end-of-tar markers: it terminates the archive.
+ "${HERE}/pax-tar" "${_stage}" "${WORK}/data.${_name}.tar"
+ gzip -n -9 -f "${WORK}/data.${_name}.tar"
+ datahash=$(sha256sum "${WORK}/data.${_name}.tar.gz" | cut -d' ' -f1)
+ printf 'datahash = %s\n' "${datahash}" >> "${_ctrl}/.PKGINFO"
+
+ # control segment (pax-tar without checksum records; host tar not trusted).
+ # The control tar must NOT carry end-of-tar markers: gunzipped, the package
+ # is one continuous tar stream (sig record, control files, data files) and
+ # only the data segment terminates it. This is the abuild-sign layout.
+ "${HERE}/pax-tar" --no-checksum "${_ctrl}" "${WORK}/control.${_name}.tar"
+ ctlsiz=$(stat -c%s "${WORK}/control.${_name}.tar")
+ head -c $((ctlsiz - 1024)) "${WORK}/control.${_name}.tar" > "${WORK}/controlnotr.${_name}.tar"
+ gzip -n -9 -f "${WORK}/controlnotr.${_name}.tar"
+ mv "${WORK}/controlnotr.${_name}.tar.gz" "${WORK}/control.${_name}.tar.gz"
+
+ # signature segment: RSA/SHA1 over the control gzip stream bytes, single tar
+ # record with NO end-of-tar blocks, prepended to control.tar.gz in place
+ # (abuild-sign layout: .apk = signed-control + data = 3 gzip members).
+ KEYNAME=$(basename "${KEY}" .rsa)
+ openssl dgst -sha1 -sign "${KEY}" -out "${WORK}/sig.${_name}.bin" "${WORK}/control.${_name}.tar.gz"
+ mkdir -p "${WORK}/sigdir.${_name}"
+ cp "${WORK}/sig.${_name}.bin" "${WORK}/sigdir.${_name}/.SIGN.RSA.${KEYNAME}.rsa.pub"
+ "${HERE}/pax-tar" --no-checksum "${WORK}/sigdir.${_name}" "${WORK}/sig.${_name}.tar"
+ sigsiz=$(stat -c%s "${WORK}/sig.${_name}.tar")
+ head -c $((sigsiz - 1024)) "${WORK}/sig.${_name}.tar" > "${WORK}/signo.${_name}.tar"
+ gzip -n -9 -f "${WORK}/signo.${_name}.tar"
+ cat "${WORK}/signo.${_name}.tar.gz" "${WORK}/control.${_name}.tar.gz" > "${WORK}/signed-control.${_name}.tar.gz"
+
+ cat "${WORK}/signed-control.${_name}.tar.gz" "${WORK}/data.${_name}.tar.gz" \
+ > "${OUTDIR}/${_name}-${PKGVER}.apk"
+
+ msg "done: ${OUTDIR}/${_name}-${PKGVER}.apk"
+}
+
+# --- subpackages: one template, several .apks ---
+# Template declares: subpackages="name ..." (hyphens fine), per-sub
+# short_desc_<underscored>/depends_<underscored> (provides/replaces optional),
+# and sub_<underscored>() splitting DESTDIR into SUBDEST via spick (move
+# semantics: the tree is partitioned, never duplicated). After all splits
+# DESTDIR must be empty - the primary packs the remainder, so for meta
+# primaries this asserts the meta carries no files by construction.
+if [ -n "${subpackages}" ]; then
+ for _sub in ${subpackages}; do
+ _u=$(printf '%s' "${_sub}" | tr '-' '_')
+ command -v "sub_${_u}" >/dev/null 2>&1 || die "no sub_${_u}() for subpackage ${_sub}"
+ eval "_sd=\${short_desc_${_u}:-}"
+ [ -n "${_sd}" ] || die "template sets no short_desc_${_u}"
+ eval "_dd=\${depends_${_u}:-}"
+ eval "_pv=\${provides_${_u}:-}"
+ eval "_rp=\${replaces_${_u}:-}"
+ SUBDEST="${WORK}/split/${_sub}"
+ rm -rf "${SUBDEST}"
+ mkdir -p "${SUBDEST}"
+ export SUBDEST
+ msg "${pkgname}: split ${_sub}"
+ "sub_${_u}"
+ unset SUBDEST
+ msg "${pkgname}: packing ${_sub} ${PKGVER}"
+ emit_apk "${WORK}/split/${_sub}" "${_sub}" "${_sd}" "${_dd}" "${_pv}" "${_rp}" "${_sub}"
+ done
+ if [ -n "$(ls -A "${DESTDIR}")" ]; then
+ die "unpartitioned files remain for primary ${pkgname}: $(ls -A "${DESTDIR}" | head)"
fi
-done
-
-# data segment (pax-tar: sorted, root-owned, per-file SHA1 headers).
-# The data tar KEEPS its end-of-tar markers: it terminates the archive.
-"${HERE}/pax-tar" "${DESTDIR}" "${WORK}/data.tar"
-gzip -n -9 -f "${WORK}/data.tar"
-datahash=$(sha256sum "${WORK}/data.tar.gz" | cut -d' ' -f1)
-printf 'datahash = %s\n' "${datahash}" >> "${CTRL}/.PKGINFO"
-
-# control segment (pax-tar without checksum records; host tar not trusted).
-# The control tar must NOT carry end-of-tar markers: gunzipped, the package
-# is one continuous tar stream (sig record, control files, data files) and
-# only the data segment terminates it. This is the abuild-sign layout.
-"${HERE}/pax-tar" --no-checksum "${CTRL}" "${WORK}/control.tar"
-ctlsiz=$(stat -c%s "${WORK}/control.tar")
-head -c $((ctlsiz - 1024)) "${WORK}/control.tar" > "${WORK}/controlnotr.tar"
-gzip -n -9 -f "${WORK}/controlnotr.tar"
-mv "${WORK}/controlnotr.tar.gz" "${WORK}/control.tar.gz"
-
-# signature segment: RSA/SHA1 over the control gzip stream bytes, single tar
-# record with NO end-of-tar blocks, prepended to control.tar.gz in place
-# (abuild-sign layout: .apk = signed-control + data = 3 gzip members).
-KEYNAME=$(basename "${KEY}" .rsa)
-openssl dgst -sha1 -sign "${KEY}" -out "${WORK}/sig.bin" "${WORK}/control.tar.gz"
-mkdir -p "${WORK}/sigdir"
-cp "${WORK}/sig.bin" "${WORK}/sigdir/.SIGN.RSA.${KEYNAME}.rsa.pub"
-"${HERE}/pax-tar" --no-checksum "${WORK}/sigdir" "${WORK}/sig.tar"
-sigsiz=$(stat -c%s "${WORK}/sig.tar")
-head -c $((sigsiz - 1024)) "${WORK}/sig.tar" > "${WORK}/signo.tar"
-gzip -n -9 -f "${WORK}/signo.tar"
-cat "${WORK}/signo.tar.gz" "${WORK}/control.tar.gz" > "${WORK}/signed-control.tar.gz"
-
-cat "${WORK}/signed-control.tar.gz" "${WORK}/data.tar.gz" \
- > "${OUTDIR}/${pkgname}-${PKGVER}.apk"
-
-msg "done: ${OUTDIR}/${pkgname}-${PKGVER}.apk"
+fi
+
+msg "${pkgname}: packing ${PKGVER}"
+emit_apk "${DESTDIR}" "${pkgname}" "${short_desc}" "${depends}" "${provides}" "${replaces}" ""
diff --git a/mk/kernel.inc b/mk/kernel.inc
@@ -0,0 +1,95 @@
+#!/bin/sh
+# mk/kernel.inc - shared kbuild recipe for the linux-{longterm,stable,
+# mainline} templates. Sourced at template top level via
+# . "${MKDIR}/kernel.inc" (MKDIR is exported by the driver before the
+# template is sourced).
+#
+# The template sets variables, then the standard phases call in:
+# version=... (checksum, distfiles as usual)
+# KDELTA_ENABLE="IKCONFIG IKCONFIG_PROC" (forced =y, no CONFIG_ prefix)
+# KDELTA_MODULE="NET_VRF" (forced =m)
+# KDELTA_DISABLE="MODULE_SIG" (forced n)
+# KASSERT_Y="IPV6 ..." KASSERT_M="NET_VRF" KASSERT_N="MODULE_SIG"
+# do_configure() { kernel_configure; }
+# do_build() { kernel_build; }
+# do_install() { kernel_install; }
+# sub_linux() { spick boot "lib/modules"; } # linux
+# sub_linux_headers() { spick usr/include; } # linux-headers
+# empty sub functions (`:`) for the two metas.
+#
+# Conventions (locked here, not per template):
+# - defconfig + explicit deltas + olddefconfig. Deltas and asserts are
+# fail-loud: a renamed/vanished symbol breaks the build, never the boot.
+# - modules_install with INSTALL_MOD_STRIP=1; build/source symlinks removed
+# (absolute links into the build tree must never ship). Module dependency
+# maps are generated at build time with host kmod depmod -b (explicit
+# version: uname lies in containers) and asserted present.
+# - headers = sanitized `headers_install` (user API). A module-build tree
+# (full source for out-of-tree .ko like openbcm) is a separate follow-up,
+# not silently half-shipped here.
+# - KREL (e.g. 6.18.51, 7.3.0-rc3) is derived via `make kernelrelease`,
+# never assumed from ${version}: pre-release trees differ (7.3_rc3 apk
+# version vs 7.3.0-rc3 uname). All install paths use KREL.
+# - Reproducibility: KBUILD_BUILD_USER/HOST/TIMESTAMP (from
+# SOURCE_DATE_EPOCH, driver-exported).
+
+kernel_configure() {
+ cd "${WRKSRC}"
+ [ -f scripts/config ] || die "kernel: scripts/config missing"
+ msg "kernel: defconfig"
+ make ARCH=x86_64 defconfig >/dev/null || die "kernel: defconfig failed"
+ for s in ${KDELTA_ENABLE:-}; do
+ ./scripts/config --enable "CONFIG_${s}" || die "kernel: cannot enable CONFIG_${s}"
+ done
+ for s in ${KDELTA_MODULE:-}; do
+ ./scripts/config --module "CONFIG_${s}" || die "kernel: cannot modularize CONFIG_${s}"
+ done
+ for s in ${KDELTA_DISABLE:-}; do
+ ./scripts/config --disable "CONFIG_${s}" || die "kernel: cannot disable CONFIG_${s}"
+ done
+ if [ -n "${KDELTA_ENABLE:-}${KDELTA_MODULE:-}${KDELTA_DISABLE:-}" ]; then
+ make ARCH=x86_64 olddefconfig >/dev/null || die "kernel: olddefconfig failed"
+ fi
+ for s in ${KASSERT_Y:-}; do
+ grep -qx "CONFIG_${s}=y" .config || die "kernel: CONFIG_${s} is not =y"
+ done
+ for s in ${KASSERT_M:-}; do
+ grep -qx "CONFIG_${s}=m" .config || die "kernel: CONFIG_${s} is not =m"
+ done
+ for s in ${KASSERT_N:-}; do
+ grep -qx "# CONFIG_${s} is not set" .config || die "kernel: CONFIG_${s} is set"
+ done
+ KREL=$(make -s ARCH=x86_64 kernelrelease) || die "kernel: kernelrelease failed"
+ [ -n "${KREL}" ] || die "kernel: empty KREL"
+ export KREL
+ msg "kernel: release ${KREL}"
+}
+
+kernel_build() {
+ cd "${WRKSRC}"
+ make ARCH=x86_64 -j"$(nproc)" \
+ KBUILD_BUILD_USER=unos \
+ KBUILD_BUILD_HOST=unos \
+ KBUILD_BUILD_TIMESTAMP="$(date -u -d "@${SOURCE_DATE_EPOCH}" '+%Y-%m-%d %H:%M:%S')" \
+ bzImage modules || die "kernel: build failed"
+ [ -f arch/x86/boot/bzImage ] || die "kernel: bzImage missing"
+}
+
+kernel_install() {
+ cd "${WRKSRC}"
+ install -D -m 644 arch/x86/boot/bzImage "${DESTDIR}/boot/vmlinuz-${KREL}"
+ install -D -m 644 System.map "${DESTDIR}/boot/System.map-${KREL}"
+ install -D -m 644 .config "${DESTDIR}/boot/config-${KREL}"
+ make ARCH=x86_64 INSTALL_MOD_PATH="${DESTDIR}" INSTALL_MOD_STRIP=1 \
+ modules_install >/dev/null || die "kernel: modules_install failed"
+ # absolute build-tree symlinks must never ship
+ rm -f "${DESTDIR}/lib/modules/${KREL}/build" "${DESTDIR}/lib/modules/${KREL}/source"
+ make ARCH=x86_64 INSTALL_HDR_PATH="${DESTDIR}/usr" \
+ headers_install >/dev/null || die "kernel: headers_install failed"
+ # dependency maps at build time (explicit version: uname lies in userns)
+ command -v depmod >/dev/null 2>&1 || die "kernel: host depmod missing"
+ depmod -b "${DESTDIR}" "${KREL}" || die "kernel: depmod failed"
+ [ -d "${DESTDIR}/lib/modules/${KREL}" ] || die "kernel: no modules dir"
+ [ -f "${DESTDIR}/lib/modules/${KREL}/modules.dep" ] || die "kernel: no modules.dep"
+ [ -f "${DESTDIR}/usr/include/linux/version.h" ] || die "kernel: no headers"
+}
diff --git a/mk/rootfs.sh b/mk/rootfs.sh
@@ -21,17 +21,33 @@ if [ -d "${ROOTFS}" ]; then
[ "${1:-}" = "--force" ] || { echo "rootfs.sh: ${ROOTFS} exists (use --force)" >&2; exit 1; }
rm -rf "${ROOTFS}"
fi
-mkdir -p "${ROOTFS}" "${CACHE}"
+mkdir -p "${ROOTFS}" "${CACHE}" "${ROOTFS}/proc"
+
+# apk executes control scripts chrooted via memfd (/proc/self/fd), so the
+# target needs /proc mounted or every post-install fails with ENOENT -
+# even though install order/dep wiring is otherwise correct. Bind host
+# /proc in a private namespace for the transactions (same unshare pattern
+# as mk/chroot.sh; the --pid flag is what makes the proc mount permitted).
+# Namespace evaporates on exit; repo/cache stay visible (same fs view).
+apk_ns() {
+ unshare --user --map-root-user --mount --propagation private --pid --fork \
+ sh -c 'mount -t proc proc "$1/proc" && shift && "$@"' \
+ _ "${ROOTFS}" "$@"
+}
echo "==> trust seed: unos-keys via explicit --allow-untrusted"
-"${APK}" --root "${ROOTFS}" --initdb --usermode \
+# NOTE: no --usermode here (apk refuses it as root, and we are mapped root
+# inside the namespace). Ownership maps back to the invoking user outside,
+# and no shipped package carries device nodes or setuid bits that would
+# behave differently - if one ever does, assembly needs a second look.
+apk_ns "${APK}" --root "${ROOTFS}" --initdb \
--cache-dir "${CACHE}" --repository "${REPO}" \
--allow-untrusted add unos-keys
echo "==> UNOS system (fully trusted from here on)"
-"${APK}" --root "${ROOTFS}" --usermode \
+apk_ns "${APK}" --root "${ROOTFS}" \
--cache-dir "${CACHE}" --repository "${REPO}" \
- add base-files glibc busybox unos-firstboot tinyssh libmnl zlib openssl iproute2 apk-tools
+ add base-files glibc busybox unos-firstboot tinyssh libmnl zlib openssl iproute2 apk-tools linux-longterm
# Deterministic loader cache: package post-installs refresh it during the
# transaction, but assembly must not depend on script-execution order.
diff --git a/packages/README.md b/packages/README.md
@@ -67,3 +67,18 @@ directory is ever built by hand on a target switch.
(`/bin/sh` must exist before apk runs their scripts). `mk/rootfs.sh`
additionally runs `ldconfig` explicitly after assembly so the chroot
cache never depends on script-execution order.
+- **Subpackages: one template, several .apks.** A template may declare
+ `subpackages="name ..."` with per-sub `short_desc_<underscored>` /
+ `depends_<underscored>` (provides/replaces optional) and a
+ `sub_<underscored>()` function partitioning `DESTDIR` into `SUBDEST`
+ via `spick` (move semantics - never duplicated). Per-subpackage control
+ scripts live at `files/<script>.<subname>` (e.g.
+ `post-install.linux`). After all splits `DESTDIR` must be empty: the
+ primary packs the remainder, so meta primaries are empty by
+ construction. Version pins between outputs must carry the full pkgver
+ incl. `-r` (`depends="linux=${version}-r${revision}"`) - apk's `=`
+ never matches a bare version. Single-output templates are unaffected.
+- **Control scripts run chrooted via memfd.** apk v3 executes scripts
+ through `/proc/self/fd`, so any `apk add` that runs scripts needs
+ `/proc` mounted in the target (live systems always have it;
+ `mk/rootfs.sh` binds it for assembly; the installer must too).
diff --git a/packages/iproute2/template b/packages/iproute2/template
@@ -1,7 +1,7 @@
# Template file for 'iproute2'
pkgname=iproute2
version=6.19.0
-revision=2
+revision=3
short_desc="IP routing and network device configuration tools"
maintainer="finwo <finwo@pm.me>"
license="GPL-2.0-only"
@@ -11,11 +11,14 @@ checksum=9332213d35480b647086a70c302de8568de83455a98774d35de216c4ce191006
depends="libmnl libcap"
# Notes:
-# - `ip` ONLY (PLAN section 10): the top-level `install: all` would build
-# every subdir (tc needs flex/bison/xtables/elf), so the template builds
-# SUBDIRS="lib ip" and cherry-picks the `ip` binary plus the iproute2
-# config files (rt_tables symbolic names live there). tc/bridge/misc
-# (ss!)/rtmon land as separate design decisions, not silent extras.
+# - `ip` + `rtmon` + `ss` ONLY (PLAN section 10): the top-level
+# `install: all` would build every subdir (tc needs flex/bison/xtables/elf),
+# so the template builds SUBDIRS="lib ip misc" and cherry-picks the three
+# binaries plus the iproute2 config files (rt_tables symbolic names live
+# there). tc/bridge/nstat/ifstat/rtacct/lnstat land as separate design
+# decisions, not silent extras. misc needs host bison/flex (build-only
+# code generators, same category as perl for openssl); its libbpf/rpc
+# includes are already gated off by the scrub below.
# - libmnl comes from the sysroot (extended-error-ack decoding) and libcap
# (drop_cap ambient-capability hygiene); configure finds both via the
# driver-exported PKG_CONFIG_PATH.
@@ -50,23 +53,27 @@ do_build() {
cd "${WRKSRC}"
# iproute2 has no kbuild-style flag tracking: objects from a previous
# configure do not rebuild when config.mk flags change. Clean
- # unconditionally (cheap for lib+ip; full-tree cost is why SUBDIRS is
- # limited here too).
- make SUBDIRS="lib ip" clean >/dev/null
- make -j"$(nproc)" SUBDIRS="lib ip"
- [ -x ip/ip ] || die "iproute2: ip binary missing after build"
- needed=$(readelf -d ip/ip | sed -n 's/^.*NEEDED.*\[\(.*\)\].*$/\1/p' | sort)
- for lib in ${needed}; do
- case "${lib}" in
- libmnl.so.*|libcap.so.*|libc.so.*) : ;;
- *) die "iproute2: unexpected NEEDED entry: ${lib}" ;;
- esac
+ # unconditionally (cheap for lib+ip+misc; full-tree cost is why SUBDIRS
+ # is limited here too).
+ make SUBDIRS="lib ip misc" clean >/dev/null
+ make -j"$(nproc)" SUBDIRS="lib ip misc"
+ for bin in ip/ip ip/rtmon misc/ss; do
+ [ -x "${bin}" ] || die "iproute2: ${bin} missing after build"
+ needed=$(readelf -d "${bin}" | sed -n 's/^.*NEEDED.*\[\(.*\)\].*$/\1/p' | sort)
+ for lib in ${needed}; do
+ case "${lib}" in
+ libmnl.so.*|libcap.so.*|libc.so.*) : ;;
+ *) die "iproute2: ${bin}: unexpected NEEDED entry: ${lib}" ;;
+ esac
+ done
done
}
do_install() {
cd "${WRKSRC}"
install -D -m 755 ip/ip "${DESTDIR}/sbin/ip"
+ install -D -m 755 ip/rtmon "${DESTDIR}/sbin/rtmon"
+ install -D -m 755 misc/ss "${DESTDIR}/sbin/ss"
for f in etc/iproute2/*; do
[ -f "${f}" ] || continue
install -D -m 644 "${f}" "${DESTDIR}/usr/share/iproute2/$(basename "${f}")"
diff --git a/packages/linux-longterm/files/post-install.linux b/packages/linux-longterm/files/post-install.linux
@@ -0,0 +1,9 @@
+#!/bin/sh
+# linux post-install: refresh module maps for the RUNNING kernel.
+# On live targets uname matches and this regenerates; at assembly time
+# (userns, host uname) /lib/modules/<running> is absent and this is a
+# no-op - the build-time maps generated with explicit -b/-version ship in
+# the package and are always present. Never bare-depmod unconditionally:
+# uname lies in containers.
+ver=$(uname -r)
+if [ -d "/lib/modules/${ver}" ]; then exec depmod; fi
diff --git a/packages/linux-longterm/template b/packages/linux-longterm/template
@@ -0,0 +1,64 @@
+# Template file for 'linux-longterm'
+#
+# One template, four outputs (driver subpackages):
+# linux-longterm meta, depends linux=<version>
+# linux-longterm-headers meta, depends linux-headers=<version>
+# linux image + modules + maps (post-install: depmod)
+# linux-headers sanitized headers_install (user API only -
+# NOT a module-build tree; openbcm needs a
+# follow-up, see mk/kernel.inc)
+pkgname=linux-longterm
+version=6.18.51
+revision=0
+short_desc="UNOS kernel meta - longterm line"
+maintainer="finwo <finwo@pm.me>"
+license="GPL-2.0-only"
+homepage="https://www.kernel.org"
+distfiles="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.51.tar.xz"
+checksum=ba2f60f858bf4d1f929101faa356c93dc8b925b17aaa9f95eabd4627758df613
+# the meta points at this line's kernel build (exact pin incl. revision -
+# apk's `=` only matches the full pkgver, so a bare version never matches)
+depends="linux=${version}-r${revision}"
+subpackages="linux-longterm-headers linux linux-headers"
+
+short_desc_linux_longterm_headers="UNOS kernel headers meta - longterm line"
+depends_linux_longterm_headers="linux-headers=${version}-r${revision}"
+short_desc_linux="Linux kernel image and modules"
+depends_linux="busybox"
+short_desc_linux_headers="Linux kernel sanitized headers"
+depends_linux_headers=
+
+# Notes:
+# - defconfig + deltas (see mk/kernel.inc for the shared mechanics).
+# Verified against 6.18 defconfig: everything load-bearing except VRF
+# (absent) and IKCONFIG (off) already defaults right; NEXTHOP is not a
+# symbol at all (nexthop objects are unconditional core). MODULE_SIG
+# already defaults off (no ephemeral signing keys). VRF's full dep
+# chain (IP_MULTIPLE_TABLES + NET_L3_MASTER_DEV + IPV6_MULTIPLE_TABLES)
+# is enabled explicitly - olddefconfig silently drops symbols whose
+# deps are unmet, which is exactly what the asserts guard against.
+# - linux needs busybox first: its post-install runs under /bin/sh
+# (same ordering rule as the library packages).
+
+# shellcheck disable=SC1091
+. "${MKDIR}/kernel.inc"
+
+KDELTA_ENABLE="IKCONFIG IKCONFIG_PROC NET_L3_MASTER_DEV IPV6_MULTIPLE_TABLES"
+KDELTA_MODULE="NET_VRF"
+KDELTA_DISABLE=""
+KASSERT_Y="IPV6 IP_MULTIPLE_TABLES IPV6_MULTIPLE_TABLES NET_L3_MASTER_DEV DEVTMPFS_MOUNT BLK_DEV_INITRD SERIAL_8250_CONSOLE MODULES IKCONFIG IKCONFIG_PROC"
+KASSERT_M="NET_VRF"
+KASSERT_N="MODULE_SIG"
+
+do_configure() { kernel_configure; }
+do_build() { kernel_build; }
+do_install() { kernel_install; }
+
+sub_linux_longterm_headers() { :; }
+sub_linux() {
+ spick boot
+ spick lib/modules
+}
+sub_linux_headers() {
+ spick usr/include
+}
diff --git a/packages/linux-mainline/files/post-install.linux b/packages/linux-mainline/files/post-install.linux
@@ -0,0 +1,9 @@
+#!/bin/sh
+# linux post-install: refresh module maps for the RUNNING kernel.
+# On live targets uname matches and this regenerates; at assembly time
+# (userns, host uname) /lib/modules/<running> is absent and this is a
+# no-op - the build-time maps generated with explicit -b/-version ship in
+# the package and are always present. Never bare-depmod unconditionally:
+# uname lies in containers.
+ver=$(uname -r)
+if [ -d "/lib/modules/${ver}" ]; then exec depmod; fi
diff --git a/packages/linux-mainline/template b/packages/linux-mainline/template
@@ -0,0 +1,62 @@
+# Template file for 'linux-mainline'
+#
+# One template, four outputs (driver subpackages):
+# linux-mainline meta, depends linux=<version>
+# linux-mainline-headers meta, depends linux-headers=<version>
+# linux image + modules + maps (post-install: depmod)
+# linux-headers sanitized headers_install (user API only -
+# NOT a module-build tree; openbcm needs a
+# follow-up, see mk/kernel.inc)
+#
+# NOTE: mainline is a moving target by design (rc snapshots weekly). The
+# pin below is TOFU: git.kernel.org publishes no sha256 for the torvalds
+# snapshots (only .tar.sign PGP). Re-pin every bump, verify the tag out
+# of band.
+pkgname=linux-mainline
+version=7.3_rc3
+revision=0
+short_desc="UNOS kernel meta - mainline snapshot"
+maintainer="finwo <finwo@pm.me>"
+license="GPL-2.0-only"
+homepage="https://www.kernel.org"
+distfiles="https://git.kernel.org/torvalds/t/linux-7.3-rc3.tar.gz"
+checksum=49b24119d7b92da75bba0daf5da4f7735c8cb948757a282e849092dcc681d8dd
+# the meta points at this line's kernel build (exact pin incl. revision -
+# apk's `=` only matches the full pkgver, so a bare version never matches)
+depends="linux=${version}-r${revision}"
+subpackages="linux-mainline-headers linux linux-headers"
+
+short_desc_linux_mainline_headers="UNOS kernel headers meta - mainline snapshot"
+depends_linux_mainline_headers="linux-headers=${version}-r${revision}"
+short_desc_linux="Linux kernel image and modules"
+depends_linux="busybox"
+short_desc_linux_headers="Linux kernel sanitized headers"
+depends_linux_headers=
+
+# Notes: same recipe as linux-longterm (defconfig + deltas, mk/kernel.inc).
+# Deltas re-verified per line - symbols move between releases, the asserts
+# fail loud if this list rots. KREL here is 7.3.0-rc3 (derived, not equal
+# to the 7.3_rc3 apk version) - all install paths use KREL.
+
+# shellcheck disable=SC1091
+. "${MKDIR}/kernel.inc"
+
+KDELTA_ENABLE="IKCONFIG IKCONFIG_PROC NET_L3_MASTER_DEV IPV6_MULTIPLE_TABLES"
+KDELTA_MODULE="NET_VRF"
+KDELTA_DISABLE=""
+KASSERT_Y="IPV6 IP_MULTIPLE_TABLES IPV6_MULTIPLE_TABLES NET_L3_MASTER_DEV DEVTMPFS_MOUNT BLK_DEV_INITRD SERIAL_8250_CONSOLE MODULES IKCONFIG IKCONFIG_PROC"
+KASSERT_M="NET_VRF"
+KASSERT_N="MODULE_SIG"
+
+do_configure() { kernel_configure; }
+do_build() { kernel_build; }
+do_install() { kernel_install; }
+
+sub_linux_mainline_headers() { :; }
+sub_linux() {
+ spick boot
+ spick lib/modules
+}
+sub_linux_headers() {
+ spick usr/include
+}
diff --git a/packages/linux-stable/files/post-install.linux b/packages/linux-stable/files/post-install.linux
@@ -0,0 +1,9 @@
+#!/bin/sh
+# linux post-install: refresh module maps for the RUNNING kernel.
+# On live targets uname matches and this regenerates; at assembly time
+# (userns, host uname) /lib/modules/<running> is absent and this is a
+# no-op - the build-time maps generated with explicit -b/-version ship in
+# the package and are always present. Never bare-depmod unconditionally:
+# uname lies in containers.
+ver=$(uname -r)
+if [ -d "/lib/modules/${ver}" ]; then exec depmod; fi
diff --git a/packages/linux-stable/template b/packages/linux-stable/template
@@ -0,0 +1,56 @@
+# Template file for 'linux-stable'
+#
+# One template, four outputs (driver subpackages):
+# linux-stable meta, depends linux=<version>
+# linux-stable-headers meta, depends linux-headers=<version>
+# linux image + modules + maps (post-install: depmod)
+# linux-headers sanitized headers_install (user API only -
+# NOT a module-build tree; openbcm needs a
+# follow-up, see mk/kernel.inc)
+pkgname=linux-stable
+version=7.2.5
+revision=0
+short_desc="UNOS kernel meta - stable line"
+maintainer="finwo <finwo@pm.me>"
+license="GPL-2.0-only"
+homepage="https://www.kernel.org"
+distfiles="https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.2.5.tar.xz"
+checksum=55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78
+# the meta points at this line's kernel build (exact pin incl. revision -
+# apk's `=` only matches the full pkgver, so a bare version never matches)
+depends="linux=${version}-r${revision}"
+subpackages="linux-stable-headers linux linux-headers"
+
+short_desc_linux_stable_headers="UNOS kernel headers meta - stable line"
+depends_linux_stable_headers="linux-headers=${version}-r${revision}"
+short_desc_linux="Linux kernel image and modules"
+depends_linux="busybox"
+short_desc_linux_headers="Linux kernel sanitized headers"
+depends_linux_headers=
+
+# Notes: same recipe as linux-longterm (defconfig + deltas, mk/kernel.inc).
+# Deltas re-verified per line - symbols move between releases, the asserts
+# fail loud if this list rots.
+
+# shellcheck disable=SC1091
+. "${MKDIR}/kernel.inc"
+
+KDELTA_ENABLE="IKCONFIG IKCONFIG_PROC NET_L3_MASTER_DEV IPV6_MULTIPLE_TABLES"
+KDELTA_MODULE="NET_VRF"
+KDELTA_DISABLE=""
+KASSERT_Y="IPV6 IP_MULTIPLE_TABLES IPV6_MULTIPLE_TABLES NET_L3_MASTER_DEV DEVTMPFS_MOUNT BLK_DEV_INITRD SERIAL_8250_CONSOLE MODULES IKCONFIG IKCONFIG_PROC"
+KASSERT_M="NET_VRF"
+KASSERT_N="MODULE_SIG"
+
+do_configure() { kernel_configure; }
+do_build() { kernel_build; }
+do_install() { kernel_install; }
+
+sub_linux_stable_headers() { :; }
+sub_linux() {
+ spick boot
+ spick lib/modules
+}
+sub_linux_headers() {
+ spick usr/include
+}