diff options
| -rw-r--r-- | kconfiglib.py | 60 | ||||
| -rwxr-xr-x | menuconfig.py | 25 | ||||
| -rw-r--r-- | tests/Kinclude_path | 12 | ||||
| -rw-r--r-- | tests/Kinclude_path_sourced_1 | 12 | ||||
| -rw-r--r-- | tests/Kinclude_path_sourced_2 | 11 | ||||
| -rw-r--r-- | testsuite.py | 46 |
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") |
