diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/checkkconfigsymbols.py | 142 | ||||
-rwxr-xr-x | scripts/checkkconfigsymbols.sh | 59 |
2 files changed, 142 insertions, 59 deletions
diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py new file mode 100644 index 000000000000..f9440891d048 --- /dev/null +++ b/scripts/checkkconfigsymbols.py | |||
@@ -0,0 +1,142 @@ | |||
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() | ||
diff --git a/scripts/checkkconfigsymbols.sh b/scripts/checkkconfigsymbols.sh deleted file mode 100755 index ccb3391882d1..000000000000 --- a/scripts/checkkconfigsymbols.sh +++ /dev/null | |||
@@ -1,59 +0,0 @@ | |||
1 | #!/bin/sh | ||
2 | # Find Kconfig variables used in source code but never defined in Kconfig | ||
3 | # Copyright (C) 2007, Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> | ||
4 | |||
5 | # Tested with dash. | ||
6 | paths="$@" | ||
7 | [ -z "$paths" ] && paths=. | ||
8 | |||
9 | # Doing this once at the beginning saves a lot of time, on a cache-hot tree. | ||
10 | Kconfigs="`find . -name 'Kconfig' -o -name 'Kconfig*[^~]'`" | ||
11 | |||
12 | printf "File list \tundefined symbol used\n" | ||
13 | find $paths -name '*.[chS]' -o -name 'Makefile' -o -name 'Makefile*[^~]'| while read i | ||
14 | do | ||
15 | # Output the bare Kconfig variable and the filename; the _MODULE part at | ||
16 | # the end is not removed here (would need perl an not-hungry regexp for that). | ||
17 | sed -ne 's!^.*\<\(UML_\)\?CONFIG_\([0-9A-Za-z_]\+\).*!\2 '$i'!p' < $i | ||
18 | done | \ | ||
19 | # Smart "sort|uniq" implemented in awk and tuned to collect the names of all | ||
20 | # files which use a given symbol | ||
21 | awk '{map[$1, count[$1]++] = $2; } | ||
22 | END { | ||
23 | for (combIdx in map) { | ||
24 | split(combIdx, separate, SUBSEP); | ||
25 | # The value may have been removed. | ||
26 | if (! ( (separate[1], separate[2]) in map ) ) | ||
27 | continue; | ||
28 | symb=separate[1]; | ||
29 | printf "%s ", symb; | ||
30 | #Use gawk extension to delete the names vector | ||
31 | delete names; | ||
32 | #Portably delete the names vector | ||
33 | #split("", names); | ||
34 | for (i=0; i < count[symb]; i++) { | ||
35 | names[map[symb, i]] = 1; | ||
36 | # Unfortunately, we may still encounter symb, i in the | ||
37 | # outside iteration. | ||
38 | delete map[symb, i]; | ||
39 | } | ||
40 | i=0; | ||
41 | for (name in names) { | ||
42 | if (i > 0) | ||
43 | printf ", %s", name; | ||
44 | else | ||
45 | printf "%s", name; | ||
46 | i++; | ||
47 | } | ||
48 | printf "\n"; | ||
49 | } | ||
50 | }' | | ||
51 | while read symb files; do | ||
52 | # Remove the _MODULE suffix when checking the variable name. This should | ||
53 | # be done only on tristate symbols, actually, but Kconfig parsing is | ||
54 | # beyond the purpose of this script. | ||
55 | symb_bare=`echo $symb | sed -e 's/_MODULE//'` | ||
56 | if ! grep -q "\<$symb_bare\>" $Kconfigs; then | ||
57 | printf "$files: \t$symb\n" | ||
58 | fi | ||
59 | done|sort | ||