11# SPDX-License-Identifier: GPL-2.0-only
22# SPDX-FileCopyrightText: 2025 TNG Technology Consulting GmbH
33
4+ from typing import Mapping
45from sbom .cmd_graph .cmd_graph import CmdGraph , iter_cmd_graph
56from sbom .path_utils import PathStr
67from sbom .spdx .build import Build
78from sbom .spdx .core import (
8- Element ,
99 SoftwareAgent ,
1010 SpdxObject ,
1111 CreationInfo ,
@@ -27,6 +27,7 @@ def build_spdx_graph(
2727 package_license : str ,
2828 build_version : str ,
2929) -> list [SpdxObject ]:
30+ # Main Skeletton
3031 spdx_document = SpdxDocument (profileConformance = ["core" , "software" , "build" , "simpleLicensing" ])
3132 agent = SoftwareAgent (name = "KernelSbom" )
3233 creation_info = CreationInfo (createdBy = [agent ])
@@ -53,16 +54,6 @@ def build_spdx_graph(
5354 from_ = output_tree_element ,
5455 to = [],
5556 )
56- src_and_output_tree_elements : list [Element ] = (
57- [
58- src_tree_element ,
59- src_tree_contains_relationship ,
60- output_tree_element ,
61- output_tree_contains_relationship ,
62- ]
63- if src_tree != output_tree
64- else []
65- )
6657
6758 # package elements
6859 package = Package (
@@ -82,29 +73,55 @@ def build_spdx_graph(
8273 to = [],
8374 )
8475
85- file_elements , build_and_relationship_elements = _build_kernel_file_graph (
86- cmd_graph , output_tree , src_tree , spdx_uri_prefix
87- )
76+ # file elements
77+ files : dict [PathStr , KernelFile ] = {
78+ node .absolute_path : build_kernel_file_element (node .absolute_path , output_tree , src_tree )
79+ for node in iter_cmd_graph (cmd_graph )
80+ }
81+ file_relationships = _build_file_relationships (cmd_graph , files , spdx_uri_prefix )
82+ file_license_identifiers = {
83+ file .license_identifier : LicenseExpression (simplelicensing_licenseExpression = file .license_identifier )
84+ for file in files .values ()
85+ if file .license_identifier is not None
86+ }
87+ file_license_relationships = [
88+ Relationship (
89+ relationshipType = "hasDeclaredLicense" ,
90+ from_ = file ,
91+ to = [file_license_identifiers [file .license_identifier ]],
92+ )
93+ for file in files .values ()
94+ if file .license_identifier is not None
95+ ]
8896
8997 # update relationships
9098 spdx_document .rootElement = [sbom ]
9199
92100 sbom .rootElement = [package ]
93101 sbom .element = [
94- * src_and_output_tree_elements ,
95102 package ,
96103 package_license_expression ,
97104 package_hasDeclaredLicense_relationship ,
98105 package_contains_roots_relationship ,
99- * file_elements ,
100- * build_and_relationship_elements ,
106+ * files .values (),
107+ * file_relationships ,
108+ * file_license_identifiers .values (),
109+ * file_license_relationships ,
101110 ]
102111
103- src_tree_contains_relationship .to = [file for file in file_elements if file .tree == "src_tree" ]
104- output_tree_contains_relationship .to = [file for file in file_elements if file .tree == "output_tree" ]
105-
106112 root_paths = {node .absolute_path for node in cmd_graph .roots }
107- package_contains_roots_relationship .to = [file for file in file_elements if file .absolute_path in root_paths ]
113+ package_contains_roots_relationship .to = [file for file in files .values () if file .absolute_path in root_paths ]
114+
115+ if src_tree != output_tree :
116+ sbom .element = [
117+ src_tree_element ,
118+ src_tree_contains_relationship ,
119+ output_tree_element ,
120+ output_tree_contains_relationship ,
121+ * sbom .element ,
122+ ]
123+ src_tree_contains_relationship .to = [file for file in files .values () if file .tree == "src_tree" ]
124+ output_tree_contains_relationship .to = [file for file in files .values () if file .tree == "output_tree" ]
108125
109126 return [
110127 spdx_document ,
@@ -115,19 +132,10 @@ def build_spdx_graph(
115132 ]
116133
117134
118- def _build_kernel_file_graph (
119- cmd_graph : CmdGraph ,
120- output_tree : PathStr ,
121- src_tree : PathStr ,
122- spdx_uri_prefix : str ,
123- ) -> tuple [list [KernelFile ], list [Build | Relationship ]]:
124- # First cmd graph traversal: create a file element for each node
125- file_elements : dict [PathStr , KernelFile ] = {
126- node .absolute_path : build_kernel_file_element (node .absolute_path , output_tree , src_tree )
127- for node in iter_cmd_graph (cmd_graph )
128- }
129-
130- # Second cmd graph traversal: create a relationship for each (child)node representing the generation of all its parents
135+ def _build_file_relationships (
136+ cmd_graph : CmdGraph , file_elements : Mapping [PathStr , File ], spdx_uri_prefix : str
137+ ) -> list [Build | Relationship ]:
138+ # create a relationship between each node (output file) and its children (input files)
131139 build_and_relationship_elements : list [Build | Relationship ] = []
132140 for node in iter_cmd_graph (cmd_graph ):
133141 if len (node .children ) == 0 :
@@ -152,4 +160,4 @@ def _build_kernel_file_graph(
152160 )
153161 build_and_relationship_elements += [build_element , hasInput_relationship , hasOutput_relationship ]
154162
155- return list ( file_elements . values ()), build_and_relationship_elements
163+ return build_and_relationship_elements
0 commit comments