packaging: ship out-of-tree DKMS modules as their own packages - #114
Draft
Christopher Obbard (obbardc) wants to merge 59 commits into
Draft
Christopher Obbard (obbardc) wants to merge 59 commits into
Christopher Obbard (obbardc) wants to merge 59 commits into
Conversation
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:15 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:18 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:21 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:21 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:22 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
force-pushed
the
wip/obbardc/dkms-module-packages
branch
from
September 15, 2026 02:40
c6c0ac8 to
4676d53
Compare
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:46 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:46 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:47 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:47 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:47 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:48 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:48 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:48 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:49 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 02:50 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:15 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:15 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:16 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:16 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:18 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:19 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 03:22 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
force-pushed
the
wip/obbardc/dkms-module-packages
branch
from
September 15, 2026 04:22
295e05a to
cb379da
Compare
Christopher Obbard (obbardc)
marked this pull request as ready for review
September 15, 2026 04:27
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 04:28 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 04:29 — with
GitHub Actions
Error
The .ko in <name>-modules-<kver> 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 --
verified both ways against installed packages, where dh-dkms correctly reports
dkms (= 3.2.2-1) rather than its own name.
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.
Verified against dpkg-gencontrol directly, for all three states of the
substvar: set yields "Built-Using: kgsl (= 1.0.6-0qli1)"; present-but-empty
drops the field silently, which is the union-dependency behaviour; and
undefined warns. The bundler always writes the key or exits, so only the first
state occurs in a real build.
Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
A -dkms package ships more than module source. kgsl-dkms also installs a modprobe.d snippet, a udev rule and an initramfs hook; audioreach-dkms a udev rule and a modprobe blacklist; iris-vpu-dkms a modprobe.d snippet. None of it reached the built module, so the module we ship has been arriving without the configuration that makes it work, and the only alternatives were to restate every package's files in the kernel packaging or to let each -dkms package grow a second delivery path. Copy them out of the -dkms package instead, at their original paths -- a udev rule or modprobe.d snippet is found by its location, so rewriting it would break it. The file list comes from dpkg -L, so a package that grows a udev rule needs no change here. Classification is by path prefix rather than by filename, with the longest matching prefix across the include and exclude lists deciding. That is what lets the narrow /usr/share/initramfs-tools/ include sit inside the broad /usr/share/ exclude without either list having to be ordered, and what lets a --include-prefix given on the command line override a built-in default without removing it. A path matching neither list is fatal. Skipping it with a warning would drop runtime integration the module may need, and that failure surfaces as a module that loads but misbehaves on a target -- far more expensive to diagnose than a failed build. It is also how the rest of this script already treats a surprise: a missing manifest, a module that produces no .ko and a debug file with no DWARF are all fatal today. Excluded: /usr/src and /var/lib/dkms, which are DKMS's own machinery; any /lib/modules content, since the modules we ship are built here; and the documentation tree. The generated package therefore needs no dependency on dkms and carries none. The copyright file is the exception to "copy at the original path": it goes to debian/<pkg>.copyright and is left to dh_installdocs, which places it under the generated package's own name rather than the -dkms package's. dh_installdocs prefers that file over debian/copyright, and dh_compress leaves a file named copyright alone. An initramfs hook that is not executable is skipped by update-initramfs without saying so, and dh_fixperms has no rule that would correct one, so the mode is asserted rather than assumed. The package adds and removes kernel modules, so the initramfs may need rebuilding after it is installed. dh_installinitramfs declares that trigger itself when it finds a copied hook, so declare it by hand only when it will not -- otherwise the trigger would be declared twice, and dpkg rejects the same trigger declared with differing await semantics. Verified by running the classifier over the real file lists: all 1055 paths in camx-dkms_1.0.3-0qli2~_arm64.deb classify as exclude, which is correct since it ships only source and documentation; every runtime file kgsl, iris-vpu and audioreach install classifies as include, including the initramfs hook under the excluded /usr/share/; and an unexpected /usr/bin or /opt path classifies as neither and so fails the build. Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The docs still described the previous arrangement throughout: modules bundled into linux-image-<KVER>, 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 two 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 how the runtime-file classification decides -- longest matching prefix across the include and exclude lists, which is what lets /usr/share/initramfs-tools/ be carried while /usr/share/ is not. Also documented: dh_installsystemd will auto-enable a copied .service unit and dh_installtmpfiles picks up a copied tmpfiles.d snippet. Neither is written anywhere in this packaging -- they follow from staging the file at all -- so the only place a reader can learn it is here. 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 in the top-level README, prepare-source.sh's --dkms help, and the DKMS_MODULES header comment in debian/rules, which is what a reader running prepare by hand sees first. Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The modules a variant builds are only installable by their versioned names,
<name>-modules-<KVER>. 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, <BINPKG>-modules-<name>, 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 <chris.obbard@oss.qualcomm.com>
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.yaml and whatever consumes the packages --
and drifts the first time one of them gains an entry.
Append one <BINPKG>-modules stanza, generated from the new
debian/control-dkms-all.in, depending on every <BINPKG>-modules-<name> 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 --
mainline and linux-next in the current matrix -- 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 <chris.obbard@oss.qualcomm.com>
Christopher Obbard (obbardc)
force-pushed
the
wip/obbardc/dkms-module-packages
branch
from
September 15, 2026 16:11
cb379da to
aa68e70
Compare
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:17 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:18 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:18 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:19 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:19 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 15, 2026 16:23 — with
GitHub Actions
Error
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 16, 2026 14:41 — with
GitHub Actions
Active
This was referenced Sep 17, 2026
Christopher Obbard (obbardc)
marked this pull request as draft
September 18, 2026 07:30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Out-of-tree DKMS modules were built against the kernel and installed into
linux-image-<kernelrelease>, which meant the image package's contents dependedon which modules happened to be listed and gave no way to install the kernel
without them.
Each module now produces its own pair of packages,
<name>-modules-<kernelrelease>and its-dbg, installing under/lib/modules/<kernelrelease>/updates/qli/and carrying the modprobe.d, udev andinitramfs files the
-dkmssource ships. They conflict with the corresponding-dkmspackage, and record it inBuilt-Using. The bundler gains--stage-rootso it can stage into a per-module package directory.
Also here: the flavour moves to the end of the kernel release string, as Debian
does, and
System.mapis installed into the image package.Depends on both #110 and #107