west: spdx: introduce support for SPDX 2.3

Minor update to existing zspdx implementation to add support for
PrimaryPackagePurpose introduced in SPDX 2.3.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
This commit is contained in:
Benjamin Cabé 2024-03-21 21:58:48 +01:00 committed by Carles Cufí
commit 9ebf341977
5 changed files with 23 additions and 11 deletions

View file

@ -74,7 +74,7 @@ See :zephyr_file:`share/zephyr-package/cmake` for details.
Software bill of materials: ``west spdx`` Software bill of materials: ``west spdx``
***************************************** *****************************************
This command generates SPDX 2.2 tag-value documents, creating relationships This command generates SPDX 2.3 tag-value documents, creating relationships
from source files to the corresponding generated build files. from source files to the corresponding generated build files.
``SPDX-License-Identifier`` comments in source files are scanned and filled ``SPDX-License-Identifier`` comments in source files are scanned and filled
into the SPDX documents. into the SPDX documents.

View file

@ -68,6 +68,9 @@ class PackageConfig:
# SPDX ID, including "SPDXRef-" # SPDX ID, including "SPDXRef-"
self.spdxID = "" self.spdxID = ""
# primary package purpose (ex. "LIBRARY", "APPLICATION", etc.)
self.primaryPurpose = ""
# the Package's declared license # the Package's declared license
self.declaredLicense = "NOASSERTION" self.declaredLicense = "NOASSERTION"
@ -95,7 +98,7 @@ class Package:
# Document that owns this Package # Document that owns this Package
self.doc = doc self.doc = doc
# verification code, calculated per section 3.9 of SPDX spec v2.2 # verification code, calculated per section 7.9 of SPDX spec v2.3
self.verificationCode = "" self.verificationCode = ""
# concluded license for this Package, if # concluded license for this Package, if
@ -161,7 +164,7 @@ class RelationshipData:
self.otherPackageID = "" self.otherPackageID = ""
# text string with Relationship type # text string with Relationship type
# from table in section 7.1 of SPDX spec v2.2 # from table 68 in section 11.1 of SPDX spec v2.3
self.rlnType = "" self.rlnType = ""
# Relationship contains the post-analysis, processed data about a relationship # Relationship contains the post-analysis, processed data about a relationship
@ -180,7 +183,7 @@ class Relationship:
self.refB = "" self.refB = ""
# text string with Relationship type # text string with Relationship type
# from table in section 7.1 of SPDX spec v2.2 # from table 68 in section 11.1 of SPDX spec v2.3
self.rlnType = "" self.rlnType = ""
# File contains the data needed to create a File element in the context of a # File contains the data needed to create a File element in the context of a

View file

@ -30,7 +30,7 @@ class ScannerConfig:
self.numLinesScanned = 20 self.numLinesScanned = 20
# should we calculate SHA256 hashes for each Package's Files? # should we calculate SHA256 hashes for each Package's Files?
# note that SHA1 hashes are mandatory, per SPDX 2.2 # note that SHA1 hashes are mandatory, per SPDX 2.3
self.doSHA256 = True self.doSHA256 = True
# should we calculate MD5 hashes for each Package's Files? # should we calculate MD5 hashes for each Package's Files?

View file

@ -157,6 +157,7 @@ class Walker:
cfgPackageApp = PackageConfig() cfgPackageApp = PackageConfig()
cfgPackageApp.name = "app-sources" cfgPackageApp.name = "app-sources"
cfgPackageApp.spdxID = "SPDXRef-app-sources" cfgPackageApp.spdxID = "SPDXRef-app-sources"
cfgPackageApp.primaryPurpose = "SOURCE"
# relativeBaseDir is app sources dir # relativeBaseDir is app sources dir
cfgPackageApp.relativeBaseDir = self.cm.paths_source cfgPackageApp.relativeBaseDir = self.cm.paths_source
pkgApp = Package(cfgPackageApp, self.docApp) pkgApp = Package(cfgPackageApp, self.docApp)
@ -235,6 +236,7 @@ class Walker:
cfgPackageZephyrModule.name = module_name cfgPackageZephyrModule.name = module_name
cfgPackageZephyrModule.spdxID = "SPDXRef-" + module_name + "-sources" cfgPackageZephyrModule.spdxID = "SPDXRef-" + module_name + "-sources"
cfgPackageZephyrModule.relativeBaseDir = module_path cfgPackageZephyrModule.relativeBaseDir = module_path
cfgPackageZephyrModule.primaryPurpose = "SOURCE"
pkgZephyrModule = Package(cfgPackageZephyrModule, self.docZephyr) pkgZephyrModule = Package(cfgPackageZephyrModule, self.docZephyr)
self.docZephyr.pkgs[pkgZephyrModule.cfg.spdxID] = pkgZephyrModule self.docZephyr.pkgs[pkgZephyrModule.cfg.spdxID] = pkgZephyrModule
@ -313,6 +315,10 @@ class Walker:
if len(cfgTarget.target.artifacts) > 0: if len(cfgTarget.target.artifacts) > 0:
# add its build file # add its build file
bf = self.addBuildFile(cfgTarget, pkg) bf = self.addBuildFile(cfgTarget, pkg)
if pkg.cfg.name == "zephyr_final":
pkg.cfg.primaryPurpose = "APPLICATION"
else:
pkg.cfg.primaryPurpose = "LIBRARY"
# get its source files if build file is found # get its source files if build file is found
if bf: if bf:

