diff --git a/.gitignore b/.gitignore index 56ef872..2583b54 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/README.md b/README.md index ac57987..74674a1 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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=...` | @@ -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 `-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 @@ -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-`. 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 @@ -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 `-dkms (>= )` 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 diff --git a/build-kernel.sh b/build-kernel.sh index 8fd946a..bb4751a 100755 --- a/build-kernel.sh +++ b/build-kernel.sh @@ -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-, without the -dkms + suffix (e.g. --dkms kgsl,camx). Each entry needs its + -dkms package available to the build. Empty by + default (bundle nothing). Paths: -k, --kernel-dir DIR Kernel source directory (default: $KERNEL_DIR) @@ -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: @@ -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 @@ -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 ;; @@ -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 @@ -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)." diff --git a/debian/control.in b/debian/control.in index 0a3650a..6c5050b 100644 --- a/debian/control.in +++ b/debian/control.in @@ -5,8 +5,7 @@ Maintainer: Bjordis Collaku 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 diff --git a/debian/rules b/debian/rules index 6b4ab45..b850cc8 100755 --- a/debian/rules +++ b/debian/rules @@ -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-, each named without the -dkms suffix +# (e.g. DKMS_MODULES=kgsl,camx). Empty (the default) bundles +# 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. # # 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 @@ -70,6 +81,7 @@ DISTRO ?= unstable SRCPKG ?= linux-qcom-next BINPKG ?= linux-image-qcom-next DEBIAN_REVISION ?= 0qcom1 +DKMS_MODULES ?= .PHONY: prepare prepare: @@ -132,6 +144,41 @@ 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" \ @@ -139,6 +186,7 @@ prepare: -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" \ @@ -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= 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: # --------------------------------------------------------------------------- diff --git a/debian/scripts/bundle-dkms-modules.sh b/debian/scripts/bundle-dkms-modules.sh index c2649f0..0f070aa 100755 --- a/debian/scripts/bundle-dkms-modules.sh +++ b/debian/scripts/bundle-dkms-modules.sh @@ -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 \ @@ -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 @@ -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): @@ -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 , or create debian/dkms-modules." + log_error "Run 'debian/rules prepare' (or prepare-source.sh) to generate it," + log_error "or pass --modules-manifest ." exit 1 fi @@ -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}//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 diff --git a/prepare-source.sh b/prepare-source.sh index 886e6d8..c50352f 100755 --- a/prepare-source.sh +++ b/prepare-source.sh @@ -10,7 +10,8 @@ set -e # 2. Activate optional config fragments from debian/config-available/ into # debian/config/ based on the --kernel-config list. # 3. Run 'debian/rules prepare' to generate debian/control, debian/changelog, -# debian/localversion, and debian/pkgversion from *.in templates. +# debian/localversion, debian/pkgversion, and debian/dkms-modules from the +# *.in templates and the --dkms list. # # This script is the CI entry point for source preparation. It runs as a # dedicated workflow step between kernel source setup (clone + PR application) @@ -39,7 +40,8 @@ Prepare kernel source for Debian packaging. Injects debian/ packaging metadata into the kernel source tree, activates optional config fragments from debian/config-available/, and runs 'debian/rules prepare' to generate debian/control, debian/changelog, -debian/localversion, and debian/pkgversion from *.in templates. +debian/localversion, debian/pkgversion, and debian/dkms-modules from the +*.in templates and the --dkms list. OPTIONS: Required: @@ -76,6 +78,15 @@ OPTIONS: redundant, since that fragment is already applied. Entries are processed in LC_ALL=C sorted order. + 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 + 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. + Debug: --debug Enable debug build: copies arch/arm64/configs/debug.config from the kernel source into debian/config/ so it is @@ -99,7 +110,8 @@ EXAMPLES: --srcpkg linux-qcom-next \\ --binpkg linux-image-qcom-next \\ --debian-revision 0qcom1 \\ - --kernel-config squashfs,systemd-boot,qcom-imsdk,docker,qemu-boot,usb-can + --kernel-config squashfs,systemd-boot,qcom-imsdk,docker,qemu-boot,usb-can \\ + --dkms kgsl EOF exit 1 } @@ -113,6 +125,7 @@ SRCPKG="$DEFAULT_SRCPKG" BINPKG="$DEFAULT_BINPKG" DEBIAN_REVISION="$DEFAULT_DEBIAN_REVISION" KERNEL_CONFIG="" +DKMS_MODULES="" DEBUG=false while [[ $# -gt 0 ]]; do @@ -125,6 +138,7 @@ while [[ $# -gt 0 ]]; do --binpkg) BINPKG="$2"; shift 2 ;; --debian-revision) DEBIAN_REVISION="$2"; shift 2 ;; --kernel-config) KERNEL_CONFIG="$2"; shift 2 ;; + --dkms) DKMS_MODULES="$2"; shift 2 ;; --debian-dir) DEBIAN_DIR="$2"; shift 2 ;; --debug) DEBUG=true; shift ;; -h|--help) usage ;; @@ -178,6 +192,7 @@ log_info " Debian revision: $DEBIAN_REVISION" [[ -n "$LOCALVERSION" ]] && log_info " LOCALVERSION: $LOCALVERSION" [[ -n "$KVER_EXTRA" ]] && log_info " KVER_EXTRA: $KVER_EXTRA" [[ -n "$KERNEL_CONFIG" ]] && log_info " Kernel config: $KERNEL_CONFIG" +[[ -n "$DKMS_MODULES" ]] && log_info " DKMS modules: $DKMS_MODULES" [[ "$DEBUG" == true ]] && log_info " Debug build: yes" echo @@ -302,6 +317,9 @@ log_step "Running debian/rules prepare..." PREPARE_ARGS="DISTRO=$DISTRO SRCPKG=$SRCPKG BINPKG=$BINPKG DEBIAN_REVISION=$DEBIAN_REVISION" [[ -n "$LOCALVERSION" ]] && PREPARE_ARGS="$PREPARE_ARGS LOCALVERSION=$LOCALVERSION" [[ -n "$KVER_EXTRA" ]] && PREPARE_ARGS="$PREPARE_ARGS KVER_EXTRA=$KVER_EXTRA" +# Spaces are stripped so a list written as "kgsl, camx" stays a single make +# argument; debian/rules validates the names it is given. +[[ -n "$DKMS_MODULES" ]] && PREPARE_ARGS="$PREPARE_ARGS DKMS_MODULES=$(tr -d ' ' <<< "$DKMS_MODULES")" # shellcheck disable=SC2086 make -f "$SOURCE_DIR/debian/rules" -C "$SOURCE_DIR" prepare $PREPARE_ARGS @@ -311,3 +329,4 @@ log_info "Generated: $SOURCE_DIR/debian/control" log_info "Generated: $SOURCE_DIR/debian/changelog" log_info "Generated: $SOURCE_DIR/debian/localversion" log_info "Generated: $SOURCE_DIR/debian/pkgversion" +log_info "Generated: $SOURCE_DIR/debian/dkms-modules"