integrate SBOM script into the kernel build process.
Assisted-by: Claude Sonnet 4.5
Assisted-by: GLM-4.7
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>
---
.gitignore | 1 +
MAINTAINERS | 6 ++++++
Makefile | 11 +++++++++--
scripts/sbom/Makefile | 33 +++++++++++++++++++++++++++++++++
scripts/sbom/sbom.py | 16 ++++++++++++++++
5 files changed, 65 insertions(+), 2 deletions(-)
create mode 100644 scripts/sbom/Makefile
create mode 100644 scripts/sbom/sbom.py
diff --git a/.gitignore b/.gitignore
index 3a7241c941f5..f3372f15eb1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,7 @@
*.s
*.so
*.so.dbg
+*.spdx.json
*.su
*.symtypes
*.tab.[ch]
diff --git a/MAINTAINERS b/MAINTAINERS
index f1b020588597..decbab52cef1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23365,6 +23365,12 @@ R: Marc Murphy <marc.murphy@sancloud.com>
S: Supported
F: arch/arm/boot/dts/ti/omap/am335x-sancloud*
+SBOM
+M: Luis Augenstein <luis.augenstein@tngtech.com>
+M: Maximilian Huber <maximilian.huber@tngtech.com>
+S: Maintained
+F: scripts/sbom/
+
SC1200 WDT DRIVER
M: Zwane Mwaikambo <zwanem@gmail.com>
S: Maintained
diff --git a/Makefile b/Makefile
index 9d38125263fb..46d4be490d7f 100644
--- a/Makefile
+++ b/Makefile
@@ -772,7 +772,7 @@ endif
# in addition to whatever we do anyway.
# Just "make" or "make all" shall build modules as well
-ifneq ($(filter all modules nsdeps compile_commands.json clang-%,$(MAKECMDGOALS)),)
+ifneq ($(filter all modules nsdeps compile_commands.json clang-% sbom,$(MAKECMDGOALS)),)
KBUILD_MODULES := y
endif
@@ -1612,7 +1612,7 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \
modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \
compile_commands.json rust/test \
rust-project.json .vmlinux.objs .vmlinux.export.c \
- .builtin-dtbs-list .builtin-dtb.S
+ .builtin-dtbs-list .builtin-dtb.S sbom-*.spdx.json
# Directories & files removed with 'make mrproper'
MRPROPER_FILES += include/config include/generated \
@@ -1728,6 +1728,7 @@ help:
@echo ''
@echo 'Tools:'
@echo ' nsdeps - Generate missing symbol namespace dependencies'
+ @echo ' sbom - Generate Software Bill of Materials'
@echo ''
@echo 'Kernel selftest:'
@echo ' kselftest - Build and run kernel selftest'
@@ -2108,6 +2109,12 @@ nsdeps: export KBUILD_NSDEPS=1
nsdeps: modules
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/nsdeps
+# Script to generate .spdx.json SBOM documents describing the build
+# ---------------------------------------------------------------------------
+PHONY += sbom
+sbom: all
+ $(Q)$(MAKE) $(build)=scripts/sbom
+
# Clang Tooling
# ---------------------------------------------------------------------------
diff --git a/scripts/sbom/Makefile b/scripts/sbom/Makefile
new file mode 100644
index 000000000000..6c8ec7356293
--- /dev/null
+++ b/scripts/sbom/Makefile
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-only OR MIT
+# Copyright (C) 2025 TNG Technology Consulting GmbH
+
+SBOM_SOURCE_FILE := $(objtree)/sbom-source.spdx.json
+SBOM_BUILD_FILE := $(objtree)/sbom-build.spdx.json
+SBOM_OUTPUT_FILE := $(objtree)/sbom-output.spdx.json
+SBOM_ROOTS_FILE := $(objtree)/sbom-roots.txt
+
+
+ifeq ($(srctree),$(objtree))
+ SBOM_TARGETS := $(SBOM_BUILD_FILE) $(SBOM_OUTPUT_FILE)
+else
+ SBOM_TARGETS := $(SBOM_SOURCE_FILE) $(SBOM_BUILD_FILE) $(SBOM_OUTPUT_FILE)
+endif
+
+SBOM_DEPS := $(objtree)/$(KBUILD_IMAGE) $(objtree)/include/generated/autoconf.h
+ifdef CONFIG_MODULES
+ SBOM_DEPS += $(objtree)/modules.order
+endif
+
+$(SBOM_TARGETS) &: $(SBOM_DEPS)
+ $(Q)echo " GEN $(notdir $(SBOM_TARGETS))"
+
+ $(Q)printf "%s\n" "$(KBUILD_IMAGE)" > $(SBOM_ROOTS_FILE)
+ $(Q)if [ "$(CONFIG_MODULES)" = "y" ]; then \
+ sed 's/\.o$$/.ko/' $(objtree)/modules.order >> $(SBOM_ROOTS_FILE); \
+ fi
+
+ $(Q)$(PYTHON3) $(srctree)/scripts/sbom/sbom.py
+
+ $(Q)rm $(SBOM_ROOTS_FILE)
+
+$(obj)/: $(SBOM_TARGETS)
diff --git a/scripts/sbom/sbom.py b/scripts/sbom/sbom.py
new file mode 100644
index 000000000000..9c2e4c7f17ce
--- /dev/null
+++ b/scripts/sbom/sbom.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only OR MIT
+# Copyright (C) 2025 TNG Technology Consulting GmbH
+
+"""
+Compute software bill of materials in SPDX format describing a kernel build.
+"""
+
+
+def main():
+ pass
+
+
+# Call main method
+if __name__ == "__main__":
+ main()
--
2.34.1
Hi Luis,
On Tue, Feb 10, 2026 at 09:54:11PM +0100, Luis Augenstein wrote:
> integrate SBOM script into the kernel build process.
>
> Assisted-by: Claude Sonnet 4.5
> Assisted-by: GLM-4.7
> 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>
...
> diff --git a/Makefile b/Makefile
> index 9d38125263fb..46d4be490d7f 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -772,7 +772,7 @@ endif
> # in addition to whatever we do anyway.
> # Just "make" or "make all" shall build modules as well
>
> -ifneq ($(filter all modules nsdeps compile_commands.json clang-%,$(MAKECMDGOALS)),)
> +ifneq ($(filter all modules nsdeps compile_commands.json clang-% sbom,$(MAKECMDGOALS)),)
> KBUILD_MODULES := y
> endif
>
> @@ -1612,7 +1612,7 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \
> modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \
> compile_commands.json rust/test \
> rust-project.json .vmlinux.objs .vmlinux.export.c \
> - .builtin-dtbs-list .builtin-dtb.S
> + .builtin-dtbs-list .builtin-dtb.S sbom-*.spdx.json
Does sbom-roots.txt need to be cleaned up as well?
A note for Greg: this will conflict with commit a76e30c2479c ("kbuild:
Delete .builtin-dtbs.S when running make clean").
> # Directories & files removed with 'make mrproper'
> MRPROPER_FILES += include/config include/generated \
> @@ -1728,6 +1728,7 @@ help:
> @echo ''
> @echo 'Tools:'
> @echo ' nsdeps - Generate missing symbol namespace dependencies'
> + @echo ' sbom - Generate Software Bill of Materials'
> @echo ''
> @echo 'Kernel selftest:'
> @echo ' kselftest - Build and run kernel selftest'
> @@ -2108,6 +2109,12 @@ nsdeps: export KBUILD_NSDEPS=1
> nsdeps: modules
> $(Q)$(CONFIG_SHELL) $(srctree)/scripts/nsdeps
>
> +# Script to generate .spdx.json SBOM documents describing the build
> +# ---------------------------------------------------------------------------
> +PHONY += sbom
> +sbom: all
> + $(Q)$(MAKE) $(build)=scripts/sbom
> +
> # Clang Tooling
> # ---------------------------------------------------------------------------
>
> diff --git a/scripts/sbom/Makefile b/scripts/sbom/Makefile
> new file mode 100644
> index 000000000000..6c8ec7356293
> --- /dev/null
> +++ b/scripts/sbom/Makefile
> @@ -0,0 +1,33 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR MIT
> +# Copyright (C) 2025 TNG Technology Consulting GmbH
> +
> +SBOM_SOURCE_FILE := $(objtree)/sbom-source.spdx.json
> +SBOM_BUILD_FILE := $(objtree)/sbom-build.spdx.json
> +SBOM_OUTPUT_FILE := $(objtree)/sbom-output.spdx.json
> +SBOM_ROOTS_FILE := $(objtree)/sbom-roots.txt
> +
> +
> +ifeq ($(srctree),$(objtree))
> + SBOM_TARGETS := $(SBOM_BUILD_FILE) $(SBOM_OUTPUT_FILE)
> +else
> + SBOM_TARGETS := $(SBOM_SOURCE_FILE) $(SBOM_BUILD_FILE) $(SBOM_OUTPUT_FILE)
> +endif
This might be easier to read if it were
ifeq ($(srctree),$(objtree))
SBOM_TARGETS := $(SBOM_SOURCE_FILE)
endif
SBOM_TARGETS += $(SBOM_BUILD_FILE) $(SBOM_OUTPUT_FILE)
> +SBOM_DEPS := $(objtree)/$(KBUILD_IMAGE) $(objtree)/include/generated/autoconf.h
> +ifdef CONFIG_MODULES
> + SBOM_DEPS += $(objtree)/modules.order
> +endif
Is $(objtree)/ necessary on these? You might be able to turn this into
SBOM_DEPS := $(KBUILD_IMAGE) include/generated/autoconf.h $(if $(CONFIG_MODULES),modules.order)
> +$(SBOM_TARGETS) &: $(SBOM_DEPS)
&: is a feature from make 4.3 but the kernel supports make 4.0 and
newer, so you should probably make this
# Use
# $(SBOM_TARGETS) &: $(SBOM_DEPS)
# when make 4.3 is the minimum.
$(SBOM_BUILD_FILE): $(SBOM_DEPS)
$(obj)/: $(SBOM_BUILD_FILE)
> + $(Q)echo " GEN $(notdir $(SBOM_TARGETS))"
> + $(Q)if [ "$(CONFIG_MODULES)" = "y" ]; then \
> + sed 's/\.o$$/.ko/' $(objtree)/modules.order >> $(SBOM_ROOTS_FILE); \
> + fi
> +
> + $(Q)$(PYTHON3) $(srctree)/scripts/sbom/sbom.py
> +
> + $(Q)rm $(SBOM_ROOTS_FILE)
I think this would look better if it used the standard quiet_cmd_ and
cmd_ syntax:
quiet_cmd_sbom = GEN $(notdir $(SBOM_TARGETS))
cmd_sbom = printf "%s\n" "$(KBUILD_IMAGE)" >$(SBOM_ROOTS_FILE); \
$(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/' $(objtree)/modules.order >> $(SBOM_ROOTS_FILE);) \
$(PYTHON3) $(srctree)/scripts/sbom/sbom.py \
--src-tree $(abspath $(srctree)) \
--obj-tree $(abspath $(objtree)) \
--roots-file $(SBOM_ROOTS_FILE) \
--output-directory $(abspath $(objtree)) \
--generate-spdx \
--package-license "GPL-2.0 WITH Linux-syscall-note" \
--package-version "$(KERNELVERSION)"; \
rm $(SBOM_ROOTS_FILE)
$(SBOM_BUILD_FILE): $(SBOM_DEPS)
$(call cmd,sbom)
> +$(obj)/: $(SBOM_TARGETS)
It seems like this could use the standard always-y Kbuild syntax but I
am not sure. Honestly, looking at the bigger picture, it feels like most
of this Makefile could be moved into the main Makefile under the sbom
target? scripts/sbom is not actually used as an output directory and the
generated files do not really need to be listed as targets since their
names are hardcoded in scripts/sbom/sbom/config.py?
# Script to generate .spdx.json SBOM documents describing the build
# ---------------------------------------------------------------------------
ifdef building_out_of_srctree
sbom_targets := sbom-source.spdx.json
endif
sbom_targets += sbom-build.spdx.json sbom-output.spdx.json
sbom_roots_file := $(objtree)/sbom-roots.txt
quiet_cmd_sbom = GEN $(notdir $(sbom_targets))
cmd_sbom = printf "%s\n" "$(KBUILD_IMAGE)" >$(sbom_roots_file); \
$(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/' $(objtree)/modules.order >> $(sbom_roots_file);) \
$(PYTHON3) $(srctree)/scripts/sbom/sbom.py \
--src-tree $(abspath $(srctree)) \
--obj-tree $(abspath $(objtree)) \
--roots-file $(sbom_roots_file) \
--output-directory $(abspath $(objtree)) \
--generate-spdx \
--package-license "GPL-2.0 WITH Linux-syscall-note" \
--package-version "$(KERNELVERSION)"; \
rm $(sbom_roots_file)
PHONY += sbom
sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if $(CONFIG_MODULES),modules modules.order)
$(call cmd,sbom)
appears to do the right thing for me?
FWIW, I get errors like
$ make -kj"$(nproc)" ARCH=arm64 CROSS_COMPILE=aarch64-linux- O=build mrproper defconfig sbom
...
GEN sbom-source.spdx.json sbom-build.spdx.json sbom-output.spdx.json
[ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py", line 630, in log_error_or_warning
Skipped parsing command ccache aarch64-linux-gcc ... -o init/main.o /src/init/main.c because no matching parser was found
[ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py", line 630, in log_error_or_warning
Skipped parsing command ccache aarch64-linux-gcc ... -o arch/arm64/kernel/asm-offsets.s /src/arch/arm64/kernel/asm-offsets.c because no matching parser was found
[ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py", line 630, in log_error_or_warning
Skipped parsing command ccache aarch64-linux-gcc ... -o kernel/bounds.s /src/kernel/bounds.c because no matching parser was found
... (Found 10435 more instances of this error)
when testing the whole series without any modifications, am I doing
something wrong?
Cheers,
Nathan
Hi Nathan,
thanks a lot for your recommendations.
> Does sbom-roots.txt need to be cleaned up as well?
This file is only required to pass the roots into the python script.
We could also use a tmp file. Then we don't need to worry about clean
up. Together with your other suggested changes something like this
should work:
# Script to generate .spdx.json SBOM documents describing the build
#
---------------------------------------------------------------------------
ifdef building_out_of_srctree
sbom_targets := sbom-source.spdx.json
endif
sbom_targets += sbom-build.spdx.json sbom-output.spdx.json
quiet_cmd_sbom = GEN $(notdir $(sbom_targets))
cmd_sbom = roots_file=$$(mktemp); \
printf "%s\n" "$(KBUILD_IMAGE)" >"$$roots_file"; \
$(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/'
$(objtree)/modules.order >> "$$roots_file";) \
$(PYTHON3) $(srctree)/scripts/sbom/sbom.py \
--src-tree $(abspath $(srctree)) \
--obj-tree $(abspath $(objtree)) \
--roots-file "$$roots_file" \
--output-directory $(abspath $(objtree)) \
--generate-spdx \
--package-license "GPL-2.0 WITH Linux-syscall-note" \
--package-version "$(KERNELVERSION)" \
--write-output-on-error;
rm -f "$$roots_file"
PHONY += sbom
sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if
$(CONFIG_MODULES),modules modules.order)
$(call cmd,sbom)
Note, I will also add the --write-output-on-error flag by default such
that the .spdx.json documents are generated as much as possible even if
some build commands are unknown to the parser.
> FWIW, I get errors like
>
> $ make -kj"$(nproc)" ARCH=arm64 CROSS_COMPILE=aarch64-linux- O=build
mrproper defconfig sbom
> ...
> GEN sbom-source.spdx.json sbom-build.spdx.json
sbom-output.spdx.json
> [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py",
line 630, in log_error_or_warning
> Skipped parsing command ccache aarch64-linux-gcc ... -o init/main.o
/src/init/main.c because no matching parser was found
> [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py",
line 630, in log_error_or_warning
> Skipped parsing command ccache aarch64-linux-gcc ... -o
arch/arm64/kernel/asm-offsets.s /src/arch/arm64/kernel/asm-offsets.c
because no matching parser was found
> [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py",
line 630, in log_error_or_warning
> Skipped parsing command ccache aarch64-linux-gcc ... -o
kernel/bounds.s /src/kernel/bounds.c because no matching parser was found
> ... (Found 10435 more instances of this error)
>
> when testing the whole series without any modifications, am I doing
> something wrong?
I was not aware of ccache. If you rebuild without using ccache the gcc
commands should be parsed correctly.
The parser expects gcc commands to be of the form
"^([^\s]+-)?(gcc|clang)\b"
When using tools like ccache this breaks. I will update the parser to
look for
"^(ccache\s+)?([^\s]+-)?(gcc|clang)\b"
instead.
Feedback like this is very helpful—thanks! Do you know of any other
commonly used tools that modify build commands in a similar way and
should be considered?
Best,
Luis
--
Luis Augenstein * luis.augenstein@tngtech.com * +49-152-25275761
TNG Technology Consulting GmbH, Beta-Str. 13, 85774 Unterföhring
Geschäftsführer: Henrik Klagges, Dr. Robert Dahlke, Thomas Endres
Aufsichtsratsvorsitzender: Christoph Stock
Sitz: Unterföhring * Amtsgericht München * HRB 135082
On Mon, Mar 30, 2026 at 10:32:00PM +0200, Luis Augenstein wrote: > Hi Nathan, > > thanks a lot for your recommendations. > > > Does sbom-roots.txt need to be cleaned up as well? > > This file is only required to pass the roots into the python script. > We could also use a tmp file. Then we don't need to worry about clean > up. Together with your other suggested changes something like this > should work: > > # Script to generate .spdx.json SBOM documents describing the build > # > --------------------------------------------------------------------------- > > ifdef building_out_of_srctree > sbom_targets := sbom-source.spdx.json > endif > sbom_targets += sbom-build.spdx.json sbom-output.spdx.json > quiet_cmd_sbom = GEN $(notdir $(sbom_targets)) > cmd_sbom = roots_file=$$(mktemp); \ > printf "%s\n" "$(KBUILD_IMAGE)" >"$$roots_file"; \ > $(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/' > $(objtree)/modules.order >> "$$roots_file";) \ > $(PYTHON3) $(srctree)/scripts/sbom/sbom.py \ > --src-tree $(abspath $(srctree)) \ > --obj-tree $(abspath $(objtree)) \ > --roots-file "$$roots_file" \ > --output-directory $(abspath $(objtree)) \ > --generate-spdx \ > --package-license "GPL-2.0 WITH Linux-syscall-note" \ > --package-version "$(KERNELVERSION)" \ > --write-output-on-error; > rm -f "$$roots_file" > PHONY += sbom > sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if > $(CONFIG_MODULES),modules modules.order) > $(call cmd,sbom) > > Note, I will also add the --write-output-on-error flag by default such > that the .spdx.json documents are generated as much as possible even if > some build commands are unknown to the parser. > > > FWIW, I get errors like > > > > $ make -kj"$(nproc)" ARCH=arm64 CROSS_COMPILE=aarch64-linux- O=build > mrproper defconfig sbom > > ... > > GEN sbom-source.spdx.json sbom-build.spdx.json > sbom-output.spdx.json > > [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py", > line 630, in log_error_or_warning > > Skipped parsing command ccache aarch64-linux-gcc ... -o init/main.o > /src/init/main.c because no matching parser was found > > [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py", > line 630, in log_error_or_warning > > Skipped parsing command ccache aarch64-linux-gcc ... -o > arch/arm64/kernel/asm-offsets.s /src/arch/arm64/kernel/asm-offsets.c > because no matching parser was found > > [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py", > line 630, in log_error_or_warning > > Skipped parsing command ccache aarch64-linux-gcc ... -o > kernel/bounds.s /src/kernel/bounds.c because no matching parser was found > > ... (Found 10435 more instances of this error) > > > > when testing the whole series without any modifications, am I doing > > something wrong? > > I was not aware of ccache. If you rebuild without using ccache the gcc > commands should be parsed correctly. > > The parser expects gcc commands to be of the form > "^([^\s]+-)?(gcc|clang)\b" > When using tools like ccache this breaks. I will update the parser to > look for > "^(ccache\s+)?([^\s]+-)?(gcc|clang)\b" > instead. > > Feedback like this is very helpful—thanks! Do you know of any other > commonly used tools that modify build commands in a similar way and > should be considered? Ick, this might get messy as you can modify the compiler with the CC= option to be anything. There are other build tools out there that do much the same as ccache does (which I should have caught this as I use ccache on my build systems), like distcc and friends, so this might just want to look at the result of "CC" instead? thanks, greg k-h
On Tue, Mar 31, 2026 at 07:15:35AM +0200, Greg KH wrote:
> On Mon, Mar 30, 2026 at 10:32:00PM +0200, Luis Augenstein wrote:
> > Hi Nathan,
> >
> > thanks a lot for your recommendations.
> >
> > > Does sbom-roots.txt need to be cleaned up as well?
> >
> > This file is only required to pass the roots into the python script.
> > We could also use a tmp file. Then we don't need to worry about clean
> > up. Together with your other suggested changes something like this
> > should work:
> >
> > # Script to generate .spdx.json SBOM documents describing the build
> > #
> > ---------------------------------------------------------------------------
> >
> > ifdef building_out_of_srctree
> > sbom_targets := sbom-source.spdx.json
> > endif
> > sbom_targets += sbom-build.spdx.json sbom-output.spdx.json
> > quiet_cmd_sbom = GEN $(notdir $(sbom_targets))
> > cmd_sbom = roots_file=$$(mktemp); \
I think I would rather have a named file in objtree instead of one in
/tmp, as we want all output to remain in the build folder.
> > printf "%s\n" "$(KBUILD_IMAGE)" >"$$roots_file"; \
> > $(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/'
> > $(objtree)/modules.order >> "$$roots_file";) \
> > $(PYTHON3) $(srctree)/scripts/sbom/sbom.py \
> > --src-tree $(abspath $(srctree)) \
> > --obj-tree $(abspath $(objtree)) \
> > --roots-file "$$roots_file" \
> > --output-directory $(abspath $(objtree)) \
> > --generate-spdx \
> > --package-license "GPL-2.0 WITH Linux-syscall-note" \
> > --package-version "$(KERNELVERSION)" \
> > --write-output-on-error;
> > rm -f "$$roots_file"
The cmd macro uses 'set -e', so consider moving this up and making it
trap "rm -rf $$roots_file" EXIT; \
like try-run in scripts/Makefile.compiler does to ensure it is always
cleaned up.
> > PHONY += sbom
> > sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if
> > $(CONFIG_MODULES),modules modules.order)
> > $(call cmd,sbom)
> >
> > Note, I will also add the --write-output-on-error flag by default such
> > that the .spdx.json documents are generated as much as possible even if
> > some build commands are unknown to the parser.
Seems reasonable to me.
> > > FWIW, I get errors like
> > >
> > > $ make -kj"$(nproc)" ARCH=arm64 CROSS_COMPILE=aarch64-linux- O=build
> > mrproper defconfig sbom
> > > ...
> > > GEN sbom-source.spdx.json sbom-build.spdx.json
> > sbom-output.spdx.json
> > > [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py",
> > line 630, in log_error_or_warning
> > > Skipped parsing command ccache aarch64-linux-gcc ... -o init/main.o
> > /src/init/main.c because no matching parser was found
> > > [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py",
> > line 630, in log_error_or_warning
> > > Skipped parsing command ccache aarch64-linux-gcc ... -o
> > arch/arm64/kernel/asm-offsets.s /src/arch/arm64/kernel/asm-offsets.c
> > because no matching parser was found
> > > [ERROR] File "/src/scripts/sbom/sbom/cmd_graph/savedcmd_parser.py",
> > line 630, in log_error_or_warning
> > > Skipped parsing command ccache aarch64-linux-gcc ... -o
> > kernel/bounds.s /src/kernel/bounds.c because no matching parser was found
> > > ... (Found 10435 more instances of this error)
> > >
> > > when testing the whole series without any modifications, am I doing
> > > something wrong?
> >
> > I was not aware of ccache. If you rebuild without using ccache the gcc
> > commands should be parsed correctly.
> >
> > The parser expects gcc commands to be of the form
> > "^([^\s]+-)?(gcc|clang)\b"
> > When using tools like ccache this breaks. I will update the parser to
> > look for
> > "^(ccache\s+)?([^\s]+-)?(gcc|clang)\b"
> > instead.
> >
> > Feedback like this is very helpful—thanks! Do you know of any other
> > commonly used tools that modify build commands in a similar way and
> > should be considered?
>
> Ick, this might get messy as you can modify the compiler with the CC=
> option to be anything. There are other build tools out there that do
> much the same as ccache does (which I should have caught this as I use
> ccache on my build systems), like distcc and friends, so this might just
> want to look at the result of "CC" instead?
Yeah, it would be much more robust to just look at $(CC) directly if it
is set (i.e., running within Kbuild) vs. having a separate parser like
this. If you want to keep a fallback for standalone usage for
development and such, that's fine, but we should use the information we
have available to be as accurate as possible.
Cheers,
Nathan
>> Ick, this might get messy as you can modify the compiler with the CC= >> option to be anything. There are other build tools out there that do >> much the same as ccache does (which I should have caught this as I use >> ccache on my build systems), like distcc and friends, so this might just >> want to look at the result of "CC" instead? > > Yeah, it would be much more robust to just look at $(CC) directly if it > is set (i.e., running within Kbuild) vs. having a separate parser like > this. If you want to keep a fallback for standalone usage for > development and such, that's fine, but we should use the information we > have available to be as accurate as possible. > > Cheers, > Nathan Ok, sounds good. I will work on that. Thanks. -- Luis Augenstein * luis.augenstein@tngtech.com * +49-152-25275761 TNG Technology Consulting GmbH, Beta-Str. 13, 85774 Unterföhring Geschäftsführer: Henrik Klagges, Dr. Robert Dahlke, Thomas Endres Aufsichtsratsvorsitzender: Christoph Stock Sitz: Unterföhring * Amtsgericht München * HRB 135082
On Tue, Mar 31, 2026 at 05:30:09PM +0200, Nathan Chancellor wrote: > On Tue, Mar 31, 2026 at 07:15:35AM +0200, Greg KH wrote: > > On Mon, Mar 30, 2026 at 10:32:00PM +0200, Luis Augenstein wrote: > > > Hi Nathan, > > > > > > thanks a lot for your recommendations. > > > > > > > Does sbom-roots.txt need to be cleaned up as well? > > > > > > This file is only required to pass the roots into the python script. > > > We could also use a tmp file. Then we don't need to worry about clean > > > up. Together with your other suggested changes something like this > > > should work: > > > > > > # Script to generate .spdx.json SBOM documents describing the build > > > # > > > --------------------------------------------------------------------------- > > > > > > ifdef building_out_of_srctree > > > sbom_targets := sbom-source.spdx.json > > > endif > > > sbom_targets += sbom-build.spdx.json sbom-output.spdx.json > > > quiet_cmd_sbom = GEN $(notdir $(sbom_targets)) > > > cmd_sbom = roots_file=$$(mktemp); \ > > I think I would rather have a named file in objtree instead of one in > /tmp, as we want all output to remain in the build folder. +1 The common way in kbuild is using '$(tmp-target)'. > > > > printf "%s\n" "$(KBUILD_IMAGE)" >"$$roots_file"; \ > > > $(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/' > > > $(objtree)/modules.order >> "$$roots_file";) \ > > > $(PYTHON3) $(srctree)/scripts/sbom/sbom.py \ > > > --src-tree $(abspath $(srctree)) \ > > > --obj-tree $(abspath $(objtree)) \ > > > --roots-file "$$roots_file" \ > > > --output-directory $(abspath $(objtree)) \ > > > --generate-spdx \ > > > --package-license "GPL-2.0 WITH Linux-syscall-note" \ > > > --package-version "$(KERNELVERSION)" \ > > > --write-output-on-error; > > > rm -f "$$roots_file" > > The cmd macro uses 'set -e', so consider moving this up and making it > > trap "rm -rf $$roots_file" EXIT; \ > > like try-run in scripts/Makefile.compiler does to ensure it is always > cleaned up. hm, well. Yes, this should do as expected, but please be aware that this also kills the $(delete-on-interrupt) which is part of $(cmd) and removes $@ in case of error or interruption by installing a trap -- which will be overwritten. See also below. I think it might become a bit cleaner if the roots file is a separate target and the 'sbom' target simply depends on it. But we can defer that. > > > > PHONY += sbom > > > sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if > > > $(CONFIG_MODULES),modules modules.order) > > > $(call cmd,sbom) > > > > > > Note, I will also add the --write-output-on-error flag by default such > > > that the .spdx.json documents are generated as much as possible even if > > > some build commands are unknown to the parser. > > Seems reasonable to me. If sbom.py is unable to parse the build commands, does it exit with a non-zero exit code, correct? As 'cmd_sbom' is run within a 'set -e' shell environment, the $(delete-on-interrupt) will delete $@, thus there should be _no_ output on error, regardless of --write-output-on-error. So, it might make sense to kill $(delete-on-interrupt) by intention; but that doesn't feel good to me, as the intention of 'cmd' will be intransparently. Kind regards, Nicolas
Hi everyone,
> If sbom.py is unable to parse the build commands, does it exit with a
> non-zero exit code, correct?
yes correct. The current behavior is to always parse as much as
possible, collect all problems, print an error summary in the end and
exit with a non-zero exit code.
>> The cmd macro uses 'set -e', so consider moving this up and making it
>>
>> trap "rm -rf $$roots_file" EXIT; \
>>
>> like try-run in scripts/Makefile.compiler does to ensure it is always
>> cleaned up.
>
> hm, well. Yes, this should do as expected, but please be aware that
> this also kills the $(delete-on-interrupt) which is part of $(cmd) and
> removes $@ in case of error or interruption by installing a trap --
> which will be overwritten. See also below.
I think $(delete-on-interrupt) only operates on non-phony targets. Since
our target is $@=sbom instead of the generated .spdx.json files
$(delete-on-interrupt) currently has no effect and wouldn't really be
"killed by intention" by introducing a trap within cmd_sbom.
> there should be _no_ output on error, regardless of
--write-output-on-error.
If this is a general convention we should follow, then we maybe want to
return a zero exit code in sbom.py in case of errors when
--write-output-on-error is set, effectively treating errors as warnings?
Alternatively, we could keep not using --write-output-on-error by
default. Or we don't follow the convention and write the output
*.spdx.json files in case of errors.
I am not sure what's the most appropriate behavior here. However, we
know that people will very likely encounter cases with commands unknown
to the parser. It's not very useful to simply deny generating the entire
documents, because in many cases the sbom will still be quite usable
although less complete. For example, if the parsing error occurs close
to the leaf nodes it cuts off only a small part of the dependency graph
which still retains most of the information.
Tim Bird apparently already encountered this problem which lead him to
manually add the --write-output-on-error flag.
https://birdcloud.org/bc/Linux_Kernel_Missing_SPDX_ID_lines_from_build_SBOMs
> The cmd macro uses 'set -e', so consider moving this up and making it
>
> trap "rm -rf $$roots_file" EXIT; \
>
> like try-run in scripts/Makefile.compiler does to ensure it is always
> cleaned up.
and
> The common way in kbuild is using '$(tmp-target)'.
This would be the new version then:
# Script to generate .spdx.json SBOM documents describing the build
#
---------------------------------------------------------------------------
ifdef building_out_of_srctree
sbom_targets := sbom-source.spdx.json
endif
sbom_targets += sbom-build.spdx.json sbom-output.spdx.json
quiet_cmd_sbom = GEN $(notdir $(sbom_targets))
cmd_sbom = roots_file="$(tmp-target)"; \
trap "rm -rf $$roots_file" EXIT; \
printf "%s\n" "$(KBUILD_IMAGE)" >"$$roots_file"; \
$(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/'
$(objtree)/modules.order >> "$$roots_file";) \
$(PYTHON3) $(srctree)/scripts/sbom/sbom.py \
--src-tree $(abspath $(srctree)) \
--obj-tree $(abspath $(objtree)) \
--roots-file "$$roots_file" \
--output-directory $(abspath $(objtree)) \
--generate-spdx \
--package-license "GPL-2.0 WITH Linux-syscall-note" \
--package-version "$(KERNELVERSION)" \
--write-output-on-error;
PHONY += sbom
sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if
$(CONFIG_MODULES),modules modules.order)
$(call cmd,sbom)
thanks,
Luis
--
Luis Augenstein * luis.augenstein@tngtech.com * +49-152-25275761
TNG Technology Consulting GmbH, Beta-Str. 13, 85774 Unterföhring
Geschäftsführer: Henrik Klagges, Dr. Robert Dahlke, Thomas Endres
Aufsichtsratsvorsitzender: Christoph Stock
Sitz: Unterföhring * Amtsgericht München * HRB 135082
© 2016 - 2026 Red Hat, Inc.