zephyr/scripts/west_commands/zspdx/sbom.py

125 lines
4.0 KiB
Python

# 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