Skip to contentSkip to conversation

Refactoring + improve bitbake-diffsigs

PATCH Applied Paul Eggleton 2 participants 7 messages Aug 27, 2012 – Sep 7, 2012

lore
Share & tools
lore
#0 lore raw

cover Refactoring + improve bitbake-diffsigs

Refactor out a class which can be used as the basis for bitbake-based utilities, and use it to improve bitbake-diffsigs to enable easily specifying a recipe and task and determine what the difference was in the signature data between the last two executions.

This change requires some support code within the metadata (since BitBake itself knows nothing about the implementation of shared state); a patch to implement this in OE-Core has been sent to the OE-Core mailing list.

Note that this is similar in spirit but does not directly overlap with Robert's bitbake-whatchanged script - that allows you to determine what will be done before it happens, whereas this tool is useful for determining why a rebuild occurred afterwards.

The following changes (against poky, but apply cleanly with -p2 against bitbake master) are available in the git repository at:

git://git.yoctoproject.org/poky-contrib paule/bbdiffsigs
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=paule/bbdiffsigs

Paul Eggleton (5):
tinfoil: create simple interface for bitbake-based utilities lib/bb/siggen: replace tabs with spaces
lib/bb/siggen.py: insert a colon between class and recipe name lib/bb/siggen.py: make signature dump/compare functions return a list
bitbake-diffsigs: allow specifying task & follow deps recursively

 bitbake/bin/bitbake-diffsigs |   96 ++++++++++++++++++++++++++-
 bitbake/bin/bitbake-dumpsig  |    4 +-
 bitbake/bin/bitbake-layers   |  146 ++++++++++++++----------------------------
 bitbake/lib/bb/siggen.py     |  109 +++++++++++++++++--------------
 bitbake/lib/bb/tinfoil.py    |   98 ++++++++++++++++++++++++++++
 5 files changed, 301 insertions(+), 152 deletions(-)
 create mode 100644 bitbake/lib/bb/tinfoil.py
Signature · 1 line
1.7.9.5
#1 lore raw

PATCH 1/5 tinfoil: create simple interface for bitbake-based utilities

The code to initialise BitBake within bitbake-layers should be useful for other utilities that need to query configuration or recipe information, so refactor it out into its own class, "Tinfoil" (to continue with our cooking metaphor).

---
 bitbake/bin/bitbake-layers |  146 ++++++++++++++------------------------------
 bitbake/lib/bb/tinfoil.py  |   98 +++++++++++++++++++++++++++++
 2 files changed, 145 insertions(+), 99 deletions(-)
 create mode 100644 bitbake/lib/bb/tinfoil.py
bitbake/bin/bitbake-layers +47 -99
index f473711..fa4e767 100755
@@ -6,10 +6,22 @@
 
 # Copyright (C) 2011 Mentor Graphics Corporation
 # Copyright (C) 2012 Intel Corporation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 import cmd
 import logging
-import warnings
 import os
 import sys
 import fnmatch
@@ -23,26 +35,14 @@ import bb.cache
 import bb.cooker
 import bb.providers
 import bb.utils
-from bb.cooker import state
-import bb.fetch2
+import bb.tinfoil
 
 
 logger = logging.getLogger('BitBake')
 
-warnings.filterwarnings("ignore", category=DeprecationWarning)
 
 def main(args):
-    # Set up logging
-    console = logging.StreamHandler(sys.stdout)
-    format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
-    bb.msg.addDefaultlogFilter(console)
-    console.setFormatter(format)
-    logger.addHandler(console)
-
-    initialenv = os.environ.copy()
-    bb.utils.clean_environment()
-
-    cmds = Commands(initialenv)
+    cmds = Commands()
     if args:
         # Allow user to specify e.g. show-layers instead of show_layers
         args = [args[0].replace('-', '_')] + args[1:]
@@ -53,46 +53,11 @@ def main(args):
 
 
 class Commands(cmd.Cmd):
