[PATCH v3 4/7] kbuild: record cmd-prereqs in .cmd files

From: Luis Augenstein

Date: Mon Oct 12 2026 - 17:37:05 EST


Record $(cmd-prereqs), i.e. $(real-prereqs) with the deps_*
dependencies filtered out, in a new .cmd file metadata field:

make_prereqs_<target> := <prerequisites>

On an incremental build, deps_<target> holds the dependencies
recorded from the depfile of the previous build. Those are filtered
out from $(real-prereqs), even if they no longer appear in the
current depfile. fixdep additionally filters the dependencies
collected from the current depfile, so that they are not recorded in
both deps_<target> and make_prereqs_<target>. This keeps the field
consistent between clean and incremental builds.

Link: https://lore.kernel.org/r/a01233b9-23a2-4666-91ed-f1cf030dcb9f@xxxxxxxxxxx
Assisted-by: LLM
Co-developed-by: Maximilian Huber <maximilian.huber@xxxxxxxxxxx>
Signed-off-by: Maximilian Huber <maximilian.huber@xxxxxxxxxxx>
Signed-off-by: Luis Augenstein <luis.augenstein@xxxxxxxxxxx>
---
scripts/Kbuild.include | 10 ++++++--
scripts/basic/fixdep.c | 54 +++++++++++++++++++++++++++++++++++++-----
2 files changed, 56 insertions(+), 8 deletions(-)

diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 8c311b997e2..7790519f92f 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -181,6 +181,9 @@ endif
# (needed for the shell)
make-cmd = $(call escsq,$(subst $(pound),$$(pound),$(subst $$,$$$$,$(cmd_$(1)))))

+# prerequisites to record in .cmd files, excluding those covered in deps_*
+cmd-prereqs = $(call escsq,$(filter-out $(deps_$@), $(real-prereqs)))
+
# Find any prerequisites that are newer than target or that do not exist.
# PHONY targets skipped in both cases.
# If there is no prerequisite other than phony targets, $(newer-prereqs) becomes
@@ -198,14 +201,17 @@ if_changed = $(if $(if-changed-cond),$(cmd_and_savecmd),@:)

cmd_and_savecmd = \
$(cmd); \
- printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd
+ printf '%s\n' \
+ 'savedcmd_$@ := $(make-cmd)' '' \
+ 'make_prereqs_$@ := $(cmd-prereqs)' > $(dot-target).cmd

# Execute the command and also postprocess generated .d dependencies file.
if_changed_dep = $(if $(if-changed-cond),$(cmd_and_fixdep),@:)

cmd_and_fixdep = \
$(cmd); \
- $(objtree)/scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).cmd;\
+ $(objtree)/scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' \
+ '$(cmd-prereqs)' > $(dot-target).cmd; \
rm -f $(depfile)

# Usage: $(call if_changed_rule,foo)
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index f514ef606ec..005c1c3df49 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -62,15 +62,17 @@
*
* It is invoked as
*
- * fixdep <depfile> <target> <cmdline>
+ * fixdep <depfile> <target> <cmdline> <prereqs>
*
* and will read the dependency file <depfile>
*
* The transformed dependency snipped is written to stdout.
*
- * It first generates a line
+ * It first generates the lines
*
- * savedcmd_<target> = <cmdline>
+ * savedcmd_<target> := <cmdline>
+ *
+ * make_prereqs_<target> := <prereqs_without_deps>
*
* and then basically copies the .<target>.d file to stdout, in the
* process filtering out the dependency on autoconf.h and adding
@@ -103,7 +105,7 @@

static void usage(void)
{
- fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
+ fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline> <prereqs>\n");
exit(1);
}

@@ -478,23 +480,63 @@ static void print_dep_file(const char *target, const struct dep_info *info)
printf("$(deps_%s):\n", target);
}

+/*
+ * Print make_prereqs_<target> from prereqs, excluding the deps listed
+ * in info.
+ */
+static void print_make_prereqs(const char *target, const char *prereqs,
+ const struct dep_info *info)
+{
+ struct item *seen_prereqs_hashtab[HASHSZ] = {};
+ const struct dep_item *dep;
+ const char *p = prereqs;
+
+ for (dep = info->deps; dep; dep = dep->next)
+ in_hashtable(dep->name, strlen(dep->name),
+ seen_prereqs_hashtab);
+
+ printf("make_prereqs_%s :=", target);
+
+ while (*p) {
+ const char *start;
+ int len;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (!*p)
+ break;
+
+ start = p;
+ while (*p && *p != ' ' && *p != '\t')
+ p++;
+ len = p - start;
+
+ if (!in_hashtable(start, len, seen_prereqs_hashtab))
+ printf(" %.*s", len, start);
+ }
+
+ printf("\n\n");
+}
+
int main(int argc, char *argv[])
{
- const char *depfile, *target, *cmdline;
+ const char *depfile, *target, *cmdline, *prereqs;
struct dep_info info;
void *buf;

- if (argc != 4)
+ if (argc != 5)
usage();

depfile = argv[1];
target = argv[2];
cmdline = argv[3];
+ prereqs = argv[4];

buf = read_file(depfile);
info = parse_dep_file(buf);

printf("savedcmd_%s := %s\n\n", target, cmdline);
+ print_make_prereqs(target, prereqs, &info);
print_dep_file(target, &info);

free_dep_info(&info);
--
2.43.0