From ca89d02ca1639b72c7b74834ff432ab10df58fe9 Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Fri, 20 Jul 2018 04:14:31 +0200 Subject: Add KCONFIG_STRICT flag for flagging refs. to undefined syms Settings KCONFIG_STRICT to y in the environment turns on warnings for all references to undefined symbols within Kconfig files (with the only gotcha that hex literals must be prefixed by 0x or 0X, to make it possible to distinguish them from undefined references). Always flagging undefined references gets awkward, as some projects (e.g. the Linux kernel) use multiple Kconfig trees with shared files, leading to some safe undefined references. It's helpful for other projects though. Having KCONFIG_STRICT as an environment variable is handy when multiple tools are involved. Piggyback a small README change re. warnings. Kconfiglib now has many more warnings than the C tools. --- kconfiglib.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 4 deletions(-) (limited to 'kconfiglib.py') diff --git a/kconfiglib.py b/kconfiglib.py index 271f533..8b798b4 100644 --- a/kconfiglib.py +++ b/kconfiglib.py @@ -585,6 +585,20 @@ class Kconfig(object): KconfigError on syntax errors. Note that Kconfig files are not the same as .config files (which store configuration symbol values). + If KCONFIG_STRICT is set in the environment (to any value), warnings + will be generated for all references to undefined symbols within + Kconfig files. The reason this isn't the default is that some projects + (e.g. the Linux kernel) use multiple Kconfig trees (one per + architecture) with many shared Kconfig files, leading to some safe + references to undefined symbols. + + KCONFIG_STRICT relies on literal hex values being prefixed with 0x/0X. + They are indistinguishable from references to undefined symbols + otherwise. + + KCONFIG_STRICT might enable other warnings that depend on there being + just a single Kconfig tree in the future. + filename (default: "Kconfig"): The Kconfig file to load. For the Linux kernel, you'll want "Kconfig" from the top-level directory, as environment variables will make sure @@ -641,10 +655,7 @@ class Kconfig(object): self.srctree = os.environ.get("srctree", "") self.config_prefix = os.environ.get("CONFIG_", "CONFIG_") - # Regular expressions for parsing .config files, with the match() - # method assigned directly as a small optimization (microscopic in this - # case, but it's consistent with the other regexes) - + # Regular expressions for parsing .config files self._set_match = _re_match(self.config_prefix + r"([^=]+)=(.*)") self._unset_match = \ _re_match(r"# {}([^ ]+) is not set".format(self.config_prefix)) @@ -771,6 +782,9 @@ class Kconfig(object): for choice in self.choices: _check_choice_sanity(choice) + if os.environ.get("KCONFIG_STRICT") == "y": + self._check_undefined_syms() + # Build Symbol._dependents for all symbols and choices self._build_dep() @@ -2980,6 +2994,50 @@ class Kconfig(object): return open(filename, "rU" if mode == "r" else mode) if _IS_PY2 else \ open(filename, mode, encoding=self._encoding) + def _check_undefined_syms(self): + # Prints warnings for all references to undefined symbols within the + # Kconfig files + + for sym in (self.syms.viewvalues() if _IS_PY2 else self.syms.values()): + # - 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 + if not sym.nodes and not _is_num(sym.name) and \ + sym.name != "MODULES": + + self._warn_undefined_sym(sym) + + def _warn_undefined_sym(self, sym): + # _check_undefined_syms() helper. Generates a warning that lists the + # locations where the undefined symbol 'sym' is referenced, including + # the referencing menu nodes in Kconfig format. + + referencing_nodes = [] + + def find_refs(node): + while node: + if sym in node.referenced: + referencing_nodes.append(node) + + if node.list: + find_refs(node.list) + + node = node.next + + find_refs(self.top_node) + + msg = "undefined symbol {}:".format(sym.name) + + for node in referencing_nodes: + msg += "\n\n- Referenced at {}:{}:\n\n{}" \ + .format(node.filename, node.linenr, node) + + self._warn(msg) + def _warn(self, msg, filename=None, linenr=None): # For printing general warnings @@ -5065,6 +5123,29 @@ def _strcmp(s1, s2): return (s1 > s2) - (s1 < s2) +def _is_num(s): + # Returns True if the string 's' looks like a number. + # + # Internally, all operands in Kconfig are symbols, only undefined symbols + # (which numbers usually are) get their name as their value. + # + # Only hex numbers that start with 0x/0X are classified as numbers. + # Otherwise, symbols whose names happen to contain only the letters A-F + # would trigger false positives. + + try: + int(s) + except ValueError: + if s.startswith(("0x", "0X")): + return False + + try: + int(s, 16) + except ValueError: + return False + + return True + def _sym_to_num(sym): # expr_value() helper for converting a symbol to a number. Raises # ValueError for symbols that can't be converted. -- cgit v1.2.3