From d2c1430c91c574dc0dfd84f3652c8d9af8c77568 Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Wed, 22 Aug 2018 02:25:20 +0200 Subject: Introduce Kconfig.unique_defined_syms and Kconfig.unique_choices These are the same as Kconfig.defined_syms and Kconfig.choices, except duplicates are removed. Kconfig order is still preserved. This is almost always what you want when iterating through symbols and choices, as it potentially saves work, avoids generating duplicates when writing output, and still preserves Kconfig order for readability. The old attributes will be kept for backwards compatibility (maybe there's some rare cases where they could be useful too). They're created internally anyway. --- allmodconfig.py | 6 +-- allnoconfig.py | 4 +- allyesconfig.py | 7 +--- examples/allnoconfig_walk.py | 2 +- examples/defconfig_oldconfig.py | 2 +- kconfiglib.py | 86 ++++++++++++++++++++++------------------- menuconfig.py | 14 +++---- 7 files changed, 60 insertions(+), 61 deletions(-) diff --git a/allmodconfig.py b/allmodconfig.py index a7804b3..92a00e1 100755 --- a/allmodconfig.py +++ b/allmodconfig.py @@ -25,9 +25,7 @@ def main(): BOOL = kconfiglib.BOOL TRISTATE = kconfiglib.TRISTATE - # The set() speeds things up for projects that use multiple definition - # locations a lot - for sym in set(kconf.defined_syms): + for sym in kconf.unique_defined_syms: if sym.orig_type == BOOL: # 'bool' choice symbols get their default value, as determined by # e.g. 'default's on the choice @@ -37,7 +35,7 @@ def main(): elif sym.orig_type == TRISTATE: sym.set_value(1) - for choice in kconf.choices: + for choice in kconf.unique_choices: choice.set_value(2 if choice.orig_type == BOOL else 1) kconf.write_config(kconfiglib.standard_config_filename()) diff --git a/allnoconfig.py b/allnoconfig.py index 173bf3f..4e1c228 100755 --- a/allnoconfig.py +++ b/allnoconfig.py @@ -31,9 +31,7 @@ def main(): # symbol types, which is what we want. kconf.disable_warnings() - # The set() speeds things up for projects that use multiple definition - # locations a lot - for sym in set(kconf.defined_syms): + for sym in kconf.unique_defined_syms: sym.set_value(2 if sym.is_allnoconfig_y else 0) kconf.write_config(kconfiglib.standard_config_filename()) diff --git a/allyesconfig.py b/allyesconfig.py index 5fe8b46..d9b799e 100755 --- a/allyesconfig.py +++ b/allyesconfig.py @@ -44,10 +44,7 @@ def main(): # # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex # symbols still take a string, because they preserve formatting). - # - # The set() speeds things up for projects that use multiple definition - # locations a lot - for sym in set(kconf.defined_syms): + for sym in kconf.unique_defined_syms: # Set choice symbols to 'm'. This value will be ignored for choices in # 'y' mode (the "normal" mode), which will instead just get their # default selection, but will set all symbols in m-mode choices to 'm', @@ -55,7 +52,7 @@ def main(): sym.set_value(1 if sym.choice else 2) # Set all choices to the highest possible mode - for choice in kconf.choices: + for choice in kconf.unique_choices: choice.set_value(2) kconf.write_config(kconfiglib.standard_config_filename()) diff --git a/examples/allnoconfig_walk.py b/examples/allnoconfig_walk.py index 8e22cd9..b94a169 100644 --- a/examples/allnoconfig_walk.py +++ b/examples/allnoconfig_walk.py @@ -40,7 +40,7 @@ def do_allnoconfig(node): kconf = Kconfig(sys.argv[1]) # Do an initial pass to set 'option allnoconfig_y' symbols to y -for sym in kconf.defined_syms: +for sym in kconf.unique_defined_syms: if sym.is_allnoconfig_y: sym.set_value(2) diff --git a/examples/defconfig_oldconfig.py b/examples/defconfig_oldconfig.py index 84aa134..3735ee1 100644 --- a/examples/defconfig_oldconfig.py +++ b/examples/defconfig_oldconfig.py @@ -29,7 +29,7 @@ kconf.write_config(".config") # Mirrors the second oldconfig kconf.load_config(".config") kconf.syms["ETHERNET"].set_value(2) -for s in kconf.defined_syms: +for s in kconf.unique_defined_syms: if s.user_value is None and 0 in s.assignable: s.set_value(0) diff --git a/kconfiglib.py b/kconfiglib.py index 6f74be6..a943f2f 100644 --- a/kconfiglib.py +++ b/kconfiglib.py @@ -432,13 +432,31 @@ class Kconfig(object): defined_syms: A list with all defined symbols, in the same order as they appear in the Kconfig files. Symbols defined in multiple locations appear multiple - times. Iterating over set(defined_syms) will visit each defined symbol - once. + times. + + Note: You probably want to use 'unique_defined_syms' instead. This + attribute is mostly maintained for backwards compatibility. + + unique_defined_syms: + A list like 'defined_syms', but with duplicates removed. Just the first + instance is kept for symbols defined in multiple locations. Kconfig order + is preserved otherwise. + + Using this attribute instead of 'defined_syms' can save work, and + automatically gives reasonable behavior when writing configuration output + (symbols defined in multiple locations only generate output once, while + still preserving Kconfig order for readability). choices: A list with all choices, in the same order as they appear in the Kconfig - files. Named choices defined in multiple locations appear multiple times. - Iterating over set(choices) will visit each choice once. + files. + + Note: You probably want to use 'unique_choices' instead. This attribute + is mostly maintained for backwards compatibility. + + unique_choices: + Analogous to 'unique_defined_syms', for choices. Named choices can have + multiple definition locations. menus: A list with all menus, in the same order as they appear in the Kconfig @@ -540,7 +558,6 @@ class Kconfig(object): "_encoding", "_functions", "_set_match", - "_unique_def_syms", "_unset_match", "_warn_for_no_prompt", "_warn_for_redun_assign", @@ -562,6 +579,8 @@ class Kconfig(object): "srctree", "syms", "top_node", + "unique_choices", + "unique_defined_syms", "variables", "warnings", "y", @@ -766,22 +785,11 @@ class Kconfig(object): self.top_node.list = self.top_node.next self.top_node.next = None - # 'defined_syms' with duplicates removed, preserving order. - # - # Symbols defined in multiple locations only generate a single output - # line in .config files and headers, at their first definition - # location, so this format is handy. - # - # U-Boot and Zephyr make heavy use of being able to define a symbol in - # multiple locations. Iterating over '_unique_def_syms' wherever - # possible makes a huge performance difference for U-Boot, speeding up - # parsing from ~4 seconds to ~0.6 seconds on my machine. - # - # Maybe it would make sense to expose this in the API at some point. - self._unique_def_syms = _ordered_unique(self.defined_syms) - self._parsing_kconfigs = False + self.unique_defined_syms = _ordered_unique(self.defined_syms) + self.unique_choices = _ordered_unique(self.choices) + # Do various post-processing of the menu tree self._finalize_tree(self.top_node, self.y) @@ -789,10 +797,10 @@ class Kconfig(object): # Do sanity checks. Some of these depend on everything being # finalized. - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: _check_sym_sanity(sym) - for choice in self.choices: + for choice in self.unique_choices: _check_choice_sanity(choice) if os.environ.get("KCONFIG_STRICT") == "y": @@ -803,7 +811,7 @@ class Kconfig(object): self._build_dep() # Check for dependency loops - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: _check_dep_loop_sym(sym, False) # Add extra dependencies from choices to choice symbols that get @@ -873,10 +881,10 @@ class Kconfig(object): # Another benefit is that invalidation must be rock solid for # it to work, making it a good test. - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: sym._was_set = False - for choice in self.choices: + for choice in self.unique_choices: choice._was_set = False # Small optimizations @@ -995,11 +1003,11 @@ class Kconfig(object): # If we're replacing the configuration, unset the symbols that # didn't get set - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: if not sym._was_set: sym.unset_value() - for choice in self.choices: + for choice in self.unique_choices: if not choice._was_set: choice.unset_value() @@ -1024,7 +1032,7 @@ class Kconfig(object): with self._open(filename, "w") as f: f.write(header) - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: # Note: _write_to_conf is determined when the value is # calculated. This is a hidden function call due to # property magic. @@ -1093,7 +1101,7 @@ class Kconfig(object): # Note: The usage of _visited here is completely independent from # the usage during dependency loop detection (which runs at the end # of parsing). The attribute is just reused. - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: sym._visited = False # The 'top_node' menu node itself doesn't generate any output, so @@ -1153,7 +1161,7 @@ class Kconfig(object): with self._open(filename, "w") as f: f.write(header) - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: # Skip symbols that cannot be changed. Only check # non-choice symbols, as selects don't affect choice # symbols. @@ -1249,7 +1257,7 @@ class Kconfig(object): # Load old values from auto.conf, if any self._load_old_vals() - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: # Note: _write_to_conf is determined when the value is # calculated. This is a hidden function call due to # property magic. @@ -1306,7 +1314,7 @@ class Kconfig(object): # by passing a flag to it, plus we only need to look at symbols here. with self._open("auto.conf", "w") as f: - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: if not (sym.orig_type in (BOOL, TRISTATE) and not sym.tri_value): f.write(sym.config_string) @@ -1319,7 +1327,7 @@ class Kconfig(object): # symbol values and restoring them later, but this is simpler and # faster. The C tools also use a dedicated field for this purpose. - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: sym._old_val = None if not os.path.exists("auto.conf"): @@ -1390,10 +1398,10 @@ class Kconfig(object): # set_value() already rejects undefined symbols, and they don't # need to be invalidated (because their value never changes), so we # can just iterate over defined symbols - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: sym.unset_value() - for choice in self.choices: + for choice in self.unique_choices: choice.unset_value() finally: self._warn_for_no_prompt = True @@ -2706,7 +2714,7 @@ class Kconfig(object): # Only calculate _dependents for defined symbols. Constant and # undefined symbols could theoretically be selected/implied, but it # wouldn't change their value, so it's not a true dependency. - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: # Symbols depend on the following: # The prompt conditions @@ -2741,7 +2749,7 @@ class Kconfig(object): # propagated to the conditions of the properties before # _build_dep() runs. - for choice in self.choices: + for choice in self.unique_choices: # Choices depend on the following: # The prompt conditions @@ -2763,7 +2771,7 @@ class Kconfig(object): # <-> dependency loops, but they make loop # detection awkward. - for choice in self.choices: + for choice in self.unique_choices: # The choice symbols themselves, because the y mode selection might # change if a choice symbol's visibility changes for sym in choice.syms: @@ -2773,10 +2781,10 @@ class Kconfig(object): # Undefined symbols never change value and don't need to be # invalidated, so we can just iterate over defined symbols. # Invalidating constant symbols would break things horribly. - for sym in self._unique_def_syms: + for sym in self.unique_defined_syms: sym._invalidate() - for choice in self.choices: + for choice in self.unique_choices: choice._invalidate() diff --git a/menuconfig.py b/menuconfig.py index 66ccca7..8f5dbe0 100755 --- a/menuconfig.py +++ b/menuconfig.py @@ -1565,14 +1565,12 @@ def _searched_nodes(cached_search_nodes=[]): # Returns a list of menu nodes to search, sorted by symbol name if not cached_search_nodes: - # Sort symbols by name and remove duplicates, then add all nodes for - # each symbol. - # - # Duplicates appear when symbols have multiple menu nodes (definition - # locations), but they appear in menu order, which isn't what we want - # here. We'd still need to go through sym.nodes as well. - for sym in sorted(set(_kconf.defined_syms), key=lambda sym: sym.name): - cached_search_nodes.extend(sym.nodes) + # Sort symbols by name, then add all nodes for each symbol + for sym in sorted(_kconf.unique_defined_syms, + key=lambda sym: sym.name): + + # += is in-place for lists + cached_search_nodes += sym.nodes return cached_search_nodes -- cgit v1.2.3