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:
Ulf Magnusson 2018-05-18 22:33:03 +02:00 committed by Anas Nashif
commit aa26289458
5 changed files with 117 additions and 133 deletions

View file

@ -88,12 +88,9 @@ def write_kconfig_rst():
# Add an index entry for the symbol that links to its RST file. Also # 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 # list its prompt(s), if any. (A symbol can have multiple prompts if it
# has multiple definitions.) # 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( index_rst += " * - :option:`CONFIG_{}`\n - {}\n".format(
sym.name, sym.name,
" / ".join(node.prompt[0].strip() " / ".join(node.prompt[0]
for node in sym.nodes if node.prompt)) for node in sym.nodes if node.prompt))
write_if_updated(os.path.join(out_dir, "index.rst"), index_rst) 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 kconf = sym.kconfig
# List all prompts on separate lines # 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) \ for node in sym.nodes if node.prompt) \
or "*(No prompt -- not directly user assignable.)*" or "*(No prompt -- not directly user assignable.)*"
@ -187,9 +184,7 @@ def write_sym_rst(sym, out_dir):
path = "" + menu.prompt[0] + path path = "" + menu.prompt[0] + path
menu = menu.parent menu = menu.parent
# The strip() avoids RST choking on leading/trailing whitespace in return "(top menu)" + path
# prompts
return ("(top menu)" + path).strip()
heading = "Kconfig definition" heading = "Kconfig definition"
if len(sym.nodes) > 1: if len(sym.nodes) > 1:

View file

@ -42,7 +42,7 @@ config SYS_LOG_CRYPTO_LEVEL
- 4 DEBUG, write SYS_LOG_DBG in addition to previous levels - 4 DEBUG, write SYS_LOG_DBG in addition to previous levels
config CRYPTO_TINYCRYPT_SHIM config CRYPTO_TINYCRYPT_SHIM
bool "Enable TinyCrypt shim driver [EXPERIMENTAL] " bool "Enable TinyCrypt shim driver [EXPERIMENTAL]"
default n default n
select TINYCRYPT select TINYCRYPT
select TINYCRYPT_AES select TINYCRYPT_AES
@ -69,7 +69,7 @@ config CRYPTO_TINYCRYPT_SHIM_DRV_NAME
Device name for TinyCrypt Pseudo device. Device name for TinyCrypt Pseudo device.
config CRYPTO_MBEDTLS_SHIM config CRYPTO_MBEDTLS_SHIM
bool "Enable mbedTLS shim driver [EXPERIMENTAL] " bool "Enable mbedTLS shim driver [EXPERIMENTAL]"
default n default n
select MBEDTLS select MBEDTLS
select MBEDTLS_ENABLE_HEAP select MBEDTLS_ENABLE_HEAP

View file

@ -268,7 +268,7 @@ config INT_LATENCY_BENCHMARK
config EXECUTION_BENCHMARKING config EXECUTION_BENCHMARKING
bool bool
prompt "Timing metrics " prompt "Timing metrics"
default n default n
help help
This option enables the tracking of various times inside the kernel This option enables the tracking of various times inside the kernel

View file

