| Message ID | 20251113184151.511039-2-stondo@gmail.com |
|---|---|
| State | New |
| Headers | show |
| Series | SPDX 3.0.1 documentation and bugfix | expand |
On Thu, Nov 13, 2025 at 11:42 AM Stefano Tondo via lists.openembedded.org <stondo=gmail.com@lists.openembedded.org> wrote: > > From: Stefano Tondo <stefano.tondo.ext@siemens.com> > > Fix incorrect function call when processing SPDX_CUSTOM_ANNOTATION_VARS. > The code was calling new_annotation() as a standalone function, but it > should be called as a method on the build_objset object. > > Error: > new_annotation(d, build_objset, build, ...) > > Corrected to: > build_objset.new_annotation(d, build_objset, build, ...) > > This bug would cause a NameError at runtime if SPDX_CUSTOM_ANNOTATION_VARS > was set to a non-empty value, preventing SPDX document generation. > > The fix aligns with how new_annotation() is called elsewhere in the > codebase and matches the SBOMObjset class method signature. LGTM, thanks Reviewed-by: Joshua Watt <JPEWhacker@gmail.com> > > Signed-off-by: Stefano Tondo <stefano.tondo.ext@siemens.com> > --- > meta/lib/oe/spdx30_tasks.py | 4 +- > meta/lib/oeqa/selftest/cases/spdx.py | 74 ++++++++++++++++++++++++++++ > 2 files changed, 75 insertions(+), 3 deletions(-) > > diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py > index f2f133005d..4d11b3c289 100644 > --- a/meta/lib/oe/spdx30_tasks.py > +++ b/meta/lib/oe/spdx30_tasks.py > @@ -498,9 +498,7 @@ def create_spdx(d): > build_objset.set_is_native(is_native) > > for var in (d.getVar("SPDX_CUSTOM_ANNOTATION_VARS") or "").split(): > - new_annotation( > - d, > - build_objset, > + build_objset.new_annotation( > build, > "%s=%s" % (var, d.getVar(var)), > oe.spdx30.AnnotationType.other, > diff --git a/meta/lib/oeqa/selftest/cases/spdx.py b/meta/lib/oeqa/selftest/cases/spdx.py > index 8cd4e83ca2..eda41cf952 100644 > --- a/meta/lib/oeqa/selftest/cases/spdx.py > +++ b/meta/lib/oeqa/selftest/cases/spdx.py > @@ -286,3 +286,77 @@ class SPDX30Check(SPDX3CheckBase, OESelftestTestCase): > break > else: > self.assertTrue(False, "Unable to find imported Host SpdxID") > + > + def test_custom_annotation_vars(self): > + """ > + Test that SPDX_CUSTOM_ANNOTATION_VARS properly creates annotations > + without runtime errors. This is a regression test for the bug where > + new_annotation() was called as a standalone function instead of as > + a method on build_objset, causing a NameError. > + > + The test verifies: > + 1. The build completes successfully (no NameError) > + 2. Each configured annotation variable appears exactly once > + 3. The annotation values match the configured variables > + > + We check for exact equality (not >=) to prevent regressions where > + one annotation might appear multiple times while another is missing. > + """ > + ANNOTATION_VAR1 = "TestAnnotation1" > + ANNOTATION_VAR2 = "TestAnnotation2" > + > + # This will fail with NameError if new_annotation() is called incorrectly > + objset = self.check_recipe_spdx( > + "base-files", > + "{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json", > + extraconf=textwrap.dedent( > + f"""\ > + ANNOTATION1 = "{ANNOTATION_VAR1}" > + ANNOTATION2 = "{ANNOTATION_VAR2}" > + SPDX_CUSTOM_ANNOTATION_VARS = "ANNOTATION1 ANNOTATION2" > + """ > + ), > + ) > + > + # If we got here, the build succeeded (no NameError) > + # Now verify the annotations were actually created > + > + # Find the build element > + build = None > + for o in objset.foreach_type(oe.spdx30.build_Build): > + build = o > + break > + > + self.assertIsNotNone(build, "Unable to find Build element") > + > + # Find annotation objects that reference our build > + found_annotations = [] > + for obj in objset.objects(): > + if isinstance(obj, oe.spdx30.Annotation): > + if hasattr(obj, "subject") and build._id == obj.subject._id: > + found_annotations.append(obj) > + > + # Check each annotation separately to ensure exactly one occurrence of each > + annotation1_count = 0 > + annotation2_count = 0 > + > + for annotation in found_annotations: > + if hasattr(annotation, "statement"): > + if f"ANNOTATION1={ANNOTATION_VAR1}" in annotation.statement: > + annotation1_count += 1 > + self.logger.info(f"Found ANNOTATION1: {annotation.statement}") > + if f"ANNOTATION2={ANNOTATION_VAR2}" in annotation.statement: > + annotation2_count += 1 > + self.logger.info(f"Found ANNOTATION2: {annotation.statement}") > + > + # Each annotation should appear exactly once > + self.assertEqual( > + annotation1_count, > + 1, > + f"Expected exactly 1 occurrence of ANNOTATION1, found {annotation1_count}", > + ) > + self.assertEqual( > + annotation2_count, > + 1, > + f"Expected exactly 1 occurrence of ANNOTATION2, found {annotation2_count}", > + ) > -- > 2.51.1 > > > -=-=-=-=-=-=-=-=-=-=-=- > Links: You receive all messages sent to this group. > View/Reply Online (#226259): https://lists.openembedded.org/g/openembedded-core/message/226259 > Mute This Topic: https://lists.openembedded.org/mt/116279440/3616693 > Group Owner: openembedded-core+owner@lists.openembedded.org > Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [JPEWhacker@gmail.com] > -=-=-=-=-=-=-=-=-=-=-=- >
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index f2f133005d..4d11b3c289 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -498,9 +498,7 @@ def create_spdx(d): build_objset.set_is_native(is_native) for var in (d.getVar("SPDX_CUSTOM_ANNOTATION_VARS") or "").split(): - new_annotation( - d, - build_objset, + build_objset.new_annotation( build, "%s=%s" % (var, d.getVar(var)), oe.spdx30.AnnotationType.other, diff --git a/meta/lib/oeqa/selftest/cases/spdx.py b/meta/lib/oeqa/selftest/cases/spdx.py index 8cd4e83ca2..eda41cf952 100644 --- a/meta/lib/oeqa/selftest/cases/spdx.py +++ b/meta/lib/oeqa/selftest/cases/spdx.py @@ -286,3 +286,77 @@ class SPDX30Check(SPDX3CheckBase, OESelftestTestCase): break else: self.assertTrue(False, "Unable to find imported Host SpdxID") + + def test_custom_annotation_vars(self): + """ + Test that SPDX_CUSTOM_ANNOTATION_VARS properly creates annotations + without runtime errors. This is a regression test for the bug where + new_annotation() was called as a standalone function instead of as + a method on build_objset, causing a NameError. + + The test verifies: + 1. The build completes successfully (no NameError) + 2. Each configured annotation variable appears exactly once + 3. The annotation values match the configured variables + + We check for exact equality (not >=) to prevent regressions where + one annotation might appear multiple times while another is missing. + """ + ANNOTATION_VAR1 = "TestAnnotation1" + ANNOTATION_VAR2 = "TestAnnotation2" + + # This will fail with NameError if new_annotation() is called incorrectly + objset = self.check_recipe_spdx( + "base-files", + "{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json", + extraconf=textwrap.dedent( + f"""\ + ANNOTATION1 = "{ANNOTATION_VAR1}" + ANNOTATION2 = "{ANNOTATION_VAR2}" + SPDX_CUSTOM_ANNOTATION_VARS = "ANNOTATION1 ANNOTATION2" + """ + ), + ) + + # If we got here, the build succeeded (no NameError) + # Now verify the annotations were actually created + + # Find the build element + build = None + for o in objset.foreach_type(oe.spdx30.build_Build): + build = o + break + + self.assertIsNotNone(build, "Unable to find Build element") + + # Find annotation objects that reference our build + found_annotations = [] + for obj in objset.objects(): + if isinstance(obj, oe.spdx30.Annotation): + if hasattr(obj, "subject") and build._id == obj.subject._id: + found_annotations.append(obj) + + # Check each annotation separately to ensure exactly one occurrence of each + annotation1_count = 0 + annotation2_count = 0 + + for annotation in found_annotations: + if hasattr(annotation, "statement"): + if f"ANNOTATION1={ANNOTATION_VAR1}" in annotation.statement: + annotation1_count += 1 + self.logger.info(f"Found ANNOTATION1: {annotation.statement}") + if f"ANNOTATION2={ANNOTATION_VAR2}" in annotation.statement: + annotation2_count += 1 + self.logger.info(f"Found ANNOTATION2: {annotation.statement}") + + # Each annotation should appear exactly once + self.assertEqual( + annotation1_count, + 1, + f"Expected exactly 1 occurrence of ANNOTATION1, found {annotation1_count}", + ) + self.assertEqual( + annotation2_count, + 1, + f"Expected exactly 1 occurrence of ANNOTATION2, found {annotation2_count}", + )