diff mbox series

[5/6] runqueue: rework 'bitbake -S printdiff' logic

Message ID 20231208101522.198832-5-alex@linutronix.de
State New
Headers show
Series [1/6] selftest/sstatetest: print output from bitbake with actual newlines, not \n | expand

Commit Message

Alexander Kanavin Dec. 8, 2023, 10:15 a.m. UTC
Previously printdiff code would iterate over tasks that were reported as invalid or absent,
trying to follow dependency chains that would reach the most basic invalid items in the tree.

While this works in tightly controlled local builds, it can lead to bizarre reports
against industrial-sized sstate caches, as the code would not consider whether the
overall target can be fulfilled from valid sstate objects, and instead report
missing sstate signature files that perhaps were never even created due to hash
equivalency providing shortcuts in builds.

This commit reworks the logic in two ways:

- start the iteration over final targets rather than missing objects
and stop at the first invalid object

- if a given object can be fulfilled from sstate, do not recurse into
its dependencies; bitbake wouldn't care if they're absent, and neither
should printdiff

This changes the console output significantly: where previously
printdiff would report the most basic invalid tasks it could find,
with this change the most top-level tasks are reported instead. This
is not a problem as diffsigs has its own task recursion facility, and will
track down the actual change anyway. This is reflected in
fixing up the selftests.

[YOCTO #15289]

Signed-off-by: Alexander Kanavin <alex@linutronix.de>
---
 bitbake/lib/bb/runqueue.py                    | 27 +++++-----
 .../perlcross_%.bbappend}                     |  0
 meta/lib/oeqa/selftest/cases/sstatetests.py   | 51 +++++++++++--------
 3 files changed, 43 insertions(+), 35 deletions(-)
 rename meta-selftest/recipes-test/{quilt-native/quilt-native_%.bbappend => perlcross/perlcross_%.bbappend} (100%)

Comments

Richard Purdie Dec. 8, 2023, 10:36 a.m. UTC | #1
On Fri, 2023-12-08 at 11:15 +0100, Alexander Kanavin wrote:
> Previously printdiff code would iterate over tasks that were reported as invalid or absent,
> trying to follow dependency chains that would reach the most basic invalid items in the tree.
> 
> While this works in tightly controlled local builds, it can lead to bizarre reports
> against industrial-sized sstate caches, as the code would not consider whether the
> overall target can be fulfilled from valid sstate objects, and instead report
> missing sstate signature files that perhaps were never even created due to hash
> equivalency providing shortcuts in builds.
> 
> This commit reworks the logic in two ways:
> 
> - start the iteration over final targets rather than missing objects
> and stop at the first invalid object
> 
> - if a given object can be fulfilled from sstate, do not recurse into
> its dependencies; bitbake wouldn't care if they're absent, and neither
> should printdiff

Unfortunately this isn't actually true, the real answer is "it
depends".

For many things that is the case, however for example an XXX-
native:do_populate_sysroot, we would need the dependencies in case they
contain shared libraries, unless it is listed as one of the special
cases in SSTATE_EXCLUDEDEPS_SYSROOT.

Bitbake works this out using setscene_depvalid() in sstate.bbclass via
BB_SETSCENE_DEPVALID.

I suspect this is the right direction but it may need the logic
tweaking slightly.

Cheers,

Richard
diff mbox series

Patch

diff --git a/bitbake/lib/bb/runqueue.py b/bitbake/lib/bb/runqueue.py
index 864708ee4a5..c9fe38615cf 100644
--- a/bitbake/lib/bb/runqueue.py
+++ b/bitbake/lib/bb/runqueue.py
@@ -1717,35 +1717,32 @@  class RunQueue:
                     valid_new.add(dep)
 
         invalidtasks = set()
-        for tid in self.rqdata.runtaskentries:
-            if tid not in valid_new and tid not in noexec:
-                invalidtasks.add(tid)
 
-        found = set()
-        processed = set()
-        for tid in invalidtasks:
+        toptasks = set(["{}:{}".format(t[3], t[2]) for t in self.rqdata.targets])
+        for tid in toptasks:
             toprocess = set([tid])
             while toprocess:
                 next = set()
                 for t in toprocess:
+                    if t not in valid_new and t not in noexec:
+                        invalidtasks.add(t)
+                        continue
+                    if t in self.rqdata.runq_setscene_tids:
+                        continue
+
                     for dep in self.rqdata.runtaskentries[t].depends:
-                        if dep in invalidtasks:
-                            found.add(tid)
-                        if dep not in processed:
-                            processed.add(dep)
-                            next.add(dep)
+                        next.add(dep)
+
                 toprocess = next
-                if tid in found:
-                    toprocess = set()
 
         tasklist = []
-        for tid in invalidtasks.difference(found):
+        for tid in invalidtasks:
             tasklist.append(tid)
 
         if tasklist:
             bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
 
-        return invalidtasks.difference(found)
+        return invalidtasks
 
     def write_diffscenetasks(self, invalidtasks):
 
diff --git a/meta-selftest/recipes-test/quilt-native/quilt-native_%.bbappend b/meta-selftest/recipes-test/perlcross/perlcross_%.bbappend
similarity index 100%
rename from meta-selftest/recipes-test/quilt-native/quilt-native_%.bbappend
rename to meta-selftest/recipes-test/perlcross/perlcross_%.bbappend
diff --git a/meta/lib/oeqa/selftest/cases/sstatetests.py b/meta/lib/oeqa/selftest/cases/sstatetests.py
index 795cd5bd85f..07e82d25c62 100644
--- a/meta/lib/oeqa/selftest/cases/sstatetests.py
+++ b/meta/lib/oeqa/selftest/cases/sstatetests.py
@@ -824,14 +824,22 @@  TMPDIR = "${{TOPDIR}}/tmp-sstateprintdiff-difftmp-{}"
 
 
     # Check if printdiff walks the full dependency chain from the image target to where the change is in a specific recipe
