summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Magnusson <ulfalizer@gmail.com>2018-07-20 04:14:31 +0200
committerUlf Magnusson <ulfalizer@gmail.com>2018-07-20 21:15:23 +0200
commitca89d02ca1639b72c7b74834ff432ab10df58fe9 (patch)
treefcda9db3e5e942e00f28de475672b1acf3852850
parentccb18af3419b72ef4ddc5f311abebebd858a36ce (diff)
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.
-rw-r--r--README.rst10
-rw-r--r--kconfiglib.py89
-rw-r--r--tests/Kstrict16
-rw-r--r--testsuite.py50
4 files changed, 154 insertions, 11 deletions
diff --git a/README.rst b/README.rst
index eb9c77a..2af9e9f 100644
--- a/README.rst
+++ b/README.rst
@@ -272,14 +272,10 @@ Other features
`multiprocessing <https://docs.python.org/3/library/multiprocessing.html>`_
module. No global state is kept.
-- **Warning parity with the C implementation**
+- **Generates more warnings than the C implementation**
- Generates the same warnings as the C implementation, plus a few extra ones.
- Also detects dependency loops and ``source`` loops.
-
- This is less important if the input is assumed to be well-formed, but makes
- Kconfiglib a viable replacement for the C tools if e.g. a ``menuconfig``
- interface is added.
+ Generates the same warnings as the C implementation, plus additional ones.
+ Also detects dependency and ``source`` loops.
All warnings point out the location(s) in the ``Kconfig`` files where a
symbol is defined, where applicable.
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.
diff --git a/tests/Kstrict b/tests/Kstrict
new file mode 100644
index 0000000..463a1c8
--- /dev/null
+++ b/tests/Kstrict
@@ -0,0 +1,16 @@
+config DEF
+ bool
+
+config BOOL
+ bool "foo" if DEF || !UNDEF_1
+ default UNDEF_2
+
+config INT
+ int
+ range UNDEF_2 8
+
+menu "menu"
+ depends on UNDEF_1
+ visible if UNDEF_3
+
+endmenu
diff --git a/testsuite.py b/testsuite.py
index f70c7e9..150150c 100644
--- a/testsuite.py
+++ b/testsuite.py
@@ -2136,6 +2136,56 @@ config PRINT_ME
])
+ print("Testing KCONFIG_STRICT")
+
+ os.environ["KCONFIG_STRICT"] = "y"
+ c = Kconfig("Kconfiglib/tests/Kstrict", warn_to_stderr=False)
+
+ verify_equal("\n".join(c.warnings), """
+warning: the int symbol INT (defined at Kconfiglib/tests/Kstrict:8) has a non-int range [UNDEF_2 (undefined), 8 (undefined)]
+warning: undefined symbol UNDEF_1:
+
+- Referenced at Kconfiglib/tests/Kstrict:4:
+
+config BOOL
+ bool
+ prompt "foo" if DEF || !UNDEF_1
+ default UNDEF_2
+
+
+- Referenced at Kconfiglib/tests/Kstrict:12:
+
+menu "menu"
+ depends on UNDEF_1
+ visible if UNDEF_3
+
+warning: undefined symbol UNDEF_2:
+
+- Referenced at Kconfiglib/tests/Kstrict:4:
+
+config BOOL
+ bool
+ prompt "foo" if DEF || !UNDEF_1
+ default UNDEF_2
+
+
+- Referenced at Kconfiglib/tests/Kstrict:8:
+
+config INT
+ int
+ range UNDEF_2 8
+
+warning: undefined symbol UNDEF_3:
+
+- Referenced at Kconfiglib/tests/Kstrict:12:
+
+menu "menu"
+ depends on UNDEF_1
+ visible if UNDEF_3
+"""[1:])
+
+ os.environ.pop("KCONFIG_STRICT")
+
print("\nAll selftests passed\n" if all_passed else
"\nSome selftests failed\n")