Blame view

scripts/checkkconfigsymbols.py 4.55 KB
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
1
  #!/usr/bin/env python
cc641d552   Valentin Rothberg   checkkconfigsymbo...
2
  """Find Kconfig identifiers that are referenced but not defined."""
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
3

cc641d552   Valentin Rothberg   checkkconfigsymbo...
4
5
  # (c) 2014 Valentin Rothberg <valentinrothberg@gmail.com>
  # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
6
  #
cc641d552   Valentin Rothberg   checkkconfigsymbo...
7
  # Licensed under the terms of the GNU GPL License version 2
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
8
9
10
11
12
  
  
  import os
  import re
  from subprocess import Popen, PIPE, STDOUT
cc641d552   Valentin Rothberg   checkkconfigsymbo...
13
14
  
  # regex expressions
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
15
  OPERATORS = r"&|\(|\)|\||\!"
cc641d552   Valentin Rothberg   checkkconfigsymbo...
16
17
  FEATURE = r"(?:\w*[A-Z0-9]\w*){2,}"
  DEF = r"^\s*(?:menu){,1}config\s+(" + FEATURE + r")\s*"
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
18
19
  EXPR = r"(?:" + OPERATORS + r"|\s|" + FEATURE + r")+"
  STMT = r"^\s*(?:if|select|depends\s+on)\s+" + EXPR
cc641d552   Valentin Rothberg   checkkconfigsymbo...
20
  SOURCE_FEATURE = r"(?:\W|\b)+[D]{,1}CONFIG_(" + FEATURE + r")"
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
21

cc641d552   Valentin Rothberg   checkkconfigsymbo...
22
  # regex objects
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
23
24
  REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$")
  REGEX_FEATURE = re.compile(r"(" + FEATURE + r")")
cc641d552   Valentin Rothberg   checkkconfigsymbo...
25
26
  REGEX_SOURCE_FEATURE = re.compile(SOURCE_FEATURE)
  REGEX_KCONFIG_DEF = re.compile(DEF)
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
27
28
29
30
31
32
33
34
35
36
37
  REGEX_KCONFIG_EXPR = re.compile(EXPR)
  REGEX_KCONFIG_STMT = re.compile(STMT)
  REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
  REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
  
  
  def main():
      """Main function of this module."""
      source_files = []
      kconfig_files = []
      defined_features = set()
cc641d552   Valentin Rothberg   checkkconfigsymbo...
38
      referenced_features = dict()  # {feature: [files]}
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
39
40
41
42
43
44
45
46
47
48
49
  
      # use 'git ls-files' to get the worklist
      pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True)
      (stdout, _) = pop.communicate()  # wait until finished
      if len(stdout) > 0 and stdout[-1] == "
  ":
          stdout = stdout[:-1]
  
      for gitfile in stdout.rsplit("
  "):
          if ".git" in gitfile or "ChangeLog" in gitfile or \
cc641d552   Valentin Rothberg   checkkconfigsymbo...
50
                  ".log" in gitfile or os.path.isdir(gitfile):
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
51
52
53
54
              continue
          if REGEX_FILE_KCONFIG.match(gitfile):
              kconfig_files.append(gitfile)
          else:
cc641d552   Valentin Rothberg   checkkconfigsymbo...
55
              # all non-Kconfig files are checked for consistency
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
56
57
58
59
60
61
62
63
64
65
              source_files.append(gitfile)
  
      for sfile in source_files:
          parse_source_file(sfile, referenced_features)
  
      for kfile in kconfig_files:
          parse_kconfig_file(kfile, defined_features, referenced_features)
  
      print "Undefined symbol used\tFile list"
      for feature in sorted(referenced_features):
cc641d552   Valentin Rothberg   checkkconfigsymbo...
66
67
68
69
          # filter some false positives
          if feature == "FOO" or feature == "BAR" or \
                  feature == "FOO_BAR" or feature == "XXX":
              continue
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
70
71
          if feature not in defined_features:
              if feature.endswith("_MODULE"):
cc641d552   Valentin Rothberg   checkkconfigsymbo...
72
                  # avoid false positives for kernel modules
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
73
74
                  if feature[:-len("_MODULE")] in defined_features:
                      continue
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
75
              files = referenced_features.get(feature)
cc641d552   Valentin Rothberg   checkkconfigsymbo...
76
              print "%s\t%s" % (feature, ", ".join(files))
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  
  
  def parse_source_file(sfile, referenced_features):
      """Parse @sfile for referenced Kconfig features."""
      lines = []
      with open(sfile, "r") as stream:
          lines = stream.readlines()
  
      for line in lines:
          if not "CONFIG_" in line:
              continue
          features = REGEX_SOURCE_FEATURE.findall(line)
          for feature in features:
              if not REGEX_FILTER_FEATURES.search(feature):
                  continue
cc641d552   Valentin Rothberg   checkkconfigsymbo...
92
93
94
              sfiles = referenced_features.get(feature, set())
              sfiles.add(sfile)
              referenced_features[feature] = sfiles
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  
  
  def get_features_in_line(line):
      """Return mentioned Kconfig features in @line."""
      return REGEX_FEATURE.findall(line)
  
  
  def parse_kconfig_file(kfile, defined_features, referenced_features):
      """Parse @kfile and update feature definitions and references."""
      lines = []
      skip = False
  
      with open(kfile, "r") as stream:
          lines = stream.readlines()
  
      for i in range(len(lines)):
          line = lines[i]
          line = line.strip('
  ')
cc641d552   Valentin Rothberg   checkkconfigsymbo...
114
          line = line.split("#")[0]  # ignore comments
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
115
116
117
118
119
120
121
122
  
          if REGEX_KCONFIG_DEF.match(line):
              feature_def = REGEX_KCONFIG_DEF.findall(line)
              defined_features.add(feature_def[0])
              skip = False
          elif REGEX_KCONFIG_HELP.match(line):
              skip = True
          elif skip:
cc641d552   Valentin Rothberg   checkkconfigsymbo...
123
              # ignore content of help messages
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
124
125
126
              pass
          elif REGEX_KCONFIG_STMT.match(line):
              features = get_features_in_line(line)
cc641d552   Valentin Rothberg   checkkconfigsymbo...
127
              # multi-line statements
24fe1f03e   Valentin Rothberg   checkkconfigsymbo...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
              while line.endswith("\\"):
                  i += 1
                  line = lines[i]
                  line = line.strip('
  ')
                  features.extend(get_features_in_line(line))
              for feature in set(features):
                  paths = referenced_features.get(feature, set())
                  paths.add(kfile)
                  referenced_features[feature] = paths
  
  
  if __name__ == "__main__":
      main()