aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorValentin Rothberg <valentinrothberg@gmail.com>2015-03-16 07:16:14 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-16 16:10:28 -0400
commitb1a3f243485fa2643bc75fd70a23bbd7cfc74f2d (patch)
treed02d3be93c41975b3825014b9dc11c5b04cceb67 /scripts
parentda2ff527e44bf3af851c1e5d9ac82d248df35417 (diff)
checkkconfigsymbols.py: make it Git aware
The script now supports to check a specified commit or a specified range of commits (i.e., commit1..commit2). Developers and maintainers are encouraged to use this functionality before sending or merging patches to avoid potential bugs and to keep the code, documentation, etc. clean. This patch adds the following options to the script: -c COMMIT, --commit=COMMIT Check if the specified commit (hash) introduces undefined Kconfig symbols. -d DIFF, --diff=DIFF Diff undefined symbols between two commits. The input format bases on Git log's 'commmit1..commit2'. --force Reset current Git tree even when it's dirty. Note that the first two options require to 'git reset --hard' the user's Git tree. This hard reset is necessary to keep the script fast, but it can lead to the loss of uncommitted data. Hence, the script aborts in case it is executed in a dirty tree. It won't abort if '--force' is passed. If neither -c nor -d is specified, the script defaults to check the entire local tree (i.e., the previous behavior). Signed-off-by: Valentin Rothberg <valentinrothberg@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/checkkconfigsymbols.py138
1 files changed, 132 insertions, 6 deletions
diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py
index 6445693df669..ce9ca60808b8 100755
--- a/scripts/checkkconfigsymbols.py
+++ b/scripts/checkkconfigsymbols.py
@@ -1,6 +1,6 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2 2
3"""Find Kconfig identifiers that are referenced but not defined.""" 3"""Find Kconfig symbols that are referenced but not defined."""
4 4
5# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr> 5# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr>
6# (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de> 6# (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
@@ -10,7 +10,9 @@
10 10
11import os 11import os
12import re 12import re
13import sys
13from subprocess import Popen, PIPE, STDOUT 14from subprocess import Popen, PIPE, STDOUT
15from optparse import OptionParser
14 16
15 17
16# regex expressions 18# regex expressions
@@ -32,16 +34,140 @@ REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
32REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$") 34REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
33 35
34 36
37def parse_options():
38 """The user interface of this module."""
39 usage = "%prog [options]\n\n" \
40 "Run this tool to detect Kconfig symbols that are referenced but " \
41 "not defined in\nKconfig. The output of this tool has the " \
42 "format \'Undefined symbol\\tFile list\'\n\n" \
43 "If no option is specified, %prog will default to check your\n" \
44 "current tree. Please note that specifying commits will " \
45 "\'git reset --hard\'\nyour current tree! You may save " \
46 "uncommitted changes to avoid losing data."
47
48 parser = OptionParser(usage=usage)
49
50 parser.add_option('-c', '--commit', dest='commit', action='store',
51 default="",
52 help="Check if the specified commit (hash) introduces "
53 "undefined Kconfig symbols.")
54
55 parser.add_option('-d', '--diff', dest='diff', action='store',
56 default="",
57 help="Diff undefined symbols between two commits. The "
58 "input format bases on Git log's "
59 "\'commmit1..commit2\'.")
60
61 parser.add_option('', '--force', dest='force', action='store_true',
62 default=False,
63 help="Reset current Git tree even when it's dirty.")
64
65 (opts, _) = parser.parse_args()
66
67 if opts.commit and opts.diff:
68 sys.exit("Please specify only one option at once.")
69
70 if opts.diff and not re.match(r"^[\w\-\.]+\.\.[\w\-\.]+$", opts.diff):
71 sys.exit("Please specify valid input in the following format: "
72 "\'commmit1..commit2\'")
73
74 if opts.commit or opts.diff:
75 if not opts.force and tree_is_dirty():
76 sys.exit("The current Git tree is dirty (see 'git status'). "
77 "Running this script may\ndelete important data since it "
78 "calls 'git reset --hard' for some performance\nreasons. "
79 " Please run this script in a clean Git tree or pass "
80 "'--force' if you\nwant to ignore this warning and "
81 "continue.")
82
83 return opts
84
85
35def main(): 86def main():
36 """Main function of this module.""" 87 """Main function of this module."""
88 opts = parse_options()
89
90 if opts.commit or opts.diff:
91 head = get_head()
92
93 # get commit range
94 commit_a = None
95 commit_b = None
96 if opts.commit:
97 commit_a = opts.commit + "~"
98 commit_b = opts.commit
99 elif opts.diff:
100 split = opts.diff.split("..")
101 commit_a = split[0]
102 commit_b = split[1]
103 undefined_a = {}
104 undefined_b = {}
105
106 # get undefined items before the commit
107 execute("git reset --hard %s" % commit_a)
108 undefined_a = check_symbols()
109
110 # get undefined items for the commit
111 execute("git reset --hard %s" % commit_b)
112 undefined_b = check_symbols()
113
114 # report cases that are present for the commit but not before
115 for feature in undefined_b:
116 # feature has not been undefined before
117 if not feature in undefined_a:
118 files = undefined_b.get(feature)
119 print "%s\t%s" % (feature, ", ".join(files))
120 # check if there are new files that reference the undefined feature
121 else:
122 files = undefined_b.get(feature) - undefined_a.get(feature)
123 if files:
124 print "%s\t%s" % (feature, ", ".join(files))
125
126 # reset to head
127 execute("git reset --hard %s" % head)
128
129 # default to check the entire tree
130 else:
131 undefined = check_symbols()
132 for feature in undefined:
133 files = undefined.get(feature)
134
135
136def execute(cmd):
137 """Execute %cmd and return stdout. Exit in case of error."""
138 pop = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True)
139 (stdout, _) = pop.communicate() # wait until finished
140 if pop.returncode != 0:
141 sys.exit(stdout)
142 return stdout
143
144
145def tree_is_dirty():
146 """Return true if the current working tree is dirty (i.e., if any file has
147 been added, deleted, modified, renamed or copied but not committed)."""
148 stdout = execute("git status --porcelain")
149 for line in stdout:
150 if re.findall(r"[URMADC]{1}", line[:2]):
151 return True
152 return False
153
154
155def get_head():
156 """Return commit hash of current HEAD."""
157 stdout = execute("git rev-parse HEAD")
158 return stdout.strip('\n')
159
160
161def check_symbols():
162 """Find undefined Kconfig symbols and return a dict with the symbol as key
163 and a list of referencing files as value."""
37 source_files = [] 164 source_files = []
38 kconfig_files = [] 165 kconfig_files = []
39 defined_features = set() 166 defined_features = set()
40 referenced_features = dict() # {feature: [files]} 167 referenced_features = dict() # {feature: [files]}
41 168
42 # use 'git ls-files' to get the worklist 169 # use 'git ls-files' to get the worklist
43 pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True) 170 stdout = execute("git ls-files")
44 (stdout, _) = pop.communicate() # wait until finished
45 if len(stdout) > 0 and stdout[-1] == "\n": 171 if len(stdout) > 0 and stdout[-1] == "\n":
46 stdout = stdout[:-1] 172 stdout = stdout[:-1]
47 173
@@ -62,7 +188,7 @@ def main():
62 for kfile in kconfig_files: 188 for kfile in kconfig_files:
63 parse_kconfig_file(kfile, defined_features, referenced_features) 189 parse_kconfig_file(kfile, defined_features, referenced_features)
64 190
65 print "Undefined symbol used\tFile list" 191 undefined = {} # {feature: [files]}
66 for feature in sorted(referenced_features): 192 for feature in sorted(referenced_features):
67 # filter some false positives 193 # filter some false positives
68 if feature == "FOO" or feature == "BAR" or \ 194 if feature == "FOO" or feature == "BAR" or \
@@ -73,8 +199,8 @@ def main():
73 # avoid false positives for kernel modules 199 # avoid false positives for kernel modules
74 if feature[:-len("_MODULE")] in defined_features: 200 if feature[:-len("_MODULE")] in defined_features:
75 continue 201 continue
76 files = referenced_features.get(feature) 202 undefined[feature] = referenced_features.get(feature)
77 print "%s\t%s" % (feature, ", ".join(files)) 203 return undefined
78 204
79 205
80def parse_source_file(sfile, referenced_features): 206def parse_source_file(sfile, referenced_features):