CheckOptions.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import print_function
  4. import re
  5. import os
  6. import sys
  7. import shlex
  8. import argparse
  9. import subprocess
  10. from os.path import *
  11. from subprocess import Popen, PIPE
  12. class CheckOptions (object):
  13. def __init__(self, args):
  14. self.option_pattern = None
  15. self.source_file = None
  16. self.sep = "\n --"
  17. self.ledger = os.path.abspath(args.ledger)
  18. self.source = os.path.abspath(args.source)
  19. self.missing_options = set()
  20. self.unknown_options = set()
  21. self.missing_functions = set()
  22. self.unknown_functions = set()
  23. def find_pattern(self, filename, pattern):
  24. regex = re.compile(pattern)
  25. return {match.group(1) for match in {regex.match(line) for line in open(filename)} if match}
  26. def find_options(self, filename):
  27. return self.find_pattern(filename, self.option_pattern)
  28. def find_functions(self, filename):
  29. return self.find_pattern(filename, self.function_pattern)
  30. def find_alternates(self):
  31. command = shlex.split('grep --no-filename OPT_ALT')
  32. for source_file in ['session', 'report']:
  33. command.append(os.path.join(self.source, 'src', '%s.cc' % source_file))
  34. try:
  35. output = subprocess.check_output(command).split('\n');
  36. except subprocess.CalledProcessError:
  37. output = ''
  38. regex = re.compile(r'OPT_ALT\([^,]*,\s*([^)]+?)_?\)');
  39. alternates = {match.group(1).replace('_', '-') for match in {regex.search(line) for line in output} if match}
  40. return alternates
  41. def ledger_options(self):
  42. pipe = Popen('%s --debug option.names parse true' %
  43. self.ledger, shell=True, stdout=PIPE, stderr=PIPE)
  44. regex = re.compile('\[DEBUG\]\s+Option:\s+(.*?)_?$')
  45. ledger_options = {match.group(1).replace('_', '-') for match in {regex.search(line.decode()) for line in pipe.stderr} if match}
  46. return ledger_options
  47. def ledger_functions(self):
  48. command = shlex.split('grep --no-filename fn_ %s' % (os.path.join(self.source, 'src', 'report.h')))
  49. try:
  50. output = subprocess.check_output(command).split('\n');
  51. except subprocess.CalledProcessError:
  52. output = ''
  53. regex = re.compile(r'fn_([^(]+)\(');
  54. functions = {match.group(1) for match in {regex.search(line) for line in output} if match}
  55. return functions
  56. def main(self):
  57. options = self.find_options(self.source_file)
  58. for option in self.ledger_options():
  59. if option not in options:
  60. self.missing_options.add(option)
  61. else:
  62. options.remove(option)
  63. known_alternates = self.find_alternates()
  64. self.unknown_options = {option for option in options if option not in known_alternates}
  65. functions = self.find_functions(self.source_file)
  66. for function in self.ledger_functions():
  67. if function not in functions:
  68. self.missing_functions.add(function)
  69. else:
  70. functions.remove(function)
  71. known_functions = ['tag', 'has_tag', 'meta', 'has_meta']
  72. self.unknown_functions = {function for function in functions if function not in known_functions}
  73. if len(self.missing_options):
  74. print("Missing %s option entries for:%s%s\n" % (self.source_type, self.sep, self.sep.join(sorted(list(self.missing_options)))))
  75. if len(self.unknown_options):
  76. print("%s entry for unknown options:%s%s\n" % (self.source_type, self.sep, self.sep.join(sorted(list(self.unknown_options)))))
  77. if len(self.missing_functions):
  78. print("Missing %s function entries for:%s%s\n" % (self.source_type, '\n ', '\n '.join(sorted(list(self.missing_functions)))))
  79. if len(self.unknown_functions):
  80. print("%s entry for unknown functions:%s%s\n" % (self.source_type, '\n ', '\n '.join(sorted(list(self.unknown_functions)))))
  81. errors = len(self.missing_options) + len(self.unknown_options) + len(self.missing_functions) + len(self.unknown_functions)
  82. return errors