summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kconfiglib.py60
-rwxr-xr-xmenuconfig.py25
-rw-r--r--tests/Kinclude_path12
-rw-r--r--tests/Kinclude_path_sourced_112
-rw-r--r--tests/Kinclude_path_sourced_211
-rw-r--r--testsuite.py46
6 files changed, 145 insertions, 21 deletions
diff --git a/kconfiglib.py b/kconfiglib.py
index a16a215..3d46630 100644
--- a/kconfiglib.py
+++ b/kconfiglib.py
@@ -590,6 +590,7 @@ class Kconfig(object):
"_file",
"_filename",
"_linenr",
+ "_include_path",
"_filestack",
"_line",
"_saved_line",
@@ -752,6 +753,7 @@ class Kconfig(object):
self.top_node.dep = self.y
self.top_node.filename = filename
self.top_node.linenr = 1
+ self.top_node.include_path = ()
# Parse the Kconfig files
@@ -760,8 +762,9 @@ class Kconfig(object):
self._has_tokens = False
# Keeps track of the location in the parent Kconfig files. Kconfig
- # files usually source other Kconfig files.
+ # files usually source other Kconfig files. See _enter_file().
self._filestack = []
+ self._include_path = ()
# The current parsing location
self._filename = filename
@@ -1542,19 +1545,35 @@ class Kconfig(object):
# self._filename (which makes it indirectly show up in
# MenuNode.filename). Equals full_filename for absolute paths.
- self._filestack.append((self._filename, self._linenr, self._file))
+ # The parent Kconfig files are represented as a list of
+ # (<include path>, <Python 'file' object for Kconfig file>) tuples.
+ #
+ # <include path> is immutable and holds a *tuple* of
+ # (<filename>, <linenr>) tuples, giving the locations of the 'source'
+ # statements in the parent Kconfig files. The current include path is
+ # also available in Kconfig._include_path.
+ #
+ # The point of this redundant setup is to allow Kconfig._include_path
+ # to be assigned directly to MenuNode.include_path without having to
+ # copy it, sharing it wherever possible.
+
+ # Save include path and 'file' object before entering the file
+ self._filestack.append((self._include_path, self._file))
+
+ # _include_path is a tuple, so this rebinds the variable instead of
+ # doing in-place modification
+ self._include_path += ((self._filename, self._linenr),)
# Check for recursive 'source'
- for name, _, _ in self._filestack:
+ for name, _ in self._include_path:
if name == rel_filename:
raise KconfigError(
"\n{}:{}: Recursive 'source' of '{}' detected. Check that "
"environment variables are set correctly.\n"
- "Backtrace:\n{}"
+ "Include path:\n{}"
.format(self._filename, self._linenr, rel_filename,
"\n".join("{}:{}".format(name, linenr)
- for name, linenr, _
- in reversed(self._filestack))))
+ for name, linenr in self._include_path)))
# Note: We already know that the file exists
@@ -1569,10 +1588,14 @@ class Kconfig(object):
self._linenr = 0
def _leave_file(self):
- # Returns from a Kconfig file to the file that sourced it
+ # Returns from a Kconfig file to the file that sourced it. See
+ # _enter_file().
self._file.close()
- self._filename, self._linenr, self._file = self._filestack.pop()
+ # Restore location from parent Kconfig file
+ self._filename, self._linenr = self._include_path[-1]
+ # Restore include path and 'file' object
+ self._include_path, self._file = self._filestack.pop()
def _next_line(self):
# Fetches and tokenizes the next line from the current Kconfig file.
@@ -2221,6 +2244,7 @@ class Kconfig(object):
node.parent = parent
node.filename = self._filename
node.linenr = self._linenr
+ node.include_path = self._include_path
sym.nodes.append(node)
@@ -2304,6 +2328,7 @@ class Kconfig(object):
node.parent = parent
node.filename = self._filename
node.linenr = self._linenr
+ node.include_path = self._include_path
self.menus.append(node)
@@ -2323,6 +2348,7 @@ class Kconfig(object):
node.parent = parent
node.filename = self._filename
node.linenr = self._linenr
+ node.include_path = self._include_path
self.comments.append(node)
@@ -2358,6 +2384,7 @@ class Kconfig(object):
node.parent = parent
node.filename = self._filename
node.linenr = self._linenr
+ node.include_path = self._include_path
choice.nodes.append(node)
@@ -3112,11 +3139,12 @@ class Kconfig(object):
# Hint printed when Kconfig files can't be found or .config files can't
# be opened
- return ". Perhaps the $srctree environment variable (set to '{}') " \
+ return ". Perhaps the $srctree environment variable ({}) " \
"is set incorrectly. Note that the current value of $srctree " \
"is saved when the Kconfig instance is created (for " \
"consistency and to cleanly separate instances)." \
- .format(self.srctree if self.srctree else "unset or blank")
+ .format("set to '{}'".format(self.srctree) if self.srctree
+ else "unset or blank")
class Symbol(object):
"""
@@ -3868,7 +3896,7 @@ class Symbol(object):
self._dependents = set()
# Used during dependency loop detection and (independently) in
- # write_config()
+ # node_iter()
self._visited = 0
def _assignable(self):
@@ -4615,6 +4643,15 @@ class MenuNode(object):
$srctree (or to the current directory if $srctree isn't set), except
absolute paths passed to 'source' and Kconfig.__init__() are preserved.
+ include_path:
+ A tuple of (filename, linenr) tuples, giving the locations of the
+ 'source' statements via which the Kconfig file containing this menu node
+ was included. The first element is the location of the 'source' statement
+ in the top-level Kconfig file passed to Kconfig.__init__(), etc.
+
+ Note that the Kconfig file of the menu node itself isn't included. Check
+ 'filename' and 'linenr' for that.
+
kconfig:
The Kconfig instance the menu node is from.
"""
@@ -4622,6 +4659,7 @@ class MenuNode(object):
"dep",
"filename",
"help",
+ "include_path",
"is_menuconfig",
"item",
"kconfig",
diff --git a/menuconfig.py b/menuconfig.py
index 8f5dbe0..8c575b5 100755
--- a/menuconfig.py
+++ b/menuconfig.py
@@ -2075,22 +2075,31 @@ def _select_imply_info(sym):
def _kconfig_def_info(item):
# Returns a string with the definition of 'item' in Kconfig syntax,
- # together with the definition location(s)
+ # together with the definition location(s) and their include and menu paths
nodes = [item] if isinstance(item, MenuNode) else item.nodes
s = "Kconfig definition{}, with propagated dependencies\n" \
.format("s" if len(nodes) > 1 else "")
- s += (len(s) - 1)*"=" + "\n\n"
-
- s += "\n\n".join("At {}:{}, in menu {}:\n\n{}".format(
- node.filename, node.linenr, _menu_path_info(node),
- textwrap.indent(node.custom_str(_name_and_val_str),
- " "))
- for node in nodes)
+ s += (len(s) - 1)*"="
+
+ for node in nodes:
+ s += "\n\n" \
+ "At {}:{}\n" \
+ "Included via {}\n" \
+ "Menu path: {}\n\n" \
+ "{}" \
+ .format(node.filename, node.linenr,
+ _include_path_info(node),
+ _menu_path_info(node),
+ textwrap.indent(node.custom_str(_name_and_val_str), " "))
return s
+def _include_path_info(node):
+ return " -> ".join("{}:{}".format(filename, linenr)
+ for filename, linenr in node.include_path)
+
def _menu_path_info(node):
# Returns a string describing the menu path leading up to 'node'
diff --git a/tests/Kinclude_path b/tests/Kinclude_path
new file mode 100644
index 0000000..7a3badb
--- /dev/null
+++ b/tests/Kinclude_path
@@ -0,0 +1,12 @@
+config TOP
+ bool
+
+source "Kinclude_path_sourced_1"
+
+config TOP
+ bool
+
+source "Kinclude_path_sourced_1"
+
+config TOP
+ bool
diff --git a/tests/Kinclude_path_sourced_1 b/tests/Kinclude_path_sourced_1
new file mode 100644
index 0000000..f4dee98
--- /dev/null
+++ b/tests/Kinclude_path_sourced_1
@@ -0,0 +1,12 @@
+config ONE_DOWN
+ bool
+
+source "Kinclude_path_sourced_2"
+
+config ONE_DOWN
+ bool
+
+source "Kinclude_path_sourced_2"
+
+config ONE_DOWN
+ bool
diff --git a/tests/Kinclude_path_sourced_2 b/tests/Kinclude_path_sourced_2
new file mode 100644
index 0000000..068f18d
--- /dev/null
+++ b/tests/Kinclude_path_sourced_2
@@ -0,0 +1,11 @@
+config TWO_DOWN
+ bool
+
+menu "menu"
+endmenu
+
+comment "comment"
+
+choice
+ bool "choice"
+endchoice
diff --git a/testsuite.py b/testsuite.py
index a610c4b..1c49209 100644
--- a/testsuite.py
+++ b/testsuite.py
@@ -1041,9 +1041,9 @@ g
except KconfigError as e:
verify_equal(str(e), """
tests/Krecursive2:1: Recursive 'source' of 'tests/Krecursive1' detected. Check that environment variables are set correctly.
-Backtrace:
-tests/Krecursive2:1
+Include path:
tests/Krecursive1:1
+tests/Krecursive2:1
"""[:-1])
except:
fail("recursive 'source' raised wrong exception")
@@ -1108,6 +1108,48 @@ tests/Krecursive1:1
os.environ.pop("srctree", None)
+ print("Testing MenuNode.include_path")
+
+ os.environ["srctree"] = "Kconfiglib/tests"
+
+ c = Kconfig("Kinclude_path")
+
+ def verify_node_path(node, *expected):
+ if node.include_path != expected:
+ fail("Wrong include path for node {!r}. Got {}, expected {}."
+ .format(node, node.include_path, expected))
+
+ def verify_sym_path(sym_name, node_i, *expected):
+ verify_node_path(c.syms[sym_name].nodes[node_i], *expected)
+
+ verify_sym_path("TOP", 0)
+ verify_sym_path("TOP", 1)
+ verify_sym_path("TOP", 2)
+
+ verify_sym_path("ONE_DOWN", 0, ("Kinclude_path", 4))
+ verify_sym_path("ONE_DOWN", 1, ("Kinclude_path", 4))
+ verify_sym_path("ONE_DOWN", 2, ("Kinclude_path", 4))
+ verify_sym_path("ONE_DOWN", 3, ("Kinclude_path", 9))
+ verify_sym_path("ONE_DOWN", 4, ("Kinclude_path", 9))
+ verify_sym_path("ONE_DOWN", 5, ("Kinclude_path", 9))
+
+ verify_sym_path("TWO_DOWN", 0,
+ ('Kinclude_path', 4), ('Kinclude_path_sourced_1', 4))
+ verify_sym_path("TWO_DOWN", 1,
+ ('Kinclude_path', 4), ('Kinclude_path_sourced_1', 9))
+ verify_sym_path("TWO_DOWN", 2,
+ ('Kinclude_path', 9), ('Kinclude_path_sourced_1', 4))
+ verify_sym_path("TWO_DOWN", 3,
+ ('Kinclude_path', 9), ('Kinclude_path_sourced_1', 9))
+
+ verify_node_path(c.top_node)
+ verify_node_path(c.menus[0], ('Kinclude_path', 4), ('Kinclude_path_sourced_1', 4))
+ verify_node_path(c.comments[0], ('Kinclude_path', 4), ('Kinclude_path_sourced_1', 4))
+ verify_node_path(c.choices[0].nodes[0], ('Kinclude_path', 4), ('Kinclude_path_sourced_1', 4))
+
+ os.environ.pop("srctree", None)
+
+
print("Testing Kconfig.choices/menus/comments")
c = Kconfig("Kconfiglib/tests/Kitemlists")