1# Copyright (c) 2020, 2021 The Linux Foundation 2# 3# SPDX-License-Identifier: Apache-2.0 4 5import os 6from dataclasses import dataclass 7 8from west import log 9 10from zspdx.scanner import ScannerConfig, scanDocument 11from zspdx.version import SPDX_VERSION_2_3 12from zspdx.walker import Walker, WalkerConfig 13from zspdx.writer import writeSPDX 14 15 16# SBOMConfig contains settings that will be passed along to the various 17# SBOM maker subcomponents. 18@dataclass(eq=True) 19class SBOMConfig: 20 # prefix for Document namespaces; should not end with "/" 21 namespacePrefix: str = "" 22 23 # location of build directory 24 buildDir: str = "" 25 26 # location of SPDX document output directory 27 spdxDir: str = "" 28 29 # SPDX specification version to use 30 spdxVersion: str = SPDX_VERSION_2_3 31 32 # should also analyze for included header files? 33 analyzeIncludes: bool = False 34 35 # should also add an SPDX document for the SDK? 36 includeSDK: bool = False 37 38 39# create Cmake file-based API directories and query file 40# Arguments: 41# 1) build_dir: build directory 42def setupCmakeQuery(build_dir): 43 # check that query dir exists as a directory, or else create it 44 cmakeApiDirPath = os.path.join(build_dir, ".cmake", "api", "v1", "query") 45 if os.path.exists(cmakeApiDirPath): 46 if not os.path.isdir(cmakeApiDirPath): 47 log.err(f'cmake api query directory {cmakeApiDirPath} exists and is not a directory') 48 return False 49 # directory exists, we're good 50 else: 51 # create the directory 52 os.makedirs(cmakeApiDirPath, exist_ok=False) 53 54 # check that codemodel-v2 exists as a file, or else create it 55 queryFilePath = os.path.join(cmakeApiDirPath, "codemodel-v2") 56 if os.path.exists(queryFilePath): 57 if not os.path.isfile(queryFilePath): 58 log.err(f'cmake api query file {queryFilePath} exists and is not a directory') 59 return False 60 # file exists, we're good 61 return True 62 else: 63 # file doesn't exist, let's create an empty file 64 with open(queryFilePath, "w"): 65 pass 66 return True 67 68 69# main entry point for SBOM maker 70# Arguments: 71# 1) cfg: SBOMConfig 72def makeSPDX(cfg): 73 # report any odd configuration settings 74 if cfg.analyzeIncludes and not cfg.includeSDK: 75 log.wrn("config: requested to analyze includes but not to generate SDK SPDX document;") 76 log.wrn("config: will proceed but will discard detected includes for SDK header files") 77 78 # set up walker configuration 79 walkerCfg = WalkerConfig() 80 walkerCfg.namespacePrefix = cfg.namespacePrefix 81 walkerCfg.buildDir = cfg.buildDir 82 walkerCfg.analyzeIncludes = cfg.analyzeIncludes 83 walkerCfg.includeSDK = cfg.includeSDK 84 85 # make and run the walker 86 w = Walker(walkerCfg) 87 retval = w.makeDocuments() 88 if not retval: 89 log.err("SPDX walker failed; bailing") 90 return False 91 92 # set up scanner configuration 93 scannerCfg = ScannerConfig() 94 95 # scan each document from walker 96 if cfg.includeSDK: 97 scanDocument(scannerCfg, w.docSDK) 98 scanDocument(scannerCfg, w.docApp) 99 scanDocument(scannerCfg, w.docZephyr) 100 scanDocument(scannerCfg, w.docBuild) 101 102 # write each document, in this particular order so that the 103 # hashes for external references are calculated 104 105 # write SDK document, if we made one 106 if cfg.includeSDK: 107 retval = writeSPDX(os.path.join(cfg.spdxDir, "sdk.spdx"), w.docSDK, cfg.spdxVersion) 108 if not retval: 109 log.err("SPDX writer failed for SDK document; bailing") 110 return False 111 112 # write app document 113 retval = writeSPDX(os.path.join(cfg.spdxDir, "app.spdx"), w.docApp, cfg.spdxVersion) 114 if not retval: 115 log.err("SPDX writer failed for app document; bailing") 116 return False 117 118 # write zephyr document 119 retval = writeSPDX(os.path.join(cfg.spdxDir, "zephyr.spdx"), w.docZephyr, cfg.spdxVersion) 120 if not retval: 121 log.err("SPDX writer failed for zephyr document; bailing") 122 return False 123 124 # write build document 125 retval = writeSPDX(os.path.join(cfg.spdxDir, "build.spdx"), w.docBuild, cfg.spdxVersion) 126 if not retval: 127 log.err("SPDX writer failed for build document; bailing") 128 return False 129 130 # write modules document 131 retval = writeSPDX( 132 os.path.join(cfg.spdxDir, "modules-deps.spdx"), w.docModulesExtRefs, cfg.spdxVersion 133 ) 134 if not retval: 135 log.err("SPDX writer failed for modules-deps document; bailing") 136 return False 137 138 return True 139