zephyr/scripts/ci/list_undef_kconfig_refs.py

89 lines
2.4 KiB
Python
Executable File

#!/usr/bin/env python3
# Prints a message giving the locations of all references to undefined symbols
# within the Kconfig files. Prints nothing if there are no undefined
# references.
#
# The top-level Kconfig file is assumed to be called 'Kconfig'.
import sys
import kconfiglib
def report():
# Returns a message (string) giving the locations of all references to
# undefined Kconfig symbols within the Kconfig files, or the empty string
# if there are no references to undefined symbols.
#
# Note: This function is called directly during CI.
kconf = kconfiglib.Kconfig()
undef_msg = ""
for name, sym in kconf.syms.items():
# - sym.nodes empty means the symbol is undefined (has no definition
# locations)
#
# - Due to Kconfig internals, numbers show up as undefined Kconfig
# symbols, but shouldn't be flagged
#
# - The MODULES symbol always exists but is unused in Zephyr
if not sym.nodes and not is_num(name) and name != "MODULES":
undef_msg += ref_locations_str(sym)
if undef_msg:
return "Error: Found references to undefined Kconfig symbols:\n" \
+ undef_msg
return ""
def is_num(name):
# Heuristic to see if a symbol name looks like a number. Internally,
# everything is a symbol, only undefined symbols (which numbers usually
# are) get their name as their value.
try:
int(name)
except ValueError:
# Require hex constants to be prefixed with 0x in Kconfig files, so
# that we can tell that e.g. F00 is an undefined symbol reference.
if not name.startswith(("0x", "0X")):
return False
try:
int(name, 16)
except ValueError:
return False
return True
def ref_locations_str(sym):
# Prints all locations where sym is referenced, along with the Kconfig
# definitions of the referencing items
msg = "\n{}\n{}".format(sym.name, len(sym.name)*"=")
def search(node):
nonlocal msg
while node:
if sym in node.referenced:
msg += "\n\n- Referenced at {}:{}:\n\n{}" \
.format(node.filename, node.linenr, node)
if node.list:
search(node.list)
node = node.next
search(sym.kconfig.top_node)
return msg
if __name__ == "__main__":
sys.stdout.write(report())