checkkconfigsymbols.py 4.8 KB

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