From: Luis Augenstein <luis.augenstein@tngtech.com>
To: nathan@kernel.org, nsc@kernel.org
Cc: linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org,
akpm@linux-foundation.org, gregkh@linuxfoundation.org,
kstewart@linuxfoundation.org, maximilian.huber@tngtech.com,
Luis Augenstein <luis.augenstein@tngtech.com>
Subject: [PATCH v3 5/7] scripts/sbom: parse make_prereqs from .cmd files
Date: Mon, 12 Oct 2026 23:33:20 +0200 [thread overview]
Message-ID: <20261012213337.2466524-6-luis.augenstein@tngtech.com> (raw)
In-Reply-To: <20261012213337.2466524-1-luis.augenstein@tngtech.com>
Add support for parsing the make_prereqs_<target> field recorded by
kbuild in .cmd files.
Refactor CmdFile.create() to collect the required fields by name
instead of deriving the file structure from the number of lines and
the position.
The source_ entry is no longer parsed, because it is always also
included in make_prereqs_<target> and therefore redundant for
KernelSbom.
Assisted-by: LLM
Co-developed-by: Maximilian Huber <maximilian.huber@tngtech.com>
Signed-off-by: Maximilian Huber <maximilian.huber@tngtech.com>
Signed-off-by: Luis Augenstein <luis.augenstein@tngtech.com>
---
scripts/sbom/sbom/cmd_graph/cmd_file.py | 103 +++++++++---------------
1 file changed, 36 insertions(+), 67 deletions(-)
diff --git a/scripts/sbom/sbom/cmd_graph/cmd_file.py b/scripts/sbom/sbom/cmd_graph/cmd_file.py
index 08819b4d117..cc576c7c6be 100644
--- a/scripts/sbom/sbom/cmd_graph/cmd_file.py
+++ b/scripts/sbom/sbom/cmd_graph/cmd_file.py
@@ -10,97 +10,66 @@ import sbom.sbom_logging as sbom_logging
from sbom.path_utils import PathStr
SAVEDCMD_PATTERN = re.compile(r"^(saved)?cmd_.*?:=\s*(?P<full_command>.+)$")
-SOURCE_PATTERN = re.compile(r"^source.*?:=\s*(?P<source_file>.+)$")
+MAKE_PREREQS_PATTERN = re.compile(r"^make_prereqs_.*?:=\s*(?P<prereqs>.+)$")
+DEPS_ASSIGNMENT_PATTERN = re.compile(r"^deps_.*?:=")
@dataclass
class CmdFile:
cmd_file_path: PathStr
savedcmd: str
- source: PathStr | None = None
+ make_prereqs: list[str] = field(default_factory=list)
deps: list[str] = field(default_factory=list)
- make_rules: list[str] = field(default_factory=list)
@classmethod
def create(cls, cmd_file_path: PathStr) -> "CmdFile | None":
- """
- Parses a .cmd file.
- .cmd files are assumed to have one of the following structures:
- 1. Full Cmd File
- (saved)?cmd_<output> := <command>
- source_<output> := <main_input>
- deps_<output> := \
- <dependencies>
- <output> := $(deps_<output>)
- $(deps_<output>):
-
- 2. Command Only Cmd File
- (saved)?cmd_<output> := <command>
-
- 3. Single Dependency Cmd File
- (saved)?cmd_<output> := <command>
- <output> : <dependency>
+ r"""
+ Parse the following fields from a `.cmd` file:
+
+ * `(saved)?cmd_<target> := <shell command>` (required) —
+ the shell command Kbuild ran to produce the build target.
+ * `make_prereqs_<target> := <prerequisites>` (required but can be empty) —
+ the non-phony make prerequisites.
+ * `deps_<target> := \` (optional) —
+ additional dependencies not covered by make prerequisites, e.g., headers.
Args:
- cmd_file_path (Path): absolute Path to a .cmd file
+ cmd_file_path (Path): absolute Path to a `.cmd` file.
Returns:
- cmd_file (CmdFile): Parsed cmd file.
+ cmd_file (CmdFile): Parsed `.cmd` file or `None` if no `savedcmd` line was found.
"""
with open(cmd_file_path, "rt", encoding="utf-8") as f:
lines = [line.strip() for line in f.readlines() if line.strip() != "" and not line.startswith("#")]
- # make_prereqs_* is recorded for future use. Ignore it for now to
- # preserve the existing parser behavior.
- lines = [line for line in lines if not line.startswith("make_prereqs_")]
+ savedcmd: str | None = None
+ make_prereqs: list[str] = []
+ deps: list[str] = []
+
+ for i, line in enumerate(lines):
+ match = SAVEDCMD_PATTERN.match(line)
+ if match is not None:
+ savedcmd = match.group("full_command")
+ continue
+
+ match = MAKE_PREREQS_PATTERN.match(line)
+ if match is not None:
+ make_prereqs = match.group("prereqs").split()
+ continue
- # savedcmd
- match = SAVEDCMD_PATTERN.match(lines[0] if lines else "")
- if match is None:
+ if DEPS_ASSIGNMENT_PATTERN.match(line):
+ j = i + 1
+ while j < len(lines) and lines[j].endswith("\\"):
+ deps.append(lines[j].removesuffix("\\").strip())
+ j += 1
+
+ if savedcmd is None:
sbom_logging.error(
"Skip parsing '{cmd_file_path}' because no 'savedcmd_' command was found.", cmd_file_path=cmd_file_path
)
return None
- savedcmd = match.group("full_command")
-
- # Command Only Cmd File
- if len(lines) == 1:
- return CmdFile(cmd_file_path, savedcmd)
-
- # Single Dependency Cmd File
- if len(lines) == 2:
- parts = lines[1].split(":", 1)
- if len(parts) != 2:
- sbom_logging.error(
- "Skip parsing '{cmd_file_path}'. Expected dependency line '<output>: <dependency>' but got {second_line}", cmd_file_path=cmd_file_path, second_line=lines[1]
- )
- return None
- dep = parts[1].strip()
- return CmdFile(cmd_file_path, savedcmd, deps=[dep])
-
- # Full Cmd File
- # source
- line1 = SOURCE_PATTERN.match(lines[1])
- if line1 is None:
- sbom_logging.error(
- "Skip parsing '{cmd_file_path}' because no 'source_' entry was found.", cmd_file_path=cmd_file_path
- )
- return CmdFile(cmd_file_path, savedcmd)
- source = line1.group("source_file")
-
- # deps
- deps: list[str] = []
- i = 3 # lines[2] includes the variable assignment but no actual dependency, so we need to start at lines[3].
- while i < len(lines):
- if not lines[i].endswith("\\"):
- break
- deps.append(lines[i][:-1].strip())
- i += 1
-
- # make_rules
- make_rules = lines[i:]
- return CmdFile(cmd_file_path, savedcmd, source, deps, make_rules)
+ return CmdFile(cmd_file_path, savedcmd, make_prereqs, deps)
def get_dependencies(
self: "CmdFile", target_path: PathStr, obj_tree: PathStr, fail_on_unknown_build_command: bool
--
2.43.0
next prev parent reply other threads:[~2026-10-12 21:34 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 21:33 [PATCH v3 0/7] kbuild: record cmd-prereqs in " Luis Augenstein
2026-10-12 21:33 ` [PATCH v3 1/7] scripts/make_fit: limit .cmd file parsing to the savedcmd_ line Luis Augenstein
2026-10-12 21:33 ` [PATCH v3 2/7] kbuild: refactor fixdep to collect dependencies before printing Luis Augenstein
2026-10-12 21:33 ` [PATCH v3 3/7] scripts/sbom: skip make_prereqs lines when parsing .cmd files Luis Augenstein
2026-10-12 21:33 ` [PATCH v3 4/7] kbuild: record cmd-prereqs in " Luis Augenstein
2026-10-12 21:33 ` Luis Augenstein [this message]
2026-10-12 21:33 ` [PATCH v3 6/7] scripts/sbom: use make_prereqs to determine build inputs Luis Augenstein
2026-10-12 21:33 ` [PATCH v3 7/7] scripts/sbom: remove the command-specific parsers Luis Augenstein
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261012213337.2466524-6-luis.augenstein@tngtech.com \
--to=luis.augenstein@tngtech.com \
--cc=akpm@linux-foundation.org \
--cc=gregkh@linuxfoundation.org \
--cc=kstewart@linuxfoundation.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maximilian.huber@tngtech.com \
--cc=nathan@kernel.org \
--cc=nsc@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®