From patchwork Tue Feb 24 23:00:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81845 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 176DCF55428 for ; Tue, 24 Feb 2026 23:02:47 +0000 (UTC) Received: from mail-oi1-f195.google.com (mail-oi1-f195.google.com [209.85.167.195]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.33715.1771974163789433946 for ; Tue, 24 Feb 2026 15:02:43 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=lNoqcPfe; spf=pass (domain: gmail.com, ip: 209.85.167.195, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f195.google.com with SMTP id 5614622812f47-463a94f8475so314155b6e.0 for ; Tue, 24 Feb 2026 15:02:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974163; x=1772578963; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kZsTCBgME1PNRjxHczDMbLqJbIl+DF2WK2U4einEvRE=; b=lNoqcPfesqjugoSewihX5qY6ugOatFomFUwlbtW8C7idzbt6y85rGkLqOsIGPL4bbK Fhtk9GXVTOf6hiKEXbbWjq3ckUvrJUeE65am5bpl622k8FQTqpIGGrlgwgdl76757QXx LRfZ+5Kmfh+Tw/N/t9jFH/Qk/e+oEemOMCk7ZCK+X4NpvBB3Os8W/7IyYja+Y+Xdisd2 i9M4Fnxb/GeI4LI2l6HwcufEum6dilRLbF9zBe1gtd+a3jx/WR/q52+eFmXNtpTRpBoP qKh2eyEkDgwOW58wVmDc8LlOFKx3NO9Kk8SLGLJplFhpV4GTgEdZzSIbOgU2LNZ186r+ gfXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974163; x=1772578963; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kZsTCBgME1PNRjxHczDMbLqJbIl+DF2WK2U4einEvRE=; b=kjl/Q2ssCwidsG0qlxeI41IGy8Inr8rW0a2EIB3tW3beJKTBeFe5n9tywolGnqwKDu JLbb0m8eQI2Dv83A1sZ7dEAc68qxQcm7MYk3RY5aWv+Zm/zU6H2NbJzRqqoJGpxUvo6D OGfBzt3up6016Qex1uV99vRgip9sCp3ZFwmvMTr6v7ZZ7J104dChtkpcbpy7uulwpnUv NW+s/NqMT/J3Imr2EoeuY9M9KbZptYxbKwNZMXVKnq8vxfcGxHTdTEs5i0CwiFHgH7mh UYEo0m2GviXibhyicF4Gp/WzHZ3jhW7OiE8plYMjSHFMfXXRdym9VWLf+mIjSi1PgoEP ps9Q== X-Gm-Message-State: AOJu0Yztszo5DGtN/jmjt66OaLpNDtz9CrCB6ad3zfjcGV0QGNhXzDom Vc2N/xHpQ2gbb5WRJtRoeE8ErDYDAbR7dnwTYy7NNivvhiaAhrWYUoGfL1rL73Qa X-Gm-Gg: AZuq6aLdzYCY32e+syY97kxwd+aubz2NwPtOfQXEevKJaBRWQba+OuJFFSKhI8z5LfD Y9valZkUvBw1lS0GP3LR+3JtIuJ8jSxK63S9Xfqn2/70l6xX+PIBYFqxTMWDYRWwQaiv7sdlFz7 ILzPJL+9H31ZPLe9fn6trji3KRFZoTC8xtzBh2dUpF0a7zV7sWr3Yx/RwGVTGbJ1S3AgWQL84uV q53RUkFc8dfoExfS/aIhu4gqGPhvQi8mBCAQc68AcOisqRpN0Lu3nkmNeEPS+bhzBe+rUDdGX8d 1brFAHoLyXEW8eeN5XqJsXmHKJRf9EpwnOZg8ALk22boWHb6jvpPhFXZ8WyBDLICVe01aEXtiUf n0tjacOD0TjXqO40zCHMXeFlsquQ+R3OVlopdHjF3tPtJIla2SH6a1uuAG6AtUimbHarzleS/+Y izKDmpYrdaaC06xSBENZF7 X-Received: by 2002:a05:6808:1903:b0:45a:6e83:fbff with SMTP id 5614622812f47-4648bd337e9mr875020b6e.20.1771974162794; Tue, 24 Feb 2026 15:02:42 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:41 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 1/8] llvm-project-source: Use allarch.bbclass Date: Tue, 24 Feb 2026 16:00:14 -0700 Message-ID: <20260224230234.679049-2-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:47 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231910 Converts the recipe to use allarch.bbclass. This is necessary because SSTATE_PKGARCH is set to "allarch" based on if allarch is inherited or not. If it is not, SSTATE_PKGARCH has the value "all", which means any data written out based on it cannot be found (because "all" is not in SSTATE_ARCHS) Signed-off-by: Joshua Watt --- meta/recipes-devtools/clang/llvm-project-source.inc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/meta/recipes-devtools/clang/llvm-project-source.inc b/meta/recipes-devtools/clang/llvm-project-source.inc index 13e54efbc2..6bb595b7bc 100644 --- a/meta/recipes-devtools/clang/llvm-project-source.inc +++ b/meta/recipes-devtools/clang/llvm-project-source.inc @@ -5,7 +5,7 @@ deltask do_populate_sysroot deltask do_populate_lic RM_WORK_EXCLUDE += "${PN}" -inherit nopackages +inherit nopackages allarch PN = "llvm-project-source-${PV}" WORKDIR = "${TMPDIR}/work-shared/llvm-project-source-${PV}-${PR}" @@ -14,14 +14,8 @@ SSTATE_SWSPEC = "sstate:llvm-project-source::${PV}:${PR}::${SSTATE_VERSION}:" STAMP = "${STAMPS_DIR}/work-shared/llvm-project-source-${PV}-${PR}" STAMPCLEAN = "${STAMPS_DIR}/work-shared/llvm-project-source-${PV}-*" -INHIBIT_DEFAULT_DEPS = "1" DEPENDS = "" PACKAGES = "" -TARGET_ARCH = "allarch" -TARGET_AS_ARCH = "none" -TARGET_CC_ARCH = "none" -TARGET_LD_ARCH = "none" -TARGET_OS = "linux" baselib = "lib" PACKAGE_ARCH = "all" From patchwork Tue Feb 24 23:00:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81846 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 240F0F5542B for ; Tue, 24 Feb 2026 23:02:47 +0000 (UTC) Received: from mail-oi1-f195.google.com (mail-oi1-f195.google.com [209.85.167.195]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.33592.1771974164945456650 for ; Tue, 24 Feb 2026 15:02:45 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=RPtWtO6B; spf=pass (domain: gmail.com, ip: 209.85.167.195, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f195.google.com with SMTP id 5614622812f47-45f171cb842so342444b6e.1 for ; Tue, 24 Feb 2026 15:02:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974164; x=1772578964; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7bs/2ESNOjlKv5F2a1L5fY1cfh3Mb2GOypfpxUG+ELw=; b=RPtWtO6BRQV7ocbGJWu2ZTo/mlkFqcEaNr7jGjQlkpIVtyVGRVDVdhKY9osdr8nmMp 9i2dt7Mjuove9GQoRyjvJb19gMNsUrSHJ9FA2xKe5Rrz+49UAdLxclrjPRW9P6ofIjNE ttDh0WjdaeeWDJXxxvWsDuvCa1CIQXTCM4+6RCEDGod/C0jlGC9X1zV3rcBmjWk6ynbH +PRs8AvLoYKnkY3AzY+WDU2H+phk0KkZC3ax3IQA3kGZ9DPtpMBI7NlM8s9SEXRG/avi 5UOJdggiWr4CK4AKH3JnyvEIJmNtPjUiHFYRbuxy2NquiDos6TT+qAeQeapiN8KmaOjy Hsjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974164; x=1772578964; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=7bs/2ESNOjlKv5F2a1L5fY1cfh3Mb2GOypfpxUG+ELw=; b=Eb6hMDWROS77XiA0NJHfO2X2hJ+6R5UJddtxxf+FVJqmXeRp/MHI91wiZyjyxKvDUI 3o4Qd0AwD/9jGclEL4bR9zvKz5Bok0EFKHYGiBPwVIwl3Mibr/aW7tIJlpaMKasLTbWH erAlMCGknqKvlT3W9cuFY8tXVvW+6exjfvxO9Zrk7q6xtLi04hU7qQHm9bmFeO00P/ae YINhrD2WszpC+moUkuBsY/6/5aA4UpyWXiQMcCSD60s0RB2LHbbRbzYZLQX6gEtQeKTj 0a3QeBe6H6a8C11CeESl+KrMg1Z+zYnM2t1Ti/mvAlKFN0qGn8W0ZV1upvwMjXZlbbAc NmFg== X-Gm-Message-State: AOJu0YxttB6OqsiLR2LL0Tgxn3JfEHdpLX3he+0aLegdlfJVEy2v4D9W UWlOJjb9645DoEAoZZ/WBA/6goB+rfRy0SbmpwFfmr+IEsJPZH8xhRKh4Aue2mvK X-Gm-Gg: AZuq6aIqOYZHATQE5oO0495m5R9nPmVA1KG1T8Fa7Z9jalHZjDi4OidlmYWs0O1TTrU k1WCuI/711n7T7hb9LslCe4O2YCIEY0F+Ul2y1hGm1c2NEz2fAZalpLn8YNGGsLllf/99uKOJ5a g4hbO2HopRC0ZTI02YBq2VJ3KF/Q8PhMlb4cq0DSk+Hms72DdtALhOzjDodqj+GOnYCDOQInLbz wjimiFmRqKztREtA892dbXjIfKRs+q7uqGaAXA9sBuBtWfRnpr1S+4hXss9ghKYS5L1CEOPuRKp kldymPhaQsoQRFd6YEvhnY6l2+VIOetKZHbyXX6FSgEHOWCT5I4EX19slIzqvPocQECxzq9pJe7 wMCt93EIpiE1l44Tv3AF6m4IkFrPPWrS1vpPOtVAFeI727ZvYt0FH6bebsYSeReb7TH5i8mCEpi L+TlgoFw7e3grptMZBFojp X-Received: by 2002:a05:6808:1997:b0:441:c8a2:ba26 with SMTP id 5614622812f47-4648bc76f6dmr914411b6e.7.1771974164054; Tue, 24 Feb 2026 15:02:44 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:43 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 2/8] gcc-source: Use allarch.bbclass Date: Tue, 24 Feb 2026 16:00:15 -0700 Message-ID: <20260224230234.679049-3-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:47 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231911 Converts the recipe to use allarch.bbclass. This is necessary because SSTATE_PKGARCH is set to "allarch" based on if allarch is inherited or not. If it is not, SSTATE_PKGARCH has the value "all", which means any data written out based on it cannot be found (because "all" is not in SSTATE_ARCHS) Signed-off-by: Joshua Watt --- meta/recipes-devtools/gcc/gcc-source.inc | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/meta/recipes-devtools/gcc/gcc-source.inc b/meta/recipes-devtools/gcc/gcc-source.inc index 265bcf4bef..3ac679b1a6 100644 --- a/meta/recipes-devtools/gcc/gcc-source.inc +++ b/meta/recipes-devtools/gcc/gcc-source.inc @@ -1,11 +1,11 @@ -deltask do_configure -deltask do_compile -deltask do_install +deltask do_configure +deltask do_compile +deltask do_install deltask do_populate_sysroot -deltask do_populate_lic +deltask do_populate_lic RM_WORK_EXCLUDE += "${PN}" -inherit nopackages +inherit nopackages allarch PN = "gcc-source-${PV}" WORKDIR = "${TMPDIR}/work-shared/gcc-${PV}-${PR}" @@ -14,14 +14,8 @@ SSTATE_SWSPEC = "sstate:gcc::${PV}:${PR}::${SSTATE_VERSION}:" STAMP = "${STAMPS_DIR}/work-shared/gcc-${PV}-${PR}" STAMPCLEAN = "${STAMPS_DIR}/work-shared/gcc-${PV}-*" -INHIBIT_DEFAULT_DEPS = "1" DEPENDS = "" PACKAGES = "" -TARGET_ARCH = "allarch" -TARGET_AS_ARCH = "none" -TARGET_CC_ARCH = "none" -TARGET_LD_ARCH = "none" -TARGET_OS = "linux" baselib = "lib" PACKAGE_ARCH = "all" From patchwork Tue Feb 24 23:00:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81844 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 194A4F55429 for ; Tue, 24 Feb 2026 23:02:47 +0000 (UTC) Received: from mail-oi1-f175.google.com (mail-oi1-f175.google.com [209.85.167.175]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.33593.1771974166491212160 for ; Tue, 24 Feb 2026 15:02:46 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=AZIA4YfP; spf=pass (domain: gmail.com, ip: 209.85.167.175, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f175.google.com with SMTP id 5614622812f47-45f171cb842so342464b6e.1 for ; Tue, 24 Feb 2026 15:02:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974165; x=1772578965; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QxdsNIqUqbcwSyqH/6pqX5OcsMM2d8NDaxyk0roRqPs=; b=AZIA4YfP17McFiefY81ImJ8NyyD+aeo5IxDxbOVI50HaV+5q/4oWcecUtaBB2Yv1/6 pvC+lwYaYRua/TkXFtoHnK0+9QBjHz5a8vvMufLLBlNDFFJCBVsAxt/Ms0F/TCqRcYwh oIXSyehTqH3I2oFaz9kiR9n0pBd38LaZeJFGSww5+BItLFIoq+TFoJl+DD7dUPBW7R2p YpptyEgH2YtLjddZe+dS915yyU9jqNSZC1EdXTAtWGvgT5RI4lu6AlcySqnc+aJCsmkz DbIeAtkgBTCO2bMSaz3Uc3BcTbNXlVz/Q2zeekoLLd137OonctdhD8sNqqGYL1CRLhZX KCAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974165; x=1772578965; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=QxdsNIqUqbcwSyqH/6pqX5OcsMM2d8NDaxyk0roRqPs=; b=iiGHhO9Kpa/S9J4qgQdStD4WGfaaSqZ2a56mEw6Ks4bkZDSFtQqeRsPF4Ki5EjyBsX WWyx/nV8ta+HJ5rU20Ls3ByggN3CtQhZs36x923oVaUqC8eJOTfuXN6giYcpWylzZWBV wwEV2XPBos0Omgb2bk5eDC509KiFuA1ndmab44YtHuq1e9b28wMeGWgSO/YE7QtrY0lT Qa0aieu+ihVdMAT42IXi0aus/92UlJmutoBUlF1qB+VlpErOeziE+2eRhXxFp6X0FIzJ Ehr1EDBPOPQjo8WdpzPug9mJR1/Y9xTc+492vZ4GRv3bVCjJcW1UsbMYgdazD4PPNjac lUsw== X-Gm-Message-State: AOJu0Yw9IdUDl/e+OOdSPP2j+rlEjeJbGd/xjoGAqGavGs00crQbVAkD bKMM5uZiQSZgSyxA9pnAaFT/UfeSWPSN9i3qJgngZzt2ptOC9jYgfB6W/AdWiQ== X-Gm-Gg: AZuq6aJ+TQhl/fEE8bpmlFBNmvKXuDm5cfQZe8jLmljScy1umEHL3Rf0LrBTeJJ1EV6 9Zclr7KuSgY9KWRHMKi0ZCJYg39c7cJwkq87rtp8WD8tGAS+gKxdbiUSVHZMb3wRtBwSoDteW8d Xf0kB4FqC0I+5kSOIZpSvLepjRP4yZkVTv+L5xtiOtbkLutyEkshTzo1pV43q4RrGuaHxl/0sJF DnaEsb/LYHpaqt0HDiFjvazVPqKxOnk9IrjFMUVK991RdDllT/KVONzgZqMOuj4z3RXWKlFlEdf fFtTfK6+cjX0UzwIels81y+6fFccKKVjkBV7UqOgo2xRCLhJs3XPD40A/IF6Q3tmHlLWJ8RtCr3 701g5tPz0CXamzZpSLry+lTxxy+owptx4PF20MyTJZCzjBB+Y7k02NcvnHlPCyrEvXdbRnqTaTq OERZ7C86xtyC9kYw3hxAVl X-Received: by 2002:a05:6808:181d:b0:462:e390:cf3f with SMTP id 5614622812f47-4648bd978c9mr886083b6e.28.1771974165366; Tue, 24 Feb 2026 15:02:45 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:44 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 3/8] spdx3: Add recipe SPDX data Date: Tue, 24 Feb 2026 16:00:16 -0700 Message-ID: <20260224230234.679049-4-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:47 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231912 Adds a new package to the SPDX output that represents the recipe data for a given recipe. Importantly, this data contains only things that can be determined statically from only the recipe, so it doesn't require fetching or building anything. This means that build time dependencies and CVE information for recipes can be analyzed without needing to actually do any builds. Sadly, license data cannot be included because NO_GENERIC_LICENSE means that actual license text might only be available after do_fetch Signed-off-by: Joshua Watt --- meta/classes-global/sstate.bbclass | 4 +- .../create-spdx-image-3.0.bbclass | 4 +- .../create-spdx-sdk-3.0.bbclass | 4 +- meta/classes-recipe/kernel.bbclass | 2 +- meta/classes-recipe/nospdx.bbclass | 1 + meta/classes/create-spdx-2.2.bbclass | 12 +- meta/classes/create-spdx-3.0.bbclass | 49 ++- meta/classes/spdx-common.bbclass | 15 +- meta/lib/oe/spdx30_tasks.py | 402 ++++++++++++------ 9 files changed, 342 insertions(+), 151 deletions(-) diff --git a/meta/classes-global/sstate.bbclass b/meta/classes-global/sstate.bbclass index 2fd29d7323..95c44f404e 100644 --- a/meta/classes-global/sstate.bbclass +++ b/meta/classes-global/sstate.bbclass @@ -954,7 +954,7 @@ def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, summary=True, extrapath = d.getVar("NATIVELSBSTRING") + "/" else: extrapath = "" - + tname = bb.runqueue.taskname_from_tid(task)[3:] if tname in ["fetch", "unpack", "patch", "populate_lic", "preconfigure"] and splithashfn[2]: @@ -1116,7 +1116,7 @@ def setscene_depvalid(task, taskdependees, notneeded, d, log=None): logit("Considering setscene task: %s" % (str(taskdependees[task])), log) - directtasks = ["do_populate_lic", "do_deploy_source_date_epoch", "do_shared_workdir", "do_stash_locale", "do_gcc_stash_builddir", "do_create_spdx", "do_deploy_archives"] + directtasks = ["do_populate_lic", "do_deploy_source_date_epoch", "do_shared_workdir", "do_stash_locale", "do_gcc_stash_builddir", "do_create_spdx", "do_create_recipe_spdx", "do_deploy_archives"] def isNativeCross(x): return x.endswith("-native") or "-cross-" in x or "-crosssdk" in x or x.endswith("-cross") diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass index 636ab14eb0..15a91e90e2 100644 --- a/meta/classes-recipe/create-spdx-image-3.0.bbclass +++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass @@ -34,7 +34,7 @@ addtask do_create_rootfs_spdx after do_rootfs before do_image SSTATETASKS += "do_create_rootfs_spdx" do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}" do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" -do_create_rootfs_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_create_rootfs_spdx[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_create_rootfs_spdx[cleandirs] += "${SPDXROOTFSDEPLOY}" do_create_rootfs_spdx[file-checksums] += "${SPDX3_DEP_FILES}" @@ -76,7 +76,7 @@ do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}" do_create_image_sbom_spdx[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" do_create_image_sbom_spdx[stamp-extra-info] = "${MACHINE_ARCH}" do_create_image_sbom_spdx[cleandirs] = "${SPDXIMAGEDEPLOYDIR}" -do_create_image_sbom_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_create_image_sbom_spdx[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_create_image_sbom_spdx[file-checksums] += "${SPDX3_DEP_FILES}" python do_create_image_sbom_spdx_setscene() { diff --git a/meta/classes-recipe/create-spdx-sdk-3.0.bbclass b/meta/classes-recipe/create-spdx-sdk-3.0.bbclass index e5f220cdfa..a4b8ed3bf9 100644 --- a/meta/classes-recipe/create-spdx-sdk-3.0.bbclass +++ b/meta/classes-recipe/create-spdx-sdk-3.0.bbclass @@ -5,14 +5,14 @@ # # SPDX SDK tasks -do_populate_sdk[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_populate_sdk[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_populate_sdk[cleandirs] += "${SPDXSDKWORK}" do_populate_sdk[postfuncs] += "sdk_create_sbom" do_populate_sdk[file-checksums] += "${SPDX3_DEP_FILES}" POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk = " sdk_host_create_spdx" POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk = " sdk_target_create_spdx" -do_populate_sdk_ext[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_populate_sdk_ext[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_populate_sdk_ext[cleandirs] += "${SPDXSDKEXTWORK}" do_populate_sdk_ext[postfuncs] += "sdk_ext_create_sbom" do_populate_sdk_ext[file-checksums] += "${SPDX3_DEP_FILES}" diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass index f989b31c47..3a2c20dec2 100644 --- a/meta/classes-recipe/kernel.bbclass +++ b/meta/classes-recipe/kernel.bbclass @@ -904,7 +904,7 @@ python do_create_kernel_config_spdx() { bb.error(f"Failed to parse kernel config file: {e}") build, build_objset = oe.sbom30.find_root_obj_in_jsonld( - d, "recipes", f"recipe-{pn}", oe.spdx30.build_Build + d, "builds", f"build-{pn}", oe.spdx30.build_Build ) kernel_build = build_objset.add_root( diff --git a/meta/classes-recipe/nospdx.bbclass b/meta/classes-recipe/nospdx.bbclass index b20e28218b..90e14442ba 100644 --- a/meta/classes-recipe/nospdx.bbclass +++ b/meta/classes-recipe/nospdx.bbclass @@ -5,6 +5,7 @@ # deltask do_collect_spdx_deps +deltask do_create_recipe_spdx deltask do_create_spdx deltask do_create_spdx_runtime deltask do_create_package_spdx diff --git a/meta/classes/create-spdx-2.2.bbclass b/meta/classes/create-spdx-2.2.bbclass index 65d10d86db..f1ee0f9afd 100644 --- a/meta/classes/create-spdx-2.2.bbclass +++ b/meta/classes/create-spdx-2.2.bbclass @@ -399,6 +399,15 @@ def get_license_list_version(license_data, d): return ".".join(license_data["licenseListVersion"].split(".")[:2]) +# This task is added for compatibility with tasks shared with SPDX 3, but +# doesn't do anything +do_create_recipe_spdx() { + : +} +do_create_recipe_spdx[noexec] = "1" +addtask do_create_recipe_spdx after do_collect_spdx_deps + + python do_create_spdx() { from datetime import datetime, timezone import oe.sbom @@ -594,7 +603,7 @@ python do_create_spdx() { } do_create_spdx[vardepsexclude] += "BB_NUMBER_THREADS" # NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source -addtask do_create_spdx after do_package do_packagedata do_unpack do_collect_spdx_deps before do_populate_sdk do_build do_rm_work +addtask do_create_spdx after do_create_recipe_spdx do_package do_packagedata do_unpack do_collect_spdx_deps before do_populate_sdk do_build do_rm_work SSTATETASKS += "do_create_spdx" do_create_spdx[sstate-inputdirs] = "${SPDXDEPLOY}" @@ -605,6 +614,7 @@ python do_create_spdx_setscene () { } addtask do_create_spdx_setscene +do_create_spdx[deptask] += "do_create_spdx" do_create_spdx[dirs] = "${SPDXWORK}" do_create_spdx[cleandirs] = "${SPDXDEPLOY} ${SPDXWORK}" do_create_spdx[depends] += " \ diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index d4575d61c4..61223ee0a5 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -159,11 +159,18 @@ SPDX3_DEP_FILES = "\ ${SPDX_LICENSES}:True \ " -python do_create_spdx() { +python do_create_recipe_spdx() { import oe.spdx30_tasks - oe.spdx30_tasks.create_spdx(d) + oe.spdx30_tasks.create_recipe_spdx(d) } -do_create_spdx[vardeps] += "\ +addtask do_create_recipe_spdx after do_collect_spdx_deps + +SSTATETASKS += "do_create_recipe_spdx" +do_create_recipe_spdx[sstate-inputdirs] = "${SPDXRECIPEDEPLOY}" +do_create_recipe_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" +do_create_recipe_spdx[file-checksums] += "${SPDX3_DEP_FILES}" +do_create_recipe_spdx[cleandirs] = "${SPDXRECIPEDEPLOY}" +do_create_recipe_spdx[vardeps] += "\ SPDX_INCLUDE_BITBAKE_PARENT_BUILD \ SPDX_PACKAGE_ADDITIONAL_PURPOSE \ SPDX_PROFILES \ @@ -171,7 +178,17 @@ do_create_spdx[vardeps] += "\ SPDX_UUID_NAMESPACE \ " +python do_create_recipe_spdx_setscene () { + sstate_setscene(d) +} +addtask do_create_recipe_spdx_setscene + +python do_create_spdx() { + import oe.spdx30_tasks + oe.spdx30_tasks.create_spdx(d) +} addtask do_create_spdx after \ + do_create_recipe_spdx \ do_collect_spdx_deps \ do_deploy_source_date_epoch \ do_populate_sysroot do_package do_packagedata \ @@ -181,18 +198,25 @@ SSTATETASKS += "do_create_spdx" do_create_spdx[sstate-inputdirs] = "${SPDXDEPLOY}" do_create_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" do_create_spdx[file-checksums] += "${SPDX3_DEP_FILES}" - -python do_create_spdx_setscene () { - sstate_setscene(d) -} -addtask do_create_spdx_setscene - +do_create_spdx[deptask] += "do_create_spdx" do_create_spdx[dirs] = "${SPDXWORK}" do_create_spdx[cleandirs] = "${SPDXDEPLOY} ${SPDXWORK}" do_create_spdx[depends] += " \ ${PATCHDEPENDENCY} \ ${@create_spdx_source_deps(d)} \ " +do_create_spdx[vardeps] += "\ + SPDX_INCLUDE_BITBAKE_PARENT_BUILD \ + SPDX_PACKAGE_ADDITIONAL_PURPOSE \ + SPDX_PROFILES \ + SPDX_NAMESPACE_PREFIX \ + SPDX_UUID_NAMESPACE \ + " + +python do_create_spdx_setscene () { + sstate_setscene(d) +} +addtask do_create_spdx_setscene python do_create_package_spdx() { import oe.spdx30_tasks @@ -205,16 +229,15 @@ SSTATETASKS += "do_create_package_spdx" do_create_package_spdx[sstate-inputdirs] = "${SPDXRUNTIMEDEPLOY}" do_create_package_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" do_create_package_spdx[file-checksums] += "${SPDX3_DEP_FILES}" +do_create_package_spdx[dirs] = "${SPDXRUNTIMEDEPLOY}" +do_create_package_spdx[cleandirs] = "${SPDXRUNTIMEDEPLOY}" +do_create_package_spdx[rdeptask] = "do_create_spdx" python do_create_package_spdx_setscene () { sstate_setscene(d) } addtask do_create_package_spdx_setscene -do_create_package_spdx[dirs] = "${SPDXRUNTIMEDEPLOY}" -do_create_package_spdx[cleandirs] = "${SPDXRUNTIMEDEPLOY}" -do_create_package_spdx[rdeptask] = "do_create_spdx" - python spdx30_build_started_handler () { import oe.spdx30_tasks d = e.data.createCopy() diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index 3110230c9e..2804c27b0b 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass @@ -23,6 +23,7 @@ SPDXDEPS = "${SPDXDIR}/deps.json" SPDX_TOOL_NAME ??= "oe-spdx-creator" SPDX_TOOL_VERSION ??= "1.0" +SPDXRECIPEDEPLOY = "${SPDXDIR}/recipe-deploy" SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy" SPDX_INCLUDE_SOURCES ??= "0" @@ -72,6 +73,8 @@ def create_spdx_source_deps(d): # ourselves if oe.spdx_common.has_task(d, "do_unpack"): deps.append("%s:do_unpack" % pn) + if oe.spdx_common.has_task(d, "do_patch"): + deps.append("%s:do_patch" % pn) if oe.spdx_common.is_work_shared_spdx(d) and \ oe.spdx_common.process_sources(d): @@ -97,8 +100,8 @@ python do_collect_spdx_deps() { # This task calculates the build time dependencies of the recipe, and is # required because while a task can deptask on itself, those dependencies # do not show up in BB_TASKDEPDATA. To work around that, this task does the - # deptask on do_create_spdx and writes out the dependencies it finds, then - # do_create_spdx reads in the found dependencies when writing the actual + # deptask on do_create_recipe_spdx and writes out the dependencies it finds, then + # downstream tasks read in the found dependencies when writing the actual # SPDX document import json import oe.spdx_common @@ -106,15 +109,13 @@ python do_collect_spdx_deps() { spdx_deps_file = Path(d.getVar("SPDXDEPS")) - deps = oe.spdx_common.collect_direct_deps(d, "do_create_spdx") + deps = oe.spdx_common.collect_direct_deps(d, "do_create_recipe_spdx") with spdx_deps_file.open("w") as f: json.dump(deps, f) } -# NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source -addtask do_collect_spdx_deps after do_unpack -do_collect_spdx_deps[depends] += "${PATCHDEPENDENCY}" -do_collect_spdx_deps[deptask] = "do_create_spdx" +addtask do_collect_spdx_deps +do_collect_spdx_deps[deptask] = "do_create_recipe_spdx" do_collect_spdx_deps[dirs] = "${SPDXDIR}" oe.spdx_common.collect_direct_deps[vardepsexclude] += "BB_TASKDEPDATA" diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 99f2892dfb..a8b4525e3d 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -32,7 +32,9 @@ def set_timestamp_now(d, o, prop): delattr(o, prop) -def add_license_expression(d, objset, license_expression, license_data): +def add_license_expression( + d, objset, license_expression, license_data, search_objsets=[] +): simple_license_text = {} license_text_map = {} license_ref_idx = 0 @@ -44,14 +46,15 @@ def add_license_expression(d, objset, license_expression, license_data): if name in simple_license_text: return simple_license_text[name] - lic = objset.find_filter( - oe.spdx30.simplelicensing_SimpleLicensingText, - name=name, - ) + for o in [objset] + search_objsets: + lic = o.find_filter( + oe.spdx30.simplelicensing_SimpleLicensingText, + name=name, + ) - if lic is not None: - simple_license_text[name] = lic - return lic + if lic is not None: + simple_license_text[name] = lic + return lic lic = objset.add( oe.spdx30.simplelicensing_SimpleLicensingText( @@ -178,7 +181,9 @@ def add_package_files( # Check if file is compiled if check_compiled_sources: - if not oe.spdx_common.is_compiled_source(filename, compiled_sources, types): + if not oe.spdx_common.is_compiled_source( + filename, compiled_sources, types + ): continue spdx_file = objset.new_file( @@ -293,17 +298,16 @@ def get_package_sources_from_debug( return dep_source_files -def collect_dep_objsets(d, build): +def collect_dep_objsets(d, subdir, fn_prefix, obj_type, **attr_filter): deps = oe.spdx_common.get_spdx_deps(d) dep_objsets = [] - dep_builds = set() + dep_objs = set() - dep_build_spdxids = set() for dep in deps: bb.debug(1, "Fetching SPDX for dependency %s" % (dep.pn)) - dep_build, dep_objset = oe.sbom30.find_root_obj_in_jsonld( - d, "recipes", "recipe-" + dep.pn, oe.spdx30.build_Build + dep_obj, dep_objset = oe.sbom30.find_root_obj_in_jsonld( + d, subdir, fn_prefix + dep.pn, obj_type, **attr_filter ) # If the dependency is part of the taskhash, return it to be linked # against. Otherwise, it cannot be linked against because this recipe @@ -311,10 +315,10 @@ def collect_dep_objsets(d, build): if dep.in_taskhash: dep_objsets.append(dep_objset) - # The build _can_ be linked against (by alias) - dep_builds.add(dep_build) + # The object _can_ be linked against (by alias) + dep_objs.add(dep_obj) - return dep_objsets, dep_builds + return dep_objsets, dep_objs def index_sources_by_hash(sources, dest): @@ -423,9 +427,7 @@ def add_download_files(d, objset): if fd.method.supports_checksum(fd): # TODO Need something better than hard coding this for checksum_id in ["sha256", "sha1"]: - expected_checksum = getattr( - fd, "%s_expected" % checksum_id, None - ) + expected_checksum = getattr(fd, "%s_expected" % checksum_id, None) if expected_checksum is None: continue @@ -462,50 +464,96 @@ def set_purposes(d, element, *var_names, force_purposes=[]): ] -def create_spdx(d): - def set_var_field(var, obj, name, package=None): - val = None - if package: - val = d.getVar("%s:%s" % (var, package)) +def set_purls(spdx_package, purls): + if purls: + spdx_package.software_packageUrl = purls[0] - if not val: - val = d.getVar(var) + for p in sorted(set(purls)): + spdx_package.externalIdentifier.append( + oe.spdx30.ExternalIdentifier( + externalIdentifierType=oe.spdx30.ExternalIdentifierType.packageUrl, + identifier=p, + ) + ) - if val: - setattr(obj, name, val) + +def create_recipe_spdx(d): + deploydir = Path(d.getVar("SPDXRECIPEDEPLOY")) + deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) + pn = d.getVar("PN") license_data = oe.spdx_common.load_spdx_license_data(d) - deploydir = Path(d.getVar("SPDXDEPLOY")) - deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) - spdx_workdir = Path(d.getVar("SPDXWORK")) - include_sources = d.getVar("SPDX_INCLUDE_SOURCES") == "1" - pkg_arch = d.getVar("SSTATE_PKGARCH") - is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( - "cross", d - ) include_vex = d.getVar("SPDX_INCLUDE_VEX") if not include_vex in ("none", "current", "all"): bb.fatal("SPDX_INCLUDE_VEX must be one of 'none', 'current', 'all'") - build_objset = oe.sbom30.ObjectSet.new_objset(d, "recipe-" + d.getVar("PN")) + recipe_objset = oe.sbom30.ObjectSet.new_objset(d, "static-" + pn) - build = build_objset.new_task_build("recipe", "recipe") - build_objset.set_element_alias(build) + recipe = recipe_objset.add_root( + oe.spdx30.software_Package( + _id=recipe_objset.new_spdxid("recipe", pn), + creationInfo=recipe_objset.doc.creationInfo, + name=d.getVar("PN"), + software_packageVersion=d.getVar("PV"), + software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.specification, + software_sourceInfo=json.dumps( + { + "FILENAME": os.path.basename(d.getVar("FILE")), + "FILE_LAYERNAME": d.getVar("FILE_LAYERNAME"), + }, + separators=(",", ":"), + ), + ) + ) - build_objset.doc.rootElement.append(build) + set_purls(recipe, (d.getVar("SPDX_PACKAGE_URLS") or "").split()) + + # TODO: This doesn't work before do_unpack because the license text has to + # be available for recipes with NO_GENERIC_LICENSE + # recipe_spdx_license = add_license_expression( + # d, + # recipe_objset, + # d.getVar("LICENSE"), + # license_data, + # ) + # recipe_objset.new_relationship( + # [recipe], + # oe.spdx30.RelationshipType.hasDeclaredLicense, + # [oe.sbom30.get_element_link_id(recipe_spdx_license)], + # ) + + if val := d.getVar("HOMEPAGE"): + recipe.software_homePage = val + + if val := d.getVar("SUMMARY"): + recipe.summary = val + + if val := d.getVar("DESCRIPTION"): + recipe.description = val + + for cpe_id in oe.cve_check.get_cpe_ids( + d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION") + ): + recipe.externalIdentifier.append( + oe.spdx30.ExternalIdentifier( + externalIdentifierType=oe.spdx30.ExternalIdentifierType.cpe23, + identifier=cpe_id, + ) + ) - build_objset.set_is_native(is_native) + dep_objsets, dep_recipes = collect_dep_objsets( + d, "static", "static-", oe.spdx30.software_Package + ) - for var in (d.getVar("SPDX_CUSTOM_ANNOTATION_VARS") or "").split(): - build_objset.new_annotation( - build, - "%s=%s" % (var, d.getVar(var)), - oe.spdx30.AnnotationType.other, + if dep_recipes: + recipe_objset.new_scoped_relationship( + [recipe], + oe.spdx30.RelationshipType.dependsOn, + oe.spdx30.LifecycleScopeType.build, + sorted(oe.sbom30.get_element_link_id(dep) for dep in dep_recipes), ) - build_inputs = set() - # Add CVEs cve_by_status = {} if include_vex != "none": @@ -514,7 +562,7 @@ def create_spdx(d): decoded_status = { "mapping": patched_cve["abbrev-status"], "detail": patched_cve["status"], - "description": patched_cve.get("justification", None) + "description": patched_cve.get("justification", None), } # If this CVE is fixed upstream, skip it unless all CVEs are @@ -531,8 +579,7 @@ def create_spdx(d): bb.debug(1, "Skipping %s since it is already fixed upstream" % cve) continue - spdx_cve = build_objset.new_cve_vuln(cve) - build_objset.set_element_alias(spdx_cve) + spdx_cve = recipe_objset.new_cve_vuln(cve) cve_by_status.setdefault(decoded_status["mapping"], {})[cve] = ( spdx_cve, @@ -540,13 +587,118 @@ def create_spdx(d): decoded_status["description"], ) + all_cves = set() + for status, cves in cve_by_status.items(): + for cve, items in cves.items(): + spdx_cve, detail, description = items + spdx_cve_id = oe.sbom30.get_element_link_id(spdx_cve) + + all_cves.add(spdx_cve) + + if status == "Patched": + recipe_objset.new_vex_patched_relationship([spdx_cve_id], [recipe]) + elif status == "Unpatched": + recipe_objset.new_vex_unpatched_relationship([spdx_cve_id], [recipe]) + elif status == "Ignored": + spdx_vex = recipe_objset.new_vex_ignored_relationship( + [spdx_cve_id], + [recipe], + impact_statement=description, + ) + + vex_just_type = d.getVarFlag("CVE_CHECK_VEX_JUSTIFICATION", detail) + if vex_just_type: + if ( + vex_just_type + not in oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS + ): + bb.fatal( + f"Unknown vex justification '{vex_just_type}', detail '{detail}', for ignored {cve}" + ) + + for v in spdx_vex: + v.security_justificationType = ( + oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS[ + vex_just_type + ] + ) + + elif status == "Unknown": + bb.note(f"Skipping {cve} with status 'Unknown'") + else: + bb.fatal(f"Unknown {cve} status '{status}'") + + if all_cves: + recipe_objset.new_relationship( + [recipe], + oe.spdx30.RelationshipType.hasAssociatedVulnerability, + sorted(list(all_cves)), + ) + + oe.sbom30.write_recipe_jsonld_doc(d, recipe_objset, "static", deploydir) + + +def load_recipe_spdx(d): + + return oe.sbom30.find_root_obj_in_jsonld( + d, + "static", + "static-" + d.getVar("PN"), + oe.spdx30.software_Package, + ) + + +def create_spdx(d): + def set_var_field(var, obj, name, package=None): + val = None + if package: + val = d.getVar("%s:%s" % (var, package)) + + if not val: + val = d.getVar(var) + + if val: + setattr(obj, name, val) + + license_data = oe.spdx_common.load_spdx_license_data(d) + + pn = d.getVar("PN") + deploydir = Path(d.getVar("SPDXDEPLOY")) + deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) + spdx_workdir = Path(d.getVar("SPDXWORK")) + include_sources = d.getVar("SPDX_INCLUDE_SOURCES") == "1" + pkg_arch = d.getVar("SSTATE_PKGARCH") + is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( + "cross", d + ) + + recipe, recipe_objset = load_recipe_spdx(d) + + build_objset = oe.sbom30.ObjectSet.new_objset(d, "build-" + pn) + + build = build_objset.new_task_build("recipe", "recipe") + build_objset.set_element_alias(build) + + build_objset.doc.rootElement.append(build) + + build_objset.set_is_native(is_native) + + for var in (d.getVar("SPDX_CUSTOM_ANNOTATION_VARS") or "").split(): + build_objset.new_annotation( + build, + "%s=%s" % (var, d.getVar(var)), + oe.spdx30.AnnotationType.other, + ) + + build_inputs = set() + cpe_ids = oe.cve_check.get_cpe_ids(d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION")) source_files = add_download_files(d, build_objset) build_inputs |= source_files recipe_spdx_license = add_license_expression( - d, build_objset, d.getVar("LICENSE"), license_data + d, build_objset, d.getVar("LICENSE"), license_data, [recipe_objset] ) build_objset.new_relationship( source_files, @@ -575,7 +727,10 @@ def create_spdx(d): build_inputs |= files index_sources_by_hash(files, dep_sources) - dep_objsets, dep_builds = collect_dep_objsets(d, build) + dep_objsets, dep_builds = collect_dep_objsets( + d, "builds", "build-", oe.spdx30.build_Build + ) + if dep_builds: build_objset.new_scoped_relationship( [build], @@ -587,6 +742,22 @@ def create_spdx(d): debug_source_ids = set() source_hash_cache = {} + # Collect all VEX statements from the recipe + vex_statements = {} + for rel in recipe_objset.foreach_filter( + oe.spdx30.Relationship, + relationshipType=oe.spdx30.RelationshipType.hasAssociatedVulnerability, + ): + for cve in rel.to: + vex_statements[cve] = [] + + for cve in vex_statements.keys(): + for rel in recipe_objset.foreach_filter( + oe.spdx30.security_VexVulnAssessmentRelationship, + from_=cve, + ): + vex_statements[cve].append(rel) + # Write out the package SPDX data now. It is not complete as we cannot # write the runtime data, so write it to a staging area and a later task # will write out the final collection @@ -645,16 +816,7 @@ def create_spdx(d): or "" ).split() - if purls: - spdx_package.software_packageUrl = purls[0] - - for p in sorted(set(purls)): - spdx_package.externalIdentifier.append( - oe.spdx30.ExternalIdentifier( - externalIdentifierType=oe.spdx30.ExternalIdentifierType.packageUrl, - identifier=p, - ) - ) + set_purls(spdx_package, purls) pkg_objset.new_scoped_relationship( [oe.sbom30.get_element_link_id(build)], @@ -663,6 +825,13 @@ def create_spdx(d): [spdx_package], ) + pkg_objset.new_scoped_relationship( + [oe.sbom30.get_element_link_id(recipe)], + oe.spdx30.RelationshipType.generates, + oe.spdx30.LifecycleScopeType.build, + [spdx_package], + ) + for cpe_id in cpe_ids: spdx_package.externalIdentifier.append( oe.spdx30.ExternalIdentifier( @@ -696,7 +865,11 @@ def create_spdx(d): package_license = d.getVar("LICENSE:%s" % package) if package_license and package_license != d.getVar("LICENSE"): package_spdx_license = add_license_expression( - d, build_objset, package_license, license_data + d, + build_objset, + package_license, + license_data, + [recipe_objset], ) else: package_spdx_license = recipe_spdx_license @@ -721,58 +894,41 @@ def create_spdx(d): [oe.sbom30.get_element_link_id(concluded_spdx_license)], ) - # NOTE: CVE Elements live in the recipe collection - all_cves = set() - for status, cves in cve_by_status.items(): - for cve, items in cves.items(): - spdx_cve, detail, description = items - spdx_cve_id = oe.sbom30.get_element_link_id(spdx_cve) - - all_cves.add(spdx_cve_id) + # Copy CVEs from recipe + if vex_statements: + pkg_objset.new_relationship( + [spdx_package], + oe.spdx30.RelationshipType.hasAssociatedVulnerability, + sorted( + oe.sbom30.get_element_link_id(cve) + for cve in vex_statements.keys() + ), + ) - if status == "Patched": + for cve, vexes in vex_statements.items(): + for vex in vexes: + if vex.relationshipType == oe.spdx30.RelationshipType.fixedIn: pkg_objset.new_vex_patched_relationship( - [spdx_cve_id], [spdx_package] + [oe.sbom30.get_element_link_id(cve)], [spdx_package] ) - elif status == "Unpatched": + elif vex.relationshipType == oe.spdx30.RelationshipType.affects: pkg_objset.new_vex_unpatched_relationship( - [spdx_cve_id], [spdx_package] + [oe.sbom30.get_element_link_id(cve)], [spdx_package] ) - elif status == "Ignored": + elif ( + vex.relationshipType == oe.spdx30.RelationshipType.doesNotAffect + ): spdx_vex = pkg_objset.new_vex_ignored_relationship( - [spdx_cve_id], + [oe.sbom30.get_element_link_id(cve)], [spdx_package], - impact_statement=description, + impact_statement=vex.security_impactStatement, ) - vex_just_type = d.getVarFlag( - "CVE_CHECK_VEX_JUSTIFICATION", detail - ) - if vex_just_type: - if ( - vex_just_type - not in oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS - ): - bb.fatal( - f"Unknown vex justification '{vex_just_type}', detail '{detail}', for ignored {cve}" - ) - + if vex.security_justificationType: for v in spdx_vex: - v.security_justificationType = oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS[ - vex_just_type - ] - - elif status == "Unknown": - bb.note(f"Skipping {cve} with status 'Unknown'") - else: - bb.fatal(f"Unknown {cve} status '{status}'") - - if all_cves: - pkg_objset.new_relationship( - [spdx_package], - oe.spdx30.RelationshipType.hasAssociatedVulnerability, - sorted(list(all_cves)), - ) + v.security_justificationType = ( + vex.security_justificationType + ) bb.debug(1, "Adding package files to SPDX for package %s" % pkg_name) package_files = add_package_files( @@ -851,14 +1007,15 @@ def create_spdx(d): status = "enabled" if feature in enabled else "disabled" build.build_parameter.append( oe.spdx30.DictionaryEntry( - key=f"PACKAGECONFIG:{feature}", - value=status + key=f"PACKAGECONFIG:{feature}", value=status ) ) - bb.note(f"Added PACKAGECONFIG entries: {len(enabled)} enabled, {len(disabled)} disabled") + bb.note( + f"Added PACKAGECONFIG entries: {len(enabled)} enabled, {len(disabled)} disabled" + ) - oe.sbom30.write_recipe_jsonld_doc(d, build_objset, "recipes", deploydir) + oe.sbom30.write_recipe_jsonld_doc(d, build_objset, "builds", deploydir) def create_package_spdx(d): @@ -1197,17 +1354,17 @@ def create_image_spdx(d): image_path = image_deploy_dir / image_filename if os.path.isdir(image_path): a = add_package_files( - d, - objset, - image_path, - lambda file_counter: objset.new_spdxid( - "imagefile", str(file_counter) - ), - lambda filepath: [], - license_data=None, - ignore_dirs=[], - ignore_top_level_dirs=[], - archive=None, + d, + objset, + image_path, + lambda file_counter: objset.new_spdxid( + "imagefile", str(file_counter) + ), + lambda filepath: [], + license_data=None, + ignore_dirs=[], + ignore_top_level_dirs=[], + archive=None, ) artifacts.extend(a) else: @@ -1234,7 +1391,6 @@ def create_image_spdx(d): set_timestamp_now(d, a, "builtTime") - if artifacts: objset.new_scoped_relationship( [image_build], From patchwork Tue Feb 24 23:00:17 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81848 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1CFE9F55425 for ; Tue, 24 Feb 2026 23:02:57 +0000 (UTC) Received: from mail-oi1-f169.google.com (mail-oi1-f169.google.com [209.85.167.169]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.33717.1771974167355734453 for ; Tue, 24 Feb 2026 15:02:47 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=VbJxLwy/; spf=pass (domain: gmail.com, ip: 209.85.167.169, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f169.google.com with SMTP id 5614622812f47-463a0e14b4dso1885381b6e.0 for ; Tue, 24 Feb 2026 15:02:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974166; x=1772578966; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kuZ09cpe+jljIdRJEO5Irjci+Kvl3vGKNxyzTjKNY64=; b=VbJxLwy/8H7mHGSZl2e8jGpzgur2S2mQFj87IepyX4zhUvEAUDPjJeb2q2HUvn6CdI G7rorqXjmY3+rmMoxpkLxwRpjzJlHmDFdlSf8APAhGjnOZajMiZKOjveT3lKKvDNOxeI fJ9kTtaleGQuVRdov4BJqYvqDYfxlbek2ml26E76ZROWr1LYUYM4nPWUuqlIPvK27Htz N7ULJOW2zH+qOLeki8JPLvfEFUUC7Gzk7YBMJUGS7Xl7sYgqiVqAP49xxBdW8Odu8YCg cJxSOI6/NjmwWjLfQ1BY4pwXE5XS7HfxuZ14pCni3oC0qGiP2cp8wLA3dbGPJam5D4zD JpEA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974166; x=1772578966; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kuZ09cpe+jljIdRJEO5Irjci+Kvl3vGKNxyzTjKNY64=; b=EDS9DPnOL4FLiLpGYinQQup0WRnkRuSr0IVrOAOhtW74MsQ/PO2tBhSf7fsiQ3khjJ GY9WF+fz6ffi2aJComjWeWKBxGSVQdh18+fzUVP+Q2hbsu5G3x5C0oDx5znBMzyDs0C6 YpGPtlLclhXZnjlL6vmW3GjtejsKHxiBAF7HKo99naVRkusYb4QKaZrrNhnm8+VvOApV GTszCUDou1qI5JzWUZu3lf3hsGRF9nUBfsL3cOSlLA2xtd/Hxd942baCjbzz3wSR7d5H ODL8NQ8Rrw3caFd5GECBVFbDEG1hEdx0dcK/VlaqPUFkUv3BqIBCi+AzNs8J0dKD3Gni oLQA== X-Gm-Message-State: AOJu0YzoaEJiRul1ABnSD7qCnxx/1qax5z1jJOlurjN1RzFrRLzDHQ1j 8VJHj2iAiezojB4/NYzU3t+Mcgh5eUqoiZAOeb3QjHS1g6Su700epIvniQfyxw== X-Gm-Gg: AZuq6aLL1ibpSz/1JU62UMpI48LSYq01c1SjgVC86Nwvhf0lccO6HdddckApVeqb3qF G14LXQqoVL/JB9RMfQYUlF/ACL2Z9+r0BQXJG7UjekzNDaEQLsHb7JdbL5LXCs/6eQDc67klqKO Kf2BF0ARwhy5AUy6GiFAUEnfJ8HFbJ7aBnOWae2enHYCLpTcrxCxx5zpcvL/abiBY8jYNImv/tn Jv2uINC800AV+iwgkyw8YnmhozZUS7YJdK6vVLF9K1r9tJtniAsM1BCKuOZEBxxICpmxnEexaBc D6959gPmcaaXgJsJQTtl7tpFSa/Dz49gr4vVmTgG4Tt8qYIcsLEl5tmlInfAz7cPPLNZaPnCEL8 MkUVwxIPCGPKZ5h6v9fk4tNiguzWWBr7kvokpciO99aTV4fR66kdos4oxLAn03hkFBm7XXMYi4y fffIWs6yov4iwsMgbNfjMIUwPuisb3V6s= X-Received: by 2002:a05:6808:320b:b0:462:d9a2:84e1 with SMTP id 5614622812f47-464463ef61fmr6505636b6e.60.1771974166451; Tue, 24 Feb 2026 15:02:46 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:45 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 4/8] spdx3: Add recipe SBoM task Date: Tue, 24 Feb 2026 16:00:17 -0700 Message-ID: <20260224230234.679049-5-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231913 Adds a task that will create the complete recipe-level SBoM for a given target recipe, following all dependencies. For example: ``` bitbake -c create_recipe_sbom zstd ``` Would produce the complete recipe SBoM for the zstd recipe, include all build time dependencies (recursively). The complete SBoM for all (target) recipes can be built with: ``` bitbake -c create_recipe_sbom meta-world-recipe-sbom ``` Signed-off-by: Joshua Watt --- meta/classes/create-spdx-3.0.bbclass | 28 +++++++++++++++++++ meta/classes/spdx-common.bbclass | 1 + meta/lib/oe/spdx30_tasks.py | 10 +++++++ .../meta/meta-world-recipe-sbom.bb | 26 +++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 meta/recipes-core/meta/meta-world-recipe-sbom.bb diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index 61223ee0a5..fdd0c60690 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -238,6 +238,34 @@ python do_create_package_spdx_setscene () { } addtask do_create_package_spdx_setscene +addtask do_create_recipe_sbom after create_recipe_spdx +python do_create_recipe_sbom() { + import oe.spdx30_tasks + from pathlib import Path + deploydir = Path(d.getVar("SPDXRECIPESBOMDEPLOY")) + oe.spdx30_tasks.create_recipe_sbom(d, deploydir) +} + +SSTATETASKS += "do_create_recipe_sbom" +do_create_recipe_sbom[recrdeptask] = "do_create_recipe_spdx" +do_create_recipe_sbom[nostamp] = "1" +do_create_recipe_sbom[sstate-inputdirs] = "${SPDXRECIPESBOMDEPLOY}" +do_create_recipe_sbom[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" +do_create_recipe_sbom[file-checksums] += "${SPDX3_DEP_FILES}" +do_create_recipe_sbom[cleandirs] = "${SPDXRECIPESBOMDEPLOY}" +do_create_recipe_sbom[vardeps] += "\ + SPDX_INCLUDE_BITBAKE_PARENT_BUILD \ + SPDX_PACKAGE_ADDITIONAL_PURPOSE \ + SPDX_PROFILES \ + SPDX_NAMESPACE_PREFIX \ + SPDX_UUID_NAMESPACE \ + " + +python do_create_recipe_sbom_setscene () { + sstate_setscene(d) +} +addtask do_create_recipe_sbom_setscene + python spdx30_build_started_handler () { import oe.spdx30_tasks d = e.data.createCopy() diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index 2804c27b0b..1ec4877a6a 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass @@ -25,6 +25,7 @@ SPDX_TOOL_VERSION ??= "1.0" SPDXRECIPEDEPLOY = "${SPDXDIR}/recipe-deploy" SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy" +SPDXRECIPESBOMDEPLOY = "${SPDXDIR}/recipes-bom-deploy" SPDX_INCLUDE_SOURCES ??= "0" SPDX_INCLUDE_COMPILED_SOURCES ??= "0" diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index a8b4525e3d..9a312a870d 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -1564,3 +1564,13 @@ def create_sdk_sbom(d, sdk_deploydir, spdx_work_dir, toolchain_outputname): oe.sbom30.write_jsonld_doc( d, objset, sdk_deploydir / (toolchain_outputname + ".spdx.json") ) + + +def create_recipe_sbom(d, deploydir): + sbom_name = d.getVar("PN") + "-recipe-sbom" + + recipe, recipe_objset = load_recipe_spdx(d) + + objset, sbom = oe.sbom30.create_sbom(d, sbom_name, [recipe], [recipe_objset]) + + oe.sbom30.write_jsonld_doc(d, objset, deploydir / (sbom_name + ".spdx.json")) diff --git a/meta/recipes-core/meta/meta-world-recipe-sbom.bb b/meta/recipes-core/meta/meta-world-recipe-sbom.bb new file mode 100644 index 0000000000..60209fba7e --- /dev/null +++ b/meta/recipes-core/meta/meta-world-recipe-sbom.bb @@ -0,0 +1,26 @@ +SUMMARY = "Generates a combined SBoM for all world recipes" +LICENSE = "MIT" + +INHIBIT_DEFAULT_DEPS = "1" + +PACKAGE_ARCH = "${MACHINE_ARCH}" + +inherit nopackages +deltask do_fetch +deltask do_unpack +deltask do_patch +deltask do_configure +deltask do_compile +deltask do_install + +do_prepare_recipe_sysroot[deptask] = "" + +WORLD_SBOM_EXCLUDE ?= "" + +python calculate_extra_depends() { + exclude = set('${WORLD_SBOM_EXCLUDE}'.split()) + for p in world_target: + if p == self_pn or p in exclude: + continue + deps.append(p) +} From patchwork Tue Feb 24 23:00:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81849 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 34F83F5542D for ; Tue, 24 Feb 2026 23:02:57 +0000 (UTC) Received: from mail-oi1-f175.google.com (mail-oi1-f175.google.com [209.85.167.175]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.33595.1771974168798694397 for ; Tue, 24 Feb 2026 15:02:48 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=dwxXNDl/; spf=pass (domain: gmail.com, ip: 209.85.167.175, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f175.google.com with SMTP id 5614622812f47-46392972257so4248540b6e.2 for ; Tue, 24 Feb 2026 15:02:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974168; x=1772578968; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=B26QEFGZ1BfU8osvAqsZdiqlbAEmeNAht5QsWjTxO+M=; b=dwxXNDl/RQl5zNvKhw7cp88Dwz1q6uLuctQyV4BJOEVEd1WrGbDuhPwaoXOLFILstc HuqkqHTw93vtsvmAYowP2TTmtsobsteNdZWXVQdxwXTeFsShp56AXqlSpRNeOFznGDxL p6uHU6DynTPVWq+NllFf1Eux9TMCfiwj2QKTvpUdOqhqZGY93+vK1hU91mjTu6T0z2Lf aoB1KtAWBOshF1fBB92ax1/Ya4XHxuVKSzUupK0HsTjxCz5V0867kAKUNINiz4tpoBio /c3EhTVc6R56dqB1z6EwfeI38PXpILcKdqxITgW3xZ+LCg8e9HA7lY0kDTH/p2pEruaz enow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974168; x=1772578968; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=B26QEFGZ1BfU8osvAqsZdiqlbAEmeNAht5QsWjTxO+M=; b=cvPie6fCnTuFxOYe8BBcUNmaGY1MjdY8QAI/iW0ZjYv5LlVzbgrDlfkLXLGlR2mIDM tA5mU74ZZetBIrBPNfrNCfDZZph1iruYGmxopKmzqPKmyRHsyno9kwcNDH666YPNKFcP A8WXJ/gCWO6VsN4UDIlxlOkevhH2lgFm8d6tDf7AoUYnwnyrWdzb9kjAJirWK6NuNMWe W48O8l0KnaibBiZMfdtt5bkaDTyUW8VGfGAIX0YtN9DVCR5RXD0q/uQkyXd2RIWIJCp6 zK/h2Xo0l+jqRfZdj1vJXdp0WnDLYMXii/UEQF2UL7GvGydLueta/onz6wfgstNfy6CS Y0fg== X-Gm-Message-State: AOJu0YzMvDfN8Sizaa/7Kxw6d6OGsCi6v08/2LtGVAKNwHEO2V37cMuM r84gHOL0ZW5nnEjHvRoafujCuR31olAz3qOvGefmZWeSnb3jZhrNLuL8YMRs5g== X-Gm-Gg: AZuq6aI2qz7UFLQARj5faC5FFm4khQrwyRyQMCTI7JUwdb6f4xui6RbnwAhnUoNj1rJ 4bd6+Z9MuyHVYN/1Z5VxMghyzOGYBsmR6Riw1m+YD9goe+zjZhp8K2NyCQQW5IbU3lF7xfsENzO YcEUAhAOJ6OcyToObM7bvpfTWVJSLAn+BY5BBioZyQZQoyYHcq/JXy+8t/4hp5gVAoubUkq2E+v D03LgPjlc8y6HRuO9BsQITOvgBl00GJ367ViQ75uEB8GvRQ4lrA9q/gTtqnMV+hXhxyVF5LUeAR A8djbh8wQCVqoJz1moRq84tqMLKkQEwAOJ2/Ei86xe20Y+U9zaBoXdTk0IkzL7tjcmp7rLi1Rpn haBsvO7JB7VMiNF+TajIpzLDt2tId2hN1Da1kwQu4Z5HTbSoXaF4qe2kbLrm9GSFYxlm1rJUv3U W4Zluxizn/Ay+KNB9CYeUb X-Received: by 2002:a05:6808:171c:b0:44f:6a19:433e with SMTP id 5614622812f47-4644617f594mr8573568b6e.15.1771974167846; Tue, 24 Feb 2026 15:02:47 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:47 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 5/8] spdx3: Add is-native property Date: Tue, 24 Feb 2026 16:00:18 -0700 Message-ID: <20260224230234.679049-6-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231914 Adds a custom is-native property to the recipe package to indicate if it is a native recipe Signed-off-by: Joshua Watt --- meta/lib/oe/sbom30.py | 20 ++++++++++++++++++++ meta/lib/oe/spdx30_tasks.py | 18 +++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 227ac51877..50a72fce39 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -118,6 +118,26 @@ class OEDocumentExtension(oe.spdx30.extension_Extension): ) +@oe.spdx30.register(OE_SPDX_BASE + "recipe-extension") +class OERecipeExtension(oe.spdx30.extension_Extension): + """ + This extension is added to recipe software_Packages to indicate various + useful bits of information about the recipe + """ + + CLOSED = True + + @classmethod + def _register_props(cls): + super()._register_props() + cls._add_property( + "is_native", + oe.spdx30.BooleanProp(), + OE_SPDX_BASE + "is-native", + max_count=1, + ) + + def spdxid_hash(*items): h = hashlib.md5() for i in items: diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 9a312a870d..fff1ca6bea 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -477,6 +477,10 @@ def set_purls(spdx_package, purls): ) +def get_is_native(d): + return bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d) + + def create_recipe_spdx(d): deploydir = Path(d.getVar("SPDXRECIPEDEPLOY")) deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) @@ -507,6 +511,11 @@ def create_recipe_spdx(d): ) ) + if get_is_native(d): + ext = oe.sbom30.OERecipeExtension() + ext.is_native = True + recipe.extension.append(ext) + set_purls(recipe, (d.getVar("SPDX_PACKAGE_URLS") or "").split()) # TODO: This doesn't work before do_unpack because the license text has to @@ -668,9 +677,7 @@ def create_spdx(d): spdx_workdir = Path(d.getVar("SPDXWORK")) include_sources = d.getVar("SPDX_INCLUDE_SOURCES") == "1" pkg_arch = d.getVar("SSTATE_PKGARCH") - is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( - "cross", d - ) + is_native = get_is_native(d) recipe, recipe_objset = load_recipe_spdx(d) @@ -1021,14 +1028,11 @@ def create_spdx(d): def create_package_spdx(d): deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) deploydir = Path(d.getVar("SPDXRUNTIMEDEPLOY")) - is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( - "cross", d - ) providers = oe.spdx_common.collect_package_providers(d) pkg_arch = d.getVar("SSTATE_PKGARCH") - if is_native: + if get_is_native(d): return bb.build.exec_func("read_subpackage_metadata", d) From patchwork Tue Feb 24 23:00:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81851 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 431DDF5542F for ; Tue, 24 Feb 2026 23:02:57 +0000 (UTC) Received: from mail-oi1-f171.google.com (mail-oi1-f171.google.com [209.85.167.171]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.33596.1771974169571849470 for ; Tue, 24 Feb 2026 15:02:49 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=YXpSlFdF; spf=pass (domain: gmail.com, ip: 209.85.167.171, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f171.google.com with SMTP id 5614622812f47-4648447c899so875272b6e.0 for ; Tue, 24 Feb 2026 15:02:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974169; x=1772578969; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6hV0ClIeawdfJviTL2VvpRpRo7713Nd0mIEcCehLwkw=; b=YXpSlFdFL3m0JunSKcQJZA6+iDFHpQXYB1azfddfOQ9L1fbDP6BI77CsF51nsV/7XN qZ+/PN0SGOFx2tZdm/ImrXV1B02iw+JjCIiMADebvxepWJFUHMjCJM1RkRj/EL4voAtv 0cspbYGtV2yVpjuMfqtKOx52PkI8l2ONmhpkJalj3Q8VyvB768FokaR7sbYYrAJOD4lL dRU+3z94RdyO1KsrDD8MKeC381E9jdx37zlYWC2bsJMbdX7vcaJAc3syyT1ZcWHs1+DO 0nd9NwZZKaJUHdvVo+O0jL2TWpfy2Pw8T8mPYxuvHFbVe94eZc2ATX9mSHuYIgYWqgFN w42w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974169; x=1772578969; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=6hV0ClIeawdfJviTL2VvpRpRo7713Nd0mIEcCehLwkw=; b=PfiPv6OwMxLurc9sCBBvT+klrQQVdcUyi88r00n7HcM8xpfFr/vEzLzWejXve74naa KZ8aWFPulZQZ+l+glntggefH/Whncme++YxXK/thtKcg0N/NAlIejp1IYkUMCTyDNN5Z 9D4c20QGZ5plnwVYZ1tALdI0YsQ9fXGgzefHK3/BNaY0ptlqD/G3lI+MKZtIkAzd0/XA ras3io2vBL9bXBzF7PYqMbnoRaNAX7HwlBdRs0GuXA/GQFbb4VuCQULi7Z43ml95J0lJ HNmt22YDSKA2WlNfTpisdverOUY5jFA/ocsgYUj82AZIpIv90TtVL8wpYWqyZgonNkl6 xECA== X-Gm-Message-State: AOJu0YztwMcVzFanRxNkKP4B6jpddWoP2iuMgj8RKMA/YGhO0n9nh2eQ kwQN9JCyMg5BUfWNkBc48SPRhiwyuAARLvsKjS9Y0zP3JPpfOvDNgEIrSan5nA== X-Gm-Gg: AZuq6aKfc6zKFCk5K4mcSnQlyHZKIQpq9b5rZyfXxMFY0RnAUajrQ+4yDtuxjZwqEe2 cF4uDVmjKHo2RflUdPCH3oF51UxpQxnYIGSxqEqg0/vJu+NoIOv0hYNkeMuljZIdaNqRS2hvv94 5iE7t2pVCpFx1ghFKUUq/MWMFkyNO4uqJxd3oDdpDCz05ijLq/xrxFgJJVfOTxe9PZhd1DTvzlW GKx8mSiphk5hYnJU+Qcks/qAwm3Wo1TxH9dy9akdpobY5OIHzoNyUukA3qmpZXz/7Lr+3FN//fh b0SGRMiSVdImkASYY3/LEHdr2qCODim/4tRQMR4mjzschAqEz5Cc+1KmsSzCsy/yIXNQVVsshKz BOc+rCKoKJF6C/EdCR4NifIsuAwOagyw++7jFRhkx29FGVSwUApgFnPV3BYJCgKJDeSgv0wC+5d a4AFEhw0zuFRKwep/s9gu5 X-Received: by 2002:a05:6808:5296:b0:451:4da2:47d1 with SMTP id 5614622812f47-464463b969bmr8417391b6e.45.1771974168591; Tue, 24 Feb 2026 15:02:48 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:48 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 6/8] spdx30: Include patch file information in VEX Date: Tue, 24 Feb 2026 16:00:19 -0700 Message-ID: <20260224230234.679049-7-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231915 Modifies the SPDX VEX output to include the patches that fix a particular vulnerability. This is done by adding a `patchedBy` relationship from the `VexFixedVulnAssessmentRelationship` to the `File` that provides the fix. If the file can be located without fetching (e.g. is a file:// in SRC_URI), the checksum will be included. Signed-off-by: Joshua Watt --- meta/lib/oe/sbom30.py | 60 ++++++++++++++------------- meta/lib/oe/spdx30_tasks.py | 81 ++++++++++++++++++++++++++++--------- 2 files changed, 92 insertions(+), 49 deletions(-) diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 50a72fce39..21f084dc16 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -620,37 +620,38 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): ) spdx_file.extension.append(OELicenseScannedExtension()) - def new_file(self, _id, name, path, *, purposes=[]): - sha256_hash = bb.utils.sha256_file(path) + def new_file(self, _id, name, path, *, purposes=[], hashfile=True): + if hashfile: + sha256_hash = bb.utils.sha256_file(path) - for f in self.by_sha256_hash.get(sha256_hash, []): - if not isinstance(f, oe.spdx30.software_File): - continue + for f in self.by_sha256_hash.get(sha256_hash, []): + if not isinstance(f, oe.spdx30.software_File): + continue - if purposes: - new_primary = purposes[0] - new_additional = [] + if purposes: + new_primary = purposes[0] + new_additional = [] - if f.software_primaryPurpose: - new_additional.append(f.software_primaryPurpose) - new_additional.extend(f.software_additionalPurpose) + if f.software_primaryPurpose: + new_additional.append(f.software_primaryPurpose) + new_additional.extend(f.software_additionalPurpose) - new_additional = sorted( - list(set(p for p in new_additional if p != new_primary)) - ) + new_additional = sorted( + list(set(p for p in new_additional if p != new_primary)) + ) - f.software_primaryPurpose = new_primary - f.software_additionalPurpose = new_additional + f.software_primaryPurpose = new_primary + f.software_additionalPurpose = new_additional - if f.name != name: - for e in f.extension: - if isinstance(e, OEFileNameAliasExtension): - e.aliases.append(name) - break - else: - f.extension.append(OEFileNameAliasExtension(aliases=[name])) + if f.name != name: + for e in f.extension: + if isinstance(e, OEFileNameAliasExtension): + e.aliases.append(name) + break + else: + f.extension.append(OEFileNameAliasExtension(aliases=[name])) - return f + return f spdx_file = oe.spdx30.software_File( _id=_id, @@ -661,12 +662,13 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): spdx_file.software_primaryPurpose = purposes[0] spdx_file.software_additionalPurpose = purposes[1:] - spdx_file.verifiedUsing.append( - oe.spdx30.Hash( - algorithm=oe.spdx30.HashAlgorithm.sha256, - hashValue=sha256_hash, + if hashfile: + spdx_file.verifiedUsing.append( + oe.spdx30.Hash( + algorithm=oe.spdx30.HashAlgorithm.sha256, + hashValue=sha256_hash, + ) ) - ) return self.add(spdx_file) diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index fff1ca6bea..1c9346128c 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -568,44 +568,63 @@ def create_recipe_spdx(d): if include_vex != "none": patched_cves = oe.cve_check.get_patched_cves(d) for cve, patched_cve in patched_cves.items(): - decoded_status = { - "mapping": patched_cve["abbrev-status"], - "detail": patched_cve["status"], - "description": patched_cve.get("justification", None), - } + mapping = patched_cve["abbrev-status"] + detail = patched_cve["status"] + description = patched_cve.get("justification", None) + resources = patched_cve.get("resource", []) # If this CVE is fixed upstream, skip it unless all CVEs are # specified. - if ( - include_vex != "all" - and "detail" in decoded_status - and decoded_status["detail"] - in ( - "fixed-version", - "cpe-stable-backport", - ) + if include_vex != "all" and detail in ( + "fixed-version", + "cpe-stable-backport", ): bb.debug(1, "Skipping %s since it is already fixed upstream" % cve) continue spdx_cve = recipe_objset.new_cve_vuln(cve) - cve_by_status.setdefault(decoded_status["mapping"], {})[cve] = ( + cve_by_status.setdefault(mapping, {})[cve] = ( spdx_cve, - decoded_status["detail"], - decoded_status["description"], + detail, + description, + resources, ) all_cves = set() for status, cves in cve_by_status.items(): for cve, items in cves.items(): - spdx_cve, detail, description = items + spdx_cve, detail, description, resources = items spdx_cve_id = oe.sbom30.get_element_link_id(spdx_cve) all_cves.add(spdx_cve) if status == "Patched": - recipe_objset.new_vex_patched_relationship([spdx_cve_id], [recipe]) + spdx_vex = recipe_objset.new_vex_patched_relationship( + [spdx_cve_id], [recipe] + ) + patches = [] + for idx, filepath in enumerate(resources): + patches.append( + recipe_objset.new_file( + recipe_objset.new_spdxid( + "patch", str(idx), os.path.basename(filepath) + ), + os.path.basename(filepath), + filepath, + purposes=[oe.spdx30.software_SoftwarePurpose.patch], + hashfile=os.path.isfile(filepath), + ) + ) + + if patches: + recipe_objset.new_scoped_relationship( + spdx_vex, + oe.spdx30.RelationshipType.patchedBy, + oe.spdx30.LifecycleScopeType.build, + patches, + ) + elif status == "Unpatched": recipe_objset.new_vex_unpatched_relationship([spdx_cve_id], [recipe]) elif status == "Ignored": @@ -751,12 +770,14 @@ def create_spdx(d): # Collect all VEX statements from the recipe vex_statements = {} + vex_patches = {} for rel in recipe_objset.foreach_filter( oe.spdx30.Relationship, relationshipType=oe.spdx30.RelationshipType.hasAssociatedVulnerability, ): for cve in rel.to: vex_statements[cve] = [] + vex_patches[cve] = [] for cve in vex_statements.keys(): for rel in recipe_objset.foreach_filter( @@ -764,6 +785,13 @@ def create_spdx(d): from_=cve, ): vex_statements[cve].append(rel) + if rel.relationshipType == oe.spdx30.RelationshipType.fixedIn: + for patch_rel in recipe_objset.foreach_filter( + oe.spdx30.Relationship, + relationshipType=oe.spdx30.RelationshipType.patchedBy, + from_=rel, + ): + vex_patches[cve].extend(patch_rel.to) # Write out the package SPDX data now. It is not complete as we cannot # write the runtime data, so write it to a staging area and a later task @@ -889,7 +917,9 @@ def create_spdx(d): # Add concluded license relationship if manually set # Only add when license analysis has been explicitly performed - concluded_license_str = d.getVar("SPDX_CONCLUDED_LICENSE:%s" % package) or d.getVar("SPDX_CONCLUDED_LICENSE") + concluded_license_str = d.getVar( + "SPDX_CONCLUDED_LICENSE:%s" % package + ) or d.getVar("SPDX_CONCLUDED_LICENSE") if concluded_license_str: concluded_spdx_license = add_license_expression( d, build_objset, concluded_license_str, license_data @@ -915,9 +945,20 @@ def create_spdx(d): for cve, vexes in vex_statements.items(): for vex in vexes: if vex.relationshipType == oe.spdx30.RelationshipType.fixedIn: - pkg_objset.new_vex_patched_relationship( + spdx_vex = pkg_objset.new_vex_patched_relationship( [oe.sbom30.get_element_link_id(cve)], [spdx_package] ) + if vex_patches[cve]: + pkg_objset.new_scoped_relationship( + spdx_vex, + oe.spdx30.RelationshipType.patchedBy, + oe.spdx30.LifecycleScopeType.build, + [ + oe.sbom30.get_element_link_id(p) + for p in vex_patches[cve] + ], + ) + elif vex.relationshipType == oe.spdx30.RelationshipType.affects: pkg_objset.new_vex_unpatched_relationship( [oe.sbom30.get_element_link_id(cve)], [spdx_package] From patchwork Tue Feb 24 23:00:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81850 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2CBEBF55429 for ; Tue, 24 Feb 2026 23:02:57 +0000 (UTC) Received: from mail-oi1-f170.google.com (mail-oi1-f170.google.com [209.85.167.170]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.33597.1771974170479411133 for ; Tue, 24 Feb 2026 15:02:50 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=lvBFP7IE; spf=pass (domain: gmail.com, ip: 209.85.167.170, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f170.google.com with SMTP id 5614622812f47-463d81452abso3898684b6e.0 for ; Tue, 24 Feb 2026 15:02:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974169; x=1772578969; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RgZWWYUETcp+KAK54Y1gHYnk1xGLeXOCGllecsNhDhw=; b=lvBFP7IEdprvke3pEGLe6PCl5+5P0xdyigPbYz4CRz2W2p92JB+5H1NDo7ToqSVxgX WMovmWo/0wegSvOvi4PxEGeBO1vr+E7asFxjbQn+rh3HBfEmRYc16phrxVTbyn8Rzfpc mXb2+GlfitLJwr7C4UZ0j6jMKNigP+a4ylD5+aJv6WMY8DDOqchMPbt1x32lLUqiv/dX tfSme4b2ikmMjZEwGsqUlsr9Eo/M3oGBAyFAzD0d/03xqxIOQrx9T7OlvsTaW0+rK51v +M7yvFRwyMN4jy8ZDDoFH32CSTy3WfcrC6LXSk013lwQiPxrVcBQ/h6Is9TVpgvHQGRR pC7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974169; x=1772578969; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RgZWWYUETcp+KAK54Y1gHYnk1xGLeXOCGllecsNhDhw=; b=I/u6htYtm9HXHhdm9O9Bkq7XAh+3jC3tdFCYvJxx0XuhGRUVoqdRRx0jtN15cvy+hW mc9b2UqsROXZVivg1GLI9oLQoycIQw9DL4hDXFfJJCxcVJlSLjvkbAR3kkqMlJLXimKi FuYswPndsdscL+CcNZO5WQcxZxnYuchpflXKOo8QebW2R8HN9NcTcCb3H96Aquj82e1Z KYCm71WE2BY7obWtSgJDKHEKIx3yaIU8xt26f4sWucPYMahNMeIFuc8v8gf9BfNjOBVf KeE6ktwhL6LFm53GkfELP3rCfsL2+c+4A0ucZp8D1UmS+4LWASjeR71xzOIOmv2bzrhx UC4g== X-Gm-Message-State: AOJu0YyDyWllaBh1MVQp+0siZ51qG2ItOsUKSXXSclMbHNKpDYnc1Hrz dZV9mMB8Z/b7BC4ARqVRaH4FK1U06wHoLadqclu8mqJj2zhDsCiwxX7cOWVXNw== X-Gm-Gg: AZuq6aI6pybtk8L6dA8zHwskH3Dl3peZXsX2+H2PbXpwV4FAuVfviZytF530gTvr0wm kR5dcMoCz8jZnor2slOJJJWfwv/lNP+Q+EVVK3SX5fMV016GoPoCA69p0fZN0WQhk1i4v+5QgER WNYbI1G2VfOrJWAWeXAG90e4LHLAB7kY+LOQn/n7D49VtjGwShRbC5WdV7+t99qxl/+E9HHRsQY HbHZHbozaZl/gFY0R7vIX2/Ji05chUc3Y45Q3RWVEI1OA1XoaOI3U7XZJk6ycE8GM3e9BoBiS0x nBw24QRwfnb9zjdWl/ipX8RnE00dAs1DUKPTh5ma/o5wf5dQEL3PMNSwlI9mAv9E39o97E/zXEX S1Q6CMRXdTXpy9hpR0txRSpOJFk1zECsh4HglTgXM/Gz2BRTl+UDusQzO+L2RYWdY8cCt+Lm5px NzLJsUss0l4d7tDkcGDJ+5 X-Received: by 2002:a05:6808:4f07:b0:45c:8bc0:fcdf with SMTP id 5614622812f47-4644616cb87mr7362730b6e.10.1771974169615; Tue, 24 Feb 2026 15:02:49 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:48 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 7/8] spdx: De-duplicate CreationInfo Date: Tue, 24 Feb 2026 16:00:20 -0700 Message-ID: <20260224230234.679049-8-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231916 De-duplicates CreationInfo objects that are identical (except for ID) when writing out an SBoM. This significantly reduces the number of CreationInfo objects that end up in the final document. Signed-off-by: Joshua Watt --- meta/lib/oe/sbom30.py | 112 ++++++++++++++++++++++++++++++------------ meta/lib/oe/spdx30.py | 2 +- 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 21f084dc16..55a2863d2d 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -14,6 +14,7 @@ import uuid import os import oe.spdx_common from datetime import datetime, timezone +from contextlib import contextmanager OE_SPDX_BASE = "https://rdf.openembedded.org/spdx/3.0/" @@ -191,6 +192,25 @@ def to_list(l): return l +class Dedup(object): + def __init__(self, objset): + self.unique = set() + self.dedup = {} + self.objset = objset + + def find_duplicates(self, cmp, typ, **kwargs): + for o in self.objset.foreach_filter(typ, **kwargs): + for u in self.unique: + if cmp(u, o): + self.dedup[o] = u + break + else: + self.unique.add(o) + + def get(self, o): + return self.dedup.get(o, o) + + class ObjectSet(oe.spdx30.SHACLObjectSet): def __init__(self, d): super().__init__() @@ -895,6 +915,45 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): self.missing_ids -= set(imports.keys()) return self.missing_ids + @contextmanager + def deduplicate(self): + d = Dedup(self) + + yield d + + visited = set() + + def visit(o, path): + if isinstance(o, oe.spdx30.SHACLObject): + if o in visited: + return False + visited.add(o) + + for k in o: + v = o[k] + if isinstance(v, oe.spdx30.SHACLObject): + o[k] = d.get(v) + + elif isinstance(o, oe.spdx30.ListProxy): + for idx, v in enumerate(o): + if isinstance(v, oe.spdx30.SHACLObject): + o[idx] = d.get(v) + + return True + + if d.dedup: + for o in self.objects: + o.walk(visit) + + for k, v in d.dedup.items(): + bb.debug( + 1, + f"Removing duplicate {k.__class__.__name__} {k._id or id(k)} -> {v._id or id(v)}", + ) + self.objects.discard(k) + + self.create_index() + def load_jsonld(d, path, required=False): deserializer = oe.spdx30.JSONLDDeserializer() @@ -1080,39 +1139,28 @@ def create_sbom(d, name, root_elements, add_objectsets=[]): # SBoM should be the only root element of the document objset.doc.rootElement = [sbom] - # De-duplicate licenses - unique = set() - dedup = {} - for lic in objset.foreach_type(oe.spdx30.simplelicensing_LicenseExpression): - for u in unique: - if ( - u.simplelicensing_licenseExpression - == lic.simplelicensing_licenseExpression - and u.simplelicensing_licenseListVersion - == lic.simplelicensing_licenseListVersion - ): - dedup[lic] = u - break - else: - unique.add(lic) - - if dedup: - for rel in objset.foreach_filter( - oe.spdx30.Relationship, - relationshipType=oe.spdx30.RelationshipType.hasDeclaredLicense, - ): - rel.to = [dedup.get(to, to) for to in rel.to] - - for rel in objset.foreach_filter( - oe.spdx30.Relationship, - relationshipType=oe.spdx30.RelationshipType.hasConcludedLicense, - ): - rel.to = [dedup.get(to, to) for to in rel.to] + def cmp_license_expression(a, b): + return ( + a.simplelicensing_licenseExpression == b.simplelicensing_licenseExpression + and a.simplelicensing_licenseListVersion + == b.simplelicensing_licenseListVersion + ) - for k, v in dedup.items(): - bb.debug(1, f"Removing duplicate License {k._id} -> {v._id}") - objset.objects.remove(k) + def cmp_creation_info(a, b): + data_a = {k: a[k] for k in a} + data_b = {k: b[k] for k in b} + data_a["@id"] = "" + data_b["@id"] = "" + return data_a == data_b + + with objset.deduplicate() as dedup: + # De-duplicate licenses + dedup.find_duplicates( + cmp_license_expression, + oe.spdx30.simplelicensing_LicenseExpression, + ) - objset.create_index() + # Deduplicate creation info + dedup.find_duplicates(cmp_creation_info, oe.spdx30.CreationInfo) return objset, sbom diff --git a/meta/lib/oe/spdx30.py b/meta/lib/oe/spdx30.py index cd97eebd18..1f58402ffc 100644 --- a/meta/lib/oe/spdx30.py +++ b/meta/lib/oe/spdx30.py @@ -701,7 +701,7 @@ class SHACLObject(object): self.__dict__["_obj_data"][iri] = prop.init() def __iter__(self): - return self._OBJ_PROPERTIES.keys() + return iter(self._OBJ_PROPERTIES.keys()) def walk(self, callback, path=None): """ From patchwork Tue Feb 24 23:00:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81847 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 27EE1F5542B for ; Tue, 24 Feb 2026 23:02:57 +0000 (UTC) Received: from mail-oi1-f170.google.com (mail-oi1-f170.google.com [209.85.167.170]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.33719.1771974171192882713 for ; Tue, 24 Feb 2026 15:02:51 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=Xv+3IsLV; spf=pass (domain: gmail.com, ip: 209.85.167.170, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f170.google.com with SMTP id 5614622812f47-45f09874c4cso3667410b6e.3 for ; Tue, 24 Feb 2026 15:02:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771974170; x=1772578970; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6Tj4cGGvEEbE1QT7Q+4TdJLcoNUT6Vuz9fhKtg+CMko=; b=Xv+3IsLVJlaXaDTPAfMpPoy9a4nm4SHaSvRq3L17KBXZX8YsecXKGbo3oHfeA2VtSr AEUbIP52X753JhT5oSJAkkO8xIxO5fCVlp68txmJZCmxZlt89fwz/1vaXgiIWHfqhK98 T19R5vlqexbsxfZrIzstAb+wH4oyctV1kPxJqe+gCZ54g6Hdk6d9o/P59hdVngRJ4vtr 0EcB0/Ha/ccEzouRs67WIfCIPb6AAVe4LlUqal2o8aHcuBUtNXcktKpHpROF2dWtL2lU Qz14+h3+dRxez5aK2VRwYXkaTyzF4ejKTbCHfqZ5sDbOY1EtQcovngy4X90yTdL8MsG7 L23g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771974170; x=1772578970; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=6Tj4cGGvEEbE1QT7Q+4TdJLcoNUT6Vuz9fhKtg+CMko=; b=LZ1XVCrBNCj3X7GQOE1P6h6RBtGAbehct4qMTx9XvGgy7sGa3YOmSp77Bnaam+H1kJ IOBS5d7aBVvkXkxouLZvOzQhgBRaH55P/cgU59EXEDTrVPrjVW3BB3RTL4Cl7nVXLj4+ 9Sck55sMB8Kc48G77VtQ7tryU/oYFX0n4/k2S83YvR+2tLolIqkKhbcmhSwuXCL0YWYL /NlF8ogN77rI8IA9Kjd71TtEeAD6HKdIDTlTAg+mkvaK4JHE2E5zs/p3pEyyn81UA9zz PsHmoce9tqVvNFaUxhiZGwXPm6xhyen0iVquu11EDkFNLtoGpzddi2mXtUISl6E7MNuC Xt7A== X-Gm-Message-State: AOJu0YwZTUxmvJB0ps4eT0H2azRK6whPxkhgKhbAaOpswzkpRYJf2TGh ftJ2x6iZgyBmDc/Rv5r4rznWMfobKv2SoY48EWbAmUx8WDkchxuCpsH8sPLMyw== X-Gm-Gg: AZuq6aLcNqhRbM9tuy5WlFlvpc1gLp1MB8Ad+tPRAdESjPSM3N15dNxHwStzFzf1yDF GVEkS+mEOzd++Y/a33cmvOqdJPAwbwpoo6m541thKc3NxhaU49//PqydcjY2ZCBroYZSDAoRPO1 IkJ7NqFZFn6JI+aB9geB1sezo6NOUUqbb+GbpUcqT3k2bieIOs6abXgI8qDO1CdQpcw6JAbqGw3 mLGqEddMLCjKmzuJXJRN1G5U6ogK+AYhUmLAzJrYr8A241hd4ujs2S4eP34XoxKqAW3xC456qIF ps0pebdKJjRmZzBK3GXGWh5PtHW0STH9L4A4y0fxTZCR5fifrXZxmTXr9xytwoC1XeC5oMLsu1N WLcXTzhl+OrvZgVuJL/dxJW5j9DYq4XxTb3fGlxRdeAml1JF2T/cro5YeXBh0xLhYJkFb6hdbls mq4afv08qMNaxoAvtEzRNJ X-Received: by 2002:a05:6808:1598:b0:44f:8f27:e39a with SMTP id 5614622812f47-4644612dcf9mr8083038b6e.8.1771974170370; Tue, 24 Feb 2026 15:02:50 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::ba6c]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4644a1efc76sm7912024b6e.18.2026.02.24.15.02.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 15:02:49 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, ross.burton@arm.com, Joshua Watt Subject: [OE-core][PATCH v2 8/8] spdx_common: Check for dependent task in task flags Date: Tue, 24 Feb 2026 16:00:21 -0700 Message-ID: <20260224230234.679049-9-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260224230234.679049-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> <20260224230234.679049-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 24 Feb 2026 23:02:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231917 Checks that the task being used to detect dependencies is present in at least one dependency task flag of the current task. This helps prevent errors where the wrong task is specified and never found. Signed-off-by: Joshua Watt --- meta/lib/oe/spdx_common.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/meta/lib/oe/spdx_common.py b/meta/lib/oe/spdx_common.py index 72c24180d5..3aaf2a9c8b 100644 --- a/meta/lib/oe/spdx_common.py +++ b/meta/lib/oe/spdx_common.py @@ -96,6 +96,17 @@ def collect_direct_deps(d, dep_task): taskdepdata = d.getVar("BB_TASKDEPDATA", False) + # Check that the task is listed one of the task dependency flags of the + # current task + depflags = ( + set((d.getVarFlag(current_task, "deptask") or "").split()) + | set((d.getVarFlag(current_task, "rdeptask") or "").split()) + | set((d.getVarFlag(current_task, "recrdeptask") or "").split()) + ) + + if not dep_task in depflags: + bb.fatal(f"Task {dep_task} was not found in any dependency flag of {pn}:{current_task}") + for this_dep in taskdepdata.values(): if this_dep[0] == pn and this_dep[1] == current_task: break