~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/scripts/checkkconfigsymbols.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

Diff markup

Differences between /scripts/checkkconfigsymbols.py (Version linux-6.12-rc7) and /scripts/checkkconfigsymbols.py (Version linux-4.15.18)


  1 #!/usr/bin/env python3                              1 #!/usr/bin/env python3
  2 # SPDX-License-Identifier: GPL-2.0-only        << 
  3                                                     2 
  4 """Find Kconfig symbols that are referenced bu      3 """Find Kconfig symbols that are referenced but not defined."""
  5                                                     4 
  6 # (c) 2014-2017 Valentin Rothberg <valentinroth      5 # (c) 2014-2017 Valentin Rothberg <valentinrothberg@gmail.com>
  7 # (c) 2014 Stefan Hengelein <stefan.hengelein@f      6 # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
  8 #                                                   7 #
                                                   >>   8 # Licensed under the terms of the GNU GPL License version 2
  9                                                     9 
 10                                                    10 
 11 import argparse                                    11 import argparse
 12 import difflib                                     12 import difflib
 13 import os                                          13 import os
 14 import re                                          14 import re
 15 import signal                                      15 import signal
 16 import subprocess                                  16 import subprocess
 17 import sys                                         17 import sys
 18 from multiprocessing import Pool, cpu_count        18 from multiprocessing import Pool, cpu_count
 19                                                    19 
 20                                                    20 
 21 # regex expressions                                21 # regex expressions
 22 OPERATORS = r"&|\(|\)|\||\!"                       22 OPERATORS = r"&|\(|\)|\||\!"
 23 SYMBOL = r"(?:\w*[A-Z0-9]\w*){2,}"                 23 SYMBOL = r"(?:\w*[A-Z0-9]\w*){2,}"
 24 DEF = r"^\s*(?:menu){,1}config\s+(" + SYMBOL +     24 DEF = r"^\s*(?:menu){,1}config\s+(" + SYMBOL + r")\s*"
 25 EXPR = r"(?:" + OPERATORS + r"|\s|" + SYMBOL +     25 EXPR = r"(?:" + OPERATORS + r"|\s|" + SYMBOL + r")+"
 26 DEFAULT = r"default\s+.*?(?:if\s.+){,1}"           26 DEFAULT = r"default\s+.*?(?:if\s.+){,1}"
 27 STMT = r"^\s*(?:if|select|imply|depends\s+on|(     27 STMT = r"^\s*(?:if|select|imply|depends\s+on|(?:" + DEFAULT + r"))\s+" + EXPR
 28 SOURCE_SYMBOL = r"(?:\W|\b)+[D]{,1}CONFIG_(" +     28 SOURCE_SYMBOL = r"(?:\W|\b)+[D]{,1}CONFIG_(" + SYMBOL + r")"
 29                                                    29 
 30 # regex objects                                    30 # regex objects
 31 REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.     31 REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$")
 32 REGEX_SYMBOL = re.compile(r'(?!\B)' + SYMBOL +     32 REGEX_SYMBOL = re.compile(r'(?!\B)' + SYMBOL + r'(?!\B)')
 33 REGEX_SOURCE_SYMBOL = re.compile(SOURCE_SYMBOL     33 REGEX_SOURCE_SYMBOL = re.compile(SOURCE_SYMBOL)
 34 REGEX_KCONFIG_DEF = re.compile(DEF)                34 REGEX_KCONFIG_DEF = re.compile(DEF)
 35 REGEX_KCONFIG_EXPR = re.compile(EXPR)              35 REGEX_KCONFIG_EXPR = re.compile(EXPR)
 36 REGEX_KCONFIG_STMT = re.compile(STMT)              36 REGEX_KCONFIG_STMT = re.compile(STMT)
                                                   >>  37 REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
 37 REGEX_FILTER_SYMBOLS = re.compile(r"[A-Za-z0-9     38 REGEX_FILTER_SYMBOLS = re.compile(r"[A-Za-z0-9]$")
 38 REGEX_NUMERIC = re.compile(r"0[xX][0-9a-fA-F]+     39 REGEX_NUMERIC = re.compile(r"0[xX][0-9a-fA-F]+|[0-9]+")
 39 REGEX_QUOTES = re.compile("(\"(.*?)\")")           40 REGEX_QUOTES = re.compile("(\"(.*?)\")")
 40                                                    41 
 41                                                    42 
 42 def parse_options():                               43 def parse_options():
 43     """The user interface of this module."""       44     """The user interface of this module."""
 44     usage = "Run this tool to detect Kconfig s     45     usage = "Run this tool to detect Kconfig symbols that are referenced but " \
 45             "not defined in Kconfig.  If no op     46             "not defined in Kconfig.  If no option is specified, "             \
 46             "checkkconfigsymbols defaults to c     47             "checkkconfigsymbols defaults to check your current tree.  "       \
 47             "Please note that specifying commi     48             "Please note that specifying commits will 'git reset --hard\' "    \
 48             "your current tree!  You may save      49             "your current tree!  You may save uncommitted changes to avoid "   \
 49             "losing data."                         50             "losing data."
 50                                                    51 
 51     parser = argparse.ArgumentParser(descripti     52     parser = argparse.ArgumentParser(description=usage)
 52                                                    53 
 53     parser.add_argument('-c', '--commit', dest     54     parser.add_argument('-c', '--commit', dest='commit', action='store',
 54                         default="",                55                         default="",
 55                         help="check if the spe     56                         help="check if the specified commit (hash) introduces "
 56                              "undefined Kconfi     57                              "undefined Kconfig symbols")
 57                                                    58 
 58     parser.add_argument('-d', '--diff', dest='     59     parser.add_argument('-d', '--diff', dest='diff', action='store',
 59                         default="",                60                         default="",
 60                         help="diff undefined s     61                         help="diff undefined symbols between two commits "
 61                              "(e.g., -d commmi     62                              "(e.g., -d commmit1..commit2)")
 62                                                    63 
 63     parser.add_argument('-f', '--find', dest='     64     parser.add_argument('-f', '--find', dest='find', action='store_true',
 64                         default=False,             65                         default=False,
 65                         help="find and show co     66                         help="find and show commits that may cause symbols to be "
 66                              "missing (require     67                              "missing (required to run with --diff)")
 67                                                    68 
 68     parser.add_argument('-i', '--ignore', dest     69     parser.add_argument('-i', '--ignore', dest='ignore', action='store',
 69                         default="",                70                         default="",
 70                         help="ignore files mat     71                         help="ignore files matching this Python regex "
 71                              "(e.g., -i '.*def     72                              "(e.g., -i '.*defconfig')")
 72                                                    73 
 73     parser.add_argument('-s', '--sim', dest='s     74     parser.add_argument('-s', '--sim', dest='sim', action='store', default="",
 74                         help="print a list of      75                         help="print a list of max. 10 string-similar symbols")
 75                                                    76 
 76     parser.add_argument('--force', dest='force     77     parser.add_argument('--force', dest='force', action='store_true',
 77                         default=False,             78                         default=False,
 78                         help="reset current Gi     79                         help="reset current Git tree even when it's dirty")
 79                                                    80 
 80     parser.add_argument('--no-color', dest='co     81     parser.add_argument('--no-color', dest='color', action='store_false',
 81                         default=True,              82                         default=True,
 82                         help="don't print colo     83                         help="don't print colored output (default when not "
 83                              "outputting to a      84                              "outputting to a terminal)")
 84                                                    85 
 85     args = parser.parse_args()                     86     args = parser.parse_args()
 86                                                    87 
 87     if args.commit and args.diff:                  88     if args.commit and args.diff:
 88         sys.exit("Please specify only one opti     89         sys.exit("Please specify only one option at once.")
 89                                                    90 
 90     if args.diff and not re.match(r"^[\w\-\.\^     91     if args.diff and not re.match(r"^[\w\-\.\^]+\.\.[\w\-\.\^]+$", args.diff):
 91         sys.exit("Please specify valid input i     92         sys.exit("Please specify valid input in the following format: "
 92                  "\'commit1..commit2\'")           93                  "\'commit1..commit2\'")
 93                                                    94 
 94     if args.commit or args.diff:                   95     if args.commit or args.diff:
 95         if not args.force and tree_is_dirty():     96         if not args.force and tree_is_dirty():
 96             sys.exit("The current Git tree is      97             sys.exit("The current Git tree is dirty (see 'git status').  "
 97                      "Running this script may\     98                      "Running this script may\ndelete important data since it "
 98                      "calls 'git reset --hard'     99                      "calls 'git reset --hard' for some performance\nreasons. "
 99                      " Please run this script     100                      " Please run this script in a clean Git tree or pass "
100                      "'--force' if you\nwant t    101                      "'--force' if you\nwant to ignore this warning and "
101                      "continue.")                 102                      "continue.")
102                                                   103 
103     if args.commit:                               104     if args.commit:
104         if args.commit.startswith('HEAD'):     << 
105             sys.exit("The --commit option can' << 
106                                                << 
107         args.find = False                         105         args.find = False
108                                                   106 
109     if args.ignore:                               107     if args.ignore:
110         try:                                      108         try:
111             re.match(args.ignore, "this/is/jus    109             re.match(args.ignore, "this/is/just/a/test.c")
112         except:                                   110         except:
113             sys.exit("Please specify a valid P    111             sys.exit("Please specify a valid Python regex.")
114                                                   112 
115     return args                                   113     return args
116                                                   114 
117                                                   115 
118 def print_undefined_symbols():                 !! 116 def main():
119     """Main function of this module."""           117     """Main function of this module."""
120     args = parse_options()                        118     args = parse_options()
121                                                   119 
122     global COLOR                                  120     global COLOR
123     COLOR = args.color and sys.stdout.isatty()    121     COLOR = args.color and sys.stdout.isatty()
124                                                   122 
125     if args.sim and not args.commit and not ar    123     if args.sim and not args.commit and not args.diff:
126         sims = find_sims(args.sim, args.ignore    124         sims = find_sims(args.sim, args.ignore)
127         if sims:                                  125         if sims:
128             print("%s: %s" % (yel("Similar sym    126             print("%s: %s" % (yel("Similar symbols"), ', '.join(sims)))
129         else:                                     127         else:
130             print("%s: no similar symbols foun    128             print("%s: no similar symbols found" % yel("Similar symbols"))
131         sys.exit(0)                               129         sys.exit(0)
132                                                   130 
133     # dictionary of (un)defined symbols           131     # dictionary of (un)defined symbols
134     defined = {}                                  132     defined = {}
135     undefined = {}                                133     undefined = {}
136                                                   134 
137     if args.commit or args.diff:                  135     if args.commit or args.diff:
138         head = get_head()                         136         head = get_head()
139                                                   137 
140         # get commit range                        138         # get commit range
141         commit_a = None                           139         commit_a = None
142         commit_b = None                           140         commit_b = None
143         if args.commit:                           141         if args.commit:
144             commit_a = args.commit + "~"          142             commit_a = args.commit + "~"
145             commit_b = args.commit                143             commit_b = args.commit
146         elif args.diff:                           144         elif args.diff:
147             split = args.diff.split("..")         145             split = args.diff.split("..")
148             commit_a = split[0]                   146             commit_a = split[0]
149             commit_b = split[1]                   147             commit_b = split[1]
150             undefined_a = {}                      148             undefined_a = {}
151             undefined_b = {}                      149             undefined_b = {}
152                                                   150 
153         # get undefined items before the commi    151         # get undefined items before the commit
154         reset(commit_a)                           152         reset(commit_a)
155         undefined_a, _ = check_symbols(args.ig    153         undefined_a, _ = check_symbols(args.ignore)
156                                                   154 
157         # get undefined items for the commit      155         # get undefined items for the commit
158         reset(commit_b)                           156         reset(commit_b)
159         undefined_b, defined = check_symbols(a    157         undefined_b, defined = check_symbols(args.ignore)
160                                                   158 
161         # report cases that are present for th    159         # report cases that are present for the commit but not before
162         for symbol in sorted(undefined_b):        160         for symbol in sorted(undefined_b):
163             # symbol has not been undefined be    161             # symbol has not been undefined before
164             if symbol not in undefined_a:         162             if symbol not in undefined_a:
165                 files = sorted(undefined_b.get    163                 files = sorted(undefined_b.get(symbol))
166                 undefined[symbol] = files         164                 undefined[symbol] = files
167             # check if there are new files tha    165             # check if there are new files that reference the undefined symbol
168             else:                                 166             else:
169                 files = sorted(undefined_b.get    167                 files = sorted(undefined_b.get(symbol) -
170                                undefined_a.get    168                                undefined_a.get(symbol))
171                 if files:                         169                 if files:
172                     undefined[symbol] = files     170                     undefined[symbol] = files
173                                                   171 
174         # reset to head                           172         # reset to head
175         reset(head)                               173         reset(head)
176                                                   174 
177     # default to check the entire tree            175     # default to check the entire tree
178     else:                                         176     else:
179         undefined, defined = check_symbols(arg    177         undefined, defined = check_symbols(args.ignore)
180                                                   178 
181     # now print the output                        179     # now print the output
182     for symbol in sorted(undefined):              180     for symbol in sorted(undefined):
183         print(red(symbol))                        181         print(red(symbol))
184                                                   182 
185         files = sorted(undefined.get(symbol))     183         files = sorted(undefined.get(symbol))
186         print("%s: %s" % (yel("Referencing fil    184         print("%s: %s" % (yel("Referencing files"), ", ".join(files)))
187                                                   185 
188         sims = find_sims(symbol, args.ignore,     186         sims = find_sims(symbol, args.ignore, defined)
189         sims_out = yel("Similar symbols")         187         sims_out = yel("Similar symbols")
190         if sims:                                  188         if sims:
191             print("%s: %s" % (sims_out, ', '.j    189             print("%s: %s" % (sims_out, ', '.join(sims)))
192         else:                                     190         else:
193             print("%s: %s" % (sims_out, "no si    191             print("%s: %s" % (sims_out, "no similar symbols found"))
194                                                   192 
195         if args.find:                             193         if args.find:
196             print("%s:" % yel("Commits changin    194             print("%s:" % yel("Commits changing symbol"))
197             commits = find_commits(symbol, arg    195             commits = find_commits(symbol, args.diff)
198             if commits:                           196             if commits:
199                 for commit in commits:            197                 for commit in commits:
200                     commit = commit.split(" ",    198                     commit = commit.split(" ", 1)
201                     print("\t- %s (\"%s\")" %     199                     print("\t- %s (\"%s\")" % (yel(commit[0]), commit[1]))
202             else:                                 200             else:
203                 print("\t- no commit found")      201                 print("\t- no commit found")
204         print()  # new line                       202         print()  # new line
205                                                   203 
206                                                   204 
207 def reset(commit):                                205 def reset(commit):
208     """Reset current git tree to %commit."""      206     """Reset current git tree to %commit."""
209     execute(["git", "reset", "--hard", commit]    207     execute(["git", "reset", "--hard", commit])
210                                                   208 
211                                                   209 
212 def yel(string):                                  210 def yel(string):
213     """                                           211     """
214     Color %string yellow.                         212     Color %string yellow.
215     """                                           213     """
216     return "\033[33m%s\033[0m" % string if COL    214     return "\033[33m%s\033[0m" % string if COLOR else string
217                                                   215 
218                                                   216 
219 def red(string):                                  217 def red(string):
220     """                                           218     """
221     Color %string red.                            219     Color %string red.
222     """                                           220     """
223     return "\033[31m%s\033[0m" % string if COL    221     return "\033[31m%s\033[0m" % string if COLOR else string
224                                                   222 
225                                                   223 
226 def execute(cmd):                                 224 def execute(cmd):
227     """Execute %cmd and return stdout.  Exit i    225     """Execute %cmd and return stdout.  Exit in case of error."""
228     try:                                          226     try:
229         stdout = subprocess.check_output(cmd,     227         stdout = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=False)
230         stdout = stdout.decode(errors='replace    228         stdout = stdout.decode(errors='replace')
231     except subprocess.CalledProcessError as fa    229     except subprocess.CalledProcessError as fail:
232         exit(fail)                                230         exit(fail)
233     return stdout                                 231     return stdout
234                                                   232 
235                                                   233 
236 def find_commits(symbol, diff):                   234 def find_commits(symbol, diff):
237     """Find commits changing %symbol in the gi    235     """Find commits changing %symbol in the given range of %diff."""
238     commits = execute(["git", "log", "--pretty    236     commits = execute(["git", "log", "--pretty=oneline",
239                        "--abbrev-commit", "-G"    237                        "--abbrev-commit", "-G",
240                        symbol, diff])             238                        symbol, diff])
241     return [x for x in commits.split("\n") if     239     return [x for x in commits.split("\n") if x]
242                                                   240 
243                                                   241 
244 def tree_is_dirty():                              242 def tree_is_dirty():
245     """Return true if the current working tree    243     """Return true if the current working tree is dirty (i.e., if any file has
246     been added, deleted, modified, renamed or     244     been added, deleted, modified, renamed or copied but not committed)."""
247     stdout = execute(["git", "status", "--porc    245     stdout = execute(["git", "status", "--porcelain"])
248     for line in stdout:                           246     for line in stdout:
249         if re.findall(r"[URMADC]{1}", line[:2]    247         if re.findall(r"[URMADC]{1}", line[:2]):
250             return True                           248             return True
251     return False                                  249     return False
252                                                   250 
253                                                   251 
254 def get_head():                                   252 def get_head():
255     """Return commit hash of current HEAD."""     253     """Return commit hash of current HEAD."""
256     stdout = execute(["git", "rev-parse", "HEA    254     stdout = execute(["git", "rev-parse", "HEAD"])
257     return stdout.strip('\n')                     255     return stdout.strip('\n')
258                                                   256 
259                                                   257 
260 def partition(lst, size):                         258 def partition(lst, size):
261     """Partition list @lst into eveni-sized li    259     """Partition list @lst into eveni-sized lists of size @size."""
262     return [lst[i::size] for i in range(size)]    260     return [lst[i::size] for i in range(size)]
263                                                   261 
264                                                   262 
265 def init_worker():                                263 def init_worker():
266     """Set signal handler to ignore SIGINT."""    264     """Set signal handler to ignore SIGINT."""
267     signal.signal(signal.SIGINT, signal.SIG_IG    265     signal.signal(signal.SIGINT, signal.SIG_IGN)
268                                                   266 
269                                                   267 
270 def find_sims(symbol, ignore, defined=[]):        268 def find_sims(symbol, ignore, defined=[]):
271     """Return a list of max. ten Kconfig symbo    269     """Return a list of max. ten Kconfig symbols that are string-similar to
272     @symbol."""                                   270     @symbol."""
273     if defined:                                   271     if defined:
274         return difflib.get_close_matches(symbo    272         return difflib.get_close_matches(symbol, set(defined), 10)
275                                                   273 
276     pool = Pool(cpu_count(), init_worker)         274     pool = Pool(cpu_count(), init_worker)
277     kfiles = []                                   275     kfiles = []
278     for gitfile in get_files():                   276     for gitfile in get_files():
279         if REGEX_FILE_KCONFIG.match(gitfile):     277         if REGEX_FILE_KCONFIG.match(gitfile):
280             kfiles.append(gitfile)                278             kfiles.append(gitfile)
281                                                   279 
282     arglist = []                                  280     arglist = []
283     for part in partition(kfiles, cpu_count())    281     for part in partition(kfiles, cpu_count()):
284         arglist.append((part, ignore))            282         arglist.append((part, ignore))
285                                                   283 
286     for res in pool.map(parse_kconfig_files, a    284     for res in pool.map(parse_kconfig_files, arglist):
287         defined.extend(res[0])                    285         defined.extend(res[0])
288                                                   286 
289     return difflib.get_close_matches(symbol, s    287     return difflib.get_close_matches(symbol, set(defined), 10)
290                                                   288 
291                                                   289 
292 def get_files():                                  290 def get_files():
293     """Return a list of all files in the curre    291     """Return a list of all files in the current git directory."""
294     # use 'git ls-files' to get the worklist      292     # use 'git ls-files' to get the worklist
295     stdout = execute(["git", "ls-files"])         293     stdout = execute(["git", "ls-files"])
296     if len(stdout) > 0 and stdout[-1] == "\n":    294     if len(stdout) > 0 and stdout[-1] == "\n":
297         stdout = stdout[:-1]                      295         stdout = stdout[:-1]
298                                                   296 
299     files = []                                    297     files = []
300     for gitfile in stdout.rsplit("\n"):           298     for gitfile in stdout.rsplit("\n"):
301         if ".git" in gitfile or "ChangeLog" in    299         if ".git" in gitfile or "ChangeLog" in gitfile or      \
302                 ".log" in gitfile or os.path.i    300                 ".log" in gitfile or os.path.isdir(gitfile) or \
303                 gitfile.startswith("tools/"):     301                 gitfile.startswith("tools/"):
304             continue                              302             continue
305         files.append(gitfile)                     303         files.append(gitfile)
306     return files                                  304     return files
307                                                   305 
308                                                   306 
309 def check_symbols(ignore):                        307 def check_symbols(ignore):
310     """Find undefined Kconfig symbols and retu    308     """Find undefined Kconfig symbols and return a dict with the symbol as key
311     and a list of referencing files as value.     309     and a list of referencing files as value.  Files matching %ignore are not
312     checked for undefined symbols."""             310     checked for undefined symbols."""
313     pool = Pool(cpu_count(), init_worker)         311     pool = Pool(cpu_count(), init_worker)
314     try:                                          312     try:
315         return check_symbols_helper(pool, igno    313         return check_symbols_helper(pool, ignore)
316     except KeyboardInterrupt:                     314     except KeyboardInterrupt:
317         pool.terminate()                          315         pool.terminate()
318         pool.join()                               316         pool.join()
319         sys.exit(1)                               317         sys.exit(1)
320                                                   318 
321                                                   319 
322 def check_symbols_helper(pool, ignore):           320 def check_symbols_helper(pool, ignore):
323     """Helper method for check_symbols().  Use    321     """Helper method for check_symbols().  Used to catch keyboard interrupts in
324     check_symbols() in order to properly termi    322     check_symbols() in order to properly terminate running worker processes."""
325     source_files = []                             323     source_files = []
326     kconfig_files = []                            324     kconfig_files = []
327     defined_symbols = []                          325     defined_symbols = []
328     referenced_symbols = dict()  # {file: [sym    326     referenced_symbols = dict()  # {file: [symbols]}
329                                                   327 
330     for gitfile in get_files():                   328     for gitfile in get_files():
331         if REGEX_FILE_KCONFIG.match(gitfile):     329         if REGEX_FILE_KCONFIG.match(gitfile):
332             kconfig_files.append(gitfile)         330             kconfig_files.append(gitfile)
333         else:                                     331         else:
334             if ignore and re.match(ignore, git !! 332             if ignore and not re.match(ignore, gitfile):
335                 continue                          333                 continue
336             # add source files that do not mat    334             # add source files that do not match the ignore pattern
337             source_files.append(gitfile)          335             source_files.append(gitfile)
338                                                   336 
339     # parse source files                          337     # parse source files
340     arglist = partition(source_files, cpu_coun    338     arglist = partition(source_files, cpu_count())
341     for res in pool.map(parse_source_files, ar    339     for res in pool.map(parse_source_files, arglist):
342         referenced_symbols.update(res)            340         referenced_symbols.update(res)
343                                                   341 
344     # parse kconfig files                         342     # parse kconfig files
345     arglist = []                                  343     arglist = []
346     for part in partition(kconfig_files, cpu_c    344     for part in partition(kconfig_files, cpu_count()):
347         arglist.append((part, ignore))            345         arglist.append((part, ignore))
348     for res in pool.map(parse_kconfig_files, a    346     for res in pool.map(parse_kconfig_files, arglist):
349         defined_symbols.extend(res[0])            347         defined_symbols.extend(res[0])
350         referenced_symbols.update(res[1])         348         referenced_symbols.update(res[1])
351     defined_symbols = set(defined_symbols)        349     defined_symbols = set(defined_symbols)
352                                                   350 
353     # inverse mapping of referenced_symbols to    351     # inverse mapping of referenced_symbols to dict(symbol: [files])
354     inv_map = dict()                              352     inv_map = dict()
355     for _file, symbols in referenced_symbols.i    353     for _file, symbols in referenced_symbols.items():
356         for symbol in symbols:                    354         for symbol in symbols:
357             inv_map[symbol] = inv_map.get(symb    355             inv_map[symbol] = inv_map.get(symbol, set())
358             inv_map[symbol].add(_file)            356             inv_map[symbol].add(_file)
359     referenced_symbols = inv_map                  357     referenced_symbols = inv_map
360                                                   358 
361     undefined = {}  # {symbol: [files]}           359     undefined = {}  # {symbol: [files]}
362     for symbol in sorted(referenced_symbols):     360     for symbol in sorted(referenced_symbols):
363         # filter some false positives             361         # filter some false positives
364         if symbol == "FOO" or symbol == "BAR"     362         if symbol == "FOO" or symbol == "BAR" or \
365                 symbol == "FOO_BAR" or symbol     363                 symbol == "FOO_BAR" or symbol == "XXX":
366             continue                              364             continue
367         if symbol not in defined_symbols:         365         if symbol not in defined_symbols:
368             if symbol.endswith("_MODULE"):        366             if symbol.endswith("_MODULE"):
369                 # avoid false positives for ke    367                 # avoid false positives for kernel modules
370                 if symbol[:-len("_MODULE")] in    368                 if symbol[:-len("_MODULE")] in defined_symbols:
371                     continue                      369                     continue
372             undefined[symbol] = referenced_sym    370             undefined[symbol] = referenced_symbols.get(symbol)
373     return undefined, defined_symbols             371     return undefined, defined_symbols
374                                                   372 
375                                                   373 
376 def parse_source_files(source_files):             374 def parse_source_files(source_files):
377     """Parse each source file in @source_files    375     """Parse each source file in @source_files and return dictionary with source
378     files as keys and lists of references Kcon    376     files as keys and lists of references Kconfig symbols as values."""
379     referenced_symbols = dict()                   377     referenced_symbols = dict()
380     for sfile in source_files:                    378     for sfile in source_files:
381         referenced_symbols[sfile] = parse_sour    379         referenced_symbols[sfile] = parse_source_file(sfile)
382     return referenced_symbols                     380     return referenced_symbols
383                                                   381 
384                                                   382 
385 def parse_source_file(sfile):                     383 def parse_source_file(sfile):
386     """Parse @sfile and return a list of refer    384     """Parse @sfile and return a list of referenced Kconfig symbols."""
387     lines = []                                    385     lines = []
388     references = []                               386     references = []
389                                                   387 
390     if not os.path.exists(sfile):                 388     if not os.path.exists(sfile):
391         return references                         389         return references
392                                                   390 
393     with open(sfile, "r", encoding='utf-8', er    391     with open(sfile, "r", encoding='utf-8', errors='replace') as stream:
394         lines = stream.readlines()                392         lines = stream.readlines()
395                                                   393 
396     for line in lines:                            394     for line in lines:
397         if "CONFIG_" not in line:                 395         if "CONFIG_" not in line:
398             continue                              396             continue
399         symbols = REGEX_SOURCE_SYMBOL.findall(    397         symbols = REGEX_SOURCE_SYMBOL.findall(line)
400         for symbol in symbols:                    398         for symbol in symbols:
401             if not REGEX_FILTER_SYMBOLS.search    399             if not REGEX_FILTER_SYMBOLS.search(symbol):
402                 continue                          400                 continue
403             references.append(symbol)             401             references.append(symbol)
404                                                   402 
405     return references                             403     return references
406                                                   404 
407                                                   405 
408 def get_symbols_in_line(line):                    406 def get_symbols_in_line(line):
409     """Return mentioned Kconfig symbols in @li    407     """Return mentioned Kconfig symbols in @line."""
410     return REGEX_SYMBOL.findall(line)             408     return REGEX_SYMBOL.findall(line)
411                                                   409 
412                                                   410 
413 def parse_kconfig_files(args):                    411 def parse_kconfig_files(args):
414     """Parse kconfig files and return tuple of    412     """Parse kconfig files and return tuple of defined and references Kconfig
415     symbols.  Note, @args is a tuple of a list    413     symbols.  Note, @args is a tuple of a list of files and the @ignore
416     pattern."""                                   414     pattern."""
417     kconfig_files = args[0]                       415     kconfig_files = args[0]
418     ignore = args[1]                              416     ignore = args[1]
419     defined_symbols = []                          417     defined_symbols = []
420     referenced_symbols = dict()                   418     referenced_symbols = dict()
421                                                   419 
422     for kfile in kconfig_files:                   420     for kfile in kconfig_files:
423         defined, references = parse_kconfig_fi    421         defined, references = parse_kconfig_file(kfile)
424         defined_symbols.extend(defined)           422         defined_symbols.extend(defined)
425         if ignore and re.match(ignore, kfile):    423         if ignore and re.match(ignore, kfile):
426             # do not collect references for fi    424             # do not collect references for files that match the ignore pattern
427             continue                              425             continue
428         referenced_symbols[kfile] = references    426         referenced_symbols[kfile] = references
429     return (defined_symbols, referenced_symbol    427     return (defined_symbols, referenced_symbols)
430                                                   428 
431                                                   429 
432 def parse_kconfig_file(kfile):                    430 def parse_kconfig_file(kfile):
433     """Parse @kfile and update symbol definiti    431     """Parse @kfile and update symbol definitions and references."""
434     lines = []                                    432     lines = []
435     defined = []                                  433     defined = []
436     references = []                               434     references = []
                                                   >> 435     skip = False
437                                                   436 
438     if not os.path.exists(kfile):                 437     if not os.path.exists(kfile):
439         return defined, references                438         return defined, references
440                                                   439 
441     with open(kfile, "r", encoding='utf-8', er    440     with open(kfile, "r", encoding='utf-8', errors='replace') as stream:
442         lines = stream.readlines()                441         lines = stream.readlines()
443                                                   442 
444     for i in range(len(lines)):                   443     for i in range(len(lines)):
445         line = lines[i]                           444         line = lines[i]
446         line = line.strip('\n')                   445         line = line.strip('\n')
447         line = line.split("#")[0]  # ignore co    446         line = line.split("#")[0]  # ignore comments
448                                                   447 
449         if REGEX_KCONFIG_DEF.match(line):         448         if REGEX_KCONFIG_DEF.match(line):
450             symbol_def = REGEX_KCONFIG_DEF.fin    449             symbol_def = REGEX_KCONFIG_DEF.findall(line)
451             defined.append(symbol_def[0])         450             defined.append(symbol_def[0])
                                                   >> 451             skip = False
                                                   >> 452         elif REGEX_KCONFIG_HELP.match(line):
                                                   >> 453             skip = True
                                                   >> 454         elif skip:
                                                   >> 455             # ignore content of help messages
                                                   >> 456             pass
452         elif REGEX_KCONFIG_STMT.match(line):      457         elif REGEX_KCONFIG_STMT.match(line):
453             line = REGEX_QUOTES.sub("", line)     458             line = REGEX_QUOTES.sub("", line)
454             symbols = get_symbols_in_line(line    459             symbols = get_symbols_in_line(line)
455             # multi-line statements               460             # multi-line statements
456             while line.endswith("\\"):            461             while line.endswith("\\"):
457                 i += 1                            462                 i += 1
458                 line = lines[i]                   463                 line = lines[i]
459                 line = line.strip('\n')           464                 line = line.strip('\n')
460                 symbols.extend(get_symbols_in_    465                 symbols.extend(get_symbols_in_line(line))
461             for symbol in set(symbols):           466             for symbol in set(symbols):
462                 if REGEX_NUMERIC.match(symbol)    467                 if REGEX_NUMERIC.match(symbol):
463                     # ignore numeric values       468                     # ignore numeric values
464                     continue                      469                     continue
465                 references.append(symbol)         470                 references.append(symbol)
466                                                   471 
467     return defined, references                    472     return defined, references
468                                                << 
469                                                << 
470 def main():                                    << 
471     try:                                       << 
472         print_undefined_symbols()              << 
473     except BrokenPipeError:                    << 
474         # Python flushes standard streams on e << 
475         # to devnull to avoid another BrokenPi << 
476         devnull = os.open(os.devnull, os.O_WRO << 
477         os.dup2(devnull, sys.stdout.fileno())  << 
478         sys.exit(1)  # Python exits with error << 
479                                                   473 
480                                                   474 
481 if __name__ == "__main__":                        475 if __name__ == "__main__":
482     main()                                        476     main()
                                                      

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php