~ [ 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-6.4.16)


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