1
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
5
# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr>
6
6
# (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
10
10
11
11
import os
12
12
import re
13
+ import sys
13
14
from subprocess import Popen , PIPE , STDOUT
15
+ from optparse import OptionParser
14
16
15
17
16
18
# regex expressions
32
34
REGEX_FILTER_FEATURES = re .compile (r"[A-Za-z0-9]$" )
33
35
34
36
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\n Kconfig. 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\' \n your 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\n delete important data since it "
78
+ "calls 'git reset --hard' for some performance\n reasons. "
79
+ " Please run this script in a clean Git tree or pass "
80
+ "'--force' if you\n want to ignore this warning and "
81
+ "continue." )
82
+
83
+ return opts
84
+
85
+
35
86
def main ():
36
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
+
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."""
37
164
source_files = []
38
165
kconfig_files = []
39
166
defined_features = set ()
40
167
referenced_features = dict () # {feature: [files]}
41
168
42
169
# 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" )
45
171
if len (stdout ) > 0 and stdout [- 1 ] == "\n " :
46
172
stdout = stdout [:- 1 ]
47
173
@@ -62,7 +188,7 @@ def main():
62
188
for kfile in kconfig_files :
63
189
parse_kconfig_file (kfile , defined_features , referenced_features )
64
190
65
- print "Undefined symbol used \t File list"
191
+ undefined = {} # {feature: [files]}
66
192
for feature in sorted (referenced_features ):
67
193
# filter some false positives
68
194
if feature == "FOO" or feature == "BAR" or \
@@ -73,8 +199,8 @@ def main():
73
199
# avoid false positives for kernel modules
74
200
if feature [:- len ("_MODULE" )] in defined_features :
75
201
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
78
204
79
205
80
206
def parse_source_file (sfile , referenced_features ):
0 commit comments