xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oe/spdx.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun#
6*4882a593Smuzhiyun# This library is intended to capture the JSON SPDX specification in a type
7*4882a593Smuzhiyun# safe manner. It is not intended to encode any particular OE specific
8*4882a593Smuzhiyun# behaviors, see the sbom.py for that.
9*4882a593Smuzhiyun#
10*4882a593Smuzhiyun# The documented SPDX spec document doesn't cover the JSON syntax for
11*4882a593Smuzhiyun# particular configuration, which can make it hard to determine what the JSON
12*4882a593Smuzhiyun# syntax should be. I've found it is actually much simpler to read the official
13*4882a593Smuzhiyun# SPDX JSON schema which can be found here: https://github.com/spdx/spdx-spec
14*4882a593Smuzhiyun# in schemas/spdx-schema.json
15*4882a593Smuzhiyun#
16*4882a593Smuzhiyun
17*4882a593Smuzhiyunimport hashlib
18*4882a593Smuzhiyunimport itertools
19*4882a593Smuzhiyunimport json
20*4882a593Smuzhiyun
21*4882a593SmuzhiyunSPDX_VERSION = "2.2"
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun#
25*4882a593Smuzhiyun# The following are the support classes that are used to implement SPDX object
26*4882a593Smuzhiyun#
27*4882a593Smuzhiyun
28*4882a593Smuzhiyunclass _Property(object):
29*4882a593Smuzhiyun    """
30*4882a593Smuzhiyun    A generic SPDX object property. The different types will derive from this
31*4882a593Smuzhiyun    class
32*4882a593Smuzhiyun    """
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun    def __init__(self, *, default=None):
35*4882a593Smuzhiyun        self.default = default
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun    def setdefault(self, dest, name):
38*4882a593Smuzhiyun        if self.default is not None:
39*4882a593Smuzhiyun            dest.setdefault(name, self.default)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun
42*4882a593Smuzhiyunclass _String(_Property):
43*4882a593Smuzhiyun    """
44*4882a593Smuzhiyun    A scalar string property for an SPDX object
45*4882a593Smuzhiyun    """
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun    def __init__(self, **kwargs):
48*4882a593Smuzhiyun        super().__init__(**kwargs)
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun    def set_property(self, attrs, name):
51*4882a593Smuzhiyun        def get_helper(obj):
52*4882a593Smuzhiyun            return obj._spdx[name]
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun        def set_helper(obj, value):
55*4882a593Smuzhiyun            obj._spdx[name] = value
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun        def del_helper(obj):
58*4882a593Smuzhiyun            del obj._spdx[name]
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun        attrs[name] = property(get_helper, set_helper, del_helper)
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun    def init(self, source):
63*4882a593Smuzhiyun        return source
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun
66*4882a593Smuzhiyunclass _Object(_Property):
67*4882a593Smuzhiyun    """
68*4882a593Smuzhiyun    A scalar SPDX object property of a SPDX object
69*4882a593Smuzhiyun    """
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun    def __init__(self, cls, **kwargs):
72*4882a593Smuzhiyun        super().__init__(**kwargs)
73*4882a593Smuzhiyun        self.cls = cls
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun    def set_property(self, attrs, name):
76*4882a593Smuzhiyun        def get_helper(obj):
77*4882a593Smuzhiyun            if not name in obj._spdx:
78*4882a593Smuzhiyun                obj._spdx[name] = self.cls()
79*4882a593Smuzhiyun            return obj._spdx[name]
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun        def set_helper(obj, value):
82*4882a593Smuzhiyun            obj._spdx[name] = value
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun        def del_helper(obj):
85*4882a593Smuzhiyun            del obj._spdx[name]
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun        attrs[name] = property(get_helper, set_helper)
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun    def init(self, source):
90*4882a593Smuzhiyun        return self.cls(**source)
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun
93*4882a593Smuzhiyunclass _ListProperty(_Property):
94*4882a593Smuzhiyun    """
95*4882a593Smuzhiyun    A list of SPDX properties
96*4882a593Smuzhiyun    """
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun    def __init__(self, prop, **kwargs):
99*4882a593Smuzhiyun        super().__init__(**kwargs)
100*4882a593Smuzhiyun        self.prop = prop
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun    def set_property(self, attrs, name):
103*4882a593Smuzhiyun        def get_helper(obj):
104*4882a593Smuzhiyun            if not name in obj._spdx:
105*4882a593Smuzhiyun                obj._spdx[name] = []
106*4882a593Smuzhiyun            return obj._spdx[name]
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun        def set_helper(obj, value):
109*4882a593Smuzhiyun            obj._spdx[name] = list(value)
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun        def del_helper(obj):
112*4882a593Smuzhiyun            del obj._spdx[name]
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun        attrs[name] = property(get_helper, set_helper, del_helper)
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun    def init(self, source):
117*4882a593Smuzhiyun        return [self.prop.init(o) for o in source]
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun
120*4882a593Smuzhiyunclass _StringList(_ListProperty):
121*4882a593Smuzhiyun    """
122*4882a593Smuzhiyun    A list of strings as a property for an SPDX object
123*4882a593Smuzhiyun    """
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun    def __init__(self, **kwargs):
126*4882a593Smuzhiyun        super().__init__(_String(), **kwargs)
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun
129*4882a593Smuzhiyunclass _ObjectList(_ListProperty):
130*4882a593Smuzhiyun    """
131*4882a593Smuzhiyun    A list of SPDX objects as a property for an SPDX object
132*4882a593Smuzhiyun    """
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun    def __init__(self, cls, **kwargs):
135*4882a593Smuzhiyun        super().__init__(_Object(cls), **kwargs)
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun
138*4882a593Smuzhiyunclass MetaSPDXObject(type):
139*4882a593Smuzhiyun    """
140*4882a593Smuzhiyun    A metaclass that allows properties (anything derived from a _Property
141*4882a593Smuzhiyun    class) to be defined for a SPDX object
142*4882a593Smuzhiyun    """
143*4882a593Smuzhiyun    def __new__(mcls, name, bases, attrs):
144*4882a593Smuzhiyun        attrs["_properties"] = {}
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun        for key in attrs.keys():
147*4882a593Smuzhiyun            if isinstance(attrs[key], _Property):
148*4882a593Smuzhiyun                prop = attrs[key]
149*4882a593Smuzhiyun                attrs["_properties"][key] = prop
150*4882a593Smuzhiyun                prop.set_property(attrs, key)
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun        return super().__new__(mcls, name, bases, attrs)
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun
155*4882a593Smuzhiyunclass SPDXObject(metaclass=MetaSPDXObject):
156*4882a593Smuzhiyun    """
157*4882a593Smuzhiyun    The base SPDX object; all SPDX spec classes must derive from this class
158*4882a593Smuzhiyun    """
159*4882a593Smuzhiyun    def __init__(self, **d):
160*4882a593Smuzhiyun        self._spdx = {}
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun        for name, prop in self._properties.items():
163*4882a593Smuzhiyun            prop.setdefault(self._spdx, name)
164*4882a593Smuzhiyun            if name in d:
165*4882a593Smuzhiyun                self._spdx[name] = prop.init(d[name])
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun    def serializer(self):
168*4882a593Smuzhiyun        return self._spdx
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun    def __setattr__(self, name, value):
171*4882a593Smuzhiyun        if name in self._properties or name == "_spdx":
172*4882a593Smuzhiyun            super().__setattr__(name, value)
173*4882a593Smuzhiyun            return
174*4882a593Smuzhiyun        raise KeyError("%r is not a valid SPDX property" % name)
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun#
177*4882a593Smuzhiyun# These are the SPDX objects implemented from the spec. The *only* properties
178*4882a593Smuzhiyun# that can be added to these objects are ones directly specified in the SPDX
179*4882a593Smuzhiyun# spec, however you may add helper functions to make operations easier.
180*4882a593Smuzhiyun#
181*4882a593Smuzhiyun# Defaults should *only* be specified if the SPDX spec says there is a certain
182*4882a593Smuzhiyun# required value for a field (e.g. dataLicense), or if the field is mandatory
183*4882a593Smuzhiyun# and has some sane "this field is unknown" (e.g. "NOASSERTION")
184*4882a593Smuzhiyun#
185*4882a593Smuzhiyun
186*4882a593Smuzhiyunclass SPDXAnnotation(SPDXObject):
187*4882a593Smuzhiyun    annotationDate = _String()
188*4882a593Smuzhiyun    annotationType = _String()
189*4882a593Smuzhiyun    annotator = _String()
190*4882a593Smuzhiyun    comment = _String()
191*4882a593Smuzhiyun
192*4882a593Smuzhiyunclass SPDXChecksum(SPDXObject):
193*4882a593Smuzhiyun    algorithm = _String()
194*4882a593Smuzhiyun    checksumValue = _String()
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun
197*4882a593Smuzhiyunclass SPDXRelationship(SPDXObject):
198*4882a593Smuzhiyun    spdxElementId = _String()
199*4882a593Smuzhiyun    relatedSpdxElement = _String()
200*4882a593Smuzhiyun    relationshipType = _String()
201*4882a593Smuzhiyun    comment = _String()
202*4882a593Smuzhiyun    annotations = _ObjectList(SPDXAnnotation)
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun
205*4882a593Smuzhiyunclass SPDXExternalReference(SPDXObject):
206*4882a593Smuzhiyun    referenceCategory = _String()
207*4882a593Smuzhiyun    referenceType = _String()
208*4882a593Smuzhiyun    referenceLocator = _String()
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun
211*4882a593Smuzhiyunclass SPDXPackageVerificationCode(SPDXObject):
212*4882a593Smuzhiyun    packageVerificationCodeValue = _String()
213*4882a593Smuzhiyun    packageVerificationCodeExcludedFiles = _StringList()
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun
216*4882a593Smuzhiyunclass SPDXPackage(SPDXObject):
217*4882a593Smuzhiyun    name = _String()
218*4882a593Smuzhiyun    SPDXID = _String()
219*4882a593Smuzhiyun    versionInfo = _String()
220*4882a593Smuzhiyun    downloadLocation = _String(default="NOASSERTION")
221*4882a593Smuzhiyun    supplier = _String(default="NOASSERTION")
222*4882a593Smuzhiyun    homepage = _String()
223*4882a593Smuzhiyun    licenseConcluded = _String(default="NOASSERTION")
224*4882a593Smuzhiyun    licenseDeclared = _String(default="NOASSERTION")
225*4882a593Smuzhiyun    summary = _String()
226*4882a593Smuzhiyun    description = _String()
227*4882a593Smuzhiyun    sourceInfo = _String()
228*4882a593Smuzhiyun    copyrightText = _String(default="NOASSERTION")
229*4882a593Smuzhiyun    licenseInfoFromFiles = _StringList(default=["NOASSERTION"])
230*4882a593Smuzhiyun    externalRefs = _ObjectList(SPDXExternalReference)
231*4882a593Smuzhiyun    packageVerificationCode = _Object(SPDXPackageVerificationCode)
232*4882a593Smuzhiyun    hasFiles = _StringList()
233*4882a593Smuzhiyun    packageFileName = _String()
234*4882a593Smuzhiyun    annotations = _ObjectList(SPDXAnnotation)
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun
237*4882a593Smuzhiyunclass SPDXFile(SPDXObject):
238*4882a593Smuzhiyun    SPDXID = _String()
239*4882a593Smuzhiyun    fileName = _String()
240*4882a593Smuzhiyun    licenseConcluded = _String(default="NOASSERTION")
241*4882a593Smuzhiyun    copyrightText = _String(default="NOASSERTION")
242*4882a593Smuzhiyun    licenseInfoInFiles = _StringList(default=["NOASSERTION"])
243*4882a593Smuzhiyun    checksums = _ObjectList(SPDXChecksum)
244*4882a593Smuzhiyun    fileTypes = _StringList()
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun
247*4882a593Smuzhiyunclass SPDXCreationInfo(SPDXObject):
248*4882a593Smuzhiyun    created = _String()
249*4882a593Smuzhiyun    licenseListVersion = _String()
250*4882a593Smuzhiyun    comment = _String()
251*4882a593Smuzhiyun    creators = _StringList()
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun
254*4882a593Smuzhiyunclass SPDXExternalDocumentRef(SPDXObject):
255*4882a593Smuzhiyun    externalDocumentId = _String()
256*4882a593Smuzhiyun    spdxDocument = _String()
257*4882a593Smuzhiyun    checksum = _Object(SPDXChecksum)
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun
260*4882a593Smuzhiyunclass SPDXExtractedLicensingInfo(SPDXObject):
261*4882a593Smuzhiyun    name = _String()
262*4882a593Smuzhiyun    comment = _String()
263*4882a593Smuzhiyun    licenseId = _String()
264*4882a593Smuzhiyun    extractedText = _String()
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun
267*4882a593Smuzhiyunclass SPDXDocument(SPDXObject):
268*4882a593Smuzhiyun    spdxVersion = _String(default="SPDX-" + SPDX_VERSION)
269*4882a593Smuzhiyun    dataLicense = _String(default="CC0-1.0")
270*4882a593Smuzhiyun    SPDXID = _String(default="SPDXRef-DOCUMENT")
271*4882a593Smuzhiyun    name = _String()
272*4882a593Smuzhiyun    documentNamespace = _String()
273*4882a593Smuzhiyun    creationInfo = _Object(SPDXCreationInfo)
274*4882a593Smuzhiyun    packages = _ObjectList(SPDXPackage)
275*4882a593Smuzhiyun    files = _ObjectList(SPDXFile)
276*4882a593Smuzhiyun    relationships = _ObjectList(SPDXRelationship)
277*4882a593Smuzhiyun    externalDocumentRefs = _ObjectList(SPDXExternalDocumentRef)
278*4882a593Smuzhiyun    hasExtractedLicensingInfos = _ObjectList(SPDXExtractedLicensingInfo)
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun    def __init__(self, **d):
281*4882a593Smuzhiyun        super().__init__(**d)
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun    def to_json(self, f, *, sort_keys=False, indent=None, separators=None):
284*4882a593Smuzhiyun        class Encoder(json.JSONEncoder):
285*4882a593Smuzhiyun            def default(self, o):
286*4882a593Smuzhiyun                if isinstance(o, SPDXObject):
287*4882a593Smuzhiyun                    return o.serializer()
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun                return super().default(o)
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun        sha1 = hashlib.sha1()
292*4882a593Smuzhiyun        for chunk in Encoder(
293*4882a593Smuzhiyun            sort_keys=sort_keys,
294*4882a593Smuzhiyun            indent=indent,
295*4882a593Smuzhiyun            separators=separators,
296*4882a593Smuzhiyun        ).iterencode(self):
297*4882a593Smuzhiyun            chunk = chunk.encode("utf-8")
298*4882a593Smuzhiyun            f.write(chunk)
299*4882a593Smuzhiyun            sha1.update(chunk)
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun        return sha1.hexdigest()
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun    @classmethod
304*4882a593Smuzhiyun    def from_json(cls, f):
305*4882a593Smuzhiyun        return cls(**json.load(f))
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun    def add_relationship(self, _from, relationship, _to, *, comment=None, annotation=None):
308*4882a593Smuzhiyun        if isinstance(_from, SPDXObject):
309*4882a593Smuzhiyun            from_spdxid = _from.SPDXID
310*4882a593Smuzhiyun        else:
311*4882a593Smuzhiyun            from_spdxid = _from
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun        if isinstance(_to, SPDXObject):
314*4882a593Smuzhiyun            to_spdxid = _to.SPDXID
315*4882a593Smuzhiyun        else:
316*4882a593Smuzhiyun            to_spdxid = _to
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun        r = SPDXRelationship(
319*4882a593Smuzhiyun            spdxElementId=from_spdxid,
320*4882a593Smuzhiyun            relatedSpdxElement=to_spdxid,
321*4882a593Smuzhiyun            relationshipType=relationship,
322*4882a593Smuzhiyun        )
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun        if comment is not None:
325*4882a593Smuzhiyun            r.comment = comment
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun        if annotation is not None:
328*4882a593Smuzhiyun            r.annotations.append(annotation)
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun        self.relationships.append(r)
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun    def find_by_spdxid(self, spdxid):
333*4882a593Smuzhiyun        for o in itertools.chain(self.packages, self.files):
334*4882a593Smuzhiyun            if o.SPDXID == spdxid:
335*4882a593Smuzhiyun                return o
336*4882a593Smuzhiyun        return None
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun    def find_external_document_ref(self, namespace):
339*4882a593Smuzhiyun        for r in self.externalDocumentRefs:
340*4882a593Smuzhiyun            if r.spdxDocument == namespace:
341*4882a593Smuzhiyun                return r
342*4882a593Smuzhiyun        return None
343