checkkconfigsymbols.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #!/usr/bin/env python
  2. """Find Kconfig identifiers that are referenced but not defined."""
  3. # (c) 2014 Valentin Rothberg <valentinrothberg@gmail.com>
  4. # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
  5. #
  6. # Licensed under the terms of the GNU GPL License version 2
  7. import os
  8. import re
  9. from subprocess import Popen, PIPE, STDOUT
  10. # regex expressions
  11. OPERATORS = r"&|\(|\)|\||\!"
  12. FEATURE = r"(?:\w*[A-Z0-9]\w*){2,}"
  13. DEF = r"^\s*(?:menu){,1}config\s+(" + FEATURE + r")\s*"
  14. EXPR = r"(?:" + OPERATORS + r"|\s|" + FEATURE + r")+"
  15. STMT = r"^\s*(?:if|select|depends\s+on)\s+" + EXPR
  16. SOURCE_FEATURE = r"(?:\W|\b)+[D]{,1}CONFIG_(" + FEATURE + r")"
  17. # regex objects
  18. REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$")
  19. REGEX_FEATURE = re.compile(r"(" + FEATURE + r")")
  20. REGEX_SOURCE_FEATURE = re.compile(SOURCE_FEATURE)
  21. REGEX_KCONFIG_DEF = re.compile(DEF)
  22. REGEX_KCONFIG_EXPR = re.compile(EXPR)
  23. REGEX_KCONFIG_STMT = re.compile(STMT)
  24. REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
  25. REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
  26. def main():
  27. """Main function of this module."""
  28. source_files = []
  29. kconfig_files = []
  30. defined_features = set()
  31. referenced_features = dict() # {feature: [files]}
  32. # use 'git ls-files' to get the worklist
  33. pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True)
  34. (stdout, _) = pop.communicate() # wait until finished
  35. if len(stdout) > 0 and stdout[-1] == "\n":
  36. stdout = stdout[:-1]
  37. for gitfile in stdout.rsplit("\n"):
  38. if ".git" in gitfile or "ChangeLog" in gitfile or \
  39. ".log" in gitfile or os.path.isdir(gitfile):
  40. continue
  41. if REGEX_FILE_KCONFIG.match(gitfile):
  42. kconfig_files.append(gitfile)
  43. else:
  44. # all non-Kconfig files are checked for consistency
  45. source_files.append(gitfile)
  46. for sfile in source_files:
  47. parse_source_file(sfile, referenced_features)
  48. for kfile in kconfig_files:
  49. parse_kconfig_file(kfile, defined_features, referenced_features)
  50. print "Undefined symbol used\tFile list"
  51. for feature in sorted(referenced_features):
  52. # filter some false positives
  53. if feature == "FOO" or feature == "BAR" or \
  54. feature == "FOO_BAR" or feature == "XXX":
  55. continue
  56. if feature not in defined_features:
  57. if feature.endswith("_MODULE"):
  58. # avoid false positives for kernel modules
  59. if feature[:-len("_MODULE")] in defined_features:
  60. continue
  61. files = referenced_features.get(feature)
  62. print "%s\t%s" % (feature, ", ".join(files))
  63. def parse_source_file(sfile, referenced_features):
  64. """Parse @sfile for referenced Kconfig features."""
  65. lines = []
  66. with open(sfile, "r") as stream:
  67. lines = stream.readlines()
  68. for line in lines:
  69. if not "CONFIG_" in line:
  70. continue
  71. features = REGEX_SOURCE_FEATURE.findall(line)
  72. for feature in features:
  73. if not REGEX_FILTER_FEATURES.search(feature):
  74. continue
  75. sfiles = referenced_features.get(feature, set())
  76. sfiles.add(sfile)
  77. referenced_features[feature] = sfiles
  78. def get_features_in_line(line):
  79. """Return mentioned Kconfig features in @line."""
  80. return REGEX_FEATURE.findall(line)
  81. def parse_kconfig_file(kfile, defined_features, referenced_features):
  82. """Parse @kfile and update feature definitions and references."""
  83. lines = []
  84. skip = False
  85. with open(kfile, "r") as stream:
  86. lines = stream.readlines()
  87. for i in range(len(lines)):
  88. line = lines[i]
  89. line = line.strip('\n')
  90. line = line.split("#")[0] # ignore comments
  91. if REGEX_KCONFIG_DEF.match(line):
  92. feature_def = REGEX_KCONFIG_DEF.findall(line)
  93. defined_features.add(feature_def[0])
  94. skip = False
  95. elif REGEX_KCONFIG_HELP.match(line):
  96. skip = True
  97. elif skip:
  98. # ignore content of help messages
  99. pass
  100. elif REGEX_KCONFIG_STMT.match(line):
  101. features = get_features_in_line(line)
  102. # multi-line statements
  103. while line.endswith("\\"):
  104. i += 1
  105. line = lines[i]
  106. line = line.strip('\n')
  107. features.extend(get_features_in_line(line))
  108. for feature in set(features):
  109. paths = referenced_features.get(feature, set())
  110. paths.add(kfile)
  111. referenced_features[feature] = paths
  112. if __name__ == "__main__":
  113. main()