Skip to content

Commit b1a3f24

Browse files
vrothberggregkh
authored andcommitted
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>
1 parent da2ff52 commit b1a3f24

File tree

1 file changed

+132
-6
lines changed

1 file changed

+132
-6
lines changed

scripts/checkkconfigsymbols.py

+132-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22

3-
"""Find Kconfig identifiers that are referenced but not defined."""
3+
"""Find Kconfig symbols that are referenced but not defined."""
44

55
# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr>
66
# (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
@@ -10,7 +10,9 @@
1010

1111
import os
1212
import re
13+
import sys
1314
from subprocess import Popen, PIPE, STDOUT
15+
from optparse import OptionParser
1416

1517

1618
# regex expressions
@@ -32,16 +34,140 @@
3234
REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
3335

3436

37+
def 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+
3586
def main():
3687
"""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+
136+
def 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+
145+
def 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+
155+
def get_head():
156+
"""Return commit hash of current HEAD."""
157+
stdout = execute("git rev-parse HEAD")
158+
return stdout.strip('\n')
159+
160+
161+
def 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."""
37164
source_files = []
38165
kconfig_files = []
39166
defined_features = set()
40167
referenced_features = dict() # {feature: [files]}
41168

42169
# 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
170+
stdout = execute("git ls-files")
45171
if len(stdout) > 0 and stdout[-1] == "\n":
46172
stdout = stdout[:-1]
47173

@@ -62,7 +188,7 @@ def main():
62188
for kfile in kconfig_files:
63189
parse_kconfig_file(kfile, defined_features, referenced_features)
64190

65-
print "Undefined symbol used\tFile list"
191+
undefined = {} # {feature: [files]}
66192
for feature in sorted(referenced_features):
67193
# filter some false positives
68194
if feature == "FOO" or feature == "BAR" or \
@@ -73,8 +199,8 @@ def main():
73199
# avoid false positives for kernel modules
74200
if feature[:-len("_MODULE")] in defined_features:
75201
continue
76-
files = referenced_features.get(feature)
77-
print "%s\t%s" % (feature, ", ".join(files))
202+
undefined[feature] = referenced_features.get(feature)
203+
return undefined
78204

79205

80206
def parse_source_file(sfile, referenced_features):

0 commit comments

Comments
 (0)