might add it in a safe way as a client API instead."""
import os
+import platform
import re
import sys
# The set of all symbols, indexed by name (a string)
self.syms = {}
# Python 2/3 compatibility hack. This is the only one needed.
- if sys.version_info[0] >= 3:
- self.syms_iter = self.syms.values
- else:
- self.syms_iter = self.syms.itervalues
+ self.syms_iter = self.syms.values if sys.version_info[0] >= 3 else \
+ self.syms.itervalues
# The set of all defined symbols in the configuration in the order they
# appear in the Kconfig files. This excludes the special symbols n, m,
self.m = register_special_symbol(TRISTATE, "m", "m")
self.y = register_special_symbol(TRISTATE, "y", "y")
# DEFCONFIG_LIST uses this
- register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2])
+ register_special_symbol(STRING, "UNAME_RELEASE", platform.uname()[2])
# The symbol with "option defconfig_list" set, containing a list of
# default .config files
self.arch = os.environ.get("ARCH")
self.srcarch = os.environ.get("SRCARCH")
+ # If you set CONFIG_ in the environment, Kconfig will prefix all symbols
+ # with its value when saving the configuration, instead of using the default, "CONFIG_".
+ self.config_prefix = os.environ.get("CONFIG_")
+ if self.config_prefix is None:
+ self.config_prefix = "CONFIG_"
+
# See Config.__init__(). We need this for get_defconfig_filename().
self.srctree = os.environ.get("srctree")
if self.srctree is None:
self.srctree = "."
self.filename = filename
- if base_dir is None:
- self.base_dir = self.srctree
- else:
- self.base_dir = os.path.expandvars(base_dir)
+ self.base_dir = self.srctree if base_dir is None else \
+ os.path.expandvars(base_dir)
# The 'mainmenu' text
self.mainmenu_text = None
self._transform_m = None
# Parse the Kconfig files
- self.top_block = self._parse_file(filename, None, None, None)
+ self.top_block = []
+ self._parse_file(filename, None, None, None, self.top_block)
# Build Symbol.dep for all symbols
self._build_dep()
"""
self._warnings = []
+ # Regular expressions for parsing .config files
+ _set_re_match = re.compile(r"{}(\w+)=(.*)".format(self.config_prefix)).match
+ _unset_re_match = re.compile(r"# {}(\w+) is not set".format(self.config_prefix)).match
+
# Put this first so that a missing file doesn't screw up our state
filename = os.path.expandvars(filename)
line_feeder = _FileFeed(filename)
with open(filename, "w") as f:
# Write header
if header is not None:
- f.write(_comment(header))
- f.write("\n")
+ f.write(_comment(header) + "\n")
# Build and write configuration
conf_strings = []
_make_block_conf(self.top_block, conf_strings.append)
- f.write("\n".join(conf_strings))
- f.write("\n")
+ f.write("\n".join(conf_strings) + "\n")
def eval(self, s):
"""Returns the value of the expression 's' -- where 's' is represented
# Kconfig parsing
#
- def _parse_file(self, filename, parent, deps, visible_if_deps, res=None):
- """Parses the Kconfig file 'filename'. Returns a list with the Items in
- the file. See _parse_block() for the meaning of the parameters."""
- return self._parse_block(_FileFeed(filename), None, parent, deps,
- visible_if_deps, res)
+ def _parse_file(self, filename, parent, deps, visible_if_deps, block):
+ """Parses the Kconfig file 'filename'. Appends the Items in the file
+ (and any file it sources) to the list passed in the 'block' parameter.
+ See _parse_block() for the meaning of the parameters."""
+ self._parse_block(_FileFeed(filename), None, parent, deps,
+ visible_if_deps, block)
def _parse_block(self, line_feeder, end_marker, parent, deps,
- visible_if_deps, res=None):
+ visible_if_deps, block):
"""Parses a block, which is the contents of either a file or an if,
- menu, or choice statement. Returns a list with the Items in the block.
+ menu, or choice statement. Appends the Items to the list passed in the
+ 'block' parameter.
line_feeder: A _FileFeed instance feeding lines from a file. The
Kconfig language is line-based in practice.
visible_if_deps (default: None): 'visible if' dependencies from
enclosing menus.
- res (default: None): The list to add items to. If None, a new list is
- created to hold the items."""
-
- block = [] if res is None else res
+ block: The list to add items to."""
while 1:
# Do we already have a tokenized line that we determined wasn't
if end_marker is not None:
raise Kconfig_Syntax_Error("Unexpected end of file {0}"
.format(line_feeder.filename))
- return block
+ return
tokens = self._tokenize(line, False, line_feeder.filename,
line_feeder.linenr)
# choice statements, the choice statement takes precedence.
if not sym.is_defined_ or isinstance(parent, Choice):
sym.parent = parent
-
sym.is_defined_ = True
+ self._parse_properties(line_feeder, sym, deps, visible_if_deps)
+
self.kconfig_syms.append(sym)
block.append(sym)
- self._parse_properties(line_feeder, sym, deps, visible_if_deps)
-
elif t0 == T_SOURCE:
kconfig_file = tokens.get_next()
exp_kconfig_file = self._expand_sym_refs(kconfig_file)
elif t0 == end_marker:
# We have reached the end of the block
- return block
+ return
elif t0 == T_IF:
# If statements are treated as syntactic sugar for adding
elif t0 == T_COMMENT:
comment = Comment()
-
comment.config = self
comment.parent = parent
comment.filename = line_feeder.filename
comment.linenr = line_feeder.linenr
comment.text = tokens.get_next()
- self.comments.append(comment)
- block.append(comment)
-
self._parse_properties(line_feeder, comment, deps,
visible_if_deps)
+ self.comments.append(comment)
+ block.append(comment)
+
elif t0 == T_MENU:
menu = Menu()
-
menu.config = self
menu.parent = parent
menu.filename = line_feeder.filename
menu.linenr = line_feeder.linenr
menu.title = tokens.get_next()
- self.menus.append(menu)
- block.append(menu)
-
- # Parse properties and contents
self._parse_properties(line_feeder, menu, deps,
visible_if_deps)
- menu.block = self._parse_block(line_feeder, T_ENDMENU, menu,
- menu.dep_expr,
- _make_and(visible_if_deps,
- menu.visible_if_expr))
+
+ # This needs to go before _parse_block() so that we get the
+ # proper menu ordering in the case of nested functions
+ self.menus.append(menu)
+ # Parse contents and put Items in menu.block
+ self._parse_block(line_feeder, T_ENDMENU, menu, menu.dep_expr,
+ _make_and(visible_if_deps,
+ menu.visible_if_expr),
+ menu.block)
+
+ block.append(menu)
elif t0 == T_CHOICE:
name = tokens.get_next()
choice.def_locations.append((line_feeder.filename,
line_feeder.linenr))
- # Parse properties and contents
self._parse_properties(line_feeder, choice, deps,
visible_if_deps)
- choice.block = self._parse_block(line_feeder, T_ENDCHOICE,
- choice, deps, visible_if_deps)
+
+ # Parse contents and put Items in choice.block
+ self._parse_block(line_feeder, T_ENDCHOICE, choice, deps,
+ visible_if_deps, choice.block)
choice._determine_actual_symbols()
"""Parses '<expr1> if <expr2>' constructs, where the 'if' part is
optional. Returns a tuple containing the parsed expressions, with
None as the second element if the 'if' part is missing."""
- val = self._parse_expr(tokens, stmt, line, filename, linenr, False)
- if tokens.check(T_IF):
- return (val, self._parse_expr(tokens, stmt, line, filename,
- linenr))
- return (val, None)
+ return (self._parse_expr(tokens, stmt, line, filename, linenr,
+ False),
+ self._parse_expr(tokens, stmt, line, filename, linenr)
+ if tokens.check(T_IF) else None)
# In case the symbol is defined in multiple locations, we need to
- # remember what prompts, defaults, and selects are new for this
- # definition, as "depends on" should only apply to the local
+ # remember what prompts, defaults, selects, and implies are new for
+ # this definition, as "depends on" should only apply to the local
# definition.
new_prompt = None
new_def_exprs = []
new_selects = []
+ new_implies = []
# Dependencies from 'depends on' statements
depends_on_expr = None
line_feeder.unget()
- elif t0 == T_SELECT or t0 == T_IMPLY:
+ elif t0 == T_SELECT:
target = tokens.get_next()
stmt.referenced_syms.add(target)
stmt.selected_syms.add(target)
- if tokens.check(T_IF):
- new_selects.append((target,
- self._parse_expr(tokens, stmt, line,
- filename, linenr)))
- else:
- new_selects.append((target, None))
+ new_selects.append(
+ (target,
+ self._parse_expr(tokens, stmt, line, filename, linenr)
+ if tokens.check(T_IF) else None))
+
+ elif t0 == T_IMPLY:
+ target = tokens.get_next()
+
+ stmt.referenced_syms.add(target)
+ stmt.implied_syms.add(target)
+
+ new_implies.append(
+ (target,
+ self._parse_expr(tokens, stmt, line, filename, linenr)
+ if tokens.check(T_IF) else None))
elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):
stmt.type = TOKEN_TO_TYPE[t0]
stmt.referenced_syms.add(low)
stmt.referenced_syms.add(high)
- if tokens.check(T_IF):
- stmt.ranges.append((low, high,
- self._parse_expr(tokens, stmt, line,
- filename, linenr)))
- else:
- stmt.ranges.append((low, high, None))
+ stmt.ranges.append(
+ (low, high,
+ self._parse_expr(tokens, stmt, line, filename, linenr)
+ if tokens.check(T_IF) else None))
elif t0 == T_DEF_TRISTATE:
stmt.type = TRISTATE
# Symbol or Choice
# See comment for 'menu_dep'
- stmt.menu_dep = depends_on_expr
+ stmt.menu_dep = _make_and(deps, depends_on_expr)
# Propagate dependencies to prompts
if new_prompt is not None:
- # Propagate 'visible if' dependencies from enclosing menus
prompt, cond_expr = new_prompt
- cond_expr = _make_and(cond_expr, visible_if_deps)
- # Propagate 'depends on' dependencies
- new_prompt = (prompt, _make_and(cond_expr, depends_on_expr))
+ # Propagate 'visible if' dependencies from menus and local
+ # 'depends on' dependencies
+ cond_expr = _make_and(_make_and(cond_expr, visible_if_deps),
+ depends_on_expr)
# Save original
- stmt.orig_prompts.append(new_prompt)
+ stmt.orig_prompts.append((prompt, cond_expr))
# Finalize with dependencies from enclosing menus and ifs
- stmt.prompts.append((new_prompt[0],
- _make_and(new_prompt[1], deps)))
+ stmt.prompts.append((prompt, _make_and(cond_expr, deps)))
# Propagate dependencies to defaults
stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))
for val_expr, cond_expr in new_def_exprs])
- # Propagate dependencies to selects
+ # Propagate dependencies to selects and implies
- # Only symbols can select
+ # Only symbols can select and imply
if isinstance(stmt, Symbol):
# Propagate 'depends on' dependencies
new_selects = [(target, _make_and(cond_expr, depends_on_expr))
for target, cond_expr in new_selects]
+ new_implies = [(target, _make_and(cond_expr, depends_on_expr))
+ for target, cond_expr in new_implies]
# Save original
stmt.orig_selects.extend(new_selects)
+ stmt.orig_implies.extend(new_implies)
# Finalize with dependencies from enclosing menus and ifs
for target, cond in new_selects:
- target.rev_dep = _make_or(target.rev_dep,
- _make_and(stmt,
- _make_and(cond, deps)))
+ target.rev_dep = \
+ _make_or(target.rev_dep,
+ _make_and(stmt, _make_and(cond, deps)))
+ for target, cond in new_implies:
+ target.weak_rev_dep = \
+ _make_or(target.weak_rev_dep,
+ _make_and(stmt, _make_and(cond, deps)))
def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,
transform_m=True):
# The directly dependent symbols of a symbol are:
# - Any symbols whose prompts, default values, rev_dep (select
- # condition), or ranges depend on the symbol
+ # condition), weak_rev_dep (imply condition) or ranges depend on the
+ # symbol
# - Any symbols that belong to the same choice statement as the symbol
# (these won't be included in 'dep' as that makes the dependency
# graph unwieldy, but Symbol._get_dependent() will include them)
add_expr_deps(e, sym)
add_expr_deps(sym.rev_dep, sym)
+ add_expr_deps(sym.weak_rev_dep, sym)
for l, u, e in sym.ranges:
add_expr_deps(l, sym)
else:
prompts_str_rows = []
for prompt, cond_expr in sc.orig_prompts:
- if cond_expr is None:
- prompts_str_rows.append(' "{0}"'.format(prompt))
- else:
- prompts_str_rows.append(
- ' "{0}" if {1}'.format(prompt,
- self._expr_val_str(cond_expr)))
+ prompts_str_rows.append(
+ ' "{0}"'.format(prompt) if cond_expr is None else
+ ' "{0}" if {1}'.format(prompt,
+ self._expr_val_str(cond_expr)))
prompts_str = "\n".join(prompts_str_rows)
# Build locations string
- if not sc.def_locations:
- locations_str = "(no locations)"
- else:
- locations_str = " ".join(["{0}:{1}".format(filename, linenr) for
- (filename, linenr) in sc.def_locations])
+ locations_str = "(no locations)" if not sc.def_locations else \
+ " ".join(["{0}:{1}".format(filename, linenr) for
+ filename, linenr in sc.def_locations])
# Build additional-dependencies-from-menus-and-ifs string
additional_deps_str = " " + \
else:
ranges_str_rows = []
for l, u, cond_expr in sc.ranges:
- if cond_expr is None:
- ranges_str_rows.append(" [{0}, {1}]".format(s(l),
- s(u)))
- else:
- ranges_str_rows.append(" [{0}, {1}] if {2}"
- .format(s(l), s(u),
- self._expr_val_str(cond_expr)))
+ ranges_str_rows.append(
+ " [{0}, {1}]".format(s(l), s(u))
+ if cond_expr is None else
+ " [{0}, {1}] if {2}"
+ .format(s(l), s(u), self._expr_val_str(cond_expr)))
ranges_str = "\n".join(ranges_str_rows)
# Build default values string
else:
selects_str_rows = []
for target, cond_expr in sc.orig_selects:
- if cond_expr is None:
- selects_str_rows.append(" {0}".format(target.name))
- else:
- selects_str_rows.append(
- " {0} if {1}".format(target.name,
- self._expr_val_str(cond_expr)))
+ selects_str_rows.append(
+ " {0}".format(target.name) if cond_expr is None else
+ " {0} if {1}".format(target.name,
+ self._expr_val_str(cond_expr)))
selects_str = "\n".join(selects_str_rows)
+ # Build implies string
+ if not sc.orig_implies:
+ implies_str = " (no implies)"
+ else:
+ implies_str_rows = []
+ for target, cond_expr in sc.orig_implies:
+ implies_str_rows.append(
+ " {0}".format(target.name) if cond_expr is None else
+ " {0} if {1}".format(target.name,
+ self._expr_val_str(cond_expr)))
+ implies_str = "\n".join(implies_str_rows)
+
res = _lines("Symbol " +
("(no name)" if sc.name is None else sc.name),
"Type : " + TYPENAME[sc.type],
defaults_str,
"Selects:",
selects_str,
+ "Implies:",
+ implies_str,
"Reverse (select-related) dependencies:",
- " (no reverse dependencies)" if sc.rev_dep == "n"
- else " " + self._expr_val_str(sc.rev_dep),
+ " (no reverse dependencies)"
+ if sc.rev_dep == "n"
+ else " " + self._expr_val_str(sc.rev_dep),
+ "Weak reverse (imply-related) dependencies:",
+ " (no weak reverse dependencies)"
+ if sc.weak_rev_dep == "n"
+ else " " + self._expr_val_str(sc.weak_rev_dep),
"Additional dependencies from enclosing menus "
"and ifs:",
additional_deps_str,
else:
defaults_str_rows = []
for sym, cond_expr in sc.orig_def_exprs:
- if cond_expr is None:
- defaults_str_rows.append(" {0}".format(sym.name))
- else:
- defaults_str_rows.append(" {0} if {1}".format(sym.name,
- self._expr_val_str(cond_expr)))
+ defaults_str_rows.append(
+ " {0}".format(sym.name) if cond_expr is None else
+ " {0} if {1}".format(sym.name,
+ self._expr_val_str(cond_expr)))
defaults_str = "\n".join(defaults_str_rows)
# Build contained symbols string
self.write_to_conf = (mode != "n")
if mode == "y":
- if choice.get_selection() is self:
- new_val = "y"
- else:
- new_val = "n"
+ new_val = "y" if choice.get_selection() is self \
+ else "n"
elif mode == "m":
if self.user_val == "m" or self.user_val == "y":
new_val = "m"
else:
# If the symbol is visible and has a user value, use that.
- # Otherwise, look at defaults.
- use_defaults = True
+ # Otherwise, look at defaults and weak reverse dependencies
+ # (implies).
+ use_defaults_and_weak_rev_deps = True
if vis != "n":
self.write_to_conf = True
if self.user_val is not None:
new_val = self.config._eval_min(self.user_val, vis)
- use_defaults = False
+ use_defaults_and_weak_rev_deps = False
- if use_defaults:
+ if use_defaults_and_weak_rev_deps:
for val_expr, cond_expr in self.def_exprs:
cond_eval = self.config._eval_expr(cond_expr)
if cond_eval != "n":
cond_eval)
break
+ weak_rev_dep_val = \
+ self.config._eval_expr(self.weak_rev_dep)
+ if weak_rev_dep_val != "n":
+ self.write_to_conf = True
+ new_val = self.config._eval_max(new_val,
+ weak_rev_dep_val)
+
# Reverse (select-related) dependencies take precedence
rev_dep_val = self.config._eval_expr(self.rev_dep)
if rev_dep_val != "n":
self.write_to_conf = True
new_val = self.config._eval_max(new_val, rev_dep_val)
- # Promote "m" to "y" for booleans
- if new_val == "m" and self.type == BOOL:
+ # We need to promote "m" to "y" in two circumstances:
+ # 1) If our type is boolean
+ # 2) If our weak_rev_dep (from IMPLY) is "y"
+ if new_val == "m" and \
+ (self.type == BOOL or
+ self.config._eval_expr(self.weak_rev_dep) == "y"):
new_val = "y"
elif self.type == INT or self.type == HEX:
get_referenced_symbols()."""
return self.selected_syms
+ def get_implied_symbols(self):
+ """Returns the set() of all symbols X for which this symbol has an
+ 'imply X' or 'imply X if Y' (regardless of whether Y is satisfied or
+ not). This is a subset of the symbols returned by
+ get_referenced_symbols()."""
+ return self.implied_syms
+
def set_user_value(self, v):
"""Sets the user value of the symbol.
self.ranges = [] # 'range' properties (for int and hex)
self.help = None # Help text
self.rev_dep = "n" # Reverse (select-related) dependencies
+ self.weak_rev_dep = "n" # Weak reverse (imply-related) dependencies
self.config = None
self.parent = None
self.user_val = None # Value set by user
- # The prompt, default value and select conditions without any
+ # The prompt, default value, select, and imply conditions without any
# dependencies from menus and ifs propagated to them
self.orig_prompts = []
self.orig_def_exprs = []
self.orig_selects = []
+ self.orig_implies = []
# Dependencies inherited from containing menus and ifs
self.deps_from_containing = None
# The set of symbols selected by this symbol (see
# get_selected_symbols())
self.selected_syms = set()
+ # The set of symbols implied by this symbol (see get_implied_symbols())
+ self.implied_syms = set()
# Like 'referenced_syms', but includes symbols from
# dependencies inherited from enclosing menus and ifs
self.all_referenced_syms = set()
- # This records only dependencies specified with 'depends on'. Needed
- # when determining actual choice items (hrrrr...). See also
- # Choice._determine_actual_symbols().
+ # This records only dependencies from enclosing ifs and menus together
+ # with local 'depends on' dependencies. Needed when determining actual
+ # choice items (hrrrr...). See Choice._determine_actual_symbols().
self.menu_dep = None
# See Symbol.get_ref/def_locations().
return
if self.type == BOOL or self.type == TRISTATE:
- if val == "y" or val == "m":
- append_fn("CONFIG_{0}={1}".format(self.name, val))
- else:
- append_fn("# CONFIG_{0} is not set".format(self.name))
+ append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val)
+ if val == "y" or val == "m" else
+ "# {0}{1} is not set".format(self.config.config_prefix, self.name))
elif self.type == INT or self.type == HEX:
- append_fn("CONFIG_{0}={1}".format(self.name, val))
+ append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val))
elif self.type == STRING:
# Escape \ and "
- append_fn('CONFIG_{0}="{1}"'
- .format(self.name,
+ append_fn('{0}{1}="{2}"'
+ .format(self.config.config_prefix, self.name,
val.replace("\\", "\\\\").replace('"', '\\"')))
else:
self.title = None
self.dep_expr = None
self.visible_if_expr = None
- self.block = None
+ self.block = [] # List of contained items
self.config = None
self.parent = None
self.prompts = []
self.def_exprs = [] # 'default' properties
self.help = None # Help text
- self.block = None # List of contained items
+ self.block = [] # List of contained items
self.config = None
self.parent = None
vis = sc.config._eval_max(vis, cond_expr)
if isinstance(sc, Symbol) and sc.is_choice_sym:
- vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
+ if sc.type == TRISTATE and vis == "m" and \
+ sc.parent.get_mode() == "y":
+ # Choice symbols with visibility "m" are not visible if the
+ # choice has mode "y"
+ vis = "n"
+ else:
+ vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
# Promote "m" to "y" if we're dealing with a non-tristate
if vis == "m" and sc.type != TRISTATE:
"prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL,
"tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL,
"def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT,
- "imply": T_IMPLY, "range": T_RANGE, "option": T_OPTION,
+ "imply" : T_IMPLY, "range": T_RANGE, "option": T_OPTION,
"allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV,
"defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES,
"visible": T_VISIBLE}.get
# trailing whitespace as an optimization.
_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match
-# Regular expressions for parsing .config files
-_set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match
-_unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match
-
# Regular expression for finding $-references to symbols in strings
_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search