View file

@ -8,14 +8,14 @@ from west import log
from zspdx.util import getHashes from zspdx.util import getHashes
# Output tag-value SPDX 2.2 content for the given Relationship object. # Output tag-value SPDX 2.3 content for the given Relationship object.
# Arguments: # Arguments:
# 1) f: file handle for SPDX document # 1) f: file handle for SPDX document
# 2) rln: Relationship object being described # 2) rln: Relationship object being described
def writeRelationshipSPDX(f, rln): def writeRelationshipSPDX(f, rln):
f.write(f"Relationship: {rln.refA} {rln.rlnType} {rln.refB}\n") f.write(f"Relationship: {rln.refA} {rln.rlnType} {rln.refB}\n")
# Output tag-value SPDX 2.2 content for the given File object. # Output tag-value SPDX 2.3 content for the given File object.
# Arguments: # Arguments:
# 1) f: file handle for SPDX document # 1) f: file handle for SPDX document
# 2) bf: File object being described # 2) bf: File object being described
@ -42,7 +42,7 @@ FileChecksum: SHA1: {bf.sha1}
writeRelationshipSPDX(f, rln) writeRelationshipSPDX(f, rln)
f.write("\n") f.write("\n")
# Output tag-value SPDX 2.2 content for the given Package object. # Output tag-value SPDX 2.3 content for the given Package object.
# Arguments: # Arguments:
# 1) f: file handle for SPDX document # 1) f: file handle for SPDX document
# 2) pkg: Package object being described # 2) pkg: Package object being described
@ -58,6 +58,9 @@ PackageLicenseConcluded: {pkg.concludedLicense}
PackageCopyrightText: {pkg.cfg.copyrightText} PackageCopyrightText: {pkg.cfg.copyrightText}
""") """)
if pkg.cfg.primaryPurpose != "":
f.write(f"PrimaryPackagePurpose: {pkg.cfg.primaryPurpose}\n")
# flag whether files analyzed / any files present # flag whether files analyzed / any files present
if len(pkg.files) > 0: if len(pkg.files) > 0:
if len(pkg.licenseInfoFromFiles) > 0: if len(pkg.licenseInfoFromFiles) > 0:
@ -82,7 +85,7 @@ PackageCopyrightText: {pkg.cfg.copyrightText}
for bf in bfs: for bf in bfs:
writeFileSPDX(f, bf) writeFileSPDX(f, bf)
# Output tag-value SPDX 2.2 content for a custom license. # Output tag-value SPDX 2.3 content for a custom license.
# Arguments: # Arguments:
# 1) f: file handle for SPDX document # 1) f: file handle for SPDX document
# 2) lic: custom license ID being described # 2) lic: custom license ID being described
@ -93,12 +96,12 @@ LicenseName: {lic}
LicenseComment: Corresponds to the license ID `{lic}` detected in an SPDX-License-Identifier: tag. LicenseComment: Corresponds to the license ID `{lic}` detected in an SPDX-License-Identifier: tag.
""") """)
# Output tag-value SPDX 2.2 content for the given Document object. # Output tag-value SPDX 2.3 content for the given Document object.
# Arguments: # Arguments:
# 1) f: file handle for SPDX document # 1) f: file handle for SPDX document
# 2) doc: Document object being described # 2) doc: Document object being described
def writeDocumentSPDX(f, doc): def writeDocumentSPDX(f, doc):
f.write(f"""SPDXVersion: SPDX-2.2 f.write(f"""SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0 DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT SPDXID: SPDXRef-DOCUMENT
DocumentName: {doc.cfg.name} DocumentName: {doc.cfg.name}