bitbake-diffsigs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #!/usr/bin/env python
  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. sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
  26. import bb.tinfoil
  27. import bb.siggen
  28. def logger_create(name, output=sys.stderr):
  29. logger = logging.getLogger(name)
  30. console = logging.StreamHandler(output)
  31. format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
  32. if output.isatty():
  33. format.enable_color()
  34. console.setFormatter(format)
  35. logger.addHandler(console)
  36. logger.setLevel(logging.INFO)
  37. return logger
  38. logger = logger_create('bitbake-diffsigs')
  39. def find_compare_task(bbhandler, pn, taskname):
  40. """ Find the most recent signature files for the specified PN/task and compare them """
  41. def get_hashval(siginfo):
  42. if siginfo.endswith('.siginfo'):
  43. return siginfo.rpartition(':')[2].partition('_')[0]
  44. else:
  45. return siginfo.rpartition('.')[2]
  46. if not hasattr(bb.siggen, 'find_siginfo'):
  47. logger.error('Metadata does not support finding signature data files')
  48. sys.exit(1)
  49. if not taskname.startswith('do_'):
  50. taskname = 'do_%s' % taskname
  51. filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
  52. latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-3:]
  53. if not latestfiles:
  54. logger.error('No sigdata files found matching %s %s' % (pn, taskname))
  55. sys.exit(1)
  56. elif len(latestfiles) < 2:
  57. logger.error('Only one matching sigdata file found for the specified task (%s %s)' % (pn, taskname))
  58. sys.exit(1)
  59. else:
  60. # It's possible that latestfiles contain 3 elements and the first two have the same hash value.
  61. # In this case, we delete the second element.
  62. # The above case is actually the most common one. Because we may have sigdata file and siginfo
  63. # file having the same hash value. Comparing such two files makes no sense.
  64. if len(latestfiles) == 3:
  65. hash0 = get_hashval(latestfiles[0])
  66. hash1 = get_hashval(latestfiles[1])
  67. if hash0 == hash1:
  68. latestfiles.pop(1)
  69. # Define recursion callback
  70. def recursecb(key, hash1, hash2):
  71. hashes = [hash1, hash2]
  72. hashfiles = bb.siggen.find_siginfo(key, None, hashes, bbhandler.config_data)
  73. recout = []
  74. if len(hashfiles) == 2:
  75. out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
  76. recout.extend(list(' ' + l for l in out2))
  77. else:
  78. recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
  79. return recout
  80. # Recurse into signature comparison
  81. output = bb.siggen.compare_sigfiles(latestfiles[0], latestfiles[1], recursecb)
  82. if output:
  83. print '\n'.join(output)
  84. sys.exit(0)
  85. parser = optparse.OptionParser(
  86. description = "Compares siginfo/sigdata files written out by BitBake",
  87. usage = """
  88. %prog -t recipename taskname
  89. %prog sigdatafile1 sigdatafile2
  90. %prog sigdatafile1""")
  91. parser.add_option("-t", "--task",
  92. help = "find the signature data files for last two runs of the specified task and compare them",
  93. action="store", dest="taskargs", nargs=2, metavar='recipename taskname')
  94. options, args = parser.parse_args(sys.argv)
  95. if options.taskargs:
  96. tinfoil = bb.tinfoil.Tinfoil()
  97. tinfoil.prepare(config_only = True)
  98. find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1])
  99. else:
  100. if len(args) == 1:
  101. parser.print_help()
  102. else:
  103. import cPickle
  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 cPickle.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)