From 8ddcba85a4e8476a88fede8b8245f955299b2ca1 Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 18 Sep 2026 01:05:12 +0100 Subject: [PATCH 1/8] packaging: install System.map into the kernel image package The image ships /boot/vmlinuz- and /boot/config- but not System.map. That is not just a missing convenience file: the depmod snippet debhelper generates for every package shipping a kernel module is if [ -e /boot/System.map-#KVERS# ]; then depmod -a -F /boot/System.map-#KVERS# #KVERS# || true fi so with no System.map installed the snippet does nothing at all. Today that is already wrong. make modules_install writes modules.dep before bundle-dkms-modules.sh adds anything under /lib/modules//extra/, so the modules.dep we ship has never listed the bundled DKMS modules, and nothing on the target regenerates it. modprobe kgsl cannot work on an installed image. Install System.map- alongside the other boot artifacts, and treat a missing one as an error rather than copying it if it happens to be there: it is produced by every kernel build that produced the vmlinuz next to it, so its absence means something is wrong with the build tree, not with the kernel's configuration. This is also what Debian and Ubuntu kernels ship, and what crash, makedumpfile and perf look for. Signed-off-by: Christopher Obbard --- debian/README.md | 4 ++++ debian/control.in | 1 + debian/rules | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/debian/README.md b/debian/README.md index 424ea9cb..60793f2d 100644 --- a/debian/README.md +++ b/debian/README.md @@ -328,6 +328,10 @@ CI writes every orig in one builder image. Installed paths: - `/boot/vmlinuz-` — compressed kernel image - `/boot/config-` — kernel `.config` +- `/boot/System.map-` — kernel symbol table. Required by the depmod + snippet debhelper generates for every package that ships a module: it is + guarded by `[ -e /boot/System.map- ]`, so without this file no + `modules.dep` is ever regenerated on the target. - `/lib/modules//` — stripped kernel modules - `/usr/lib/linux-image-/` — all DTBs (vendor subdirs preserved) - `/lib/modules//build` → `/usr/src/linux-headers-/` (symlink) diff --git a/debian/control.in b/debian/control.in index 6741d426..94603330 100644 --- a/debian/control.in +++ b/debian/control.in @@ -23,6 +23,7 @@ Description: Qualcomm ARM64 Linux kernel image @KVER@ Files installed: - /boot/vmlinuz-@KVER@ - /boot/config-@KVER@ + - /boot/System.map-@KVER@ - /lib/modules/@KVER@/ - /usr/lib/linux-image-@KVER@/ (DTBs) - /usr/lib/firmware/@KVER@/device-tree (Ubuntu flash-kernel symlink) diff --git a/debian/rules b/debian/rules index 7891ccf8..94978e6c 100755 --- a/debian/rules +++ b/debian/rules @@ -421,10 +421,20 @@ override_dh_auto_install: [ -n "$$OBJ_DIR" ] && CFG_PATH="$$OBJ_DIR/.config"; \ [ -f "$$CFG_PATH" ] || { echo "ERROR: Missing .config at $$CFG_PATH"; exit 1; }; \ \ + # Locate System.map (objdir-aware). Required, not optional: the depmod + # snippet debhelper generates for every package shipping a module is + # guarded by [ -e /boot/System.map- ], so without this file the + # snippet is a no-op and nothing on the target ever regenerates + # modules.dep. + MAP_PATH="System.map"; \ + [ -n "$$OBJ_DIR" ] && MAP_PATH="$$OBJ_DIR/System.map"; \ + [ -f "$$MAP_PATH" ] || { echo "ERROR: Missing System.map at $$MAP_PATH"; exit 1; }; \ + \ # /boot artifacts mkdir -p "$$PKG/boot"; \ cp -a -T "$$IMG_PATH" "$$PKG/boot/vmlinuz-$$BASE"; \ cp -a -T "$$CFG_PATH" "$$PKG/boot/config-$$BASE"; \ + cp -a -T "$$MAP_PATH" "$$PKG/boot/System.map-$$BASE"; \ \ # Modules (stripped) $(MAKE) $(KBUILD_O_ARG) ARCH=$(ARCH) modules_install \ From 1d9b6b539d2d7655a4e281de039a833453d7b20b Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 18 Sep 2026 01:05:36 +0100 Subject: [PATCH 2/8] packaging: add a --stage-root option to the DKMS bundler The bundler writes into two staging trees named on the command line, --image-pkg-dir and --dbg-pkg-dir. Once each module ships in its own binary package that stops being enough: the set of trees to write into is derived from the module list, not known when the script is invoked, so the caller can only name the directory the trees are created in. Add --stage-root for that directory -- debian/ in a source package -- and require it. Nothing reads it yet; this commit only establishes the option, its validation and its plumbing from debian/rules, so the commit that moves the staging is about the move alone. Required rather than optional, and with no default derived from the other paths: every caller already passes four staging arguments, a fifth is no extra burden, and guessing debian/ from the parent of --image-pkg-dir would be wrong for the standalone developer path the script documents. Validated the way --headers-dir is: absolute, because the script is documented as callable from anywhere, and existing, because it is a directory the caller staged rather than one we create. Signed-off-by: Christopher Obbard --- debian/rules | 1 + debian/scripts/bundle-dkms-modules.sh | 31 +++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/debian/rules b/debian/rules index 94978e6c..42bfa8da 100755 --- a/debian/rules +++ b/debian/rules @@ -535,6 +535,7 @@ override_dh_auto_install: --headers-dir "$(CURDIR)/$$HDRDIR" \ --image-pkg-dir "$(CURDIR)/$$PKG" \ --dbg-pkg-dir "$(CURDIR)/$$DBG_PKG" \ + --stage-root "$(CURDIR)/debian" \ --arch "$(DKMS_ARCH)" \ --objcopy "$(OBJCOPY)" \ --modules-manifest "$(CURDIR)/debian/dkms-modules" diff --git a/debian/scripts/bundle-dkms-modules.sh b/debian/scripts/bundle-dkms-modules.sh index 28bad3b6..40d5bcbc 100755 --- a/debian/scripts/bundle-dkms-modules.sh +++ b/debian/scripts/bundle-dkms-modules.sh @@ -35,6 +35,8 @@ set -euo pipefail # lib/modules//kernel/ (in-tree modules, for collision detection) # boot/config- (kernel .config, for BUILD_EXCLUSIVE_CONFIG checks) # - The debug package staging tree must exist at --dbg-pkg-dir. +# - The directory holding the per-package staging trees must exist at +# --stage-root (absolute). In a source package this is debian/. # - The kernel headers must be fully staged at --headers-dir (absolute path). # This is the directory containing Makefile, include/, scripts/, arch/, etc. # It must be an absolute path: dkms invokes make from inside the module @@ -50,6 +52,7 @@ set -euo pipefail # --headers-dir "$(CURDIR)/debian/linux-headers-$BASE/usr/src/linux-headers-$BASE" \ # --image-pkg-dir "$(CURDIR)/debian/linux-image-$BASE" \ # --dbg-pkg-dir "$(CURDIR)/debian/linux-image-$BASE-dbg" \ +# --stage-root "$(CURDIR)/debian" \ # --arch "$(DKMS_ARCH)" \ # --objcopy "$(OBJCOPY)" \ # --modules-manifest "$(CURDIR)/debian/dkms-modules" @@ -77,6 +80,7 @@ KVER="" HEADERS_DIR="" IMAGE_PKG_DIR="" DBG_PKG_DIR="" +STAGE_ROOT="" # Default manifest: debian/dkms-modules (one level up from debian/scripts/) MODULES_MANIFEST="${SCRIPT_DIR}/../dkms-modules" # dkms --arch speaks uname -m vocabulary (aarch64), not kbuild vocabulary (arm64). @@ -122,6 +126,10 @@ REQUIRED: /usr/lib/debug/lib/modules//extra/ In debian/rules this is: \$(CURDIR)/debian/linux-image-\$BASE-dbg + --stage-root DIR Directory the per-package staging trees live in. + MUST be absolute, and must already exist. + In debian/rules this is: + \$(CURDIR)/debian OPTIONAL: --modules-manifest FILE Path to the dkms-modules manifest. @@ -145,8 +153,10 @@ PREREQUISITES (developer standalone use): 3. --image-pkg-dir must contain lib/modules//kernel/ (in-tree modules) and boot/config- (kernel .config). 4. --dbg-pkg-dir must exist (can be empty; subdirs are created as needed). - 5. --headers-dir must be an absolute path. - 6. This script does NOT cross-compile: dkms builds each module with the host + 5. --stage-root must exist; it is the directory per-package staging trees + are created in, i.e. debian/ in a source package. + 6. --headers-dir and --stage-root must be absolute paths. + 7. This script does NOT cross-compile: dkms builds each module with the host toolchain (no ARCH/CROSS_COMPILE is plumbed). Run it on a native arm64 host (or an arm64 chroot / qemu-user environment) so the produced .ko matches the target kernel. --arch only sets the dkms architecture label @@ -199,6 +209,7 @@ while [[ $# -gt 0 ]]; do --headers-dir) require_val "$@"; HEADERS_DIR="$2"; shift 2 ;; --image-pkg-dir) require_val "$@"; IMAGE_PKG_DIR="$2"; shift 2 ;; --dbg-pkg-dir) require_val "$@"; DBG_PKG_DIR="$2"; shift 2 ;; + --stage-root) require_val "$@"; STAGE_ROOT="$2"; shift 2 ;; --modules-manifest) require_val "$@"; MODULES_MANIFEST="$2"; shift 2 ;; --arch) require_val "$@"; DKMS_ARCH="$2"; shift 2 ;; --objcopy) require_val "$@"; OBJCOPY="$2"; shift 2 ;; @@ -217,6 +228,7 @@ _missing=() [[ -n "$HEADERS_DIR" ]] || _missing+=(--headers-dir) [[ -n "$IMAGE_PKG_DIR" ]] || _missing+=(--image-pkg-dir) [[ -n "$DBG_PKG_DIR" ]] || _missing+=(--dbg-pkg-dir) +[[ -n "$STAGE_ROOT" ]] || _missing+=(--stage-root) if [[ ${#_missing[@]} -gt 0 ]]; then log_error "Missing required arguments: ${_missing[*]}" log_error "Run with --help for usage." @@ -232,6 +244,14 @@ fi exit 1 } +# --stage-root must be absolute for the same reason every other staging path +# here is: this script is called with the build tree's cwd from debian/rules +# but is also documented as standalone-callable from anywhere. +[[ "$STAGE_ROOT" == /* ]] || { + log_error "--stage-root must be an absolute path (got: $STAGE_ROOT)" + exit 1 +} + # --------------------------------------------------------------------------- # Read and parse the manifest # --------------------------------------------------------------------------- @@ -278,12 +298,19 @@ fi if [[ ! -d "$DBG_PKG_DIR" ]]; then log_warn "--dbg-pkg-dir does not exist: $DBG_PKG_DIR (will be created as needed)" fi +[[ -d "$STAGE_ROOT" ]] || { + log_error "--stage-root does not exist: $STAGE_ROOT" + log_error "It is the directory the per-package staging trees are created in" + log_error "(debian/ in a source package), so it must already be there." + exit 1 +} log_step "DKMS module bundling configuration:" log_info " kver: $KVER" log_info " headers-dir: $HEADERS_DIR" log_info " image-pkg-dir: $IMAGE_PKG_DIR" log_info " dbg-pkg-dir: $DBG_PKG_DIR" +log_info " stage-root: $STAGE_ROOT" log_info " modules-manifest: $MODULES_MANIFEST" log_info " arch: $DKMS_ARCH" log_info " objcopy: $OBJCOPY" From 551a477da2b30dec9d1150c797aa6df6f118a731 Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 18 Sep 2026 01:07:28 +0100 Subject: [PATCH 3/8] packaging: ship DKMS modules in per-module binary packages Every module named in DKMS_MODULES was copied into linux-image- under /lib/modules//extra/, so a system installing the kernel got kgsl, camx, iris-vpu and audioreach whether or not it had the hardware for any of them, and the only way to decline one was a rootfs-wide /etc/modprobe.d blacklist. That blacklist is not tied to any kernel package, so it survives kernel upgrades and kernel switches and goes on suppressing the modules for kernels that wanted them. Ship each module in its own -modules- package instead, with its debug symbols in a matching -dbg. The modules are still built from the same -dkms source against the kernel produced by the same build, and a listed module is still a presence contract; only where they land changes. 'prepare' appends a pair of stanzas per entry in DKMS_LIST, substituted from the new debian/control-dkms.in. They cannot be spelled in control.in because there is one per element of a list that is a build input, and they come from the same DKMS_LIST that drives Build-Depends and the dkms-modules manifest, so a module cannot be declared in control without being built or vice versa. Conflicts and Replaces, both unversioned, against the -dkms package. The two forms are deliberately mutually exclusive: a -dkms install builds a second copy into updates/dkms/, which sits at the same depmod precedence as ours, so allowing both would leave which module loads up to depmod's ordering. Replaces costs nothing and makes the overlap declaratively legal rather than incidentally avoided. The modules move from extra/ to updates/qli/. extra/ is not in depmod's default search order on Debian at all -- it is Fedora's convention, and dkms picks it only there -- so the bundled modules have had the *lowest* precedence rather than the highest. updates/qli/ is the exact structural sibling of the updates/dkms/ a real DKMS install would use. Three consequences handled here: - The in-tree collision guard matters more, not less. Under extra/ an in-tree module of the same name won; under updates/ ours silently wins. The guard stays and its message now says which way the precedence runs. - The duplicate-basename guard had to change shape. It tested [[ -e "$dest" ]] against the one shared image tree; with a tree per package that test sees only the current module, while on the target they all unpack into a single updates/qli/ and still collide. It is now a run-scoped associative array keyed on basename, naming the manifest entry that claimed it. - A generated stanza with nothing staged under it produces a silently empty .deb -- dh_installdeb, dh_gencontrol and dh_builddeb are all happy with an empty tree and dh_missing does not look here. Assert a non-zero staged count per module package, so a wrong --stage-root fails where it happened. --dbg-pkg-dir is dropped: the debug trees are derived from --stage-root now. --image-pkg-dir stays, read-only, for the in-tree module list and the boot/config- that BUILD_EXCLUSIVE_CONFIG analysis reads. Only the modules are shipped. Whatever modprobe.d snippets, udev rules or initramfs hooks a -dkms package carries are not carried over with them; that is a separate change, and the package description says so rather than leaving it to be discovered. Verified by running prepare over a stub kernel tree: DKMS_MODULES=kgsl,camx yields 9 binary packages, all parsing clean through Dpkg::Control::Info; an empty list yields the same 5 packages as before with an unchanged Build-Depends. Signed-off-by: Christopher Obbard --- debian/control-dkms.in | 32 +++++++ debian/rules | 38 ++++++-- debian/scripts/bundle-dkms-modules.sh | 126 ++++++++++++++++++-------- 3 files changed, 151 insertions(+), 45 deletions(-) create mode 100644 debian/control-dkms.in diff --git a/debian/control-dkms.in b/debian/control-dkms.in new file mode 100644 index 00000000..f4812302 --- /dev/null +++ b/debian/control-dkms.in @@ -0,0 +1,32 @@ +Package: @MODNAME@-modules-@KVER@ +Architecture: arm64 +Section: kernel +Depends: ${misc:Depends}, linux-image-@KVER@ (= ${binary:Version}) +Conflicts: @MODNAME@-dkms +Replaces: @MODNAME@-dkms +Provides: @MODNAME@-modules +Description: @MODNAME@ kernel modules for Qualcomm ARM64 Linux @KVER@ + Out-of-tree @MODNAME@ modules, built from the @MODNAME@-dkms source against + kernel release @KVER@ and shipped prebuilt, so nothing has to be compiled on + the target and neither dkms nor the kernel headers need to be installed there. + . + The modules are installed in /lib/modules/@KVER@/updates/qli/. Only the + modules are shipped: any modprobe.d snippets, udev rules or initramfs hooks + the @MODNAME@-dkms package carries are not carried over with them. + . + This package conflicts with @MODNAME@-dkms. That package would build a second + copy of the same modules into /lib/modules/@KVER@/updates/dkms/, which sits at + the same depmod precedence as the copy here, so which one loaded would depend + on depmod's ordering rather than on a decision anyone made. + +Package: @MODNAME@-modules-@KVER@-dbg +Architecture: arm64 +Section: debug +Priority: optional +Depends: ${misc:Depends}, @MODNAME@-modules-@KVER@ (= ${binary:Version}) +Description: @MODNAME@ kernel module debug symbols for @KVER@ + Debug symbols for the @MODNAME@ modules in @MODNAME@-modules-@KVER@, following + the GNU debuglink layout used by the upstream kernel builddeb script. + . + Files installed: + - /usr/lib/debug/lib/modules/@KVER@/updates/qli/ (per-module .ko debug symbols) diff --git a/debian/rules b/debian/rules index 42bfa8da..b179e6e3 100755 --- a/debian/rules +++ b/debian/rules @@ -61,12 +61,16 @@ KREL_FILE := debian/kernel.release # SRCPKG Source package name (default: linux-qcom-next). # BINPKG Binary metapackage name (default: linux-image-qcom-next). # DEBIAN_REVISION Debian revision component of the package version (default: 0qcom1). -# DKMS_MODULES Comma-separated out-of-tree DKMS modules to build and bundle -# into linux-image-, each named without the -dkms suffix -# (e.g. DKMS_MODULES=kgsl,camx). Empty (the default) bundles +# DKMS_MODULES Comma-separated out-of-tree DKMS modules to build and ship, +# each named without the -dkms suffix (e.g. +# DKMS_MODULES=kgsl,camx). Empty (the default) builds # nothing. Each name must have a -dkms package available # in the build environment; the Build-Depends entry for it is # generated from this list, so nothing else declares it. +# Each entry produces its own pair of binary packages, +# -modules- and -modules--dbg, whose +# control stanzas are generated from debian/control-dkms.in. +# The modules are NOT installed into linux-image-. # GIT_CLONE Kernel repository URL, recorded in debian/changelog. # GIT_REF Resolved kernel ref (tag or branch), recorded in the changelog. # GIT_SHA Full kernel HEAD commit SHA, recorded in the changelog. @@ -83,7 +87,9 @@ KREL_FILE := debian/kernel.release # by hand. # # Outputs (generated files, all listed in debian/clean): -# debian/control Substituted from debian/control.in +# debian/control Substituted from debian/control.in, with one pair of +# stanzas per DKMS_MODULES entry appended from +# debian/control-dkms.in # debian/changelog Substituted from debian/changelog.in # debian/dkms-modules Manifest of the DKMS_MODULES entries, read at build time # by debian/scripts/bundle-dkms-modules.sh. Always written, @@ -243,6 +249,23 @@ prepare: -e "s|@PKGVER@|$$PKG_VERSION|g" \ -e "s|@DKMS_BUILD_DEPENDS@|$$DKMS_BUILD_DEPENDS|g" \ debian/control.in > debian/control; \ + \ + # One pair of binary package stanzas per bundled module, appended to the + # generated control. Every module named in DKMS_MODULES ships as its own + # -modules- package rather than being copied into the image, so + # the stanzas cannot be spelled in control.in: there is one per entry in a + # list that is a build input. + # + # Appended from the same DKMS_LIST that drives Build-Depends and the + # dkms-modules manifest, so a module cannot be declared in control without + # also being built, or built without being declared. + for mod in $$DKMS_LIST; do \ + printf '\n' >> debian/control; \ + sed \ + -e "s|@MODNAME@|$$mod|g" \ + -e "s|@KVER@|$$KVER_RESOLVED|g" \ + debian/control-dkms.in >> debian/control; \ + done; \ sed \ -e "s|@SRCPKG@|$(SRCPKG)|g" \ -e "s|@PKGVER@|$$PKG_VERSION|g" \ @@ -528,13 +551,16 @@ override_dh_auto_install: # Delegated to debian/scripts/bundle-dkms-modules.sh. # The script reads debian/dkms-modules, builds each listed module against the # staging headers produced earlier in this same dpkg-buildpackage run, and - # bundles the resulting .ko files into the linux-image and -dbg staging trees. + # stages the resulting .ko files into debian/-modules-/ and their + # debug symbols into debian/-modules--dbg/ — the packages whose + # control stanzas 'prepare' generated from the same manifest. + # --image-pkg-dir is read, not written: the in-tree module list for the + # collision check and boot/config- for BUILD_EXCLUSIVE_CONFIG analysis. # It can also be invoked directly by a developer — see the script's --help. $(CURDIR)/debian/scripts/bundle-dkms-modules.sh \ --kver "$$BASE" \ --headers-dir "$(CURDIR)/$$HDRDIR" \ --image-pkg-dir "$(CURDIR)/$$PKG" \ - --dbg-pkg-dir "$(CURDIR)/$$DBG_PKG" \ --stage-root "$(CURDIR)/debian" \ --arch "$(DKMS_ARCH)" \ --objcopy "$(OBJCOPY)" \ diff --git a/debian/scripts/bundle-dkms-modules.sh b/debian/scripts/bundle-dkms-modules.sh index 40d5bcbc..e25f21dd 100755 --- a/debian/scripts/bundle-dkms-modules.sh +++ b/debian/scripts/bundle-dkms-modules.sh @@ -3,14 +3,19 @@ # SPDX-License-Identifier: BSD-3-Clause set -euo pipefail -# bundle-dkms-modules.sh — Build and bundle out-of-tree DKMS modules into the -# linux-image staging tree at dpkg-buildpackage time. +# bundle-dkms-modules.sh — Build out-of-tree DKMS modules against the kernel +# being produced and stage each one into its own binary package tree at +# dpkg-buildpackage time. # # This script is the single source of truth for DKMS module integration. # It is called by debian/rules override_dh_auto_install after the kernel image, # modules, headers, and debug packages have been staged, and can also be invoked # directly by a developer who has already staged those trees manually. # +# The modules do NOT go into linux-image-. Each one ships in its own +# -modules- package, whose control stanza 'debian/rules prepare' +# generated from the same manifest entry that brings the module here. +# # What it does (for each module listed in the manifest): # 1. Resolves the installed -dkms package via dpkg -L (authoritative, no globbing). # 2. Reads PACKAGE_NAME / PACKAGE_VERSION from the package's dkms.conf. @@ -22,9 +27,9 @@ set -euo pipefail # On failure: adds BUILD_EXCLUSIVE gate analysis when dkms attempted no # build, then hard-fails — a manifest entry is a presence contract. # 6. For each produced .ko: -# - Collision-checks against already-bundled modules and in-tree modules. -# - Installs to /lib/modules//extra/.ko -# - Extracts debug symbols to /usr/lib/debug/lib/modules//extra/.ko +# - Collision-checks against already-staged modules and in-tree modules. +# - Installs to /-modules-/lib/modules//updates/qli/.ko +# - Extracts debug symbols to /-modules--dbg/usr/lib/debug/lib/modules//updates/qli/.ko # via objcopy --only-keep-debug (Stage 1, non-destructive). # - Strips the shipped copy with `strip --strip-debug` (Stage 2). # --strip-debug is required for kernel modules: a full strip drops the @@ -34,7 +39,6 @@ set -euo pipefail # - The kernel image staging tree must exist at --image-pkg-dir with: # lib/modules//kernel/ (in-tree modules, for collision detection) # boot/config- (kernel .config, for BUILD_EXCLUSIVE_CONFIG checks) -# - The debug package staging tree must exist at --dbg-pkg-dir. # - The directory holding the per-package staging trees must exist at # --stage-root (absolute). In a source package this is debian/. # - The kernel headers must be fully staged at --headers-dir (absolute path). @@ -51,7 +55,6 @@ set -euo pipefail # --kver "$BASE" \ # --headers-dir "$(CURDIR)/debian/linux-headers-$BASE/usr/src/linux-headers-$BASE" \ # --image-pkg-dir "$(CURDIR)/debian/linux-image-$BASE" \ -# --dbg-pkg-dir "$(CURDIR)/debian/linux-image-$BASE-dbg" \ # --stage-root "$(CURDIR)/debian" \ # --arch "$(DKMS_ARCH)" \ # --objcopy "$(OBJCOPY)" \ @@ -62,7 +65,7 @@ set -euo pipefail # --kver 6.12.0-qcom-next-20260210 \ # --headers-dir /path/to/kernel-source/debian/linux-headers-6.12.0-qcom-next-20260210/usr/src/linux-headers-6.12.0-qcom-next-20260210 \ # --image-pkg-dir /path/to/kernel-source/debian/linux-image-6.12.0-qcom-next-20260210 \ -# --dbg-pkg-dir /path/to/kernel-source/debian/linux-image-6.12.0-qcom-next-20260210-dbg +# --stage-root /path/to/kernel-source/debian SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -79,7 +82,6 @@ _DKMS_SRC_ROOT="${_BUNDLE_DKMS_SRC_ROOT:-/usr/src}" KVER="" HEADERS_DIR="" IMAGE_PKG_DIR="" -DBG_PKG_DIR="" STAGE_ROOT="" # Default manifest: debian/dkms-modules (one level up from debian/scripts/) MODULES_MANIFEST="${SCRIPT_DIR}/../dkms-modules" @@ -116,18 +118,18 @@ REQUIRED: module source directory, so a relative path fails. In debian/rules this is: \$(CURDIR)/debian/linux-headers-\$BASE/usr/src/linux-headers-\$BASE - --image-pkg-dir DIR Path to the linux-image staging tree root. - .ko files are installed under: - /lib/modules//extra/ + --image-pkg-dir DIR Path to the linux-image staging tree root. Read + from, never written to: it supplies the in-tree + module list for the collision check and + boot/config- for BUILD_EXCLUSIVE_CONFIG + analysis. In debian/rules this is: \$(CURDIR)/debian/linux-image-\$BASE - --dbg-pkg-dir DIR Path to the debug package staging tree root. - Debug symbols are installed under: - /usr/lib/debug/lib/modules//extra/ - In debian/rules this is: - \$(CURDIR)/debian/linux-image-\$BASE-dbg --stage-root DIR Directory the per-package staging trees live in. MUST be absolute, and must already exist. + Each module is staged into two trees below it: + /-modules-/ + /-modules--dbg/ In debian/rules this is: \$(CURDIR)/debian @@ -152,11 +154,10 @@ PREREQUISITES (developer standalone use): 2. --headers-dir must point to a fully staged kernel headers tree. 3. --image-pkg-dir must contain lib/modules//kernel/ (in-tree modules) and boot/config- (kernel .config). - 4. --dbg-pkg-dir must exist (can be empty; subdirs are created as needed). - 5. --stage-root must exist; it is the directory per-package staging trees + 4. --stage-root must exist; it is the directory per-package staging trees are created in, i.e. debian/ in a source package. - 6. --headers-dir and --stage-root must be absolute paths. - 7. This script does NOT cross-compile: dkms builds each module with the host + 5. --headers-dir and --stage-root must be absolute paths. + 6. This script does NOT cross-compile: dkms builds each module with the host toolchain (no ARCH/CROSS_COMPILE is plumbed). Run it on a native arm64 host (or an arm64 chroot / qemu-user environment) so the produced .ko matches the target kernel. --arch only sets the dkms architecture label @@ -175,14 +176,14 @@ EXAMPLES: --kver 6.12.0-qcom-next-20260210 \\ --headers-dir /build/kernel/debian/linux-headers-6.12.0-qcom-next-20260210/usr/src/linux-headers-6.12.0-qcom-next-20260210 \\ --image-pkg-dir /build/kernel/debian/linux-image-6.12.0-qcom-next-20260210 \\ - --dbg-pkg-dir /build/kernel/debian/linux-image-6.12.0-qcom-next-20260210-dbg + --stage-root /build/kernel/debian # Developer standalone path: debian/scripts/bundle-dkms-modules.sh \\ --kver 6.12.0-qcom-next-20260210 \\ --headers-dir /path/to/staged/linux-headers-6.12.0-qcom-next-20260210 \\ --image-pkg-dir /path/to/staged/linux-image-6.12.0-qcom-next-20260210 \\ - --dbg-pkg-dir /path/to/staged/linux-image-6.12.0-qcom-next-20260210-dbg \\ + --stage-root /path/to/staged/debian \\ --arch aarch64 # With explicit manifest and objcopy: @@ -190,7 +191,7 @@ EXAMPLES: --kver 6.12.0-qcom-next-20260210 \\ --headers-dir /path/to/headers \\ --image-pkg-dir /path/to/image-pkg \\ - --dbg-pkg-dir /path/to/dbg-pkg \\ + --stage-root /path/to/debian \\ --modules-manifest /path/to/debian/dkms-modules \\ --objcopy aarch64-linux-gnu-objcopy EOF @@ -208,7 +209,6 @@ while [[ $# -gt 0 ]]; do --kver) require_val "$@"; KVER="$2"; shift 2 ;; --headers-dir) require_val "$@"; HEADERS_DIR="$2"; shift 2 ;; --image-pkg-dir) require_val "$@"; IMAGE_PKG_DIR="$2"; shift 2 ;; - --dbg-pkg-dir) require_val "$@"; DBG_PKG_DIR="$2"; shift 2 ;; --stage-root) require_val "$@"; STAGE_ROOT="$2"; shift 2 ;; --modules-manifest) require_val "$@"; MODULES_MANIFEST="$2"; shift 2 ;; --arch) require_val "$@"; DKMS_ARCH="$2"; shift 2 ;; @@ -227,7 +227,6 @@ _missing=() [[ -n "$KVER" ]] || _missing+=(--kver) [[ -n "$HEADERS_DIR" ]] || _missing+=(--headers-dir) [[ -n "$IMAGE_PKG_DIR" ]] || _missing+=(--image-pkg-dir) -[[ -n "$DBG_PKG_DIR" ]] || _missing+=(--dbg-pkg-dir) [[ -n "$STAGE_ROOT" ]] || _missing+=(--stage-root) if [[ ${#_missing[@]} -gt 0 ]]; then log_error "Missing required arguments: ${_missing[*]}" @@ -295,9 +294,6 @@ fi log_error "The linux-image staging tree must exist before calling this script." exit 1 } -if [[ ! -d "$DBG_PKG_DIR" ]]; then - log_warn "--dbg-pkg-dir does not exist: $DBG_PKG_DIR (will be created as needed)" -fi [[ -d "$STAGE_ROOT" ]] || { log_error "--stage-root does not exist: $STAGE_ROOT" log_error "It is the directory the per-package staging trees are created in" @@ -309,7 +305,6 @@ log_step "DKMS module bundling configuration:" log_info " kver: $KVER" log_info " headers-dir: $HEADERS_DIR" log_info " image-pkg-dir: $IMAGE_PKG_DIR" -log_info " dbg-pkg-dir: $DBG_PKG_DIR" log_info " stage-root: $STAGE_ROOT" log_info " modules-manifest: $MODULES_MANIFEST" log_info " arch: $DKMS_ARCH" @@ -357,6 +352,17 @@ echo DKMS_TREE="$(mktemp -d)" trap 'rm -rf "$DKMS_TREE"' EXIT +# --------------------------------------------------------------------------- +# Every module staged by this run, keyed on the basename it will install under. +# +# Each module package stages into its own tree, but on the target they all +# unpack into one /lib/modules//updates/qli/, so two modules sharing a +# basename still collide there. A per-tree existence check cannot see that; +# this can. The value is the manifest entry that claimed the name, so the +# error can say which one. +# --------------------------------------------------------------------------- +declare -A seen_ko=() + # --------------------------------------------------------------------------- # Main loop: build and bundle each listed module # --------------------------------------------------------------------------- @@ -504,10 +510,26 @@ for name in $DKMS_MODULES; do [[ -z "$comp" ]] || \ echo " Note: found compressed module output ($comp); compressed dkms output is not supported." >&2 fi - log_error "Refusing to ship linux-image-$KVER without $PKG_NAME." + log_error "Refusing to ship $name-modules-$KVER without $PKG_NAME." exit 1 fi + # ── Per-module staging trees ───────────────────────────────────────────── + # The modules ship in -modules-, not in the kernel image, and + # their debug files in the matching -dbg package. Both names are the ones + # 'debian/rules prepare' spelled into debian/control from the same manifest + # entry, so a typo here surfaces as an empty package rather than silently + # landing in the wrong one -- which is what the post-staging assertion + # below is for. + # + # $name, not $PKG_NAME: the package name stem is what the manifest, the + # Build-Depends entry and the control stanza all agree on. dkms.conf's + # PACKAGE_NAME is free to differ from it (and does -- iris-vpu builds + # iris_vpu), and is only used to address the dkms tree. + MOD_PKG_DIR="$STAGE_ROOT/$name-modules-$KVER" + MOD_DBG_DIR="$STAGE_ROOT/$name-modules-$KVER-dbg" + staged_ko=0 + # ── Install, extract debug, and strip each produced .ko ────────────────── # Mirror the in-tree module treatment: # Stage 1 — install the .ko (unstripped at this point) @@ -522,22 +544,30 @@ for name in $DKMS_MODULES; do # the script on collision errors. while IFS= read -r ko; do b="$(basename "$ko")" - dest="$IMAGE_PKG_DIR/lib/modules/$KVER/extra/$b" - dbg="$DBG_PKG_DIR/usr/lib/debug/lib/modules/$KVER/extra/$b" - - # Guard: duplicate bundled module name (two manifest entries → same basename) - if [[ -e "$dest" ]]; then + dest="$MOD_PKG_DIR/lib/modules/$KVER/updates/qli/$b" + dbg="$MOD_DBG_DIR/usr/lib/debug/lib/modules/$KVER/updates/qli/$b" + + # Guard: duplicate module name across every module staged by this run. + # Checked against the run-scoped set rather than the destination tree: + # each module package has its own tree, so two packages claiming one + # basename would each look unoccupied while still colliding in the + # single updates/qli/ they share once installed. + if [[ -n "${seen_ko[$b]:-}" ]]; then log_error "Duplicate bundled module name: $b" - log_error "Already bundled by an earlier manifest entry — check $MODULES_MANIFEST" + log_error "Already staged by manifest entry '${seen_ko[$b]}' — check $MODULES_MANIFEST" exit 1 fi + seen_ko[$b]="$name" # Guard: in-tree collision (bundled module shares name with an in-tree module) intree="$(find "$IMAGE_PKG_DIR/lib/modules/$KVER/kernel" \ -name "$b" -print -quit 2>/dev/null || true)" if [[ -n "$intree" ]]; then log_error "Bundled module $b collides with in-tree module: $intree" - log_error "Module precedence on the target would be ambiguous (depmod search order)." + log_error " in-tree: $intree" + log_error " this one: /lib/modules/$KVER/updates/qli/$b" + log_error "updates/ outranks kernel/ in depmod's default search order, so" + log_error "this module would silently take precedence over the in-tree one." exit 1 fi @@ -585,12 +615,30 @@ for name in $DKMS_MODULES; do # Stage 3: strip the shipped copy in place strip --strip-debug "$dest" + staged_ko=$((staged_ko + 1)) + log_info " Installed: $dest (stripped)" log_info " Debug: $dbg" done < <(printf '%s\n' "$kos") - log_info "Bundled $PKG_NAME modules into $(basename "$IMAGE_PKG_DIR")" + # ── Assert the package is not empty ────────────────────────────────────── + # dkms produced at least one .ko -- that is checked above -- but nothing + # downstream notices if none of them reached the staging tree. A binary + # package with an empty tree builds cleanly: dh_installdeb creates DEBIAN/, + # dh_gencontrol and dh_builddeb are happy, and dh_missing does not look + # here. The result is a published .deb containing nothing. + # + # Check the destination rather than trusting the loop, so a wrong + # --stage-root or a mistyped package name is caught where it happened. + if [[ "$staged_ko" -eq 0 ]]; then + log_error "Staged no modules into $name-modules-$KVER despite dkms producing:" + printf '%s\n' "$kos" | sed 's/^/ | /' >&2 + log_error "The package would ship empty. Check --stage-root ($STAGE_ROOT)." + exit 1 + fi + + log_info "Staged $staged_ko module(s) into $name-modules-$KVER" echo done From 5ac7c7c5b5b5dc060d4d58932cea9c0ad7933815 Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 18 Sep 2026 01:07:50 +0100 Subject: [PATCH 4/8] packaging: record the DKMS source package in Built-Using The .ko in -modules- is compiled from source that lives in a different source package entirely, and nothing in the Debian metadata says so. The binary is emitted by src:linux-qcom-next, which does not contain a line of the module's code, so an archive holding the binary has no reason to keep the source that produced it. Built-Using names that source and its exact version, which is what makes the archive retain it alongside the binary. The bundler is the only place that can know the value: it is a property of the -dkms package installed in the build chroot, not of anything in this source tree, so it cannot be spelled in the template and reaches the stanza as a substvar. ${source:Package} / ${source:Version} rather than the binary's own name and version. dpkg parses these out of the binary's "Source: name (ver)" field when it has one and falls back to the binary version otherwise, epoch included. Writing the file during override_dh_auto_install is safe: dh_prep is the only thing that truncates a .substvars and it runs before dh_auto_install, while every writer after that point merges through addsubstvar. The write filters any previous key out rather than appending, because this script is documented as callable by hand outside the dh sequence, where nothing has cleared the file and a second run would otherwise leave two copies of the key. A failed dpkg-query is fatal with our own message rather than dpkg's. The package resolved through dpkg -L a few lines earlier, so a failure here means something genuinely strange rather than a missing build dependency. The generated files are added to .gitignore and debian/clean alongside the other build-time artifacts, so a dirty tree does not carry them. Signed-off-by: Christopher Obbard --- .gitignore | 3 +++ debian/clean | 1 + debian/control-dkms.in | 1 + debian/scripts/bundle-dkms-modules.sh | 33 +++++++++++++++++++++++++++ 4 files changed, 38 insertions(+) diff --git a/.gitignore b/.gitignore index 2583b54a..364c6267 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ debian/kernel.release debian/localversion debian/pkgversion +# Generated by debian/scripts/bundle-dkms-modules.sh during the build +debian/*-modules-*.substvars + # Generated maintainer scripts (versioned names, created during build) debian/linux-image-*.postinst debian/linux-image-*.preinst diff --git a/debian/clean b/debian/clean index 39dfb22e..3e21df42 100644 --- a/debian/clean +++ b/debian/clean @@ -1,4 +1,5 @@ debian/kernel.release +debian/*-modules-*.substvars debian/linux-image-*.postinst debian/linux-image-*.preinst debian/linux-image-*.postrm diff --git a/debian/control-dkms.in b/debian/control-dkms.in index f4812302..985f1cf3 100644 --- a/debian/control-dkms.in +++ b/debian/control-dkms.in @@ -5,6 +5,7 @@ Depends: ${misc:Depends}, linux-image-@KVER@ (= ${binary:Version}) Conflicts: @MODNAME@-dkms Replaces: @MODNAME@-dkms Provides: @MODNAME@-modules +Built-Using: ${dkms:Built-Using} Description: @MODNAME@ kernel modules for Qualcomm ARM64 Linux @KVER@ Out-of-tree @MODNAME@ modules, built from the @MODNAME@-dkms source against kernel release @KVER@ and shipped prebuilt, so nothing has to be compiled on diff --git a/debian/scripts/bundle-dkms-modules.sh b/debian/scripts/bundle-dkms-modules.sh index e25f21dd..7c31e99d 100755 --- a/debian/scripts/bundle-dkms-modules.sh +++ b/debian/scripts/bundle-dkms-modules.sh @@ -638,7 +638,40 @@ for name in $DKMS_MODULES; do exit 1 fi + # ── Record the DKMS source package in Built-Using ──────────────────────── + # The .ko in this package was compiled from source that lives in another + # source package entirely, and nothing in the Debian metadata would say so: + # the binary is emitted by src:linux-qcom-next, which does not contain a + # line of the module's code. Built-Using names the exact source that did, + # which is what keeps it retained in the archive alongside the binary. + # + # ${source:Package} / ${source:Version} rather than the binary's own name + # and version: dpkg parses these out of the binary's "Source: name (ver)" + # field when it has one, and falls back to the binary version, epoch + # included, when it does not. Both are the right answer. + dkms_src="$(dpkg-query -W -f='${source:Package} (= ${source:Version})' \ + "${name}-dkms" 2>/dev/null)" || { + log_error "Could not read the source package of ${name}-dkms" + log_error "It resolved through dpkg -L a moment ago, so this is unexpected." + exit 1 + } + + # Written into the staging root, which is where dh_gencontrol looks. Safe + # to write here: dh_prep is the only thing that truncates a .substvars and + # it runs before dh_auto_install; everything after this point merges. + # + # Rewritten rather than appended, because this script is also callable by + # hand outside the dh sequence, where no dh_prep has cleared the file and a + # second run would otherwise accumulate duplicate keys. + substvars="$STAGE_ROOT/$name-modules-$KVER.substvars" + if [[ -f "$substvars" ]]; then + grep -v '^dkms:Built-Using=' "$substvars" > "$substvars.new" || true + mv "$substvars.new" "$substvars" + fi + printf 'dkms:Built-Using=%s\n' "$dkms_src" >> "$substvars" + log_info "Staged $staged_ko module(s) into $name-modules-$KVER" + log_info " Built-Using: $dkms_src" echo done From d926ae273d082308125c3c4021e7026e4846c8ee Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 18 Sep 2026 01:08:24 +0100 Subject: [PATCH 5/8] packaging: add an unversioned metapackage per DKMS module The modules a variant builds are only installable by their versioned names, -modules-. That name changes on every snapshot, so nothing can depend on "the kgsl modules for this kernel" the way linux-image-qcom-next already lets something depend on "the newest image for this variant": an image metapackage exists, a module one does not, and a user who wants the modules to follow the kernel has to reinstall by hand after each build. Append a third stanza per DKMS_LIST entry, -modules-, depending on that entry's versioned package at (= ${binary:Version}). It is generated in the same loop, from the same debian/control-dkms.in, so a module cannot gain a metapackage without being built or be built without gaining one. Named off BINPKG rather than from a stem derived out of it. The variant already has exactly one unversioned name the matrix gives it, and extending it means every unversioned package a variant publishes shares one prefix -- linux-image-qcom-next, linux-image-qcom-next-modules-kgsl -- so they list together and no second naming scheme has to be kept in step with the first. The substitution is @BINPKG@, which control.in already spells, so the loop only had to pass it through. No -dbg metapackage. Debug symbols are fetched for a specific build, which is the case the versioned name already serves. Verified by running prepare over a stub kernel tree: DKMS_MODULES=kgsl,camx now yields 11 binary packages, parsing clean through Dpkg::Control::Info, and an empty list still yields the same 5 with an unchanged Build-Depends. Signed-off-by: Christopher Obbard --- debian/control-dkms.in | 9 +++++++++ debian/rules | 10 ++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/debian/control-dkms.in b/debian/control-dkms.in index 985f1cf3..8bd2942c 100644 --- a/debian/control-dkms.in +++ b/debian/control-dkms.in @@ -31,3 +31,12 @@ Description: @MODNAME@ kernel module debug symbols for @KVER@ . Files installed: - /usr/lib/debug/lib/modules/@KVER@/updates/qli/ (per-module .ko debug symbols) + +Package: @BINPKG@-modules-@MODNAME@ +Architecture: arm64 +Section: kernel +Depends: @MODNAME@-modules-@KVER@ (= ${binary:Version}), ${misc:Depends} +Description: @MODNAME@ kernel modules for Qualcomm ARM64 Linux (meta-package) + This meta-package always depends on the @MODNAME@ modules built for the latest + @BINPKG@ kernel image, so installing it keeps the @MODNAME@ modules in step + with the kernel across apt upgrades. diff --git a/debian/rules b/debian/rules index b179e6e3..a8b43a36 100755 --- a/debian/rules +++ b/debian/rules @@ -67,8 +67,9 @@ KREL_FILE := debian/kernel.release # nothing. Each name must have a -dkms package available # in the build environment; the Build-Depends entry for it is # generated from this list, so nothing else declares it. -# Each entry produces its own pair of binary packages, -# -modules- and -modules--dbg, whose +# Each entry produces its own binary packages, the versioned +# -modules- and -modules--dbg plus +# the unversioned -modules- metapackage, whose # control stanzas are generated from debian/control-dkms.in. # The modules are NOT installed into linux-image-. # GIT_CLONE Kernel repository URL, recorded in debian/changelog. @@ -87,7 +88,7 @@ KREL_FILE := debian/kernel.release # by hand. # # Outputs (generated files, all listed in debian/clean): -# debian/control Substituted from debian/control.in, with one pair of +# debian/control Substituted from debian/control.in, with one group of # stanzas per DKMS_MODULES entry appended from # debian/control-dkms.in # debian/changelog Substituted from debian/changelog.in @@ -250,7 +251,7 @@ prepare: -e "s|@DKMS_BUILD_DEPENDS@|$$DKMS_BUILD_DEPENDS|g" \ debian/control.in > debian/control; \ \ - # One pair of binary package stanzas per bundled module, appended to the + # One group of binary package stanzas per bundled module, appended to the # generated control. Every module named in DKMS_MODULES ships as its own # -modules- package rather than being copied into the image, so # the stanzas cannot be spelled in control.in: there is one per entry in a @@ -264,6 +265,7 @@ prepare: sed \ -e "s|@MODNAME@|$$mod|g" \ -e "s|@KVER@|$$KVER_RESOLVED|g" \ + -e "s|@BINPKG@|$(BINPKG)|g" \ debian/control-dkms.in >> debian/control; \ done; \ sed \ From 2b3b6eee418dd0a663e97eab34549f460d74750c Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 18 Sep 2026 01:09:10 +0100 Subject: [PATCH 6/8] packaging: add a metapackage covering all the DKMS modules A variant now has one metapackage per module, but nothing names the set. An image or a rootfs recipe that wants everything this kernel was built with has to enumerate the modules itself, which means the list lives in two places -- the dkms field in ci/build-matrix.json and whatever consumes the packages -- and drifts the first time one of them gains an entry. Append one -modules stanza, generated from the new debian/control-dkms-all.in, depending on every -modules- the loop above just emitted. Installing that one name pulls in the whole out-of-tree module set for the variant, and the set is described where it is decided rather than by whoever installs it. It depends on the metapackages, not on the versioned packages, so what it names survives a snapshot: only the metapackages underneath it have to re-point at new versioned names, and this stanza's Depends list changes only when DKMS_MODULES itself does. Emitted only when DKMS_LIST is non-empty. A variant that bundles nothing would otherwise publish an empty metapackage depending on nothing, which is not a thing anyone would install on purpose. @DKMS_META_DEPENDS@ is appended after ${misc:Depends} the same way @DKMS_BUILD_DEPENDS@ is appended to Build-Depends, so the field never carries a dangling comma. Verified by running prepare over a stub kernel tree: DKMS_MODULES=kgsl,camx now yields 12 binary packages, parsing clean through Dpkg::Control::Info, with linux-image-qcom-next-modules depending on both per-module metapackages; BINPKG=linux-image-qcom-next-debug names them all off that prefix instead; and an empty list still yields the same 5 packages with an unchanged Build-Depends. Signed-off-by: Christopher Obbard --- debian/control-dkms-all.in | 9 +++++++++ debian/rules | 24 +++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 debian/control-dkms-all.in diff --git a/debian/control-dkms-all.in b/debian/control-dkms-all.in new file mode 100644 index 00000000..da1e6d2d --- /dev/null +++ b/debian/control-dkms-all.in @@ -0,0 +1,9 @@ +Package: @BINPKG@-modules +Architecture: arm64 +Section: kernel +Depends: ${misc:Depends}@DKMS_META_DEPENDS@ +Description: Qualcomm ARM64 out-of-tree kernel modules (meta-package) + This meta-package always depends on every out-of-tree module meta-package + built for the latest kernel image of this variant, so installing it alongside + @BINPKG@ pulls in the full module set and keeps it in step with the kernel + across apt upgrades. diff --git a/debian/rules b/debian/rules index a8b43a36..c94f9db8 100755 --- a/debian/rules +++ b/debian/rules @@ -71,6 +71,9 @@ KREL_FILE := debian/kernel.release # -modules- and -modules--dbg plus # the unversioned -modules- metapackage, whose # control stanzas are generated from debian/control-dkms.in. +# A non-empty list also produces one -modules +# metapackage depending on all of them, generated from +# debian/control-dkms-all.in. # The modules are NOT installed into linux-image-. # GIT_CLONE Kernel repository URL, recorded in debian/changelog. # GIT_REF Resolved kernel ref (tag or branch), recorded in the changelog. @@ -90,7 +93,9 @@ KREL_FILE := debian/kernel.release # Outputs (generated files, all listed in debian/clean): # debian/control Substituted from debian/control.in, with one group of # stanzas per DKMS_MODULES entry appended from -# debian/control-dkms.in +# debian/control-dkms.in, plus a single all-modules +# metapackage stanza from debian/control-dkms-all.in +# when the list is not empty # debian/changelog Substituted from debian/changelog.in # debian/dkms-modules Manifest of the DKMS_MODULES entries, read at build time # by debian/scripts/bundle-dkms-modules.sh. Always written, @@ -268,6 +273,23 @@ prepare: -e "s|@BINPKG@|$(BINPKG)|g" \ debian/control-dkms.in >> debian/control; \ done; \ + \ + # One more stanza, once, when anything was bundled: the metapackage that + # pulls in every per-module metapackage above, so the whole out-of-tree + # module set for a variant installs under a single name. + # It depends on those metapackages rather than on the versioned packages, + # so what it names does not have to be re-resolved on every snapshot. + if [ -n "$$DKMS_LIST" ]; then \ + DKMS_META_DEPENDS=""; \ + for mod in $$DKMS_LIST; do \ + DKMS_META_DEPENDS="$$DKMS_META_DEPENDS, $(BINPKG)-modules-$$mod (= \$${binary:Version})"; \ + done; \ + printf '\n' >> debian/control; \ + sed \ + -e "s|@BINPKG@|$(BINPKG)|g" \ + -e "s|@DKMS_META_DEPENDS@|$$DKMS_META_DEPENDS|g" \ + debian/control-dkms-all.in >> debian/control; \ + fi; \ sed \ -e "s|@SRCPKG@|$(SRCPKG)|g" \ -e "s|@PKGVER@|$$PKG_VERSION|g" \ From 9a242039f1c98b49f0059f8f8e2c3cb1e62a5dd2 Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 18 Sep 2026 01:11:42 +0100 Subject: [PATCH 7/8] packaging: document the per-module DKMS packages The docs still described the previous arrangement throughout: modules bundled into linux-image-, five binary packages per variant, DKMS debug symbols under the image's -dbg in extra/. All of that changed. debian/README.md carries the substance. The "DKMS module bundling" section becomes "Out-of-tree DKMS modules" and gains what the arrangement now is: the packages each list entry produces, why they Conflict with the -dkms package they came from, what Built-Using records and why the module source is never copied into src:linux-qcom-next. Two things there are worth writing down because they are not visible in any one file. Why updates/qli/ rather than extra/: extra/ is not in depmod's default search order on Debian at all, so the modules had the lowest precedence rather than the highest. And why /boot/System.map- matters to these packages: the depmod snippet debhelper generates for each of them is guarded by its presence. The section also states plainly that only the modules are shipped -- the modprobe.d snippets, udev rules and initramfs hooks a -dkms package carries are not carried over with them -- so a reader does not have to infer it from the absence of any mention. The remaining edits are corrections rather than additions: the package naming table and count, the -dbg package's installed paths, the matrix's dkms field description and artifact table in the top-level README, and the --dkms help in prepare-source.sh and build-kernel.sh. Signed-off-by: Christopher Obbard --- README.md | 16 +++-- build-kernel.sh | 12 ++-- debian/README.md | 154 +++++++++++++++++++++++++++++++++++++--------- prepare-source.sh | 12 +++- 4 files changed, 151 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 44c02bfa..74d0a647 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ its own values for: | `srcpkg` | Debian source package name. | | `binpkg` | Kernel image metapackage name. | | `kernel_config` | Extra fragments applied on top of `debian/config-available/`, all of which is applied to every build, one per array element. A bare name selects `debian/config-available/.config`; an `intree:` entry names a fragment shipped by the kernel source, as a path relative to the kernel source root (e.g. `intree:arch/arm64/configs/qcom_debug.config`), so it stays versioned with the kernel it targets. Empty for variants that need nothing beyond `config-available/`; today it carries only `intree:` fragments. `resolve-matrix.sh` joins it into the comma-separated `kernel-config` workflow input. | -| `dkms` | Out-of-tree DKMS modules built against this kernel and bundled into its `linux-image` package, as an object keyed by suite, each entry a list of modules named as the stem of their `-dkms` package (e.g. `kgsl`). A suite's entry is optional: a suite with no entry, and `{}` itself, bundles nothing. An empty list does the same for the suite it names. A listed module is a presence contract: a build fails rather than shipping an image without it. There is no default or fallback, so suites that can't build a given module (e.g. an Ubuntu-family suite lacking a package) simply list less, or omit an entry entirely. `resolve-matrix.sh` joins the resolved leg's suite into the comma-separated `dkms` workflow input. | +| `dkms` | Out-of-tree DKMS modules built against this kernel, as an object keyed by suite, each entry a list of modules named as the stem of their `-dkms` package (e.g. `kgsl`). Each module produces its own `-modules-` package plus an unversioned `-modules-` metapackage, rather than being installed into the image; a non-empty list also produces one `-modules` metapackage covering them all. A suite's entry is optional: a suite with no entry, and `{}` itself, builds nothing. An empty list does the same for the suite it names. A listed module is a presence contract: a build fails rather than publishing without it. There is no default or fallback, so suites that can't build a given module (e.g. an Ubuntu-family suite lacking a package) simply list less, or omit an entry entirely. `resolve-matrix.sh` joins the resolved leg's suite into the comma-separated `dkms` workflow input; see [debian/README.md](debian/README.md) for what the packaging does with it. | | `debian_version_stub` | Base Debian revision, shared by a variant's Daily and Release rows. Must not end in `~`; the suite suffix is derived, not stored here. | | `debian_version_suffix` | `~` for Daily rows, empty for Release rows. Documents the delivery-type half of the revision formula on the row itself; `resolve-matrix.sh` rejects a row where this disagrees with `type`, but derivation always computes this suffix from `type`, never reads this field. | | `localversion`, `kver_extra` | Optional version overrides forwarded to packaging. | @@ -253,8 +253,8 @@ This repository contains two separate parts: decides *how* it is built. This document covers the CI generator. For the packaging internals: `debian/rules` -targets, the config fragment merge pipeline, DKMS module bundling and the produced -package layout see [debian/README.md](debian/README.md). +targets, the config fragment merge pipeline, the out-of-tree DKMS module packages +and the produced package layout see [debian/README.md](debian/README.md). ```mermaid flowchart LR @@ -397,7 +397,11 @@ For the current matrix, package generation produces: | `linux-image-qcom-next__arm64.deb` | Image metapackage that tracks the newest kernel image. | | `linux-headers-__arm64.deb` | Versioned headers for DKMS and out-of-tree modules. | | `linux-headers-qcom-next__arm64.deb` | Headers metapackage. | -| `linux-image--dbg__arm64.deb` | Kernel and module debug symbols. | +| `linux-image--dbg__arm64.deb` | Kernel and in-tree module debug symbols. | +| `-modules-__arm64.deb` | Prebuilt out-of-tree modules, one package per `dkms` entry, built from that `-dkms` source against this kernel. Installs under `/lib/modules//updates/qli/`. Conflicts with `-dkms`. | +| `-modules--dbg__arm64.deb` | Debug symbols for the above. | +| `linux-image-qcom-next-modules-__arm64.deb` | Modules metapackage, one per `dkms` entry, that tracks the newest build of that module for this variant. | +| `linux-image-qcom-next-modules__arm64.deb` | Metapackage depending on every `linux-image-qcom-next-modules-` above, so the whole out-of-tree module set installs under one name. Built only when the variant has `dkms` entries. | `-rcN` remains in `uname -r`, module paths, boot assets, and versioned package names. Only the Debian version field converts it to `~rcN`, so a release @@ -427,7 +431,9 @@ sudo apt install linux-image-qcom-next When installing downloaded artifacts directly, install the versioned image and its metapackage together. Add the headers packages when DKMS or other -out-of-tree module builds are required. +out-of-tree module builds are required, and the module packages for whichever +out-of-tree modules the hardware needs — `linux-image-qcom-next-modules` takes +the whole set. ## Manual Builds diff --git a/build-kernel.sh b/build-kernel.sh index 147678a4..10be1f4c 100755 --- a/build-kernel.sh +++ b/build-kernel.sh @@ -71,11 +71,13 @@ OPTIONS: which is always applied in full. An "intree:" prefix names a path relative to the kernel source root (e.g. intree:arch/arm64/configs/qcom_debug.config) - --dkms LIST Comma-separated out-of-tree DKMS modules to build and - bundle into linux-image-, without the -dkms - suffix (e.g. --dkms kgsl,camx). Each entry needs its - -dkms package available to the build. Empty by - default (bundle nothing). + --dkms LIST Comma-separated out-of-tree DKMS modules to build + against this kernel, without the -dkms suffix + (e.g. --dkms kgsl,camx). Each entry needs its + -dkms package available to the build, and + ships in its own -modules- package + rather than in linux-image-. Empty by + default (build nothing). Paths: -k, --kernel-dir DIR Kernel source directory (default: $KERNEL_DIR) diff --git a/debian/README.md b/debian/README.md index 60793f2d..88326a21 100644 --- a/debian/README.md +++ b/debian/README.md @@ -19,6 +19,10 @@ Packages follow the standard Debian/Ubuntu kernel naming convention: | `linux-image--dbg` | Debug symbols | `linux-image-7.2.0-qcom-next-20260826-dbg` | | `` | Image metapackage tracking the newest kernel image | `linux-image-qcom-next` | | `` | Headers metapackage tracking the newest headers | `linux-headers-qcom-next` | +| `-modules-` | Prebuilt out-of-tree modules, one per `DKMS_MODULES` entry | `kgsl-modules-7.2.0-qcom-next-20260826` | +| `-modules--dbg` | Debug symbols for the above | `kgsl-modules-7.2.0-qcom-next-20260826-dbg` | +| `-modules-` | Modules metapackage tracking the newest build of that module | `linux-image-qcom-next-modules-kgsl` | +| `-modules` | Metapackage depending on every `-modules-` above | `linux-image-qcom-next-modules` | **``** is the full `kernelrelease` string (`uname -r`), which includes the base kernel version and the LOCALVERSION suffix encoding the variant and @@ -28,12 +32,19 @@ the versioned packages carry no separate flavour suffix. **``** and **``** are per-variant metapackage names set from the build matrix (`binpkg` / derived headers name). They stay constant across snapshots and depend on the newest versioned package, so installing -`linux-image-qcom-next` follows the latest build of that variant. - -`debian/control.in` declares all five, so a build publishes five binary packages -per variant. `dh_strip` is run with `--no-automatic-dbgsym`, so debhelper -generates no additional `-dbgsym` package: debug symbols are shipped only by the -declared `linux-image--dbg`. +`linux-image-qcom-next` follows the latest build of that variant. The module +metapackages extend `` rather than deriving a name of their own, so +every unversioned name a variant publishes shares one prefix and `apt search +linux-image-qcom-next` finds the whole set. + +`debian/control.in` declares the first five, so a build with no DKMS modules +publishes five binary packages per variant. Each `DKMS_MODULES` entry adds three +more, appended to `debian/control` by `prepare` from `debian/control-dkms.in`, +and a non-empty list adds one `-modules` stanza from +`debian/control-dkms-all.in`. A variant with two modules therefore publishes +twelve binary packages. `dh_strip` is run with `--no-automatic-dbgsym`, so +debhelper generates no additional `-dbgsym` package: debug symbols are shipped +only by the declared `-dbg` packages. **`-qcom`** is a static flavour suffix appended by the packaging, identifying Qualcomm-packaged kernels independently of the branch name. @@ -53,6 +64,8 @@ pkg-linux-qcom/ ├── build-source-package.sh ← Prepared tree → reproducible .orig.tar.gz, .dsc, .changes ├── debian/ │ ├── control.in ← Source-of-truth template (version-controlled) +│ ├── control-dkms.in ← Per-DKMS-module stanza template (version-controlled) +│ ├── control-dkms-all.in ← All-modules metapackage template (version-controlled) │ ├── changelog.in ← Source-of-truth template (version-controlled) │ ├── control ← Generated by 'prepare' (gitignored) │ ├── changelog ← Generated by 'prepare' (gitignored) @@ -75,7 +88,7 @@ pkg-linux-qcom/ │ │ └── usb-can.config ← USB CAN adapters │ ├── dkms-modules ← Generated by 'prepare' from DKMS_MODULES (gitignored) │ └── scripts/ -│ └── bundle-dkms-modules.sh ← DKMS build-and-bundle tool (called by rules; standalone-capable) +│ └── bundle-dkms-modules.sh ← DKMS build-and-stage tool (called by rules; standalone-capable) ├── .gitignore └── README.md ``` @@ -88,6 +101,8 @@ pkg-linux-qcom/ | File | Status | Description | |------|--------|-------------| | `debian/control.in` | ✅ Committed | Template with `@KVER@` placeholder | +| `debian/control-dkms.in` | ✅ Committed | Template for the `-modules-@KVER@` stanza group, appended once per `DKMS_MODULES` entry | +| `debian/control-dkms-all.in` | ✅ Committed | Template for the `@BINPKG@-modules` stanza, appended once when `DKMS_MODULES` is not empty | | `debian/changelog.in` | ✅ Committed | Template with `@KVER@` placeholder | | `debian/rules` | ✅ Committed | Build rules + `prepare` target | | `debian/linux-image.preinst.in` | ✅ Committed | Pre-install script template (`@KVER@` substituted at build time) | @@ -97,10 +112,11 @@ pkg-linux-qcom/ | `debian/config/*.config` | ✅ Committed | Always-applied config fragments | | `debian/config-available/*.config` | ✅ Committed | Packaging fragments, all applied to every build | | `debian/dkms-modules` | 🔄 Generated | Produced by `make -f debian/rules prepare DKMS_MODULES=...` | -| `debian/scripts/bundle-dkms-modules.sh` | ✅ Committed | DKMS build-and-bundle tool | +| `debian/scripts/bundle-dkms-modules.sh` | ✅ Committed | DKMS build-and-stage tool | | `debian/control` | 🔄 Generated | Produced by `make -f debian/rules prepare KVER=...` | | `debian/changelog` | 🔄 Generated | Produced by `make -f debian/rules prepare KVER=...` | | `debian/kernel.release` | 🔄 Generated | Produced during `dpkg-buildpackage` | +| `debian/*-modules-*.substvars` | 🔄 Generated | Written by `bundle-dkms-modules.sh` during `dpkg-buildpackage` (the `Built-Using` value) | --- @@ -153,17 +169,19 @@ make -f debian/rules prepare KVER=7.2.0-qcom-next-20260826 # With optional extra suffix (CI build ID, user tag, etc.): make -f debian/rules prepare LOCALVERSION=-qcom-next-20260826 KVER_EXTRA=-ci42 -# Selecting the out-of-tree DKMS modules to bundle: +# Selecting the out-of-tree DKMS modules to build: make -f debian/rules prepare LOCALVERSION=-qcom-next-20260826 DKMS_MODULES=kgsl,camx ``` This produces: - `debian/control` — with the versioned package names, e.g. `linux-image-7.2.0-qcom-next-20260826`, the metapackage names from the - matrix, e.g. `linux-image-qcom-next`, and one `-dkms` build dependency - per `DKMS_MODULES` entry + matrix, e.g. `linux-image-qcom-next`, one `-dkms` build dependency per + `DKMS_MODULES` entry, one group of `-modules-` stanzas per entry, + appended from `debian/control-dkms.in`, and the `-modules` stanza + from `debian/control-dkms-all.in` when the list is not empty - `debian/changelog` — with the source package name, e.g. `linux-qcom-next` -- `debian/dkms-modules` — the manifest of out-of-tree modules to bundle, +- `debian/dkms-modules` — the manifest of out-of-tree modules to build, written from `DKMS_MODULES` (empty when none were requested) **Why this step is required:** `dpkg-buildpackage` reads `debian/control` before @@ -430,7 +448,7 @@ script (`scripts/package/builddeb`). Debug symbols are installed under both can be installed simultaneously. Installed paths: -- `/usr/lib/debug/lib/modules//` — per-module debug symbols extracted via `objcopy --only-keep-debug` +- `/usr/lib/debug/lib/modules//kernel/` — in-tree module debug symbols extracted via `objcopy --only-keep-debug` - `/usr/lib/debug/lib/modules//vmlinux` — unstripped vmlinux (for `perf`, `crash`) - `/usr/lib/debug/boot/vmlinux-` → symlink to vmlinux (for `systemtap`) - `/usr/lib/debug/vmlinux-` → symlink to vmlinux (for `kdump-tools`) @@ -439,20 +457,69 @@ Depends on `linux-image-` (same version). Virtual packages provided: `linux-image-dbg` +### `-modules-` — Out-of-tree modules + +One package per `DKMS_MODULES` entry, built from that entry's `-dkms` +source against the kernel this same build produced. + +Installed paths: +- `/lib/modules//updates/qli/*.ko` — the modules, stripped + +Depends on `linux-image-` (same version), and `Conflicts`/`Replaces` +`-dkms`. Provides the virtual package `-modules`. + +Only the modules are shipped. Whatever `modprobe.d` snippets, udev rules or +initramfs hooks the `-dkms` package carries are not carried over with +them. + +### `-modules--dbg` — Out-of-tree module debug symbols + +Installed paths: +- `/usr/lib/debug/lib/modules//updates/qli/*.ko` — per-module debug symbols + +Depends on `-modules-` (same version). + +### `-modules-` and `-modules` — Module metapackages + +`-modules-` depends on the newest `-modules-` for +this variant, one per `DKMS_MODULES` entry, so the modules follow the kernel +across snapshots the way `` makes the image follow it. + +`-modules` depends on every `-modules-` above, so the +whole out-of-tree module set for the variant installs under a single name. It +is generated only when the module list is non-empty. + --- -## DKMS module bundling +## Out-of-tree DKMS modules Out-of-tree kernel modules selected for a build are built at -`dpkg-buildpackage` time and bundled directly into `linux-image-`. -The target device receives the pre-built `.ko` without needing a compiler, -kernel headers, or DKMS tooling installed. +`dpkg-buildpackage` time, against the kernel that same build produced, and each +one ships in its own binary package. The target device receives the pre-built +`.ko` without needing a compiler, kernel headers, or DKMS tooling installed. + +The modules are **not** installed into `linux-image-`. A system installs +the modules it has hardware for and nothing else, which is what the packages +above are for: the choice is a package, not a rootfs-wide `/etc/modprobe.d` +blacklist that would outlive the kernel it was written for. + +`-modules-` depends on the exact `linux-image-` it was built +against, and `Conflicts`/`Replaces` `-dkms`. The two forms are +deliberately mutually exclusive: a `-dkms` install would build a second copy of +the same modules into `/lib/modules//updates/dkms/`, at the same depmod +precedence as the copy shipped here, leaving which one loads up to depmod's +ordering rather than to a decision anyone made. + +`Built-Using` names the source package and version the modules were compiled +from, so the archive retains that source alongside the binary. The module +source itself is never copied into `src:linux-qcom-next`; the existing DKMS +source packages are unchanged by any of this. The module set is an input to the build, not a property of the packaging: it comes in as a comma-separated list (`prepare-source.sh --dkms`, `build-kernel.sh --dkms`, or `DKMS_MODULES=` straight to `debian/rules -prepare`), and CI supplies it from the `dkms` field of its build matrix. No -modules are bundled when the list is empty. +prepare`), and CI supplies it from the `dkms` field of its build matrix. An +empty list produces no module packages at all. ### How it works @@ -467,18 +534,33 @@ packages have been staged. The script: 3. Reads `PACKAGE_NAME` / `PACKAGE_VERSION` from the package's `dkms.conf`. 4. Builds with `dkms build` against the staged kernel headers, using a private `--dkmstree` (`mktemp`) to avoid writing to the root-owned `/var/lib/dkms/`. + DKMS remains the abstraction: the packaging never learns how a given module + builds. 5. Judges the outcome by `.ko` artifact presence, not `dkms` exit code. On failure: prints `make.log` tail (compile error) or `BUILD_EXCLUSIVE` gate analysis (skip), then hard-fails — a manifest entry is a presence contract. -6. For each produced `.ko`: collision-checks against already-bundled and in-tree - modules; installs to `lib/modules//extra/`; extracts debug symbols via - `objcopy --only-keep-debug` into the `-dbg` package; strips with - `strip --strip-debug` (required for kernel modules — a full strip drops the - symtab and relocations needed by the module loader). +6. For each produced `.ko`: collision-checks against modules already staged by + this run and against in-tree modules; installs to + `-modules-/lib/modules//updates/qli/`; extracts debug + symbols via `objcopy --only-keep-debug` into the matching `-dbg` package; + strips with `strip --strip-debug` (required for kernel modules — a full + strip drops the symtab and relocations needed by the module loader). +7. Writes `debian/.substvars` with the `Built-Using` value read from the + `-dkms` package. + +`updates/` rather than `extra/`: `extra/` is not in depmod's default search +order on Debian at all — it is Fedora's convention — so a module placed there +has the *lowest* precedence. `updates/qli/` is the structural sibling of the +`updates/dkms/` a real DKMS install uses. + +`/boot/System.map-` matters here. The depmod snippet debhelper generates +for each of these packages is guarded by `[ -e /boot/System.map- ]`, so +without the image shipping that file nothing would regenerate `modules.dep` on +the target and the modules would not be loadable. ### Selecting modules -`debian/rules prepare` turns one `DKMS_MODULES` list into the two things a +`debian/rules prepare` turns one `DKMS_MODULES` list into the three things a build needs, so they cannot drift apart: ```bash @@ -488,18 +570,23 @@ prepare-source.sh --source-dir /path/to/kernel --dkms kgsl,camx - `debian/control` gains `dkms, kgsl-dkms, camx-dkms` in `Build-Depends`, so the module sources are installed in the build environment. An empty list adds nothing, not even `dkms`. +- `debian/control` also gains a `-modules-` stanza group per entry, + appended from `debian/control-dkms.in`: the versioned modules package, its + `-dbg`, and the unversioned `-modules-` metapackage that depends + on the versioned one. A non-empty list adds one `-modules` stanza on + top, from `debian/control-dkms-all.in`, depending on every per-module + metapackage, so the whole set installs under a single name. - `debian/dkms-modules` is written as the manifest `bundle-dkms-modules.sh` reads — one name per line, without the `-dkms` suffix. -Both files are generated and gitignored; neither is edited by hand. Entries are +All are generated and gitignored; none is edited by hand. Entries are validated at `prepare` time: a name must look like a package stem, must omit the `-dkms` suffix, and must not repeat. A listed module is a presence contract — if it fails to build, or its `BUILD_EXCLUSIVE` gates exclude this kernel, the package build fails rather than -shipping a kernel image without it. To stop bundling a module, drop it from the -list its build was given (for CI builds, the `dkms` field in -`ci/build-matrix.json`). +publishing without it. To stop building a module, drop it from the list its +build was given (for CI builds, the `dkms` field in `ci/build-matrix.json`). ### Standalone developer use @@ -511,9 +598,13 @@ debian/scripts/bundle-dkms-modules.sh \ --kver 6.12.0-qcom-next-20260210 \ --headers-dir /path/to/kernel-source/debian/linux-headers-6.12.0-qcom-next-20260210/usr/src/linux-headers-6.12.0-qcom-next-20260210 \ --image-pkg-dir /path/to/kernel-source/debian/linux-image-6.12.0-qcom-next-20260210 \ - --dbg-pkg-dir /path/to/kernel-source/debian/linux-image-6.12.0-qcom-next-20260210-dbg + --stage-root /path/to/kernel-source/debian ``` +`--image-pkg-dir` is read, never written: it supplies the in-tree module list +for the collision check and `boot/config-` for `BUILD_EXCLUSIVE_CONFIG` +analysis. The module packages are staged under `--stage-root`. + Run `debian/scripts/bundle-dkms-modules.sh --help` for full usage, prerequisites, and all available options (`--arch`, `--objcopy`, `--modules-manifest`). @@ -547,6 +638,9 @@ sudo dpkg -i linux-headers-_1-1_arm64.deb # Install debug symbols sudo dpkg -i linux-image--dbg_1-1_arm64.deb +# Install an out-of-tree module set built against this kernel +sudo dpkg -i kgsl-modules-_1-1_arm64.deb + # Remove a specific kernel version (does not affect other installed kernels) sudo dpkg -r linux-image- diff --git a/prepare-source.sh b/prepare-source.sh index db701f01..f68e89ef 100755 --- a/prepare-source.sh +++ b/prepare-source.sh @@ -106,12 +106,18 @@ OPTIONS: DKMS modules: --dkms LIST Comma-separated out-of-tree DKMS modules to build - against this kernel and bundle into - linux-image-, each named without the -dkms + against this kernel, each named without the -dkms suffix (e.g. --dkms kgsl,camx). Each entry needs a -dkms package available to the build; the Build-Depends entry is generated from this list. - Empty (the default) bundles no modules. + Each entry produces its own binary packages, + -modules-, its -dbg, and the + unversioned -modules- metapackage; + a non-empty list also produces one + -modules metapackage depending on all of + them. The modules are not installed into + linux-image-. + Empty (the default) builds no modules. Paths: --debian-dir DIR Path to the debian/ packaging directory From ba65f1aede7b06487a7da9b1fffcbde16373ffbf Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Tue, 22 Sep 2026 19:26:25 +0100 Subject: [PATCH 8/8] Revert "ci: drop dkms modules from trixie and forky" This reverts commit d37e8aeb60e46d7f2c15fd985ba38e856a8a6814. The modules no longer go into linux-image-: each one ships in its own -modules- package, installable independently of the kernel image and of each other. A system that has no use for camx, iris-vpu or audioreach simply does not install those packages, so listing them for trixie and forky no longer puts them on anything that doesn't ask for them. Restore the full trixie and forky lists; resolute is unchanged. Signed-off-by: Christopher Obbard --- ci/build-matrix.json | 57 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/ci/build-matrix.json b/ci/build-matrix.json index e038ed8d..2d5fd875 100644 --- a/ci/build-matrix.json +++ b/ci/build-matrix.json @@ -21,6 +21,18 @@ "binpkg": "linux-image-qcom-next", "kernel_config": [], "dkms": { + "trixie": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ], + "forky": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ], "resolute": [ "kgsl" ] @@ -42,7 +54,20 @@ "srcpkg": "linux-qcom-next", "binpkg": "linux-image-qcom-next", "kernel_config": [], - "dkms": {}, + "dkms": { + "trixie": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ], + "forky": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ] + }, "debian_version_stub": "0qli1", "debian_version_suffix": "" }, @@ -63,7 +88,20 @@ "intree:arch/arm64/configs/qcom_debug.config", "intree:kernel/configs/debug.config" ], - "dkms": {}, + "dkms": { + "trixie": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ], + "forky": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ] + }, "debian_version_stub": "0qli1", "debian_version_suffix": "~" }, @@ -84,7 +122,20 @@ "intree:arch/arm64/configs/qcom_debug.config", "intree:kernel/configs/debug.config" ], - "dkms": {}, + "dkms": { + "trixie": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ], + "forky": [ + "kgsl", + "camx", + "iris-vpu", + "audioreach" + ] + }, "debian_version_stub": "0qli1", "debian_version_suffix": "" }