kconfig: Get rid of leading/trailing whitespace in prompts
Leading/trailing whitespace in prompts requires ugly workarounds in genrest.py, as e.g. *prompt * is invalid RST. strip() all prompts in Kconfiglib and get rid of the genrest.py workarounds. Add a warning too. The Kconfiglib update has some unrelated cleanups and fixes (that won't affect Zephyr). Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
parent
f93ca23765
commit
aa26289458
5 changed files with 117 additions and 133 deletions
|
@ -88,12 +88,9 @@ def write_kconfig_rst():
|
|||
# Add an index entry for the symbol that links to its RST file. Also
|
||||
# list its prompt(s), if any. (A symbol can have multiple prompts if it
|
||||
# has multiple definitions.)
|
||||
#
|
||||
# The strip() avoids RST choking on stuff like *foo *, when people
|
||||
# accidentally include leading/trailing whitespace in prompts.
|
||||
index_rst += " * - :option:`CONFIG_{}`\n - {}\n".format(
|
||||
sym.name,
|
||||
" / ".join(node.prompt[0].strip()
|
||||
" / ".join(node.prompt[0]
|
||||
for node in sym.nodes if node.prompt))
|
||||
|
||||
write_if_updated(os.path.join(out_dir, "index.rst"), index_rst)
|
||||
|
@ -104,7 +101,7 @@ def write_sym_rst(sym, out_dir):
|
|||
kconf = sym.kconfig
|
||||
|
||||
# List all prompts on separate lines
|
||||
prompt_str = "\n\n".join("*{}*".format(node.prompt[0].strip())
|
||||
prompt_str = "\n\n".join("*{}*".format(node.prompt[0])
|
||||
for node in sym.nodes if node.prompt) \
|
||||
or "*(No prompt -- not directly user assignable.)*"
|
||||
|
||||
|
@ -187,9 +184,7 @@ def write_sym_rst(sym, out_dir):
|
|||
path = " → " + menu.prompt[0] + path
|
||||
menu = menu.parent
|
||||
|
||||
# The strip() avoids RST choking on leading/trailing whitespace in
|
||||
# prompts
|
||||
return ("(top menu)" + path).strip()
|
||||
return "(top menu)" + path
|
||||
|
||||
heading = "Kconfig definition"
|
||||
if len(sym.nodes) > 1:
|
||||
|
|
|
@ -95,21 +95,6 @@ possibilities for ARCH and SRCARCH. Kconfiglib will print a warning if an unset
|
|||
environment variable is referenced inside the Kconfig files.
|
||||
|
||||
|
||||
Gotcha
|
||||
******
|
||||
|
||||
It's important to set $SRCARCH even if you don't care about values and only
|
||||
want to extract information from Kconfig files, because the top-level Kconfig
|
||||
file does this (as of writing):
|
||||
|
||||
source "arch/$SRCARCH/Kconfig"
|
||||
|
||||
If $SRCARCH is not set, this expands to "arch//Kconfig", and arch/Kconfig
|
||||
happens to be an existing file, giving something that appears to work but is
|
||||
actually a truncated configuration. The available symbols will differ depending
|
||||
on the arch as well.
|
||||
|
||||
|
||||
Intro to symbol values
|
||||
======================
|
||||
|
||||
|
@ -745,7 +730,7 @@ class Kconfig(object):
|
|||
"""
|
||||
See the class documentation.
|
||||
"""
|
||||
return os.path.expandvars(self.top_node.prompt[0])
|
||||
return _expand(self.top_node.prompt[0])
|
||||
|
||||
@property
|
||||
def defconfig_filename(self):
|
||||
|
@ -758,7 +743,7 @@ class Kconfig(object):
|
|||
for filename, cond in self.defconfig_list.defaults:
|
||||
if expr_value(cond):
|
||||
try:
|
||||
with self._open(os.path.expandvars(filename.str_value)) as f:
|
||||
with self._open(_expand(filename.str_value)) as f:
|
||||
return f.name
|
||||
except IOError:
|
||||
continue
|
||||
|
@ -1943,20 +1928,20 @@ class Kconfig(object):
|
|||
prev.next = prev = node
|
||||
|
||||
elif t0 == _T_SOURCE:
|
||||
self._enter_file(os.path.expandvars(self._expect_str_and_eol()))
|
||||
self._enter_file(_expand(self._expect_str_and_eol()))
|
||||
prev = self._parse_block(None, parent, prev)
|
||||
self._leave_file()
|
||||
|
||||
elif t0 == _T_RSOURCE:
|
||||
self._enter_file(os.path.join(
|
||||
os.path.dirname(self._filename),
|
||||
os.path.expandvars(self._expect_str_and_eol())
|
||||
_expand(self._expect_str_and_eol())
|
||||
))
|
||||
prev = self._parse_block(None, parent, prev)
|
||||
self._leave_file()
|
||||
|
||||
elif t0 in (_T_GSOURCE, _T_GRSOURCE):
|
||||
pattern = os.path.expandvars(self._expect_str_and_eol())
|
||||
pattern = _expand(self._expect_str_and_eol())
|
||||
if t0 == _T_GRSOURCE:
|
||||
# Relative gsource
|
||||
pattern = os.path.join(os.path.dirname(self._filename),
|
||||
|
@ -2132,21 +2117,9 @@ class Kconfig(object):
|
|||
continue
|
||||
|
||||
if t0 in _TYPE_TOKENS:
|
||||
new_type = _TOKEN_TO_TYPE[t0]
|
||||
|
||||
if node.item.orig_type not in (UNKNOWN, new_type):
|
||||
self._warn("{} defined with multiple types, {} will be used"
|
||||
.format(_name_and_loc(node.item),
|
||||
TYPE_TO_STR[new_type]))
|
||||
|
||||
node.item.orig_type = new_type
|
||||
|
||||
self._set_type(node, _TOKEN_TO_TYPE[t0])
|
||||
if self._peek_token() is not None:
|
||||
if node.prompt:
|
||||
self._warn("{} defined with multiple prompts in single location"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
node.prompt = (self._expect_str(), self._parse_cond())
|
||||
self._parse_prompt(node)
|
||||
|
||||
elif t0 == _T_DEPENDS:
|
||||
if not self._check_token(_T_ON):
|
||||
|
@ -2155,59 +2128,7 @@ class Kconfig(object):
|
|||
node.dep = self._make_and(node.dep, self._parse_expr(True))
|
||||
|
||||
elif t0 == _T_HELP:
|
||||
# Find first non-blank (not all-space) line and get its
|
||||
# indentation
|
||||
|
||||
if node.help is not None:
|
||||
self._warn("{} defined with more than one help text -- "
|
||||
"only the last one will be used"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
# Small optimization. This code is pretty hot.
|
||||
readline = self._file.readline
|
||||
|
||||
while 1:
|
||||
line = readline()
|
||||
self._linenr += 1
|
||||
if not line or not line.isspace():
|
||||
break
|
||||
|
||||
if not line:
|
||||
self._warn("{} has 'help' but empty help text"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
node.help = ""
|
||||
break
|
||||
|
||||
indent = _indentation(line)
|
||||
if indent == 0:
|
||||
# If the first non-empty lines has zero indent, there is no
|
||||
# help text
|
||||
self._warn("{} has 'help' but empty help text"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
node.help = ""
|
||||
self._saved_line = line # "Unget" the line
|
||||
break
|
||||
|
||||
help_lines = [_dedent_rstrip(line, indent)]
|
||||
# Small optimization
|
||||
add_help_line = help_lines.append
|
||||
|
||||
# The help text goes on till the first non-empty line with less
|
||||
# indent
|
||||
|
||||
while 1:
|
||||
line = readline()
|
||||
self._linenr += 1
|
||||
if not (line and (line.isspace() or \
|
||||
_indentation(line) >= indent)):
|
||||
break
|
||||
|
||||
add_help_line(_dedent_rstrip(line, indent))
|
||||
|
||||
node.help = "\n".join(help_lines).rstrip() + "\n"
|
||||
self._saved_line = line # "Unget" the line
|
||||
self._parse_help(node)
|
||||
|
||||
elif t0 == _T_SELECT:
|
||||
if not isinstance(node.item, Symbol):
|
||||
|
@ -2228,27 +2149,12 @@ class Kconfig(object):
|
|||
self._parse_cond()))
|
||||
|
||||
elif t0 in (_T_DEF_BOOL, _T_DEF_TRISTATE):
|
||||
new_type = _TOKEN_TO_TYPE[t0]
|
||||
|
||||
if node.item.orig_type not in (UNKNOWN, new_type):
|
||||
self._warn("{} defined with multiple types, {} will be used"
|
||||
.format(_name_and_loc(node.item),
|
||||
TYPE_TO_STR[new_type]))
|
||||
|
||||
node.item.orig_type = new_type
|
||||
|
||||
self._set_type(node, _TOKEN_TO_TYPE[t0])
|
||||
node.defaults.append((self._parse_expr(False),
|
||||
self._parse_cond()))
|
||||
|
||||
elif t0 == _T_PROMPT:
|
||||
# 'prompt' properties override each other within a single
|
||||
# definition of a symbol, but additional prompts can be added
|
||||
# by defining the symbol multiple times
|
||||
if node.prompt:
|
||||
self._warn("{} defined with multiple prompts in single location"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
node.prompt = (self._expect_str(), self._parse_cond())
|
||||
self._parse_prompt(node)
|
||||
|
||||
elif t0 == _T_RANGE:
|
||||
node.ranges.append((self._expect_sym(),
|
||||
|
@ -2263,15 +2169,15 @@ class Kconfig(object):
|
|||
env_var = self._expect_str_and_eol()
|
||||
node.item.env_var = env_var
|
||||
|
||||
if env_var not in os.environ:
|
||||
if env_var in os.environ:
|
||||
node.defaults.append(
|
||||
(self._lookup_const_sym(os.environ[env_var]),
|
||||
self.y))
|
||||
else:
|
||||
self._warn("{1} has 'option env=\"{0}\"', "
|
||||
"but the environment variable {0} is not "
|
||||
"set".format(node.item.name, env_var),
|
||||
self._filename, self._linenr)
|
||||
else:
|
||||
node.defaults.append(
|
||||
(self._lookup_const_sym(os.environ[env_var]),
|
||||
self.y))
|
||||
|
||||
elif self._check_token(_T_DEFCONFIG_LIST):
|
||||
if not self.defconfig_list:
|
||||
|
@ -2329,6 +2235,92 @@ class Kconfig(object):
|
|||
self._tokens_i = -1
|
||||
return
|
||||
|
||||
def _set_type(self, node, new_type):
|
||||
if node.item.orig_type not in (UNKNOWN, new_type):
|
||||
self._warn("{} defined with multiple types, {} will be used"
|
||||
.format(_name_and_loc(node.item),
|
||||
TYPE_TO_STR[new_type]))
|
||||
|
||||
node.item.orig_type = new_type
|
||||
|
||||
def _parse_prompt(self, node):
|
||||
# 'prompt' properties override each other within a single definition of
|
||||
# a symbol, but additional prompts can be added by defining the symbol
|
||||
# multiple times
|
||||
if node.prompt:
|
||||
self._warn("{} defined with multiple prompts in single location"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
prompt = self._expect_str()
|
||||
if prompt != prompt.strip():
|
||||
self._warn("{} has leading or trailing whitespace in its prompt"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
# This avoid issues for e.g. reStructuredText documentation, where
|
||||
# '*prompt *' is invalid
|
||||
prompt = prompt.strip()
|
||||
|
||||
node.prompt = (prompt, self._parse_cond())
|
||||
|
||||
def _parse_help(self, node):
|
||||
# Find first non-blank (not all-space) line and get its indentation
|
||||
|
||||
if node.help is not None:
|
||||
self._warn("{} defined with more than one help text -- only the "
|
||||
"last one will be used"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
# Small optimization. This code is pretty hot.
|
||||
readline = self._file.readline
|
||||
|
||||
while 1:
|
||||
line = readline()
|
||||
self._linenr += 1
|
||||
if not line or not line.isspace():
|
||||
break
|
||||
|
||||
if not line:
|
||||
self._warn("{} has 'help' but empty help text"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
node.help = ""
|
||||
return
|
||||
|
||||
indent = _indentation(line)
|
||||
if indent == 0:
|
||||
# If the first non-empty lines has zero indent, there is no help
|
||||
# text
|
||||
self._warn("{} has 'help' but empty help text"
|
||||
.format(_name_and_loc(node.item)))
|
||||
|
||||
node.help = ""
|
||||
self._saved_line = line # "Unget" the line
|
||||
return
|
||||
|
||||
# The help text goes on till the first non-empty line with less indent
|
||||
# than the first line
|
||||
|
||||
help_lines = []
|
||||
# Small optimization
|
||||
add_help_line = help_lines.append
|
||||
|
||||
while line and (line.isspace() or _indentation(line) >= indent):
|
||||
# De-indent 'line' by 'indent' spaces and rstrip() it to remove any
|
||||
# newlines (which gets rid of other trailing whitespace too, but
|
||||
# that's fine).
|
||||
#
|
||||
# This prepares help text lines in a speedy way: The [indent:]
|
||||
# might already remove trailing newlines for lines shorter than
|
||||
# indent (e.g. empty lines). The rstrip() makes it consistent,
|
||||
# meaning we can join the lines with "\n" later.
|
||||
add_help_line(line.expandtabs()[indent:].rstrip())
|
||||
|
||||
line = readline()
|
||||
self._linenr += 1
|
||||
|
||||
node.help = "\n".join(help_lines).rstrip() + "\n"
|
||||
self._saved_line = line # "Unget" the line
|
||||
|
||||
def _parse_expr(self, transform_m):
|
||||
# Parses an expression from the tokens in Kconfig._tokens using a
|
||||
# simple top-down approach. See the module docstring for the expression
|
||||
|
@ -4532,6 +4524,15 @@ def _make_depend_on(sym, expr):
|
|||
_internal_error("Internal error while fetching symbols from an "
|
||||
"expression with token stream {}.".format(expr))
|
||||
|
||||
def _expand(s):
|
||||
# The predefined UNAME_RELEASE symbol is expanded in one of the 'default's
|
||||
# of the DEFCONFIG_LIST symbol in the Linux kernel. This function maintains
|
||||
# compatibility with it even though environment variables in strings are
|
||||
# now expanded directly.
|
||||
|
||||
# platform.uname() has an internal cache, so this is speedy enough
|
||||
return os.path.expandvars(s.replace("$UNAME_RELEASE", platform.uname()[2]))
|
||||
|
||||
def _parenthesize(expr, type_):
|
||||
# expr_str() helper. Adds parentheses around expressions of type 'type_'.
|
||||
|
||||
|
@ -4546,18 +4547,6 @@ def _indentation(line):
|
|||
line = line.expandtabs()
|
||||
return len(line) - len(line.lstrip())
|
||||
|
||||
def _dedent_rstrip(line, indent):
|
||||
# De-indents 'line' by 'indent' spaces and rstrip()s it to remove any
|
||||
# newlines (which gets rid of other trailing whitespace too, but that's
|
||||
# fine).
|
||||
#
|
||||
# Used to prepare help text lines in a speedy way: The [indent:] might
|
||||
# already remove trailing newlines for lines shorter than indent (e.g.
|
||||
# empty lines). The rstrip() makes it consistent, meaning we can join the
|
||||
# lines with "\n" later.
|
||||
|
||||
return line.expandtabs()[indent:].rstrip()
|
||||
|
||||
def _is_base_n(s, n):
|
||||
try:
|
||||
int(s, n)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue