Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ debian/control
debian/changelog

# Generated by debian/rules prepare — runtime state files
debian/dkms-modules
debian/kernel.release
debian/localversion
debian/pkgversion
Expand Down
51 changes: 38 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pkg-linux-qcom/
│ │ ├── qemu-boot.config ← virtio drivers for QEMU testing
│ │ ├── systemd-boot.config ← EFI_ZBOOT for systemd-boot
│ │ └── usb-can.config ← USB CAN adapters
│ ├── dkms-modules ← Manifest of out-of-tree modules to bundle at build time
│ ├── dkms-modules ← Generated by 'prepare' from DKMS_MODULES (gitignored)
│ └── scripts/
│ └── bundle-dkms-modules.sh ← DKMS build-and-bundle tool (called by rules; standalone-capable)
├── .gitignore
Expand All @@ -89,7 +89,7 @@ pkg-linux-qcom/
| `debian/linux-image.postrm` | ✅ Committed | Post-remove maintainer script |
| `debian/config/*.config` | ✅ Committed | Always-applied config fragments |
| `debian/config-available/*.config` | ✅ Committed | Packaging fragments, all applied to every build |
| `debian/dkms-modules` | ✅ Committed | Manifest of out-of-tree DKMS modules to bundle |
| `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/control` | 🔄 Generated | Produced by `make -f debian/rules prepare KVER=...` |
| `debian/changelog` | 🔄 Generated | Produced by `make -f debian/rules prepare KVER=...` |
Expand Down Expand Up @@ -145,13 +145,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:
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`, and the metapackage names from the
matrix, e.g. `linux-image-qcom-next`
`linux-image-7.2.0-qcom-next-20260826`, the metapackage names from the
matrix, e.g. `linux-image-qcom-next`, and one `<name>-dkms` build dependency
per `DKMS_MODULES` entry
- `debian/changelog` — with the source package name, e.g. `linux-qcom-next`
- `debian/dkms-modules` — the manifest of out-of-tree modules to bundle,
written from `DKMS_MODULES` (empty when none were requested)

**Why this step is required:** `dpkg-buildpackage` reads `debian/control` before
calling any `debian/rules` targets. The `@KVER@` substitution must therefore
Expand Down Expand Up @@ -332,11 +338,17 @@ Virtual packages provided: `linux-image-dbg`

## DKMS module bundling

Out-of-tree kernel modules listed in `debian/dkms-modules` are built at
Out-of-tree kernel modules selected for a build are built at
`dpkg-buildpackage` time and bundled directly into `linux-image-<KVER>`.
The target device receives the pre-built `.ko` without needing a compiler,
kernel headers, or DKMS tooling installed.

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.

### How it works

`debian/rules` calls `debian/scripts/bundle-dkms-modules.sh` at the end of
Expand All @@ -359,17 +371,30 @@ packages have been staged. The script:
`strip --strip-debug` (required for kernel modules — a full strip drops the
symtab and relocations needed by the module loader).

### `debian/dkms-modules` manifest
### Selecting modules

```
# One module name per line (without the -dkms suffix).
# A corresponding Build-Depends entry must exist in debian/control.in.
kgsl
`debian/rules prepare` turns one `DKMS_MODULES` list into the two things a
build needs, so they cannot drift apart:

```bash
prepare-source.sh --source-dir /path/to/kernel --dkms kgsl,camx
```

To add a module: append its name and add `<name>-dkms (>= <version>)` to
`Build-Depends` in `debian/control.in`. To disable temporarily: comment out
the line. `debian/rules` and `bundle-dkms-modules.sh` are untouched in either case.
- `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/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
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` on the CI branch).

### Standalone developer use

Expand Down
10 changes: 10 additions & 0 deletions build-kernel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ 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-<KVER>, without the -dkms
suffix (e.g. --dkms kgsl,camx). Each entry needs its
<name>-dkms package available to the build. Empty by
default (bundle nothing).

Paths:
-k, --kernel-dir DIR Kernel source directory (default: $KERNEL_DIR)
Expand All @@ -76,6 +81,7 @@ EXAMPLES:
$0 --local-source /path/to/kernel --build-mode native
$0 --local-source /path/to/kernel --kver-extra -mybuild
$0 --latest-tag --kernel-config docker,systemd-boot
$0 --latest-tag --dkms kgsl
$0 --latest-tag --profiles debug

DISTRIBUTIONS:
Expand All @@ -93,6 +99,7 @@ TAG=""; LATEST_TAG=false; BRANCH="$DEFAULT_BRANCH"; REPO="$DEFAULT_REPO"
DISTRO="$DEFAULT_DISTRO"; BUILD_MODE="$DEFAULT_BUILD_MODE"
LOCALVERSION=""; KVER_EXTRA=""; PROFILES=""; CLEAN=false
LOCAL_SOURCE=""; ENABLE_CONFIGS="squashfs,systemd-boot,qcom-imsdk,docker,qemu-boot,usb-can"; SKIP_PREPARE=false
DKMS_MODULES=""

while [[ $# -gt 0 ]]; do
case $1 in
Expand All @@ -110,6 +117,7 @@ while [[ $# -gt 0 ]]; do
--kver-extra) KVER_EXTRA="$2"; shift 2 ;;
--profiles) PROFILES="$2"; shift 2 ;;
--kernel-config) ENABLE_CONFIGS="$2"; shift 2 ;;
--dkms) DKMS_MODULES="$2"; shift 2 ;;
--enable-configs) ENABLE_CONFIGS="$2"; shift 2 ;; # deprecated alias
--build-mode) BUILD_MODE="$2"; shift 2 ;;
--skip-prepare) SKIP_PREPARE=true; shift ;;
Expand Down Expand Up @@ -167,6 +175,7 @@ log_info " Distro: $DISTRO mode: $BUILD_MODE"
[[ -n "$KVER_EXTRA" ]] && log_info " KVER_EXTRA: $KVER_EXTRA"
[[ -n "$PROFILES" ]] && log_info " Profiles: $PROFILES"
[[ -n "$ENABLE_CONFIGS" ]] && log_info " Extra configs: $ENABLE_CONFIGS"
[[ -n "$DKMS_MODULES" ]] && log_info " DKMS modules: $DKMS_MODULES"
[[ "$SKIP_PREPARE" == true ]] && log_info " Skip prepare: yes (source already prepared by prepare-source.sh)"
echo

Expand Down Expand Up @@ -257,6 +266,7 @@ if [[ "$SKIP_PREPARE" != true ]]; then
[[ -n "$LOCALVERSION" ]] && PREPARE_ARGS+=(--localversion "$LOCALVERSION")
[[ -n "$KVER_EXTRA" ]] && PREPARE_ARGS+=(--kver-extra "$KVER_EXTRA")
[[ -n "$ENABLE_CONFIGS" ]] && PREPARE_ARGS+=(--kernel-config "$ENABLE_CONFIGS")
[[ -n "$DKMS_MODULES" ]] && PREPARE_ARGS+=(--dkms "$DKMS_MODULES")
"$SCRIPT_DIR/prepare-source.sh" "${PREPARE_ARGS[@]}"
else
log_info "Skipping source preparation (--skip-prepare set)."
Expand Down
3 changes: 1 addition & 2 deletions debian/control.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ Maintainer: Bjordis Collaku <[email protected]>
Standards-Version: 4.6.2
Build-Depends: debhelper-compat (= 13),
bc, bison, flex, libssl-dev, libelf-dev, dwarves,
python3, kmod, cpio, rsync, pkg-config, gcc, make,
dkms, kgsl-dkms
python3, kmod, cpio, rsync, pkg-config, gcc, make@DKMS_BUILD_DEPENDS@
Homepage: https://kernel.org
Rules-Requires-Root: binary-targets

Expand Down
64 changes: 62 additions & 2 deletions debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,21 @@ 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-<KVER>, each named without the -dkms suffix
# (e.g. DKMS_MODULES=kgsl,camx). Empty (the default) bundles
# nothing. Each name must have a <name>-dkms package available
# in the build environment; the Build-Depends entry for it is
# generated from this list, so nothing else declares it.
#
# Outputs (generated files, all listed in debian/clean):
# debian/control Substituted from debian/control.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,
# so an empty DKMS_MODULES is a genuine "bundle nothing"
# rather than the missing-manifest misconfiguration that
# the bundler rejects.
# debian/localversion Plain file containing the LOCALVERSION suffix (e.g. -qcom-next-20260722).
# Read by override_dh_auto_build to pass LOCALVERSION= to make.
# debian/pkgversion Plain file containing the full Debian package version
Expand All @@ -70,6 +81,7 @@ DISTRO ?= unstable
SRCPKG ?= linux-qcom-next
BINPKG ?= linux-image-qcom-next
DEBIAN_REVISION ?= 0qcom1
DKMS_MODULES ?=

.PHONY: prepare
prepare:
Expand Down Expand Up @@ -132,13 +144,49 @@ prepare:
# ── Derive HDRPKG from BINPKG ─────────────────────────────────────────────
HDRPKG=$$(echo "$(BINPKG)" | sed 's/^linux-image-/linux-headers-/'); \
\
# ── Resolve the DKMS module list ──────────────────────────────────────────
# One CSV list drives both generated artifacts: the Build-Depends entries in
# debian/control (so the -dkms packages are present in the build chroot) and
# the debian/dkms-modules manifest (so the bundler builds exactly those).
# Deriving both from the same variable is what keeps them from drifting.
DKMS_LIST=""; \
for mod in $$(echo "$(DKMS_MODULES)" | tr ',' ' '); do \
case "$$mod" in \
*-dkms) \
echo "ERROR: DKMS_MODULES entries must omit the -dkms suffix (got '$$mod'; use '$${mod%-dkms}')"; \
exit 1 ;; \
esac; \
echo "$$mod" | grep -qE '^[a-z0-9][a-z0-9+.-]*$$' || { \
echo "ERROR: invalid DKMS module name: '$$mod'"; \
echo " Expected a package name stem, e.g. DKMS_MODULES=kgsl,camx"; \
exit 1; \
}; \
case " $$DKMS_LIST " in \
*" $$mod "*) echo "ERROR: duplicate DKMS module in DKMS_MODULES: $$mod"; exit 1 ;; \
esac; \
DKMS_LIST="$$DKMS_LIST $$mod"; \
done; \
DKMS_LIST="$${DKMS_LIST# }"; \
\
# Build-Depends tail appended after the last unconditional entry, so an
# empty list leaves the field exactly as control.in spells it — no dangling
# comma, and no dkms tooling pulled into a build that bundles nothing.
DKMS_BUILD_DEPENDS=""; \
if [ -n "$$DKMS_LIST" ]; then \
for mod in $$DKMS_LIST; do \
DKMS_BUILD_DEPENDS="$$DKMS_BUILD_DEPENDS, $$mod-dkms"; \
done; \
DKMS_BUILD_DEPENDS=", dkms$$DKMS_BUILD_DEPENDS"; \
fi; \
\
# ── Substitute templates ──────────────────────────────────────────────────
sed \
-e "s|@SRCPKG@|$(SRCPKG)|g" \
-e "s|@BINPKG@|$(BINPKG)|g" \
-e "s|@HDRPKG@|$$HDRPKG|g" \
-e "s|@KVER@|$$KVER_RESOLVED|g" \
-e "s|@PKGVER@|$$PKG_VERSION|g" \
-e "s|@DKMS_BUILD_DEPENDS@|$$DKMS_BUILD_DEPENDS|g" \
debian/control.in > debian/control; \
sed \
-e "s|@SRCPKG@|$(SRCPKG)|g" \
Expand All @@ -158,9 +206,21 @@ prepare:
# debian/pkgversion: the full Debian package version, read by override_dh_gencontrol.
printf '%s\n' "$$PKG_VERSION" > debian/pkgversion; \
\
echo "Generated debian/control, debian/changelog, debian/localversion, debian/pkgversion"; \
# debian/dkms-modules: the manifest read at build time by
# debian/scripts/bundle-dkms-modules.sh. Written unconditionally — the
# bundler treats a missing manifest as a misconfiguration and a comment-only
# one as a legitimate empty module set.
{ \
echo "# Generated by debian/rules prepare from DKMS_MODULES — do not edit."; \
echo "# One module name per line, without the -dkms suffix."; \
echo "# Pass DKMS_MODULES=<csv> to 'prepare' (prepare-source.sh --dkms) to change it."; \
for mod in $$DKMS_LIST; do echo "$$mod"; done; \
} > debian/dkms-modules; \
\
echo "Generated debian/control, debian/changelog, debian/localversion, debian/pkgversion, debian/dkms-modules"; \
echo " SRCPKG=$(SRCPKG) BINPKG=$(BINPKG) HDRPKG=$$HDRPKG"; \
echo " KVER=$$KVER_RESOLVED PKGVER=$$PKG_VERSION DISTRO=$(DISTRO)"
echo " KVER=$$KVER_RESOLVED PKGVER=$$PKG_VERSION DISTRO=$(DISTRO)"; \
echo " DKMS modules: $${DKMS_LIST:-(none)}"

override_dh_auto_configure:
# ---------------------------------------------------------------------------
Expand Down
21 changes: 15 additions & 6 deletions debian/scripts/bundle-dkms-modules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ set -euo pipefail
# It must be an absolute path: dkms invokes make from inside the module
# source directory, so a relative path would resolve to nothing from there.
# - Each module listed in the manifest must have its -dkms package installed
# in the build environment (declared as Build-Depends in debian/control.in).
# in the build environment. Both the manifest and the matching Build-Depends
# entries are generated by 'debian/rules prepare' from DKMS_MODULES, so the
# two cannot disagree.
#
# USAGE (from debian/rules — CI path):
# debian/scripts/bundle-dkms-modules.sh \
Expand Down Expand Up @@ -123,6 +125,9 @@ OPTIONAL:
--modules-manifest FILE Path to the dkms-modules manifest.
Default: debian/dkms-modules (relative to this script's
location, i.e. \$(dirname \$0)/../dkms-modules).
That file is generated by 'debian/rules prepare' from
DKMS_MODULES; pass this flag to bundle a different set
without re-running prepare.
--arch ARCH Architecture token in uname -m vocabulary passed to
dkms --arch and used for BUILD_EXCLUSIVE_ARCH matching.
Default: aarch64
Expand All @@ -148,7 +153,9 @@ PREREQUISITES (developer standalone use):
MANIFEST FORMAT (debian/dkms-modules):
One module name per line (without the -dkms suffix).
Lines starting with # and blank lines are ignored.
A corresponding Build-Depends entry must exist in debian/control.in.
Generated by 'debian/rules prepare' from DKMS_MODULES (prepare-source.sh
--dkms), which also generates the matching Build-Depends entries. Edit the
module list at its source rather than the generated file.

EXAMPLES:
# CI path (called from debian/rules):
Expand Down Expand Up @@ -228,11 +235,13 @@ fi
# ---------------------------------------------------------------------------
# A missing manifest is a misconfiguration, not an empty module set: fail
# loudly rather than silently shipping a kernel without its declared modules
# (the manifest is a presence contract). An existing-but-empty or comment-only
# manifest is a legitimate "no modules" and skips below.
# (the manifest is a presence contract) — 'debian/rules prepare' always writes
# the file, so its absence means prepare never ran. An existing-but-empty or
# comment-only manifest is a legitimate "no modules" and skips below.
if [[ ! -f "$MODULES_MANIFEST" ]]; then
log_error "Modules manifest not found: $MODULES_MANIFEST"
log_error "Pass --modules-manifest <file>, or create debian/dkms-modules."
log_error "Run 'debian/rules prepare' (or prepare-source.sh) to generate it,"
log_error "or pass --modules-manifest <file>."
exit 1
fi

Expand Down Expand Up @@ -304,7 +313,7 @@ for name in $DKMS_MODULES; do

if [[ -z "$conf" ]]; then
log_error "${name}-dkms is not installed or ships no ${_DKMS_SRC_ROOT}/<dir>/dkms.conf"
log_error "Is ${name}-dkms declared in Build-Depends in debian/control.in?"
log_error "Is ${name} listed in DKMS_MODULES? Its Build-Depends entry is generated from that list."
exit 1
fi

Expand Down
Loading
Loading