summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kconfiglib.py187
-rw-r--r--tests/Kmisc4
2 files changed, 115 insertions, 76 deletions
diff --git a/kconfiglib.py b/kconfiglib.py
index 6fe9f88..d5143cf 100644
--- a/kconfiglib.py
+++ b/kconfiglib.py
@@ -420,6 +420,7 @@ class Kconfig(object):
"""
__slots__ = (
"_choices",
+ "_loading_config",
"_print_undef_assign",
"_print_warnings",
"_set_re_match",
@@ -583,6 +584,8 @@ class Kconfig(object):
# Build Symbol._dependents for all symbols
self._build_dep()
+ self._loading_config = False
+
@property
def mainmenu_text(self):
"""
@@ -624,12 +627,30 @@ class Kconfig(object):
True if all existing user values should be cleared before loading the
.config.
"""
+ # Are we currently loading a .config file? This disables a warning.
+ self._loading_config = True
+
+ # This stub only exists to make sure _loading_config gets unset
+ try:
+ self._load_config(filename, replace)
+ finally:
+ self._loading_config = False
+
+ def _load_config(self, filename, replace):
with self._open(filename) as f:
if replace:
- # Invalidates all symbols as a side effect
- self.unset_values()
- else:
- self._invalidate_all()
+ # If we're replacing the configuration, keep track of which
+ # symbols and choices got set so that we can unset the rest
+ # later. This avoids invalidating everything and is a tiny bit
+ # faster in the test suite. The main benefit though is that
+ # invalidation must be rock solid for it to work, making it a
+ # good test.
+
+ for sym in self.defined_syms:
+ sym._was_set = False
+
+ for choice in self._choices:
+ choice._was_set = False
# Small optimizations
set_re_match = self._set_re_match
@@ -715,7 +736,7 @@ class Kconfig(object):
# Done parsing the assignment. Set the value.
- if sym.user_value is not None:
+ if sym._was_set:
# Use strings for tristate values in the warning
if sym.orig_type in (BOOL, TRISTATE):
display_val = TRI_TO_STR[val]
@@ -729,7 +750,19 @@ class Kconfig(object):
.format(name, display_user_val, display_val),
filename, linenr)
- sym._set_value_no_invalidate(val, True)
+ sym.set_value(val)
+
+ if replace:
+ # If we're replacing the configuration, unset the symbols that
+ # didn't get set
+
+ for sym in self.defined_syms:
+ if not sym._was_set:
+ sym.unset_value()
+
+ for choice in self._choices:
+ if not choice._was_set:
+ choice.unset_value()
def write_config(self, filename,
header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
@@ -791,16 +824,11 @@ 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.defined_syms:
- # We're iterating over all (defined) symbols, so no need for
- # symbols to invalidate their dependent symbols
- sym.user_value = None
- sym._invalidate()
+ sym.unset_value()
for choice in self._choices:
- choice.user_value = choice.user_selection = None
- choice._invalidate()
+ choice.unset_value()
def enable_warnings(self):
"""
@@ -2187,6 +2215,7 @@ class Symbol(object):
"_cached_tri_val",
"_cached_vis",
"_dependents",
+ "_was_set",
"_write_to_conf",
"choice",
"defaults",
@@ -2482,21 +2511,69 @@ class Symbol(object):
BOOL) are ignored and won't be stored in Symbol.user_str/tri_value.
Kconfiglib will print a warning by default for invalid assignments.
"""
- self._set_value_no_invalidate(value, False)
+ if value == self.user_value:
+ # We know the value must be valid if it was successfully set
+ # previously
+ self._was_set = True
+ return
- if self is self.kconfig.modules:
- # Changing MODULES has wide-ranging effects
- self.kconfig._invalidate_all()
+ # Check if the value is valid for our type
+ if not ((self.orig_type == BOOL and value in (0, 2) ) or
+ (self.orig_type == TRISTATE and value in (0, 1, 2) ) or
+ (self.orig_type == STRING and isinstance(value, str)) or
+ (self.orig_type == INT and isinstance(value, str)
+ and _is_base_n(value, 10) ) or
+ (self.orig_type == HEX and isinstance(value, str)
+ and _is_base_n(value, 16))):
+
+ warning = "the value '{}' is invalid for {}, which has type {}" \
+ .format(value, self.name, TYPE_TO_STR[self.orig_type])
+
+ if self.orig_type in (BOOL, TRISTATE) and \
+ value in ("n", "m", "y"):
+ warning += ' (pass 0, 1, 2 for n, m, y, respectively)'
+
+ self.kconfig._warn(warning)
+
+ return
+
+ if not self.nodes:
+ self.kconfig._warn_undef_assign(
+ "{} is constant or undefined. '{}' assignment ignored."
+ .format(self.name, value))
+ return
+
+ # Assignments to promptless symbols are expected when loading a .config
+ if not self.kconfig._loading_config:
+ for node in self.nodes:
+ if node.prompt is not None:
+ break
+ else:
+ self.kconfig._warn("{} has no prompt. '{}' assignment ignored."
+ .format(self.name, value))
+ return
+
+ if self.choice is not None and value == 2:
+ # Remember this as a choice selection only. Makes switching back
+ # and forth between choice modes work as expected, and makes the
+ # check for whether the user value is the same as before above
+ # safe.
+ self.choice.user_selection = self
+ self.choice._rec_invalidate()
+ self.choice._was_set = True
else:
+ self.user_value = value
self._rec_invalidate()
+ self._was_set = True
def unset_value(self):
"""
Resets the user value of the symbol, as if the symbol had never gotten
a user value via Kconfig.load_config() or Symbol.set_value().
"""
- self.user_value = None
- self._rec_invalidate()
+ if self.user_value is not None:
+ self.user_value = None
+ self._rec_invalidate()
def __repr__(self):
"""
@@ -2618,6 +2695,8 @@ class Symbol(object):
self.env_var = None
self.is_allnoconfig_y = False
+ self._was_set = False
+
# Should the symbol get an entry in .config? Calculated along with the
# value.
self._write_to_conf = False
@@ -2665,55 +2744,6 @@ class Symbol(object):
return (1,)
- def _set_value_no_invalidate(self, value, suppress_prompt_warning):
- """
- Like set_value(), but does not invalidate any symbols.
-
- suppress_prompt_warning:
- The warning about assigning a value to a promptless symbol gets
- spammy for Linux defconfigs, so turn it off when loading .configs.
- It's still helpful when manually invoking set_value().
- """
- # Check if the value is valid for our type
- if not ((self.orig_type == BOOL and value in (0, 2) ) or
- (self.orig_type == TRISTATE and value in (0, 1, 2) ) or
- (self.orig_type == STRING and isinstance(value, str)) or
- (self.orig_type == INT and isinstance(value, str)
- and _is_base_n(value, 10) ) or
- (self.orig_type == HEX and isinstance(value, str)
- and _is_base_n(value, 16))):
-
- warning = "the value '{}' is invalid for {}, which has type {}" \
- .format(value, self.name, TYPE_TO_STR[self.orig_type])
-
- if self.orig_type in (BOOL, TRISTATE) and \
- value in ("n", "m", "y"):
- warning += ' (pass 0, 1, 2 for n, m, y, respectively)'
-
- self.kconfig._warn(warning)
-
- return
-
- if not self.nodes:
- self.kconfig._warn_undef_assign(
- 'assigning the value "{}" to the undefined symbol {} will '
- "have no effect".format(value, self.name))
-
- if not suppress_prompt_warning:
- for node in self.nodes:
- if node.prompt is not None:
- break
- else:
- self.kconfig._warn('assigning the value "{}" to the '
- "promptless symbol {} will have no effect"
- .format(value, self.name))
-
- self.user_value = value
-
- if self.choice is not None and value == 2:
- self.choice.user_selection = self
- self.choice._rec_invalidate()
-
def _invalidate(self):
"""
Marks the symbol as needing to be recalculated.
@@ -2725,10 +2755,10 @@ class Symbol(object):
"""
Invalidates the symbol and all items that (possibly) depend on it.
"""
- # Constant symbols must never be invalidated, because they lose their
- # value. They never appear as dependencies, but can still be manually
- # assigned a user value (and that's OK, though pointless).
- if not self.is_constant:
+ if self is self.kconfig.modules:
+ # Invalidating MODULES has wide-ranging effects
+ self.kconfig._invalidate_all()
+ else:
self._invalidate()
for item in self._dependents:
@@ -2892,6 +2922,7 @@ class Choice(object):
"_cached_selection",
"_cached_vis",
"_dependents",
+ "_was_set",
"defaults",
"is_constant",
"is_optional",
@@ -3015,6 +3046,12 @@ class Choice(object):
attribute (is_optional) can never be in n mode, but 0 is still accepted
(and ignored) since it's not a malformed value.
"""
+ if value == self.user_value:
+ # We know the value must be valid if it was successfully set
+ # previously
+ self._was_set = True
+ return
+
if not ((self.orig_type == BOOL and value in (0, 2) ) or
(self.orig_type == TRISTATE and value in (0, 1, 2))):
self.kconfig._warn("the value '{}' is invalid for the choice, "
@@ -3024,14 +3061,16 @@ class Choice(object):
self.user_value = value
self._rec_invalidate()
+ self._was_set = True
def unset_value(self):
"""
Resets the user value (mode) and user selection of the Choice, as if
the user had never touched the mode or any of the choice symbols.
"""
- self.user_value = self.user_selection = None
- self._rec_invalidate()
+ if self.user_value is not None or self.user_selection is not None:
+ self.user_value = self.user_selection = None
+ self._rec_invalidate()
def __repr__(self):
"""
diff --git a/tests/Kmisc b/tests/Kmisc
index c3a21f8..cea3ca9 100644
--- a/tests/Kmisc
+++ b/tests/Kmisc
@@ -39,13 +39,13 @@ config BOOL
bool "bool" if NOT_DEFINED_1
config TRISTATE
- tristate # Visibility should not affect user value
+ tristate "A"
config STRING
string "string"
config INT
- int # Visibility should not affect user value
+ int "INT"
config HEX
hex "hex"