@ -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. 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 Intro to symbol values
====================== ======================
@ -745,7 +730,7 @@ class Kconfig(object):
""" """
See the class documentation. See the class documentation.
""" """
return os.path.expandvars(self.top_node.prompt[0]) return _expand(self.top_node.prompt[0])
@property @property
def defconfig_filename(self): def defconfig_filename(self):
@ -758,7 +743,7 @@ class Kconfig(object):
for filename, cond in self.defconfig_list.defaults: for filename, cond in self.defconfig_list.defaults:
if expr_value(cond): if expr_value(cond):
try: try:
with self._open(os.path.expandvars(filename.str_value)) as f: with self._open(_expand(filename.str_value)) as f:
return f.name return f.name
except IOError: except IOError:
continue continue
@ -1943,20 +1928,20 @@ class Kconfig(object):
prev.next = prev = node prev.next = prev = node
elif t0 == _T_SOURCE: 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) prev = self._parse_block(None, parent, prev)
self._leave_file() self._leave_file()
elif t0 == _T_RSOURCE: elif t0 == _T_RSOURCE:
self._enter_file(os.path.join( self._enter_file(os.path.join(
os.path.dirname(self._filename), 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) prev = self._parse_block(None, parent, prev)
self._leave_file() self._leave_file()
elif t0 in (_T_GSOURCE, _T_GRSOURCE): 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: if t0 == _T_GRSOURCE:
# Relative gsource # Relative gsource
pattern = os.path.join(os.path.dirname(self._filename), pattern = os.path.join(os.path.dirname(self._filename),
@ -2132,21 +2117,9 @@ class Kconfig(object):
continue continue
if t0 in _TYPE_TOKENS: if t0 in _TYPE_TOKENS:
new_type = _TOKEN_TO_TYPE[t0] self._set_type(node, _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
if self._peek_token() is not None: if self._peek_token() is not None:
if node.prompt: self._parse_prompt(node)
self._warn("{} defined with multiple prompts in single location"
.format(_name_and_loc(node.item)))
node.prompt = (self._expect_str(), self._parse_cond())
elif t0 == _T_DEPENDS: elif t0 == _T_DEPENDS:
if not self._check_token(_T_ON): 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)) node.dep = self._make_and(node.dep, self._parse_expr(True))
elif t0 == _T_HELP: elif t0 == _T_HELP:
# Find first non-blank (not all-space) line and get its self._parse_help(node)
# 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
elif t0 == _T_SELECT: elif t0 == _T_SELECT:
if not isinstance(node.item, Symbol): if not isinstance(node.item, Symbol):
@ -2228,27 +2149,12 @@ class Kconfig(object):
self._parse_cond())) self._parse_cond()))
elif t0 in (_T_DEF_BOOL, _T_DEF_TRISTATE): elif t0 in (_T_DEF_BOOL, _T_DEF_TRISTATE):
new_type = _TOKEN_TO_TYPE[t0] self._set_type(node, _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
node.defaults.append((self._parse_expr(False), node.defaults.append((self._parse_expr(False),
self._parse_cond())) self._parse_cond()))
elif t0 == _T_PROMPT: elif t0 == _T_PROMPT:
# 'prompt' properties override each other within a single self._parse_prompt(node)
# 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())
elif t0 == _T_RANGE: elif t0 == _T_RANGE:
node.ranges.append((self._expect_sym(), node.ranges.append((self._expect_sym(),
@ -2263,15 +2169,15 @@ class Kconfig(object):
env_var = self._expect_str_and_eol() env_var = self._expect_str_and_eol()
node.item.env_var = env_var 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}\"', " self._warn("{1} has 'option env=\"{0}\"', "
"but the environment variable {0} is not " "but the environment variable {0} is not "
"set".format(node.item.name, env_var), "set".format(node.item.name, env_var),
self._filename, self._linenr) 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): elif self._check_token(_T_DEFCONFIG_LIST):
if not self.defconfig_list: if not self.defconfig_list:
@ -2329,6 +2235,92 @@ class Kconfig(object):
self._tokens_i = -1 self._tokens_i = -1
return 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): def _parse_expr(self, transform_m):
# Parses an expression from the tokens in Kconfig._tokens using a # Parses an expression from the tokens in Kconfig._tokens using a
# simple top-down approach. See the module docstring for the expression # 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 " _internal_error("Internal error while fetching symbols from an "
"expression with token stream {}.".format(expr)) "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_): def _parenthesize(expr, type_):
# expr_str() helper. Adds parentheses around expressions of type 'type_'. # expr_str() helper. Adds parentheses around expressions of type 'type_'.
@ -4546,18 +4547,6 @@ def _indentation(line):
line = line.expandtabs() line = line.expandtabs()
return len(line) - len(line.lstrip()) 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): def _is_base_n(s, n):
try: try:
int(s, n) int(s, n)

View file

@ -42,7 +42,7 @@ config SETTINGS_FCB_NUM_AREAS
int int
default 8 default 8
depends on SETTINGS && SETTINGS_FCB depends on SETTINGS && SETTINGS_FCB
prompt "Number of flash areas used by the settings subsystem " prompt "Number of flash areas used by the settings subsystem"
help help
Number of areas to allocate in the settings FCB. A smaller number is Number of areas to allocate in the settings FCB. A smaller number is
used if the flash hardware cannot support this value. used if the flash hardware cannot support this value.