diff options
Diffstat (limited to 'meta/lib/oe/sbom.py')
| -rw-r--r-- | meta/lib/oe/sbom.py | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/meta/lib/oe/sbom.py b/meta/lib/oe/sbom.py new file mode 100644 index 0000000000..fd4b6895d8 --- /dev/null +++ b/meta/lib/oe/sbom.py | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | # | ||
| 2 | # Copyright OpenEmbedded Contributors | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | |||
| 7 | import collections | ||
| 8 | |||
| 9 | DepRecipe = collections.namedtuple("DepRecipe", ("doc", "doc_sha1", "recipe")) | ||
| 10 | DepSource = collections.namedtuple("DepSource", ("doc", "doc_sha1", "recipe", "file")) | ||
| 11 | |||
| 12 | |||
| 13 | def get_recipe_spdxid(d): | ||
| 14 | return "SPDXRef-%s-%s" % ("Recipe", d.getVar("PN")) | ||
| 15 | |||
| 16 | |||
| 17 | def get_download_spdxid(d, idx): | ||
| 18 | return "SPDXRef-Download-%s-%d" % (d.getVar("PN"), idx) | ||
| 19 | |||
| 20 | |||
| 21 | def get_package_spdxid(pkg): | ||
| 22 | return "SPDXRef-Package-%s" % pkg | ||
| 23 | |||
| 24 | |||
| 25 | def get_source_file_spdxid(d, idx): | ||
| 26 | return "SPDXRef-SourceFile-%s-%d" % (d.getVar("PN"), idx) | ||
| 27 | |||
| 28 | |||
| 29 | def get_packaged_file_spdxid(pkg, idx): | ||
| 30 | return "SPDXRef-PackagedFile-%s-%d" % (pkg, idx) | ||
| 31 | |||
| 32 | |||
| 33 | def get_image_spdxid(img): | ||
| 34 | return "SPDXRef-Image-%s" % img | ||
| 35 | |||
| 36 | |||
| 37 | def get_sdk_spdxid(sdk): | ||
| 38 | return "SPDXRef-SDK-%s" % sdk | ||
| 39 | |||
| 40 | |||
| 41 | def _doc_path_by_namespace(spdx_deploy, arch, doc_namespace): | ||
| 42 | return spdx_deploy / "by-namespace" / arch / doc_namespace.replace("/", "_") | ||
| 43 | |||
| 44 | |||
| 45 | def doc_find_by_namespace(spdx_deploy, search_arches, doc_namespace): | ||
| 46 | for pkgarch in search_arches: | ||
| 47 | p = _doc_path_by_namespace(spdx_deploy, pkgarch, doc_namespace) | ||
| 48 | if os.path.exists(p): | ||
| 49 | return p | ||
| 50 | return None | ||
| 51 | |||
| 52 | |||
| 53 | def _doc_path_by_hashfn(spdx_deploy, arch, doc_name, hashfn): | ||
| 54 | return ( | ||
| 55 | spdx_deploy / "by-hash" / arch / hashfn.split()[1] / (doc_name + ".spdx.json") | ||
| 56 | ) | ||
| 57 | |||
| 58 | |||
| 59 | def doc_find_by_hashfn(spdx_deploy, search_arches, doc_name, hashfn): | ||
| 60 | for pkgarch in search_arches: | ||
| 61 | p = _doc_path_by_hashfn(spdx_deploy, pkgarch, doc_name, hashfn) | ||
| 62 | if os.path.exists(p): | ||
| 63 | return p | ||
| 64 | return None | ||
| 65 | |||
| 66 | |||
| 67 | def doc_path(spdx_deploy, doc_name, arch, subdir): | ||
| 68 | return spdx_deploy / arch / subdir / (doc_name + ".spdx.json") | ||
| 69 | |||
| 70 | |||
| 71 | def write_doc(d, spdx_doc, arch, subdir, spdx_deploy=None, indent=None): | ||
| 72 | from pathlib import Path | ||
| 73 | |||
| 74 | if spdx_deploy is None: | ||
| 75 | spdx_deploy = Path(d.getVar("SPDXDEPLOY")) | ||
| 76 | |||
| 77 | dest = doc_path(spdx_deploy, spdx_doc.name, arch, subdir) | ||
| 78 | dest.parent.mkdir(exist_ok=True, parents=True) | ||
| 79 | with dest.open("wb") as f: | ||
| 80 | doc_sha1 = spdx_doc.to_json(f, sort_keys=True, indent=indent) | ||
| 81 | |||
| 82 | l = _doc_path_by_namespace(spdx_deploy, arch, spdx_doc.documentNamespace) | ||
| 83 | l.parent.mkdir(exist_ok=True, parents=True) | ||
| 84 | l.symlink_to(os.path.relpath(dest, l.parent)) | ||
| 85 | |||
| 86 | l = _doc_path_by_hashfn( | ||
| 87 | spdx_deploy, arch, spdx_doc.name, d.getVar("BB_HASHFILENAME") | ||
| 88 | ) | ||
| 89 | l.parent.mkdir(exist_ok=True, parents=True) | ||
| 90 | l.symlink_to(os.path.relpath(dest, l.parent)) | ||
| 91 | |||
| 92 | return doc_sha1 | ||
| 93 | |||
| 94 | |||
| 95 | def read_doc(fn): | ||
| 96 | import hashlib | ||
| 97 | import oe.spdx | ||
| 98 | import io | ||
| 99 | import contextlib | ||
| 100 | |||
| 101 | @contextlib.contextmanager | ||
| 102 | def get_file(): | ||
| 103 | if isinstance(fn, io.IOBase): | ||
| 104 | yield fn | ||
| 105 | else: | ||
| 106 | with fn.open("rb") as f: | ||
| 107 | yield f | ||
| 108 | |||
| 109 | with get_file() as f: | ||
| 110 | sha1 = hashlib.sha1() | ||
| 111 | while True: | ||
| 112 | chunk = f.read(4096) | ||
| 113 | if not chunk: | ||
| 114 | break | ||
| 115 | sha1.update(chunk) | ||
| 116 | |||
| 117 | f.seek(0) | ||
| 118 | doc = oe.spdx.SPDXDocument.from_json(f) | ||
| 119 | |||
| 120 | return (doc, sha1.hexdigest()) | ||
