bitbake-diffsigs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #!/usr/bin/env python3
  2. # bitbake-diffsigs
  3. # BitBake task signature data comparison utility
  4. #
  5. # Copyright (C) 2012-2013 Intel Corporation
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License version 2 as
  9. # published by the Free Software Foundation.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License along
  17. # with this program; if not, write to the Free Software Foundation, Inc.,
  18. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. import os
  20. import sys
  21. import warnings
  22. import fnmatch
  23. import optparse
  24. import logging
  25. import pickle
  26. sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
  27. import bb.tinfoil
  28. import bb.siggen
  29. def logger_create(name, output=sys.stderr):
  30. logger = logging.getLogger(name)
  31. console = logging.StreamHandler(output)
  32. format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
  33. if output.isatty():
  34. format.enable_color()
  35. console.setFormatter(format)
  36. logger.addHandler(console)
  37. logger.setLevel(logging.INFO)
  38. return logger
  39. logger = logger_create('bitbake-diffsigs')
  40. def find_compare_task(bbhandler, pn, taskname):
  41. """ Find the most recent signature files for the specified PN/task and compare them """
  42. def get_hashval(siginfo):
  43. if siginfo.endswith('.siginfo'):
  44. return siginfo.rpartition(':')[2].partition('_')[0]
  45. else:
  46. return siginfo.rpartition('.')[2]
  47. if not hasattr(bb.siggen, 'find_siginfo'):
  48. logger.error('Metadata does not support finding signature data files')
  49. sys.exit(1)
  50. if not taskname.startswith('do_'):
  51. taskname = 'do_%s' % taskname
  52. filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
  53. latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-3:]
  54. if not latestfiles:
  55. logger.error('No sigdata files found matching %s %s' % (pn, taskname))
  56. sys.exit(1)
  57. elif len(latestfiles) < 2:
  58. logger.error('Only one matching sigdata file found for the specified task (%s %s)' % (pn, taskname))
  59. sys.exit(1)
  60. else:
  61. # It's possible that latestfiles contain 3 elements and the first two have the same hash value.
  62. # In this case, we delete the second element.
  63. # The above case is actually the most common one. Because we may have sigdata file and siginfo
  64. # file having the same hash value. Comparing such two files makes no sense.
  65. if len(latestfiles) == 3:
  66. hash0 = get_hashval(latestfiles[0])
  67. hash1 = get_hashval(latestfiles[1])
  68. if hash0 == hash1:
  69. latestfiles.pop(1)
  70. # Define recursion callback
  71. def recursecb(key, hash1, hash2):
  72. hashes = [hash1, hash2]
  73. hashfiles = bb.siggen.find_siginfo(key, None, hashes, bbhandler.config_data)
  74. recout = []
  75. if len(hashfiles) == 2:
  76. out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
  77. recout.extend(list(' ' + l for l in out2))
  78. else:
  79. recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
  80. return recout
  81. # Recurse into signature comparison
  82. output = bb.siggen.compare_sigfiles(latestfiles[0], latestfiles[1], recursecb)
  83. if output:
  84. print('\n'.join(output))
  85. sys.exit(0)
  86. parser = optparse.OptionParser(
  87. description = "Compares siginfo/sigdata files written out by BitBake",
  88. usage = """
  89. %prog -t recipename taskname
  90. %prog sigdatafile1 sigdatafile2
  91. %prog sigdatafile1""")
  92. parser.add_option("-t", "--task",
  93. help = "find the signature data files for last two runs of the specified task and compare them",
  94. action="store", dest="taskargs", nargs=2, metavar='recipename taskname')
  95. options, args = parser.parse_args(sys.argv)
  96. if options.taskargs:
  97. tinfoil = bb.tinfoil.Tinfoil()
  98. tinfoil.prepare(config_only = True)
  99. find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1])
  100. else:
  101. if len(args) == 1:
  102. parser.print_help()
  103. else:
  104. try:
  105. if len(args) == 2:
  106. output = bb.siggen.dump_sigfile(sys.argv[1])
  107. else:
  108. output = bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
  109. except IOError as e:
  110. logger.error(str(e))
  111. sys.exit(1)
  112. except (pickle.UnpicklingError, EOFError):
  113. logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files')
  114. sys.exit(1)
  115. if output:
  116. print('\n'.join(output))