| # Copyright (c) 2020, 2021 The Linux Foundation |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import os |
| |
| from west import log |
| |
| from zspdx.walker import WalkerConfig, Walker |
| from zspdx.scanner import ScannerConfig, scanDocument |
| from zspdx.writer import writeSPDX |
| |
| # SBOMConfig contains settings that will be passed along to the various |
| # SBOM maker subcomponents. |
| class SBOMConfig: |
| def __init__(self): |
| super(SBOMConfig, self).__init__() |
| |
| # prefix for Document namespaces; should not end with "/" |
| self.namespacePrefix = "" |
| |
| # location of build directory |
| self.buildDir = "" |
| |
| # location of SPDX document output directory |
| self.spdxDir = "" |
| |
| # should also analyze for included header files? |
| self.analyzeIncludes = False |
| |
| # should also add an SPDX document for the SDK? |
| self.includeSDK = False |
| |
| # create Cmake file-based API directories and query file |
| # Arguments: |
| # 1) build_dir: build directory |
| def setupCmakeQuery(build_dir): |
| # check that query dir exists as a directory, or else create it |
| cmakeApiDirPath = os.path.join(build_dir, ".cmake", "api", "v1", "query") |
| if os.path.exists(cmakeApiDirPath): |
| if not os.path.isdir(cmakeApiDirPath): |
| log.err(f'cmake api query directory {cmakeApiDirPath} exists and is not a directory') |
| return False |
| # directory exists, we're good |
| else: |
| # create the directory |
| os.makedirs(cmakeApiDirPath, exist_ok=False) |
| |
| # check that codemodel-v2 exists as a file, or else create it |
| queryFilePath = os.path.join(cmakeApiDirPath, "codemodel-v2") |
| if os.path.exists(queryFilePath): |
| if not os.path.isfile(queryFilePath): |
| log.err(f'cmake api query file {queryFilePath} exists and is not a directory') |
| return False |
| # file exists, we're good |
| return True |
| else: |
| # file doesn't exist, let's create an empty file |
| cm_fd = open(queryFilePath, "w") |
| cm_fd.close() |
| return True |
| |
| # main entry point for SBOM maker |
| # Arguments: |
| # 1) cfg: SBOMConfig |
| def makeSPDX(cfg): |
| # report any odd configuration settings |
| if cfg.analyzeIncludes and not cfg.includeSDK: |
| log.wrn(f"config: requested to analyze includes but not to generate SDK SPDX document;") |
| log.wrn(f"config: will proceed but will discard detected includes for SDK header files") |
| |
| # set up walker configuration |
| walkerCfg = WalkerConfig() |
| walkerCfg.namespacePrefix = cfg.namespacePrefix |
| walkerCfg.buildDir = cfg.buildDir |
| walkerCfg.analyzeIncludes = cfg.analyzeIncludes |
| walkerCfg.includeSDK = cfg.includeSDK |
| |
| # make and run the walker |
| w = Walker(walkerCfg) |
| retval = w.makeDocuments() |
| if not retval: |
| log.err("SPDX walker failed; bailing") |
| return False |
| |
| # set up scanner configuration |
| scannerCfg = ScannerConfig() |
| |
| # scan each document from walker |
| if cfg.includeSDK: |
| scanDocument(scannerCfg, w.docSDK) |
| scanDocument(scannerCfg, w.docApp) |
| scanDocument(scannerCfg, w.docZephyr) |
| scanDocument(scannerCfg, w.docBuild) |
| |
| # write each document, in this particular order so that the |
| # hashes for external references are calculated |
| |
| # write SDK document, if we made one |
| if cfg.includeSDK: |
| retval = writeSPDX(os.path.join(cfg.spdxDir, "sdk.spdx"), w.docSDK) |
| if not retval: |
| log.err("SPDX writer failed for SDK document; bailing") |
| return False |
| |
| # write app document |
| retval = writeSPDX(os.path.join(cfg.spdxDir, "app.spdx"), w.docApp) |
| if not retval: |
| log.err("SPDX writer failed for app document; bailing") |
| return False |
| |
| # write zephyr document |
| writeSPDX(os.path.join(cfg.spdxDir, "zephyr.spdx"), w.docZephyr) |
| if not retval: |
| log.err("SPDX writer failed for zephyr document; bailing") |
| return False |
| |
| # write build document |
| writeSPDX(os.path.join(cfg.spdxDir, "build.spdx"), w.docBuild) |
| if not retval: |
| log.err("SPDX writer failed for build document; bailing") |
| return False |
| |
| return True |