-    def __init__(self, initialenv):
+    def __init__(self):
         cmd.Cmd.__init__(self)
+        self.bbhandler = bb.tinfoil.Tinfoil()
         self.returncode = 0
-        self.config = Config(parse_only=True)
-        self.cooker = bb.cooker.BBCooker(self.config,
-                                         self.register_idle_function,
-                                         initialenv)
-        self.config_data = self.cooker.configuration.data
-        bb.providers.logger.setLevel(logging.ERROR)
-        self.cooker_data = None
-        self.bblayers = (self.config_data.getVar('BBLAYERS', True) or "").split()
-
-    def register_idle_function(self, function, data):
-        pass
-
-    def prepare_cooker(self):
-        sys.stderr.write("Parsing recipes..")
-        logger.setLevel(logging.WARNING)
-
-        try:
-            while self.cooker.state in (state.initial, state.parsing):
-                self.cooker.updateCache()
-        except KeyboardInterrupt:
-            self.cooker.shutdown()
-            self.cooker.updateCache()
-            sys.exit(2)
-
-        logger.setLevel(logging.INFO)
-        sys.stderr.write("done.\n")
-
-        self.cooker_data = self.cooker.status
-        self.cooker_data.appends = self.cooker.appendlist
-
-    def check_prepare_cooker(self, config_only = False):
-        if not self.cooker_data:
-            if config_only:
-                self.cooker.parseConfiguration()
-                self.cooker_data = self.cooker.status
-            else:
-                self.prepare_cooker()
+        self.bblayers = (self.bbhandler.config_data.getVar('BBLAYERS', True) or "").split()
 
     def default(self, line):
         """Handle unrecognised commands"""
@@ -117,13 +82,13 @@ class Commands(cmd.Cmd):
 
     def do_show_layers(self, args):
         """show current configured layers"""
-        self.check_prepare_cooker(config_only = True)
+        self.bbhandler.prepare(config_only = True)
         logger.plain("%s  %s  %s" % ("layer".ljust(20), "path".ljust(40), "priority"))
         logger.plain('=' * 74)
         for layerdir in self.bblayers:
             layername = self.get_layer_name(layerdir)
             layerpri = 0
-            for layer, _, regex, pri in self.cooker.status.bbfile_config_priorities:
+            for layer, _, regex, pri in self.bbhandler.cooker.status.bbfile_config_priorities:
                 if regex.match(os.path.join(layerdir, 'test')):
                     layerpri = pri
                     break
@@ -154,7 +119,7 @@ Options:
        recipes with the ones they overlay indented underneath
   -s   only list overlayed recipes where the version is the same
 """
-        self.check_prepare_cooker()
+        self.bbhandler.prepare()
 
         show_filenames = False
         show_same_ver_only = False
@@ -186,7 +151,7 @@ Options:
         # factor - however, each layer.conf is free to either prepend or append to
         # BBPATH (or indeed do crazy stuff with it). Thus the order in BBPATH might
         # not be exactly the order present in bblayers.conf either.
-        bbpath = str(self.config_data.getVar('BBPATH', True))
+        bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
         overlayed_class_found = False
         for (classfile, classdirs) in classes.items():
             if len(classdirs) > 1:
@@ -237,7 +202,7 @@ Options:
   -m   only list where multiple recipes (in the same layer or different
        layers) exist for the same recipe name
 """
-        self.check_prepare_cooker()
+        self.bbhandler.prepare()
 
         show_filenames = False
         show_multi_provider_only = False
@@ -259,15 +224,15 @@ Options:
 
 
     def list_recipes(self, title, pnspec, show_overlayed_only, show_same_ver_only, show_filenames, show_multi_provider_only):
