blob: 6445693df66923007af4116d7d4a4bbdcb76203d [file] [log] [blame]
Valentin Rothberg24fe1f02014-09-27 16:30:45 +02001#!/usr/bin/env python
2
Valentin Rothbergcc641d552014-11-08 20:56:35 +01003"""Find Kconfig identifiers that are referenced but not defined."""
Valentin Rothberg24fe1f02014-09-27 16:30:45 +02004
Valentin Rothberg208d5112015-02-25 15:15:23 +01005# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr>
Valentin Rothbergcc641d552014-11-08 20:56:35 +01006# (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
Valentin Rothberg24fe1f02014-09-27 16:30:45 +02007#
Valentin Rothbergcc641d552014-11-08 20:56:35 +01008# Licensed under the terms of the GNU GPL License version 2
Valentin Rothberg24fe1f02014-09-27 16:30:45 +02009
10
11import os
12import re
13from subprocess import Popen, PIPE, STDOUT
14
Valentin Rothbergcc641d552014-11-08 20:56:35 +010015
16# regex expressions
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020017OPERATORS = r"&|\(|\)|\||\!"
Valentin Rothbergcc641d552014-11-08 20:56:35 +010018FEATURE = r"(?:\w*[A-Z0-9]\w*){2,}"
19DEF = r"^\s*(?:menu){,1}config\s+(" + FEATURE + r")\s*"
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020020EXPR = r"(?:" + OPERATORS + r"|\s|" + FEATURE + r")+"
21STMT = r"^\s*(?:if|select|depends\s+on)\s+" + EXPR
Valentin Rothbergcc641d552014-11-08 20:56:35 +010022SOURCE_FEATURE = r"(?:\W|\b)+[D]{,1}CONFIG_(" + FEATURE + r")"
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020023
Valentin Rothbergcc641d552014-11-08 20:56:35 +010024# regex objects
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020025REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$")
26REGEX_FEATURE = re.compile(r"(" + FEATURE + r")")
Valentin Rothbergcc641d552014-11-08 20:56:35 +010027REGEX_SOURCE_FEATURE = re.compile(SOURCE_FEATURE)
28REGEX_KCONFIG_DEF = re.compile(DEF)
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020029REGEX_KCONFIG_EXPR = re.compile(EXPR)
30REGEX_KCONFIG_STMT = re.compile(STMT)
31REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
32REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
33
34
35def main():
36 """Main function of this module."""
37 source_files = []
38 kconfig_files = []
39 defined_features = set()
Valentin Rothbergcc641d552014-11-08 20:56:35 +010040 referenced_features = dict() # {feature: [files]}
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020041
42 # use 'git ls-files' to get the worklist
43 pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True)
44 (stdout, _) = pop.communicate() # wait until finished
45 if len(stdout) > 0 and stdout[-1] == "\n":
46 stdout = stdout[:-1]
47
48 for gitfile in stdout.rsplit("\n"):
Valentin Rothberg208d5112015-02-25 15:15:23 +010049 if ".git" in gitfile or "ChangeLog" in gitfile or \
50 ".log" in gitfile or os.path.isdir(gitfile) or \
51 gitfile.startswith("tools/"):
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020052 continue
53 if REGEX_FILE_KCONFIG.match(gitfile):
54 kconfig_files.append(gitfile)
55 else:
Valentin Rothbergcc641d552014-11-08 20:56:35 +010056 # all non-Kconfig files are checked for consistency
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020057 source_files.append(gitfile)
58
59 for sfile in source_files:
60 parse_source_file(sfile, referenced_features)
61
62 for kfile in kconfig_files:
63 parse_kconfig_file(kfile, defined_features, referenced_features)
64
65 print "Undefined symbol used\tFile list"
66 for feature in sorted(referenced_features):
Valentin Rothbergcc641d552014-11-08 20:56:35 +010067 # filter some false positives
68 if feature == "FOO" or feature == "BAR" or \
69 feature == "FOO_BAR" or feature == "XXX":
70 continue
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020071 if feature not in defined_features:
72 if feature.endswith("_MODULE"):
Valentin Rothbergcc641d552014-11-08 20:56:35 +010073 # avoid false positives for kernel modules
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020074 if feature[:-len("_MODULE")] in defined_features:
75 continue
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020076 files = referenced_features.get(feature)
Valentin Rothbergcc641d552014-11-08 20:56:35 +010077 print "%s\t%s" % (feature, ", ".join(files))
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020078
79
80def parse_source_file(sfile, referenced_features):
81 """Parse @sfile for referenced Kconfig features."""
82 lines = []
83 with open(sfile, "r") as stream:
84 lines = stream.readlines()
85
86 for line in lines:
87 if not "CONFIG_" in line:
88 continue
89 features = REGEX_SOURCE_FEATURE.findall(line)
90 for feature in features:
91 if not REGEX_FILTER_FEATURES.search(feature):
92 continue
Valentin Rothbergcc641d552014-11-08 20:56:35 +010093 sfiles = referenced_features.get(feature, set())
94 sfiles.add(sfile)
95 referenced_features[feature] = sfiles
Valentin Rothberg24fe1f02014-09-27 16:30:45 +020096
97
98def get_features_in_line(line):
99 """Return mentioned Kconfig features in @line."""
100 return REGEX_FEATURE.findall(line)
101
102
103def parse_kconfig_file(kfile, defined_features, referenced_features):
104 """Parse @kfile and update feature definitions and references."""
105 lines = []
106 skip = False
107
108 with open(kfile, "r") as stream:
109 lines = stream.readlines()
110
111 for i in range(len(lines)):
112 line = lines[i]
113 line = line.strip('\n')
Valentin Rothbergcc641d552014-11-08 20:56:35 +0100114 line = line.split("#")[0] # ignore comments
Valentin Rothberg24fe1f02014-09-27 16:30:45 +0200115
116 if REGEX_KCONFIG_DEF.match(line):
117 feature_def = REGEX_KCONFIG_DEF.findall(line)
118 defined_features.add(feature_def[0])
119 skip = False
120 elif REGEX_KCONFIG_HELP.match(line):
121 skip = True
122 elif skip:
Valentin Rothbergcc641d552014-11-08 20:56:35 +0100123 # ignore content of help messages
Valentin Rothberg24fe1f02014-09-27 16:30:45 +0200124 pass
125 elif REGEX_KCONFIG_STMT.match(line):
126 features = get_features_in_line(line)
Valentin Rothbergcc641d552014-11-08 20:56:35 +0100127 # multi-line statements
Valentin Rothberg24fe1f02014-09-27 16:30:45 +0200128 while line.endswith("\\"):
129 i += 1
130 line = lines[i]
131 line = line.strip('\n')
132 features.extend(get_features_in_line(line))
133 for feature in set(features):
134 paths = referenced_features.get(feature, set())
135 paths.add(kfile)
136 referenced_features[feature] = paths
137
138
139if __name__ == "__main__":
140 main()