3 """Find Kconfig identifiers that are referenced but not defined."""
5 # (c) 2014 Valentin Rothberg <valentinrothberg@gmail.com>
6 # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
8 # Licensed under the terms of the GNU GPL License version 2
13 from subprocess
import Popen
, PIPE
, STDOUT
17 OPERATORS
= r
"&|\(|\)|\||\!"
18 FEATURE
= r
"(?:\w*[A-Z0-9]\w*){2,}"
19 DEF
= r
"^\s*(?:menu){,1}config\s+(" + FEATURE
+ r
")\s*"
20 EXPR
= r
"(?:" + OPERATORS
+ r
"|\s|" + FEATURE
+ r
")+"
21 STMT
= r
"^\s*(?:if|select|depends\s+on)\s+" + EXPR
22 SOURCE_FEATURE
= r
"(?:\W|\b)+[D]{,1}CONFIG_(" + FEATURE
+ r
")"
25 REGEX_FILE_KCONFIG
= re
.compile(r
".*Kconfig[\.\w+\-]*$")
26 REGEX_FEATURE
= re
.compile(r
"(" + FEATURE
+ r
")")
27 REGEX_SOURCE_FEATURE
= re
.compile(SOURCE_FEATURE
)
28 REGEX_KCONFIG_DEF
= re
.compile(DEF
)
29 REGEX_KCONFIG_EXPR
= re
.compile(EXPR
)
30 REGEX_KCONFIG_STMT
= re
.compile(STMT
)
31 REGEX_KCONFIG_HELP
= re
.compile(r
"^\s+(help|---help---)\s*$")
32 REGEX_FILTER_FEATURES
= re
.compile(r
"[A-Za-z0-9]$")
36 """Main function of this module."""
39 defined_features
= set()
40 referenced_features
= dict() # {feature: [files]}
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":
48 for gitfile
in stdout
.rsplit("\n"):
49 if ".git" in gitfile
or "ChangeLog" in gitfile
or \
50 ".log" in gitfile
or os
.path
.isdir(gitfile
):
52 if REGEX_FILE_KCONFIG
.match(gitfile
):
53 kconfig_files
.append(gitfile
)
55 # all non-Kconfig files are checked for consistency
56 source_files
.append(gitfile
)
58 for sfile
in source_files
:
59 parse_source_file(sfile
, referenced_features
)
61 for kfile
in kconfig_files
:
62 parse_kconfig_file(kfile
, defined_features
, referenced_features
)
64 print "Undefined symbol used\tFile list"
65 for feature
in sorted(referenced_features
):
66 # filter some false positives
67 if feature
== "FOO" or feature
== "BAR" or \
68 feature
== "FOO_BAR" or feature
== "XXX":
70 if feature
not in defined_features
:
71 if feature
.endswith("_MODULE"):
72 # avoid false positives for kernel modules
73 if feature
[:-len("_MODULE")] in defined_features
:
75 files
= referenced_features
.get(feature
)
76 print "%s\t%s" % (feature
, ", ".join(files
))
79 def parse_source_file(sfile
, referenced_features
):
80 """Parse @sfile for referenced Kconfig features."""
82 with
open(sfile
, "r") as stream
:
83 lines
= stream
.readlines()
86 if not "CONFIG_" in line
:
88 features
= REGEX_SOURCE_FEATURE
.findall(line
)
89 for feature
in features
:
90 if not REGEX_FILTER_FEATURES
.search(feature
):
92 sfiles
= referenced_features
.get(feature
, set())
94 referenced_features
[feature
] = sfiles
97 def get_features_in_line(line
):
98 """Return mentioned Kconfig features in @line."""
99 return REGEX_FEATURE
.findall(line
)
102 def parse_kconfig_file(kfile
, defined_features
, referenced_features
):
103 """Parse @kfile and update feature definitions and references."""
107 with
open(kfile
, "r") as stream
:
108 lines
= stream
.readlines()
110 for i
in range(len(lines
)):
112 line
= line
.strip('\n')
113 line
= line
.split("#")[0] # ignore comments
115 if REGEX_KCONFIG_DEF
.match(line
):
116 feature_def
= REGEX_KCONFIG_DEF
.findall(line
)
117 defined_features
.add(feature_def
[0])
119 elif REGEX_KCONFIG_HELP
.match(line
):
122 # ignore content of help messages
124 elif REGEX_KCONFIG_STMT
.match(line
):
125 features
= get_features_in_line(line
)
126 # multi-line statements
127 while line
.endswith("\\"):
130 line
= line
.strip('\n')
131 features
.extend(get_features_in_line(line
))
132 for feature
in set(features
):
133 paths
= referenced_features
.get(feature
, set())
135 referenced_features
[feature
] = paths
138 if __name__
== "__main__":