| #!/usr/bin/env python3 | 
|   | 
| # bitbake-diffsigs / bitbake-dumpsig | 
| # BitBake task signature data dump and comparison utility | 
| # | 
| # Copyright (C) 2012-2013, 2017 Intel Corporation | 
| # | 
| # SPDX-License-Identifier: GPL-2.0-only | 
| # | 
|   | 
| import os | 
| import sys | 
| import warnings | 
| warnings.simplefilter("default") | 
| import argparse | 
| import logging | 
| import pickle | 
|   | 
| sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib')) | 
|   | 
| import bb.tinfoil | 
| import bb.siggen | 
| import bb.msg | 
|   | 
| myname = os.path.basename(sys.argv[0]) | 
| logger = bb.msg.logger_create(myname) | 
|   | 
| is_dump = myname == 'bitbake-dumpsig' | 
|   | 
| def find_siginfo(tinfoil, pn, taskname, sigs=None): | 
|     result = None | 
|     tinfoil.set_event_mask(['bb.event.FindSigInfoResult', | 
|                             'logging.LogRecord', | 
|                             'bb.command.CommandCompleted', | 
|                             'bb.command.CommandFailed']) | 
|     ret = tinfoil.run_command('findSigInfo', pn, taskname, sigs) | 
|     if ret: | 
|         while True: | 
|             event = tinfoil.wait_event(1) | 
|             if event: | 
|                 if isinstance(event, bb.command.CommandCompleted): | 
|                     break | 
|                 elif isinstance(event, bb.command.CommandFailed): | 
|                     logger.error(str(event)) | 
|                     sys.exit(2) | 
|                 elif isinstance(event, bb.event.FindSigInfoResult): | 
|                     result = event.result | 
|                 elif isinstance(event, logging.LogRecord): | 
|                     logger.handle(event) | 
|     else: | 
|         logger.error('No result returned from findSigInfo command') | 
|         sys.exit(2) | 
|     return result | 
|   | 
| def find_siginfo_task(bbhandler, pn, taskname, sig1=None, sig2=None): | 
|     """ Find the most recent signature files for the specified PN/task """ | 
|   | 
|     if not taskname.startswith('do_'): | 
|         taskname = 'do_%s' % taskname | 
|   | 
|     if sig1 and sig2: | 
|         sigfiles = find_siginfo(bbhandler, pn, taskname, [sig1, sig2]) | 
|         if len(sigfiles) == 0: | 
|             logger.error('No sigdata files found matching %s %s matching either %s or %s' % (pn, taskname, sig1, sig2)) | 
|             sys.exit(1) | 
|         elif not sig1 in sigfiles: | 
|             logger.error('No sigdata files found matching %s %s with signature %s' % (pn, taskname, sig1)) | 
|             sys.exit(1) | 
|         elif not sig2 in sigfiles: | 
|             logger.error('No sigdata files found matching %s %s with signature %s' % (pn, taskname, sig2)) | 
|             sys.exit(1) | 
|         latestfiles = [sigfiles[sig1], sigfiles[sig2]] | 
|     else: | 
|         filedates = find_siginfo(bbhandler, pn, taskname) | 
|         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) | 
|   | 
|     return latestfiles | 
|   | 
|   | 
| # Define recursion callback | 
| def recursecb(key, hash1, hash2): | 
|     hashes = [hash1, hash2] | 
|     hashfiles = find_siginfo(tinfoil, key, None, hashes) | 
|   | 
|     recout = [] | 
|     if len(hashfiles) == 0: | 
|         recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2)) | 
|     elif not hash1 in hashfiles: | 
|         recout.append("Unable to find matching sigdata for %s with hash %s" % (key, hash1)) | 
|     elif not hash2 in hashfiles: | 
|         recout.append("Unable to find matching sigdata for %s with hash %s" % (key, hash2)) | 
|     else: | 
|         out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb, color=color) | 
|         for change in out2: | 
|             for line in change.splitlines(): | 
|                 recout.append('    ' + line) | 
|   | 
|     return recout | 
|   | 
|   | 
| parser = argparse.ArgumentParser( | 
|     description=("Dumps" if is_dump else "Compares") + " siginfo/sigdata files written out by BitBake") | 
|   | 
| parser.add_argument('-D', '--debug', | 
|                     help='Enable debug output', | 
|                     action='store_true') | 
|   | 
| if is_dump: | 
|     parser.add_argument("-t", "--task", | 
|             help="find the signature data file for the last run of the specified task", | 
|             action="store", dest="taskargs", nargs=2, metavar=('recipename', 'taskname')) | 
|   | 
|     parser.add_argument("sigdatafile1", | 
|             help="Signature file to dump. Not used when using -t/--task.", | 
|             action="store", nargs='?', metavar="sigdatafile") | 
| else: | 
|     parser.add_argument('-c', '--color', | 
|             help='Colorize the output (where %(metavar)s is %(choices)s)', | 
|             choices=['auto', 'always', 'never'], default='auto', metavar='color') | 
|   | 
|     parser.add_argument('-d', '--dump', | 
|             help='Dump the last signature data instead of comparing (equivalent to using bitbake-dumpsig)', | 
|             action='store_true') | 
|   | 
|     parser.add_argument("-t", "--task", | 
|             help="find the signature data files for the last two runs of the specified task and compare them", | 
|             action="store", dest="taskargs", nargs=2, metavar=('recipename', 'taskname')) | 
|   | 
|     parser.add_argument("-s", "--signature", | 
|             help="With -t/--task, specify the signatures to look for instead of taking the last two", | 
|             action="store", dest="sigargs", nargs=2, metavar=('fromsig', 'tosig')) | 
|   | 
|     parser.add_argument("sigdatafile1", | 
|             help="First signature file to compare (or signature file to dump, if second not specified). Not used when using -t/--task.", | 
|             action="store", nargs='?') | 
|   | 
|     parser.add_argument("sigdatafile2", | 
|             help="Second signature file to compare", | 
|             action="store", nargs='?') | 
|   | 
| options = parser.parse_args() | 
| if is_dump: | 
|     options.color = 'never' | 
|     options.dump = True | 
|     options.sigdatafile2 = None | 
|     options.sigargs = None | 
|   | 
| if options.debug: | 
|     logger.setLevel(logging.DEBUG) | 
|   | 
| color = (options.color == 'always' or (options.color == 'auto' and sys.stdout.isatty())) | 
|   | 
| if options.taskargs: | 
|     with bb.tinfoil.Tinfoil() as tinfoil: | 
|         tinfoil.prepare(config_only=True) | 
|         if not options.dump and options.sigargs: | 
|             files = find_siginfo_task(tinfoil, options.taskargs[0], options.taskargs[1], options.sigargs[0], options.sigargs[1]) | 
|         else: | 
|             files = find_siginfo_task(tinfoil, options.taskargs[0], options.taskargs[1]) | 
|   | 
|         if options.dump: | 
|             logger.debug("Signature file: %s" % files[-1]) | 
|             output = bb.siggen.dump_sigfile(files[-1]) | 
|         else: | 
|             if len(files) < 2: | 
|                 logger.error('Only one matching sigdata file found for the specified task (%s %s)' % (options.taskargs[0], options.taskargs[1])) | 
|                 sys.exit(1) | 
|   | 
|             # Recurse into signature comparison | 
|             logger.debug("Signature file (previous): %s" % files[-2]) | 
|             logger.debug("Signature file (latest): %s" % files[-1]) | 
|             output = bb.siggen.compare_sigfiles(files[-2], files[-1], recursecb, color=color) | 
| else: | 
|     if options.sigargs: | 
|         logger.error('-s/--signature can only be used together with -t/--task') | 
|         sys.exit(1) | 
|     try: | 
|         if not options.dump and options.sigdatafile1 and options.sigdatafile2: | 
|             with bb.tinfoil.Tinfoil() as tinfoil: | 
|                 tinfoil.prepare(config_only=True) | 
|                 output = bb.siggen.compare_sigfiles(options.sigdatafile1, options.sigdatafile2, recursecb, color=color) | 
|         elif options.sigdatafile1: | 
|             output = bb.siggen.dump_sigfile(options.sigdatafile1) | 
|         else: | 
|             logger.error('Must specify signature file(s) or -t/--task') | 
|             parser.print_help() | 
|             sys.exit(1) | 
|     except IOError as e: | 
|         logger.error(str(e)) | 
|         sys.exit(1) | 
|     except (pickle.UnpicklingError, EOFError): | 
|         logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files') | 
|         sys.exit(1) | 
|   | 
| if output: | 
|     print('\n'.join(output)) |