-        pkg_pn = self.cooker.status.pkg_pn
-        (latest_versions, preferred_versions) = bb.providers.findProviders(self.cooker.configuration.data, self.cooker.status, pkg_pn)
-        allproviders = bb.providers.allProviders(self.cooker.status)
+        pkg_pn = self.bbhandler.cooker.status.pkg_pn
+        (latest_versions, preferred_versions) = bb.providers.findProviders(self.bbhandler.cooker.configuration.data, self.bbhandler.cooker.status, pkg_pn)
+        allproviders = bb.providers.allProviders(self.bbhandler.cooker.status)
 
         # Ensure we list skipped recipes
         # We are largely guessing about PN, PV and the preferred version here,
         # but we have no choice since skipped recipes are not fully parsed
-        skiplist = self.cooker.skiplist.keys()
-        skiplist.sort( key=lambda fileitem: self.cooker.calc_bbfile_priority(fileitem) )
+        skiplist = self.bbhandler.cooker.skiplist.keys()
+        skiplist.sort( key=lambda fileitem: self.bbhandler.cooker.calc_bbfile_priority(fileitem) )
         skiplist.reverse()
         for fn in skiplist:
             recipe_parts = os.path.splitext(os.path.basename(fn))[0].split('_')
@@ -375,7 +340,7 @@ build results (as the layer priority order has effectively changed).
             logger.error('Directory %s exists and is non-empty, please clear it out first' % outputdir)
             return
 
-        self.check_prepare_cooker()
+        self.bbhandler.prepare()
         layers = self.bblayers
         if len(arglist) > 2:
             layernames = arglist[:-1]
@@ -405,8 +370,8 @@ build results (as the layer priority order has effectively changed).
         appended_recipes = []
         for layer in layers:
             overlayed = []
-            for f in self.cooker.overlayed.iterkeys():
-                for of in self.cooker.overlayed[f]:
+            for f in self.bbhandler.cooker.overlayed.iterkeys():
+                for of in self.bbhandler.cooker.overlayed[f]:
                     if of.startswith(layer):
                         overlayed.append(of)
 
@@ -430,8 +395,8 @@ build results (as the layer priority order has effectively changed).
                                     logger.warn('Overwriting file %s', fdest)
                             bb.utils.copyfile(f1full, fdest)
                             if ext == '.bb':
-                                if f1 in self.cooker_data.appends:
-                                    appends = self.cooker_data.appends[f1]
+                                if f1 in self.bbhandler.cooker.appendlist:
+                                    appends = self.bbhandler.cooker.appendlist[f1]
                                     if appends:
                                         logger.plain('  Applying appends to %s' % fdest )
                                         for appendname in appends:
@@ -440,9 +405,9 @@ build results (as the layer priority order has effectively changed).
                                     appended_recipes.append(f1)
 
         # Take care of when some layers are excluded and yet we have included bbappends for those recipes
-        for recipename in self.cooker_data.appends.iterkeys():
+        for recipename in self.bbhandler.cooker.appendlist.iterkeys():
             if recipename not in appended_recipes:
-                appends = self.cooker_data.appends[recipename]
+                appends = self.bbhandler.cooker.appendlist[recipename]
                 first_append = None
                 for appendname in appends:
                     layer = layer_path_match(appendname)
@@ -460,14 +425,14 @@ build results (as the layer priority order has effectively changed).
         # have come from)
         first_regex = None
         layerdir = layers[0]
-        for layername, pattern, regex, _ in self.cooker.status.bbfile_config_priorities:
+        for layername, pattern, regex, _ in self.bbhandler.cooker.status.bbfile_config_priorities:
             if regex.match(os.path.join(layerdir, 'test')):
                 first_regex = regex
                 break
 
         if first_regex:
             # Find the BBFILES entries that match (which will have come from this conf/layer.conf file)
-            bbfiles = str(self.config_data.getVar('BBFILES', True)).split()
+            bbfiles = str(self.bbhandler.config_data.getVar('BBFILES', True)).split()
             bbfiles_layer = []
             for item in bbfiles:
                 if first_regex.match(item):
