From 462caea10a6ac5e156f54ebae9c0f6f9ed0aa727 Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 28 Aug 2026 14:57:17 +0100 Subject: [PATCH 1/5] d/rules: merge config fragments with in-tree merge_config.sh Appending fragments to .config left duplicate and conflicting entries for olddefconfig to resolve silently. Use scripts/kconfig/merge_config.sh -m -r instead, so later fragments properly override earlier ones and any override is reported. Signed-off-by: Christopher Obbard --- debian/rules | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/debian/rules b/debian/rules index 7d98654..908cad1 100755 --- a/debian/rules +++ b/debian/rules @@ -175,7 +175,9 @@ override_dh_auto_configure: # debian/config-available/, plus any intree: fragments named in # --kernel-config. Applied in LC_ALL=C filename order so the # result does not depend on the builder's locale. - # All fragments: append to .config + make olddefconfig. + # All fragments are merged in one scripts/kconfig/merge_config.sh -m -r + # invocation (later fragments win, overrides are reported), followed by + # make olddefconfig to resolve the resulting dependencies. # 4. Re-check CONFIG_LOCALVERSION_AUTO (merges may re-enable it) # 5. arch/arm64/configs/debug.config — only when DEB_BUILD_PROFILES=debug # --------------------------------------------------------------------------- @@ -219,10 +221,14 @@ override_dh_auto_configure: echo "Note: no packaging fragments in debian/config/ (none activated via --kernel-config)"; \ fi; \ if [ -n "$$FRAGMENTS" ]; then \ - for frag in $$FRAGMENTS; do \ - echo "Applying config fragment: $$frag"; \ - cat "$$frag" >> "$$CONFIG_FILE"; \ - done; \ + [ -x scripts/kconfig/merge_config.sh ] || { \ + echo "ERROR: scripts/kconfig/merge_config.sh not found or not executable"; \ + exit 1; \ + }; \ + echo "Merging config fragments into $$CONFIG_FILE:$$FRAGMENTS"; \ + ARCH=$(ARCH) scripts/kconfig/merge_config.sh -m -r \ + $(if $(BUILD_OBJ),-O $(BUILD_OBJ)) \ + "$$CONFIG_FILE" $$FRAGMENTS; \ echo "Running olddefconfig to resolve all fragment conflicts..."; \ $(MAKE) $(KBUILD_O_ARG) ARCH=$(ARCH) olddefconfig; \ else \ From 50c6af26d37bec2151140390125be8353448012b Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 28 Aug 2026 15:31:34 +0100 Subject: [PATCH 2/5] d/rules: merge prune.config before qcom.config prune.config strips options out of the base defconfig, so merging it after qcom.config let it drop options that qcom.config had deliberately enabled. Merge it first instead so that qcom.config wins on any overlap. Signed-off-by: Christopher Obbard --- debian/rules | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/debian/rules b/debian/rules index 908cad1..6b4ab45 100755 --- a/debian/rules +++ b/debian/rules @@ -168,8 +168,8 @@ override_dh_auto_configure: # 1. ARM64 base defconfig # 2. Disable CONFIG_LOCALVERSION_AUTO (must happen before any merges) # 3. Config fragments applied in a single unified pipeline: - # a. arch/arm64/configs/qcom.config (kernel-source, always applied if present) - # b. arch/arm64/configs/prune.config (kernel-source, always applied if present) + # a. arch/arm64/configs/prune.config (kernel-source, always applied if present) + # b. arch/arm64/configs/qcom.config (kernel-source, always applied if present) # c. debian/config/*.config (packaging fragments) # Populated by prepare-source.sh: every fragment in # debian/config-available/, plus any intree: fragments named in @@ -200,18 +200,18 @@ override_dh_auto_configure: # Step 3: Unified config fragment pipeline. @CONFIG_FILE="$(if $(BUILD_OBJ),$(BUILD_OBJ)/).config"; \ FRAGMENTS=""; \ - if [ -f arch/arm64/configs/qcom.config ]; then \ - echo "Found kernel-source fragment: arch/arm64/configs/qcom.config"; \ - FRAGMENTS="arch/arm64/configs/qcom.config"; \ - else \ - echo "Note: arch/arm64/configs/qcom.config not found (not a qcom-next tree?)"; \ - fi; \ if [ -f arch/arm64/configs/prune.config ]; then \ echo "Found kernel-source fragment: arch/arm64/configs/prune.config"; \ - FRAGMENTS="$$FRAGMENTS arch/arm64/configs/prune.config"; \ + FRAGMENTS="arch/arm64/configs/prune.config"; \ else \ echo "Note: arch/arm64/configs/prune.config not found (skipping)"; \ fi; \ + if [ -f arch/arm64/configs/qcom.config ]; then \ + echo "Found kernel-source fragment: arch/arm64/configs/qcom.config"; \ + FRAGMENTS="$$FRAGMENTS arch/arm64/configs/qcom.config"; \ + else \ + echo "Note: arch/arm64/configs/qcom.config not found (not a qcom-next tree?)"; \ + fi; \ if [ -d debian/config ] && ls debian/config/*.config >/dev/null 2>&1; then \ for f in $$(ls debian/config/*.config | LC_ALL=C sort); do \ echo "Found packaging fragment: $$f"; \ From 859b44cc678fddf7917892e273dcb654c6b3100f Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 28 Aug 2026 15:31:34 +0100 Subject: [PATCH 3/5] docs: describe the config fragment merge pipeline Update the README to match debian/rules: fragments are merged with scripts/kconfig/merge_config.sh rather than appended to .config, and prune.config is merged before qcom.config. Signed-off-by: Christopher Obbard --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9346dde..4eaf0d3 100644 --- a/README.md +++ b/README.md @@ -440,7 +440,7 @@ this fixed order: |------|--------|-------------| | 1 | `make defconfig` | ARM64 base defconfig from kernel source | | 2 | `debian/rules` | Disable `CONFIG_LOCALVERSION_AUTO` (prevents git hash in `uname -r`) | -| 3 | Unified fragment pipeline | `arch/arm64/configs/qcom.config` (if present), then `debian/config/*.config` (sorted) | +| 3 | Unified fragment pipeline | `arch/arm64/configs/prune.config`, then `arch/arm64/configs/qcom.config` (both if present), then `debian/config/*.config` (sorted) | | 4 | `debian/rules` | Re-check `CONFIG_LOCALVERSION_AUTO` (merges may re-enable it) | | 5 | `arch/arm64/configs/debug.config` | Debug options — only when `DEB_BUILD_PROFILES=debug` | @@ -449,12 +449,17 @@ this fixed order: All config fragments are processed through a single unified pipeline: 1. Collect fragments in order: - - `arch/arm64/configs/qcom.config` first (kernel-source fragment, present in `qcom-next`) + - `arch/arm64/configs/prune.config` first (kernel-source fragment) + - then `arch/arm64/configs/qcom.config` (kernel-source fragment, present in + `qcom-next`), so it wins over anything `prune.config` turned off - then `debian/config/*.config` in sorted filename order -2. Append all collected fragments to `.config` +2. Merge all collected fragments into `.config` in one + `scripts/kconfig/merge_config.sh -m -r` invocation — later fragments override + earlier ones, and every override is reported 3. Run `make olddefconfig` once to resolve all conflicts and fill in dependencies -`qcom.config` and `debian/config/*.config` use exactly the same mechanism. +`prune.config`, `qcom.config` and `debian/config/*.config` use exactly the same +mechanism. ### `debian/config/` — always-applied fragments From d76506e5d95625a834a7e4412c986ec2f4cf879b Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 28 Aug 2026 16:00:01 +0100 Subject: [PATCH 4/5] prepare-source: resolve intree: fragments from the kernel source root An "intree:" entry names a bare fragment, resolved under arch/arm64/configs/, so nothing outside that one directory can be referenced. The kernel ships useful fragments elsewhere, notably kernel/configs/debug.config. Take the whole path relative to the kernel source root instead, e.g. intree:arch/arm64/configs/qcom_debug.config, and copy the file into debian/config/ under its basename. Every directory in the tree becomes reachable and the entry says exactly which file it activates rather than leaving the directory implicit. Reject an absolute path or one containing "..", so an entry cannot reach outside the source tree, and require the .config suffix that override_dh_auto_configure globs for, so a misnamed fragment fails here instead of being copied in and silently ignored. The existing collision check now also catches two in-tree paths sharing a basename, which the wider namespace makes reachable: arch/arm64/configs/hardening.config and kernel/configs/hardening.config both exist. The old bare form is no longer accepted; it fails with a message showing the path form. ci/build-matrix.json in the CI branch already emits full paths. Signed-off-by: Christopher Obbard --- prepare-source.sh | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/prepare-source.sh b/prepare-source.sh index 6908768..c180691 100755 --- a/prepare-source.sh +++ b/prepare-source.sh @@ -208,7 +208,6 @@ log_info "Copied $ACTUAL_DEBIAN_DIR -> $SOURCE_DIR/debian" # has already been applied. ACTIVE_DIR="$SOURCE_DIR/debian/config" AVAIL_DIR="$SOURCE_DIR/debian/config-available" -INTREE_DIR="$SOURCE_DIR/arch/arm64/configs" mkdir -p "$ACTIVE_DIR" log_step "Applying all packaging fragments from debian/config-available/" @@ -233,22 +232,45 @@ if [[ -n "$KERNEL_CONFIG" ]]; then if [[ "$cfg" == intree:* ]]; then # In-tree fragment, shipped by the kernel source rather than by this # repository. Referenced instead of vendored so it stays versioned - # with the kernel it targets. - frag="${cfg#intree:}" - frag="${frag%.config}.config" - src="$INTREE_DIR/$frag" + # with the kernel it targets. The entry spells the whole path + # relative to the kernel source root, so fragments outside + # arch/arm64/configs/ are reachable too (kernel/configs/debug.config). + path="${cfg#intree:}" + case "$path" in + "" | /* | ".." | "../"* | *"/../"* | *"/..") + log_error "Invalid in-tree fragment: ${cfg}" + log_error "Expected a path relative to the kernel source root, e.g. intree:arch/arm64/configs/qcom_debug.config" + exit 1 + ;; + esac + # debian/rules globs debian/config/*.config, so a fragment named + # anything else would be copied in and then silently ignored. + [[ "$path" == *.config ]] || { + log_error "In-tree fragment must name a .config file: $cfg" + log_error "Spell the whole path, e.g. intree:arch/arm64/configs/qcom_debug.config" + exit 1 + } + src="$SOURCE_DIR/$path" + frag="$(basename "$path")" + dir="$(dirname "$path")" [[ -f "$src" ]] || { - log_error "In-tree fragment not found: arch/arm64/configs/$frag" - log_error "Available: $(ls "$INTREE_DIR"/*.config 2>/dev/null | xargs -n1 basename | tr '\n' ' ')" + log_error "In-tree fragment not found: $path" + log_error "Paths are relative to the kernel source root ($SOURCE_DIR)." + avail="$(ls "$SOURCE_DIR/$dir"/*.config 2>/dev/null | xargs -n1 basename | tr '\n' ' ')" + [[ -n "$avail" ]] && log_error "Available in $dir/: $avail" exit 1 } + # The fragment keeps only its basename in debian/config/, so two + # in-tree paths can collide with each other as well as with a + # packaging fragment (kernel/configs/hardening.config and + # arch/arm64/configs/hardening.config both exist). [[ -e "$ACTIVE_DIR/$frag" ]] && { log_error "Fragment name collision in debian/config/: $frag" - log_error "An in-tree fragment must not share a filename with a packaging fragment." + log_error "An in-tree fragment must not share a filename with an already applied fragment." exit 1 } cp "$src" "$ACTIVE_DIR/$frag" - log_info " Applied: $frag (from arch/arm64/configs)" + log_info " Applied: $frag (from $dir)" else frag="${cfg%.config}.config" [[ -f "$AVAIL_DIR/$frag" ]] || { From cb7ec1be44dccbb3c809e6d1bf5899c6f7b18bd8 Mon Sep 17 00:00:00 2001 From: Christopher Obbard Date: Fri, 28 Aug 2026 16:00:55 +0100 Subject: [PATCH 5/5] docs: describe intree: fragments as kernel-source-relative paths Follow the resolution change through the places that document the option: the --kernel-config help in prepare-source.sh and build-kernel.sh and the config fragment section of the README. State what the new form buys: any directory in the kernel tree, not just arch/arm64/configs/ and record the constraints a caller can trip over: the required .config suffix, the rejection of absolute and ".." paths and the basename collision that two in-tree paths can now cause. Signed-off-by: Christopher Obbard --- README.md | 24 ++++++++++++++++-------- build-kernel.sh | 4 ++-- prepare-source.sh | 12 +++++++----- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4eaf0d3..ac57987 100644 --- a/README.md +++ b/README.md @@ -504,18 +504,26 @@ verbatim so drift is a plain diff; only the SPDX header is added locally. | `usb-can.config` | slcan, gs_usb, peak_usb | USB CAN adapter demos/testing | **In-tree fragments.** `--kernel-config` carries fragments applied *in addition* -to the set above. Today that means fragments shipped by the kernel source under -`arch/arm64/configs/`, referenced with an `intree:` prefix rather than copied -into this repository so they stay versioned with the kernel they target: +to the set above. Today that means fragments shipped by the kernel source, +referenced with an `intree:` prefix rather than copied into this repository so +they stay versioned with the kernel they target: ``` ---kernel-config intree:qcom_debug +--kernel-config intree:arch/arm64/configs/qcom_debug.config,intree:kernel/configs/debug.config ``` -`intree:` resolves to `arch/arm64/configs/.config`. A bare name is -still accepted for compatibility but is redundant, since that fragment is -already applied. An in-tree fragment sharing a filename with a packaging -fragment is rejected rather than silently overwriting it. +`intree:` names a file relative to the kernel source root, so any +directory in the tree can be used, not just `arch/arm64/configs/`. The path must +end in `.config`, and an absolute path or one containing `..` is rejected. The +fragment is copied into `debian/config/` under its basename, so two entries +resolving to the same filename — whether two in-tree paths +(`arch/arm64/configs/hardening.config` and `kernel/configs/hardening.config`) or +an in-tree path and a packaging fragment — are rejected rather than one silently +overwriting the other. + +A bare name (no `intree:` prefix) is still accepted for compatibility but is +redundant, since every fragment in `debian/config-available/` is already +applied. **Activating via `build-kernel.sh`:** diff --git a/build-kernel.sh b/build-kernel.sh index 70d9b8f..8fd946a 100755 --- a/build-kernel.sh +++ b/build-kernel.sh @@ -53,8 +53,8 @@ OPTIONS: --profiles PROFILES DEB_BUILD_PROFILES (e.g. debug) --kernel-config LIST Extra config fragments, beyond debian/config-available/ which is always applied in full. An "intree:" prefix - resolves to arch/arm64/configs/.config in the - kernel source (e.g. intree:qcom_debug) + names a path relative to the kernel source root + (e.g. intree:arch/arm64/configs/qcom_debug.config) Paths: -k, --kernel-dir DIR Kernel source directory (default: $KERNEL_DIR) diff --git a/prepare-source.sh b/prepare-source.sh index c180691..886e6d8 100755 --- a/prepare-source.sh +++ b/prepare-source.sh @@ -65,11 +65,13 @@ OPTIONS: --kernel-config LIST Comma-separated fragments to apply in addition to debian/config-available/, every entry of which is applied to every build regardless of this option. - An "intree:" prefix resolves to - arch/arm64/configs/.config in the kernel - source, for fragments that ship with the kernel - and are versioned with it - (e.g. intree:qcom_debug). + An "intree:" prefix names a path relative to the + kernel source root, for fragments that ship with + the kernel and are versioned with it + (e.g. intree:arch/arm64/configs/qcom_debug.config + or intree:kernel/configs/debug.config). The path + must end in .config; absolute paths and ".." are + rejected. A bare name is accepted for compatibility but is redundant, since that fragment is already applied. Entries are processed in LC_ALL=C sorted order.