1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0-only OR MIT 3# Copyright (C) 2025 TNG Technology Consulting GmbH 4 5""" 6Compute software bill of materials in SPDX format describing a kernel build. 7""" 8 9import logging 10import os 11import sys 12import time 13import sbom.sbom_logging as sbom_logging 14from sbom.config import get_config 15from sbom.path_utils import is_relative_to 16from sbom.cmd_graph import CmdGraph 17 18 19def _exit_with_summary(write_output_on_error: bool = False) -> None: 20 warning_summary = sbom_logging.summarize_warnings() 21 error_summary = sbom_logging.summarize_errors() 22 if warning_summary: 23 logging.warning(warning_summary) 24 if error_summary: 25 logging.error(error_summary) 26 if not write_output_on_error: 27 logging.info( 28 "Use --write-output-on-error to generate output documents even when errors occur. " 29 "Note that in this case the generated documents may be incomplete." 30 ) 31 sys.exit(1) 32 33 34def main(): 35 # Read config 36 config = get_config() 37 38 # Configure logging 39 logging.basicConfig( 40 level=logging.DEBUG if config.debug else logging.INFO, 41 format="[%(levelname)s] %(message)s", 42 ) 43 44 # Build cmd graph 45 logging.debug("Start building cmd graph") 46 start_time = time.time() 47 cmd_graph = CmdGraph.create(config.root_paths, config) 48 logging.debug(f"Built cmd graph in {time.time() - start_time} seconds") 49 50 # Save used files document 51 if config.generate_used_files: 52 if config.src_tree == config.obj_tree: 53 logging.info( 54 f"Extracting all files from the cmd graph to {config.used_files_file_name} " 55 "instead of only source files because source files cannot be " 56 "reliably classified when the source and object trees are identical.", 57 ) 58 used_files = [os.path.relpath(node.absolute_path, config.src_tree) for node in cmd_graph] 59 logging.debug(f"Found {len(used_files)} files in cmd graph.") 60 else: 61 used_files = [ 62 os.path.relpath(node.absolute_path, config.src_tree) 63 for node in cmd_graph 64 if is_relative_to(node.absolute_path, config.src_tree) 65 and not is_relative_to(node.absolute_path, config.obj_tree) 66 ] 67 logging.debug(f"Found {len(used_files)} source files in cmd graph") 68 if not sbom_logging.has_errors() or config.write_output_on_error: 69 used_files_path = os.path.join(config.output_directory, config.used_files_file_name) 70 with open(used_files_path, "w", encoding="utf-8") as f: 71 f.write("\n".join(str(file_path) for file_path in used_files)) 72 logging.debug(f"Successfully saved {used_files_path}") 73 74 _exit_with_summary(config.write_output_on_error) 75 76 77# Call main method 78if __name__ == "__main__": 79 main() 80