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