-    def test_image_minimal_vs_quilt(self):
-        expected_output = ("Task quilt-native:do_install couldn't be used from the cache because:",
+    def test_image_minimal_vs_perlcross(self):
+        expected_output = ("Task core-image-minimal:do_create_spdx couldn't be used from the cache because:",
+"Task linux-yocto:do_deploy couldn't be used from the cache because:",
+"Task core-image-minimal:do_create_runtime_spdx couldn't be used from the cache because:",
+"Task core-image-minimal:do_populate_lic_deploy couldn't be used from the cache because:",
+"Task run-postinsts:do_package_write_rpm couldn't be used from the cache because:",
+"Task packagegroup-core-boot:do_package_write_rpm couldn't be used from the cache because:",
 "We need hash",
 "most recent matching task was")
-        expected_sametmp_output = expected_output + ("Variable do_install value changed",'+    echo "this changes the task signature"')
+        expected_sametmp_output = expected_output + (
+"Hash for task dependency perlcross-native:do_install changed from",
+"Variable do_install value changed",
+'+    echo "this changes the task signature"')
         expected_difftmp_output = expected_output
 
-        self.run_test_printdiff_changerecipe("core-image-minimal", "quilt-native", "-c do_install quilt-native",
+        self.run_test_printdiff_changerecipe("core-image-minimal", "perlcross", "-c do_install perlcross-native",
 """
 do_install:append() {
     echo "this changes the task signature"
@@ -843,13 +851,18 @@  expected_sametmp_output, expected_difftmp_output)
     def test_gcc_runtime_vs_gcc_source(self):
         gcc_source_pn = 'gcc-source-%s' % get_bb_vars(['PV'], 'gcc')['PV']
 
-        expected_output = ("Task {}:do_preconfigure couldn't be used from the cache because:".format(gcc_source_pn),
+        expected_output = ("Task gcc-runtime:do_create_spdx couldn't be used from the cache because:",
+"Task gcc-runtime:do_create_runtime_spdx couldn't be used from the cache because:",
+"Task gcc-runtime:do_populate_sysroot couldn't be used from the cache because:",
+"Task gcc-runtime:do_package_qa couldn't be used from the cache because:",
+"Task gcc-runtime:do_packagedata couldn't be used from the cache because:",
+"Task gcc-runtime:do_package_write_rpm couldn't be used from the cache because:",
 "We need hash",
 "most recent matching task was")
-        expected_sametmp_output = expected_output + ("Variable do_preconfigure value changed",'+    print("this changes the task signature")')
-        #FIXME: printdiff is supposed to find at least one preconfigure task signature in the sstate cache, but isn't able to
-        #expected_difftmp_output = expected_output
-        expected_difftmp_output = ()
+        expected_sametmp_output = expected_output + ("Hash for task dependency {}:do_preconfigure changed from".format(gcc_source_pn),
+"Variable do_preconfigure value changed",
+'+    print("this changes the task signature")')
+        expected_difftmp_output = expected_output
 
         self.run_test_printdiff_changerecipe("gcc-runtime", "gcc-source", "-c do_preconfigure {}".format(gcc_source_pn),
 """
@@ -861,19 +874,17 @@  expected_sametmp_output, expected_difftmp_output)
 
     # Check if changing a really base task definiton is reported against multiple core recipes using it
     def test_image_minimal_vs_base_do_configure(self):
-        expected_output = ("Task zstd-native:do_configure couldn't be used from the cache because:",
-"Task texinfo-dummy-native:do_configure couldn't be used from the cache because:",
-"Task ldconfig-native:do_configure couldn't be used from the cache because:",
-"Task gettext-minimal-native:do_configure couldn't be used from the cache because:",
-"Task tzcode-native:do_configure couldn't be used from the cache because:",
-"Task makedevs-native:do_configure couldn't be used from the cache because:",
-"Task pigz-native:do_configure couldn't be used from the cache because:",
-"Task update-rc.d-native:do_configure couldn't be used from the cache because:",
-"Task unzip-native:do_configure couldn't be used from the cache because:",
-"Task gnu-config-native:do_configure couldn't be used from the cache because:",
+        expected_output = ("Task core-image-minimal:do_create_spdx couldn't be used from the cache because:",
+"Task linux-yocto:do_deploy couldn't be used from the cache because:",
+"Task core-image-minimal:do_create_runtime_spdx couldn't be used from the cache because:",
+"Task core-image-minimal:do_populate_lic_deploy couldn't be used from the cache because:",
+"Task run-postinsts:do_package_write_rpm couldn't be used from the cache because:",
+"Task packagegroup-core-boot:do_package_write_rpm couldn't be used from the cache because:",
 "We need hash",
 "most recent matching task was")
-        expected_sametmp_output = expected_output + ("Variable base_do_configure value changed",'+	echo "this changes base_do_configure() definiton "')
+        expected_sametmp_output = expected_output + ("Hash for task dependency gnu-config-native:do_configure changed from",
+"Variable base_do_configure value changed",
+'+	echo "this changes base_do_configure() definiton "')
         expected_difftmp_output = expected_output
 
         self.run_test_printdiff_changeconfig("core-image-minimal",