mirror of
https://git.yoctoproject.org/poky
synced 2026-09-14 03:49:32 +02:00
classes/create-spdx: Backport
Backports the create-spdx classes from the latest versions on master. This backport is a simple copy with no modifications, as its too complex to cherry-pick all the corresponding changes. This will give an appropriate base commit for subsequent changes and if necessary additional backport cherry-picks from master in the future. (From OE-Core rev: 48fbddf32ffa3ec44a788f42895c1730a84b5a91) Signed-off-by: Joshua Watt <JPEWhacker@gmail.com> Signed-off-by: Steve Sakoman <steve@sakoman.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
committed by
Richard Purdie
parent
0ce97636c9
commit
7e24067f78
1069
meta/classes/create-spdx-2.2.bbclass
Normal file
1069
meta/classes/create-spdx-2.2.bbclass
Normal file
File diff suppressed because it is too large
Load Diff
8
meta/classes/create-spdx.bbclass
Normal file
8
meta/classes/create-spdx.bbclass
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
# Copyright OpenEmbedded Contributors
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
#
|
||||||
|
# Include this class when you don't care what version of SPDX you get; it will
|
||||||
|
# be updated to the latest stable version that is supported
|
||||||
|
inherit create-spdx-2.2
|
||||||
5937
meta/files/spdx-licenses.json
Normal file
5937
meta/files/spdx-licenses.json
Normal file
File diff suppressed because it is too large
Load Diff
84
meta/lib/oe/sbom.py
Normal file
84
meta/lib/oe/sbom.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#
|
||||||
|
# Copyright OpenEmbedded Contributors
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
#
|
||||||
|
|
||||||
|
import collections
|
||||||
|
|
||||||
|
DepRecipe = collections.namedtuple("DepRecipe", ("doc", "doc_sha1", "recipe"))
|
||||||
|
DepSource = collections.namedtuple("DepSource", ("doc", "doc_sha1", "recipe", "file"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_recipe_spdxid(d):
|
||||||
|
return "SPDXRef-%s-%s" % ("Recipe", d.getVar("PN"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_download_spdxid(d, idx):
|
||||||
|
return "SPDXRef-Download-%s-%d" % (d.getVar("PN"), idx)
|
||||||
|
|
||||||
|
|
||||||
|
def get_package_spdxid(pkg):
|
||||||
|
return "SPDXRef-Package-%s" % pkg
|
||||||
|
|
||||||
|
|
||||||
|
def get_source_file_spdxid(d, idx):
|
||||||
|
return "SPDXRef-SourceFile-%s-%d" % (d.getVar("PN"), idx)
|
||||||
|
|
||||||
|
|
||||||
|
def get_packaged_file_spdxid(pkg, idx):
|
||||||
|
return "SPDXRef-PackagedFile-%s-%d" % (pkg, idx)
|
||||||
|
|
||||||
|
|
||||||
|
def get_image_spdxid(img):
|
||||||
|
return "SPDXRef-Image-%s" % img
|
||||||
|
|
||||||
|
|
||||||
|
def get_sdk_spdxid(sdk):
|
||||||
|
return "SPDXRef-SDK-%s" % sdk
|
||||||
|
|
||||||
|
|
||||||
|
def write_doc(d, spdx_doc, subdir, spdx_deploy=None, indent=None):
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
if spdx_deploy is None:
|
||||||
|
spdx_deploy = Path(d.getVar("SPDXDEPLOY"))
|
||||||
|
|
||||||
|
dest = spdx_deploy / subdir / (spdx_doc.name + ".spdx.json")
|
||||||
|
dest.parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
with dest.open("wb") as f:
|
||||||
|
doc_sha1 = spdx_doc.to_json(f, sort_keys=True, indent=indent)
|
||||||
|
|
||||||
|
l = spdx_deploy / "by-namespace" / spdx_doc.documentNamespace.replace("/", "_")
|
||||||
|
l.parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
l.symlink_to(os.path.relpath(dest, l.parent))
|
||||||
|
|
||||||
|
return doc_sha1
|
||||||
|
|
||||||
|
|
||||||
|
def read_doc(fn):
|
||||||
|
import hashlib
|
||||||
|
import oe.spdx
|
||||||
|
import io
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def get_file():
|
||||||
|
if isinstance(fn, io.IOBase):
|
||||||
|
yield fn
|
||||||
|
else:
|
||||||
|
with fn.open("rb") as f:
|
||||||
|
yield f
|
||||||
|
|
||||||
|
with get_file() as f:
|
||||||
|
sha1 = hashlib.sha1()
|
||||||
|
while True:
|
||||||
|
chunk = f.read(4096)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
sha1.update(chunk)
|
||||||
|
|
||||||
|
f.seek(0)
|
||||||
|
doc = oe.spdx.SPDXDocument.from_json(f)
|
||||||
|
|
||||||
|
return (doc, sha1.hexdigest())
|
||||||
357
meta/lib/oe/spdx.py
Normal file
357
meta/lib/oe/spdx.py
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
#
|
||||||
|
# Copyright OpenEmbedded Contributors
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This library is intended to capture the JSON SPDX specification in a type
|
||||||
|
# safe manner. It is not intended to encode any particular OE specific
|
||||||
|
# behaviors, see the sbom.py for that.
|
||||||
|
#
|
||||||
|
# The documented SPDX spec document doesn't cover the JSON syntax for
|
||||||
|
# particular configuration, which can make it hard to determine what the JSON
|
||||||
|
# syntax should be. I've found it is actually much simpler to read the official
|
||||||
|
# SPDX JSON schema which can be found here: https://github.com/spdx/spdx-spec
|
||||||
|
# in schemas/spdx-schema.json
|
||||||
|
#
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import itertools
|
||||||
|
import json
|
||||||
|
|
||||||
|
SPDX_VERSION = "2.2"
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# The following are the support classes that are used to implement SPDX object
|
||||||
|
#
|
||||||
|
|
||||||
|
class _Property(object):
|
||||||
|
"""
|
||||||
|
A generic SPDX object property. The different types will derive from this
|
||||||
|
class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *, default=None):
|
||||||
|
self.default = default
|
||||||
|
|
||||||
|
def setdefault(self, dest, name):
|
||||||
|
if self.default is not None:
|
||||||
|
dest.setdefault(name, self.default)
|
||||||
|
|
||||||
|
|
||||||
|
class _String(_Property):
|
||||||
|
"""
|
||||||
|
A scalar string property for an SPDX object
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
def set_property(self, attrs, name):
|
||||||
|
def get_helper(obj):
|
||||||
|
return obj._spdx[name]
|
||||||
|
|
||||||
|
def set_helper(obj, value):
|
||||||
|
obj._spdx[name] = value
|
||||||
|
|
||||||
|
def del_helper(obj):
|
||||||
|
del obj._spdx[name]
|
||||||
|
|
||||||
|
attrs[name] = property(get_helper, set_helper, del_helper)
|
||||||
|
|
||||||
|
def init(self, source):
|
||||||
|
return source
|
||||||
|
|
||||||
|
|
||||||
|
class _Object(_Property):
|
||||||
|
"""
|
||||||
|
A scalar SPDX object property of a SPDX object
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cls, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.cls = cls
|
||||||
|
|
||||||
|
def set_property(self, attrs, name):
|
||||||
|
def get_helper(obj):
|
||||||
|
if not name in obj._spdx:
|
||||||
|
obj._spdx[name] = self.cls()
|
||||||
|
return obj._spdx[name]
|
||||||
|
|
||||||
|
def set_helper(obj, value):
|
||||||
|
obj._spdx[name] = value
|
||||||
|
|
||||||
|
def del_helper(obj):
|
||||||
|
del obj._spdx[name]
|
||||||
|
|
||||||
|
attrs[name] = property(get_helper, set_helper)
|
||||||
|
|
||||||
|
def init(self, source):
|
||||||
|
return self.cls(**source)
|
||||||
|
|
||||||
|
|
||||||
|
class _ListProperty(_Property):
|
||||||
|
"""
|
||||||
|
A list of SPDX properties
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, prop, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.prop = prop
|
||||||
|
|
||||||
|
def set_property(self, attrs, name):
|
||||||
|
def get_helper(obj):
|
||||||
|
if not name in obj._spdx:
|
||||||
|
obj._spdx[name] = []
|
||||||
|
return obj._spdx[name]
|
||||||
|
|
||||||
|
def set_helper(obj, value):
|
||||||
|
obj._spdx[name] = list(value)
|
||||||
|
|
||||||
|
def del_helper(obj):
|
||||||
|
del obj._spdx[name]
|
||||||
|
|
||||||
|
attrs[name] = property(get_helper, set_helper, del_helper)
|
||||||
|
|
||||||
|
def init(self, source):
|
||||||
|
return [self.prop.init(o) for o in source]
|
||||||
|
|
||||||
|
|
||||||
|
class _StringList(_ListProperty):
|
||||||
|
"""
|
||||||
|
A list of strings as a property for an SPDX object
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(_String(), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class _ObjectList(_ListProperty):
|
||||||
|
"""
|
||||||
|
A list of SPDX objects as a property for an SPDX object
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cls, **kwargs):
|
||||||
|
super().__init__(_Object(cls), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MetaSPDXObject(type):
|
||||||
|
"""
|
||||||
|
A metaclass that allows properties (anything derived from a _Property
|
||||||
|
class) to be defined for a SPDX object
|
||||||
|
"""
|
||||||
|
def __new__(mcls, name, bases, attrs):
|
||||||
|
attrs["_properties"] = {}
|
||||||
|
|
||||||
|
for key in attrs.keys():
|
||||||
|
if isinstance(attrs[key], _Property):
|
||||||
|
prop = attrs[key]
|
||||||
|
attrs["_properties"][key] = prop
|
||||||
|
prop.set_property(attrs, key)
|
||||||
|
|
||||||
|
return super().__new__(mcls, name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXObject(metaclass=MetaSPDXObject):
|
||||||
|
"""
|
||||||
|
The base SPDX object; all SPDX spec classes must derive from this class
|
||||||
|
"""
|
||||||
|
def __init__(self, **d):
|
||||||
|
self._spdx = {}
|
||||||
|
|
||||||
|
for name, prop in self._properties.items():
|
||||||
|
prop.setdefault(self._spdx, name)
|
||||||
|
if name in d:
|
||||||
|
self._spdx[name] = prop.init(d[name])
|
||||||
|
|
||||||
|
def serializer(self):
|
||||||
|
return self._spdx
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
if name in self._properties or name == "_spdx":
|
||||||
|
super().__setattr__(name, value)
|
||||||
|
return
|
||||||
|
raise KeyError("%r is not a valid SPDX property" % name)
|
||||||
|
|
||||||
|
#
|
||||||
|
# These are the SPDX objects implemented from the spec. The *only* properties
|
||||||
|
# that can be added to these objects are ones directly specified in the SPDX
|
||||||
|
# spec, however you may add helper functions to make operations easier.
|
||||||
|
#
|
||||||
|
# Defaults should *only* be specified if the SPDX spec says there is a certain
|
||||||
|
# required value for a field (e.g. dataLicense), or if the field is mandatory
|
||||||
|
# and has some sane "this field is unknown" (e.g. "NOASSERTION")
|
||||||
|
#
|
||||||
|
|
||||||
|
class SPDXAnnotation(SPDXObject):
|
||||||
|
annotationDate = _String()
|
||||||
|
annotationType = _String()
|
||||||
|
annotator = _String()
|
||||||
|
comment = _String()
|
||||||
|
|
||||||
|
class SPDXChecksum(SPDXObject):
|
||||||
|
algorithm = _String()
|
||||||
|
checksumValue = _String()
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXRelationship(SPDXObject):
|
||||||
|
spdxElementId = _String()
|
||||||
|
relatedSpdxElement = _String()
|
||||||
|
relationshipType = _String()
|
||||||
|
comment = _String()
|
||||||
|
annotations = _ObjectList(SPDXAnnotation)
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXExternalReference(SPDXObject):
|
||||||
|
referenceCategory = _String()
|
||||||
|
referenceType = _String()
|
||||||
|
referenceLocator = _String()
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXPackageVerificationCode(SPDXObject):
|
||||||
|
packageVerificationCodeValue = _String()
|
||||||
|
packageVerificationCodeExcludedFiles = _StringList()
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXPackage(SPDXObject):
|
||||||
|
ALLOWED_CHECKSUMS = [
|
||||||
|
"SHA1",
|
||||||
|
"SHA224",
|
||||||
|
"SHA256",
|
||||||
|
"SHA384",
|
||||||
|
"SHA512",
|
||||||
|
"MD2",
|
||||||
|
"MD4",
|
||||||
|
"MD5",
|
||||||
|
"MD6",
|
||||||
|
]
|
||||||
|
|
||||||
|
name = _String()
|
||||||
|
SPDXID = _String()
|
||||||
|
versionInfo = _String()
|
||||||
|
downloadLocation = _String(default="NOASSERTION")
|
||||||
|
supplier = _String(default="NOASSERTION")
|
||||||
|
homepage = _String()
|
||||||
|
licenseConcluded = _String(default="NOASSERTION")
|
||||||
|
licenseDeclared = _String(default="NOASSERTION")
|
||||||
|
summary = _String()
|
||||||
|
description = _String()
|
||||||
|
sourceInfo = _String()
|
||||||
|
copyrightText = _String(default="NOASSERTION")
|
||||||
|
licenseInfoFromFiles = _StringList(default=["NOASSERTION"])
|
||||||
|
externalRefs = _ObjectList(SPDXExternalReference)
|
||||||
|
packageVerificationCode = _Object(SPDXPackageVerificationCode)
|
||||||
|
hasFiles = _StringList()
|
||||||
|
packageFileName = _String()
|
||||||
|
annotations = _ObjectList(SPDXAnnotation)
|
||||||
|
checksums = _ObjectList(SPDXChecksum)
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXFile(SPDXObject):
|
||||||
|
SPDXID = _String()
|
||||||
|
fileName = _String()
|
||||||
|
licenseConcluded = _String(default="NOASSERTION")
|
||||||
|
copyrightText = _String(default="NOASSERTION")
|
||||||
|
licenseInfoInFiles = _StringList(default=["NOASSERTION"])
|
||||||
|
checksums = _ObjectList(SPDXChecksum)
|
||||||
|
fileTypes = _StringList()
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXCreationInfo(SPDXObject):
|
||||||
|
created = _String()
|
||||||
|
licenseListVersion = _String()
|
||||||
|
comment = _String()
|
||||||
|
creators = _StringList()
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXExternalDocumentRef(SPDXObject):
|
||||||
|
externalDocumentId = _String()
|
||||||
|
spdxDocument = _String()
|
||||||
|
checksum = _Object(SPDXChecksum)
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXExtractedLicensingInfo(SPDXObject):
|
||||||
|
name = _String()
|
||||||
|
comment = _String()
|
||||||
|
licenseId = _String()
|
||||||
|
extractedText = _String()
|
||||||
|
|
||||||
|
|
||||||
|
class SPDXDocument(SPDXObject):
|
||||||
|
spdxVersion = _String(default="SPDX-" + SPDX_VERSION)
|
||||||
|
dataLicense = _String(default="CC0-1.0")
|
||||||
|
SPDXID = _String(default="SPDXRef-DOCUMENT")
|
||||||
|
name = _String()
|
||||||
|
documentNamespace = _String()
|
||||||
|
creationInfo = _Object(SPDXCreationInfo)
|
||||||
|
packages = _ObjectList(SPDXPackage)
|
||||||
|
files = _ObjectList(SPDXFile)
|
||||||
|
relationships = _ObjectList(SPDXRelationship)
|
||||||
|
externalDocumentRefs = _ObjectList(SPDXExternalDocumentRef)
|
||||||
|
hasExtractedLicensingInfos = _ObjectList(SPDXExtractedLicensingInfo)
|
||||||
|
|
||||||
|
def __init__(self, **d):
|
||||||
|
super().__init__(**d)
|
||||||
|
|
||||||
|
def to_json(self, f, *, sort_keys=False, indent=None, separators=None):
|
||||||
|
class Encoder(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
if isinstance(o, SPDXObject):
|
||||||
|
return o.serializer()
|
||||||
|
|
||||||
|
return super().default(o)
|
||||||
|
|
||||||
|
sha1 = hashlib.sha1()
|
||||||
|
for chunk in Encoder(
|
||||||
|
sort_keys=sort_keys,
|
||||||
|
indent=indent,
|
||||||
|
separators=separators,
|
||||||
|
).iterencode(self):
|
||||||
|
chunk = chunk.encode("utf-8")
|
||||||
|
f.write(chunk)
|
||||||
|
sha1.update(chunk)
|
||||||
|
|
||||||
|
return sha1.hexdigest()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_json(cls, f):
|
||||||
|
return cls(**json.load(f))
|
||||||
|
|
||||||
|
def add_relationship(self, _from, relationship, _to, *, comment=None, annotation=None):
|
||||||
|
if isinstance(_from, SPDXObject):
|
||||||
|
from_spdxid = _from.SPDXID
|
||||||
|
else:
|
||||||
|
from_spdxid = _from
|
||||||
|
|
||||||
|
if isinstance(_to, SPDXObject):
|
||||||
|
to_spdxid = _to.SPDXID
|
||||||
|
else:
|
||||||
|
to_spdxid = _to
|
||||||
|
|
||||||
|
r = SPDXRelationship(
|
||||||
|
spdxElementId=from_spdxid,
|
||||||
|
relatedSpdxElement=to_spdxid,
|
||||||
|
relationshipType=relationship,
|
||||||
|
)
|
||||||
|
|
||||||
|
if comment is not None:
|
||||||
|
r.comment = comment
|
||||||
|
|
||||||
|
if annotation is not None:
|
||||||
|
r.annotations.append(annotation)
|
||||||
|
|
||||||
|
self.relationships.append(r)
|
||||||
|
|
||||||
|
def find_by_spdxid(self, spdxid):
|
||||||
|
for o in itertools.chain(self.packages, self.files):
|
||||||
|
if o.SPDXID == spdxid:
|
||||||
|
return o
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_external_document_ref(self, namespace):
|
||||||
|
for r in self.externalDocumentRefs:
|
||||||
|
if r.spdxDocument == namespace:
|
||||||
|
return r
|
||||||
|
return None
|
||||||
Reference in New Issue
Block a user