@@ -490,7 +455,7 @@ build results (as the layer priority order has effectively changed).
                                 logger.warning("File %s does not match the flattened layer's BBFILES setting, you may need to edit conf/layer.conf or move the file elsewhere" % f1full)
 
     def get_file_layer(self, filename):
-        for layer, _, regex, _ in self.cooker.status.bbfile_config_priorities:
+        for layer, _, regex, _ in self.bbhandler.cooker.status.bbfile_config_priorities:
             if regex.match(filename):
                 for layerdir in self.bblayers:
                     if regex.match(os.path.join(layerdir, 'test')):
@@ -516,14 +481,14 @@ usage: show-appends
 
 Recipes are listed with the bbappends that apply to them as subitems.
 """
-        self.check_prepare_cooker()
-        if not self.cooker_data.appends:
+        self.bbhandler.prepare()
+        if not self.bbhandler.cooker.appendlist:
             logger.plain('No append files found')
             return
 
         logger.plain('=== Appended recipes ===')
 
-        pnlist = list(self.cooker_data.pkg_pn.keys())
+        pnlist = list(self.bbhandler.cooker_data.pkg_pn.keys())
         pnlist.sort()
         for pn in pnlist:
             self.show_appends_for_pn(pn)
@@ -531,19 +496,19 @@ Recipes are listed with the bbappends that apply to them as subitems.
         self.show_appends_for_skipped()
 
     def show_appends_for_pn(self, pn):
-        filenames = self.cooker_data.pkg_pn[pn]
+        filenames = self.bbhandler.cooker_data.pkg_pn[pn]
 
         best = bb.providers.findBestProvider(pn,
-                                             self.cooker.configuration.data,
-                                             self.cooker_data,
-                                             self.cooker_data.pkg_pn)
+                                             self.bbhandler.cooker.configuration.data,
+                                             self.bbhandler.cooker_data,
+                                             self.bbhandler.cooker_data.pkg_pn)
         best_filename = os.path.basename(best[3])
 
         self.show_appends_output(filenames, best_filename)
 
     def show_appends_for_skipped(self):
         filenames = [os.path.basename(f)
-                    for f in self.cooker.skiplist.iterkeys()]
+                    for f in self.bbhandler.cooker.skiplist.iterkeys()]
         self.show_appends_output(filenames, None, " (skipped)")
 
     def show_appends_output(self, filenames, best_filename, name_suffix = ''):
@@ -569,7 +534,7 @@ Recipes are listed with the bbappends that apply to them as subitems.
                 continue
 
             basename = os.path.basename(filename)
-            appends = self.cooker_data.appends.get(basename)
+            appends = self.bbhandler.cooker.appendlist.get(basename)
             if appends:
                 appended.append((basename, list(appends)))
             else:
@@ -577,22 +542,5 @@ Recipes are listed with the bbappends that apply to them as subitems.
         return appended, notappended
 
 
-class Config(object):
-    def __init__(self, **options):
-        self.pkgs_to_build = []
-        self.debug_domains = []
-        self.extra_assume_provided = []
-        self.prefile = []
-        self.postfile = []
-        self.debug = 0
-        self.__dict__.update(options)
-
-    def __getattr__(self, attribute):
-        try:
-            return super(Config, self).__getattribute__(attribute)
-        except AttributeError:
-            return None
-
-
 if __name__ == '__main__':
     sys.exit(main(sys.argv[1:]) or 0)
bitbake/lib/bb/tinfoil.py new file +98 -0

98 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#2 lore raw

PATCH 2/5 lib/bb/siggen: replace tabs with spaces

In reply to Paul Eggleton

We had one section of the code mixing tabs with spaces, which is particularly undesirable with python code.

---
 bitbake/lib/bb/siggen.py |   34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)
bitbake/lib/bb/siggen.py +17 -17
index 02a4268..b04a8bc 100644
@@ -185,8 +185,8 @@ class SignatureGeneratorBasic(SignatureGenerator):
         if task in dataCache.file_checksums[fn]:
             checksums = bb.fetch2.get_file_checksums(dataCache.file_checksums[fn][task], recipename)
             for (f,cs) in checksums:
-               self.file_checksum_values[k][f] = cs
-               data = data + cs
+                self.file_checksum_values[k][f] = cs
+                data = data + cs
 
         taint = self.read_taint(fn, task, dataCache.stamp[fn])
         if taint:
@@ -372,24 +372,24 @@ def compare_sigfiles(a, b):
         changed, added, removed = dict_diff(a, b)
         if added:
             for dep in added:
-	        bdep_found = False
-	        if removed:
-		    for bdep in removed:
-		    	if a[dep] == b[bdep]:
-			   #print "Dependency on task %s was replaced by %s with same hash" % (dep, bdep)
-			   bdep_found = True
-		if not bdep_found:
+                bdep_found = False
+                if removed:
+                    for bdep in removed:
+                        if a[dep] == b[bdep]:
+                            #print "Dependency on task %s was replaced by %s with same hash" % (dep, bdep)
+                            bdep_found = True
+                if not bdep_found:
                     print "Dependency on task %s was added with hash %s" % (dep, a[dep])
         if removed:
             for dep in removed:
-	        adep_found = False
-	        if added:
-		    for adep in added:
-		    	if a[adep] == b[dep]:
-			   #print "Dependency on task %s was replaced by %s with same hash" % (adep, dep)
-			   adep_found = True
-		if not adep_found:
-                   print "Dependency on task %s was removed with hash %s" % (dep, b[dep])
+                adep_found = False
+                if added:
+                    for adep in added:
+                        if a[adep] == b[dep]:
+                            #print "Dependency on task %s was replaced by %s with same hash" % (adep, dep)
+                            adep_found = True
+                if not adep_found:
+                    print "Dependency on task %s was removed with hash %s" % (dep, b[dep])
         if changed:
             for dep in changed:
                 print "Hash for dependent task %s changed from %s to %s" % (dep, a[dep], b[dep])
Signature · 1 line
1.7.9.5
#3 lore raw

PATCH 3/5 lib/bb/siggen.py: insert a colon between class and recipe name

In reply to Paul Eggleton

before:
virtual:nativeautomake_1.12.1.bb.do_compile
after:
virtual:native:automake_1.12.1.bb.do_compile

This separation ensures that the key is readable, and if necessary, parsable.

Unfortunately this invalidates previous native sstate signatures with OE-Core - not much that can be done about that; however that occurs frequently during the development cycle so it's par for the course.

---
 bitbake/lib/bb/siggen.py |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
bitbake/lib/bb/siggen.py +1 -1
index b04a8bc..d0a4d18 100644
@@ -290,7 +290,7 @@ def dump_this_task(outfile, d):
 
 def clean_basepath(a):
     if a.startswith("virtual:"):
-        b = a.rsplit(":", 1)[0] + a.rsplit("/", 1)[1]
+        b = a.rsplit(":", 1)[0] + ":" + a.rsplit("/", 1)[1]
     else:
         b = a.rsplit("/", 1)[1]
     return b
Signature · 1 line
1.7.9.5
#4 lore raw

PATCH 4/5 lib/bb/siggen.py: make signature dump/compare functions return a list

In reply to Paul Eggleton

These functions become a little bit more reusable if they return a list containing the output rather than just printing it.

---
 bitbake/bin/bitbake-diffsigs |    7 +++--
 bitbake/bin/bitbake-dumpsig  |    4 ++-
 bitbake/lib/bb/siggen.py     |   68 +++++++++++++++++++++++-------------------
 3 files changed, 46 insertions(+), 33 deletions(-)
bitbake/bin/bitbake-diffsigs +5 -2
index 5eb77ce..146cab8 100755
@@ -7,6 +7,9 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), '
 import bb.siggen
 
 if len(sys.argv) > 2:
-    bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
+    output = bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
 else:
-    bb.siggen.dump_sigfile(sys.argv[1])
+    output = bb.siggen.dump_sigfile(sys.argv[1])
+
+if output:
+    print '\n'.join(output)
bitbake/bin/bitbake-dumpsig +3 -1
index 59740c8..ccbc412 100755
@@ -6,4 +6,6 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), '
 
 import bb.siggen
 
-bb.siggen.dump_sigfile(sys.argv[1])
+output = bb.siggen.dump_sigfile(sys.argv[1])
+if output:
+    print '\n'.join(output)
bitbake/lib/bb/siggen.py +38 -30

152 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#5 lore raw

PATCH 5/5 bitbake-diffsigs: allow specifying task & follow deps recursively

In reply to Paul Eggleton

Add the ability to compare the two most recent runs of a specified task, and follow dependent hash changes recursively. This enables you to trace back and find exactly why a task was re-run after the fact.

Note that this relies on the metadata providing a function, hooked in as bb.siggen.find_siginfo, which allows searching in the appropriate places to find signature data files.

---
 bitbake/bin/bitbake-diffsigs |   97 +++++++++++++++++++++++++++++++++++++++---
 bitbake/lib/bb/siggen.py     |   17 +++++---
 2 files changed, 102 insertions(+), 12 deletions(-)
bitbake/bin/bitbake-diffsigs +92 -5
index 146cab8..62f6827 100755
@@ -1,15 +1,102 @@
 #!/usr/bin/env python
+
+# bitbake-diffsigs
+# BitBake task signature data comparison utility
+#
+# Copyright (C) 2012 Intel Corporation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
 import os
 import sys
 import warnings
+import fnmatch
+import optparse
+import logging
+
 sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
 
+import bb.tinfoil
 import bb.siggen
 
-if len(sys.argv) > 2:
-    output = bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
+logger = logging.getLogger('BitBake')
+
+def find_compare_task(bbhandler, pn, taskname):
+    """ Find the most recent signature files for the specified PN/task and compare them """
+
+    if not hasattr(bb.siggen, 'find_siginfo'):
+        logger.error('Metadata does not support finding signature data files')
+        sys.exit(1)
+
+    filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
+    latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-2:]
+    if not latestfiles:
+        logger.error('No sigdata files found matching %s %s' % (pn, taskname))
+        sys.exit(1)
+    elif len(latestfiles) < 2:
+        logger.error('Only one matching sigdata file found for the specified task (%s %s)' % (pn, taskname))
+        sys.exit(1)
+    else:
+        # Define recursion callback
+        def recursecb(key, hash1, hash2):
+            hashes = [hash1, hash2]
+            hashfiles = bb.siggen.find_siginfo(key, None, hashes, bbhandler.config_data)
+
+            recout = []
+            if len(hashfiles) == 2:
+                out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
+                recout.extend(list('  ' + l for l in out2))
+            else:
+                recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
+
+            return recout
+
+        # Recurse into signature comparison
+        output = bb.siggen.compare_sigfiles(latestfiles[0], latestfiles[1], recursecb)
+        if output:
+            print '\n'.join(output)
+    sys.exit(0)
+
+
+
+parser = optparse.OptionParser(
+    usage = """
+  %prog -t recipename taskname
+  %prog sigdatafile1 sigdatafile2
+  %prog sigdatafile1""")
+
+parser.add_option("-t", "--task",
+        help = "find the signature data files for last two runs of the specified task and compare them",
+        action="store_true", dest="taskmode")
+
+options, args = parser.parse_args(sys.argv)
+
+if len(args) == 1:
+    parser.print_help()
 else:
-    output = bb.siggen.dump_sigfile(sys.argv[1])
+    tinfoil = bb.tinfoil.Tinfoil()
+    if options.taskmode:
+        if len(args) < 3:
+            logger.error("Please specify a recipe and task name")
+            sys.exit(1)
+        tinfoil.prepare(config_only = True)
+        find_compare_task(tinfoil, args[1], args[2])
+    else:
+        if len(args) == 2:
+            output = bb.siggen.dump_sigfile(sys.argv[1])
+        else:
+            output = bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
 
-if output:
-    print '\n'.join(output)
+        if output:
+            print '\n'.join(output)
bitbake/lib/bb/siggen.py +10 -7
index 8d1501b..8fe59b9 100644
@@ -301,7 +301,7 @@ def clean_basepaths(a):
         b[clean_basepath(x)] = a[x]
     return b
 
-def compare_sigfiles(a, b):
+def compare_sigfiles(a, b, recursecb = None):
     output = []
 
     p1 = pickle.Unpickler(open(a, "rb"))
@@ -369,8 +369,8 @@ def compare_sigfiles(a, b):
 
 
     if 'runtaskhashes' in a_data and 'runtaskhashes' in b_data:
-        a = clean_basepaths(a_data['runtaskhashes'])
-        b = clean_basepaths(b_data['runtaskhashes'])
+        a = a_data['runtaskhashes']
+        b = b_data['runtaskhashes']
         changed, added, removed = dict_diff(a, b)
         if added:
             for dep in added:
@@ -381,7 +381,7 @@ def compare_sigfiles(a, b):
                             #output.append("Dependency on task %s was replaced by %s with same hash" % (dep, bdep))
                             bdep_found = True
                 if not bdep_found:
-                    output.append("Dependency on task %s was added with hash %s" % (dep, a[dep]))
+                    output.append("Dependency on task %s was added with hash %s" % (clean_basepath(dep), a[dep]))
         if removed:
             for dep in removed:
                 adep_found = False
@@ -391,11 +391,14 @@ def compare_sigfiles(a, b):
                             #output.append("Dependency on task %s was replaced by %s with same hash" % (adep, dep))
                             adep_found = True
                 if not adep_found:
-                    output.append("Dependency on task %s was removed with hash %s" % (dep, b[dep]))
+                    output.append("Dependency on task %s was removed with hash %s" % (clean_basepath(dep), b[dep]))
         if changed:
             for dep in changed:
-                output.append("Hash for dependent task %s changed from %s to %s" % (dep, a[dep], b[dep]))
-
+                output.append("Hash for dependent task %s changed from %s to %s" % (clean_basepath(dep), a[dep], b[dep]))
+                if callable(recursecb):
+                    recout = recursecb(dep, a[dep], b[dep])
+                    if recout:
+                        output.extend(recout)
 
     a_taint = a_data.get('taint', None)
     b_taint = b_data.get('taint', None)
Signature · 1 line
1.7.9.5
#6 lore raw
In reply to Paul Eggleton
Quoted 22 lines · Paul Eggleton

Refactor out a class which can be used as the basis for bitbake-based utilities, and use it to improve bitbake-diffsigs to enable easily specifying a recipe and task and determine what the difference was in the signature data between the last two executions.

This change requires some support code within the metadata (since BitBake itself knows nothing about the implementation of shared state); a patch to implement this in OE-Core has been sent to the OE-Core mailing list.

Note that this is similar in spirit but does not directly overlap with Robert's bitbake-whatchanged script - that allows you to determine what will be done before it happens, whereas this tool is useful for determining why a rebuild occurred afterwards.

The following changes (against poky, but apply cleanly with -p2 against bitbake master) are available in the git repository at:

git://git.yoctoproject.org/poky-contrib paule/bbdiffsigs
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=paule/bbdiffsigs

Quoted 6 lines

Paul Eggleton (5):
tinfoil: create simple interface for bitbake-based utilities lib/bb/siggen: replace tabs with spaces
lib/bb/siggen.py: insert a colon between class and recipe name lib/bb/siggen.py: make signature dump/compare functions return a list
bitbake-diffsigs: allow specifying task & follow deps recursively

Merged to master, thanks.

Nice to have this working better :)

Cheers,

Richard