Skip to content

Commit 7a428aa

Browse files
committed
Switch to more sensible globbing statements (w/ backwards compatibility)
Instead of having 'source' and 'gsource', have 'source' always glob, but require the pattern to match at least one file, throwing KconfigError otherwise. Have separate 'osource' and 'orsource' statements (the o is for "optional") for cases where it's okay for the pattern to not match any files. This is analogous to 'include' and '-include' in Make. The biggest flaw with 'gsource' was that there was no way to do a globbing match while requiring something to match, possibly leading to subtle failures. Preserve backwards compatibility by having "gsource" and "grsource" be aliases for "osource" and "orsource", respectively. Also include some related changes: - Kconfig.srctree is now set to the empty string if $srctree is unset, rather than to None. This gives nice behavior with os.path.join() and os.path.relpath(), which treat the empty string as the current directory (without adding './', for os.path.join()). - When $srctree is set, Kconfig files in the current directory will no longer override Kconfig files in $srctree when the relative paths match. This was likely a bug all along in the C tools, and probably only makes sense for .config files. I've seen it cause breakage in Zephyr. - Clarify the behavior of $srctree in the Kconfig.__init__() docstring. - Make MenuNode.filename be relative to $srctree for the Kconfig file passed to Kconfig.__init__(). This makes it consistent. The major version will be bumped later due to the small Kconfig.srctree API change.
1 parent d8a7421 commit 7a428aa

6 files changed

Lines changed: 170 additions & 105 deletions

File tree

README.rst

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,19 @@ Kconfig language extensions
194194
~~~~~~~~~~~~~~~~~~~~~~~~~~~
195195

196196
The following Kconfig extensions are available:
197+
198+
- ``source`` supports glob patterns and includes each matching file. At pattern
199+
is required to match at least one file.
200+
201+
A separate ``osource`` statement is available for cases where it's okay for
202+
the pattern to match no files (in which case ``osource`` turns into a no-op).
197203

198-
- A relative ``source`` statement (``rsource``) where Kconfig
199-
file paths are specified relative to the directory of
200-
the current Kconfig file.
201-
202-
- A globbing ``source`` (``gsource``) that doubles
203-
as an include-if-exists function.
204+
- A relative ``source`` statement (``rsource``) is available, where file paths
205+
are specified relative to the directory of the current Kconfig file. An
206+
``orsource`` statement is available as well, analogous to ``osource``.
204207

205-
- Environment variables are expanded directly in e.g. ``source``
206-
and ``mainmenu`` statements, meaning ``option env`` symbols
207-
are redundant.
208+
- Environment variables are expanded directly in e.g. ``source`` and
209+
``mainmenu`` statements, meaning ``option env`` symbols are redundant.
208210

209211
This is the standard behavior with the new `Kconfig preprocessor
210212
<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-macro-language.txt>`_,

kconfiglib.py

Lines changed: 115 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -342,24 +342,33 @@
342342
If absolute path is given to 'rsource' then it follows behavior of 'source'.
343343
344344
345-
Globbed sourcing with 'gsource' and 'grsource'
346-
----------------------------------------------
345+
Globbed sourcing
346+
----------------
347347
348-
The 'gsource' statement works like 'source', but takes a glob pattern and
349-
sources all matching Kconfig files. For example, the following statement might
350-
source 'sub1/foofoofoo' and 'sub2/foobarfoo':
348+
'source' and 'rsource' accept glob patterns, sourcing all matching Kconfig
349+
files. They require at least one matching file, throwing a KconfigError
350+
otherwise.
351351
352-
gsource "sub[12]/foo*foo"
352+
For example, the following statement might source sub1/foofoofoo and
353+
sub2/foobarfoo:
354+
355+
source "sub[12]/foo*foo"
353356
354357
The glob patterns accepted are the same as for the standard glob.glob()
355358
function.
356359
357-
If no file matches the pattern, gsource is a no-op, and hence doubles as an
358-
include-if-exists function when given a plain filename (similar to '-include'
359-
in 'make'). It might help to think of the 'g' as "generalized" in that case.
360+
Two additional statements are provided for cases where it's acceptable for a
361+
pattern to match no files: 'osource' and 'orsource' (the o is for "optional").
362+
363+
For example, the following statements will be no-ops if neither "foo" nor any
364+
files matching "bar*" exist:
365+
366+
osource "foo"
367+
osource "bar*"
360368
361-
'grsource' is the 'rsource' version of 'gsource' and globs relative to the
362-
directory of the current Kconfig file.
369+
'orsource' does a relative optional source.
370+
371+
'source' and 'osource' are analogous to 'include' and '-include' in Make.
363372
364373
365374
Feedback
@@ -499,10 +508,15 @@ class Kconfig(object):
499508
500509
srctree:
501510
The value of the $srctree environment variable when the configuration was
502-
loaded, or None if $srctree wasn't set. Kconfig and .config files are
503-
looked up relative to $srctree if they are not found in the base path
504-
(unless absolute paths are used). This is used to support out-of-tree
505-
builds. The C tools use this environment variable in the same way.
511+
loaded, or the empty string if $srctree wasn't set. This gives nice
512+
behavior with os.path.join(), which treats "" as the current directory,
513+
without adding "./".
514+
515+
Kconfig files are looked up relative to $srctree (unless absolute paths
516+
are used), and .config files are looked up relative to $srctree if they
517+
are not found in the current directory. This is used to support
518+
out-of-tree builds. The C tools use this environment variable in the same
519+
way.
506520
507521
Changing $srctree after creating the Kconfig instance has no effect. Only
508522
the value when the configuration is loaded matters. This avoids surprises
@@ -572,7 +586,7 @@ def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
572586
as .config files (which store configuration symbol values).
573587
574588
filename (default: "Kconfig"):
575-
The base Kconfig file. For the Linux kernel, you'll want "Kconfig"
589+
The Kconfig file to load. For the Linux kernel, you'll want "Kconfig"
576590
from the top-level directory, as environment variables will make sure
577591
the right Kconfig is included from there (arch/$SRCARCH/Kconfig as of
578592
writing).
@@ -581,8 +595,15 @@ def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
581595
the base base Kconfig file will be in sys.argv[1]. It's currently
582596
always "Kconfig" in practice.
583597
584-
The $srctree environment variable is used to look up Kconfig files if
585-
set. See the class documentation.
598+
The $srctree environment variable is used to look up Kconfig files
599+
referenced in Kconfig files if set. See the class documentation.
600+
601+
Note: '(o)source' statements in Kconfig files always work relative to
602+
$srctree (or the current directory if $srctree is unset), even if
603+
'filename' is a path with directories. This allows a subset of
604+
Kconfig files to be loaded without breaking references to other
605+
Kconfig files, e.g. by doing Kconfig("./sub/Kconfig"). sub/Kconfig
606+
might expect to be sourced by ./Kconfig.
586607
587608
warn (default: True):
588609
True if warnings related to this configuration should be generated.
@@ -617,7 +638,7 @@ def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
617638
618639
Related PEP: https://www.python.org/dev/peps/pep-0538/
619640
"""
620-
self.srctree = os.environ.get("srctree")
641+
self.srctree = os.environ.get("srctree") or ""
621642
self.config_prefix = os.environ.get("CONFIG_", "CONFIG_")
622643

623644
# Regular expressions for parsing .config files, with the match()
@@ -700,7 +721,7 @@ def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
700721
self.top_node.prompt = ("Main menu", self.y)
701722
self.top_node.parent = None
702723
self.top_node.dep = self.y
703-
self.top_node.filename = filename
724+
self.top_node.filename = os.path.relpath(filename, self.srctree)
704725
self.top_node.linenr = 1
705726

706727
# Parse the Kconfig files
@@ -714,11 +735,11 @@ def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
714735
self._filestack = []
715736

716737
# The current parsing location
717-
self._filename = filename
738+
self._filename = os.path.relpath(filename, self.srctree)
718739
self._linenr = 0
719740

720741
# Open the top-level Kconfig file
721-
self._file = self._open(filename)
742+
self._file = self._open_enc(filename, _UNIVERSAL_NEWLINES_MODE)
722743

723744
try:
724745
# Parse everything
@@ -778,7 +799,7 @@ def defconfig_filename(self):
778799
for filename, cond in self.defconfig_list.defaults:
779800
if expr_value(cond):
780801
try:
781-
with self._open(filename.str_value) as f:
802+
with self._open_config(filename.str_value) as f:
782803
return f.name
783804
except IOError:
784805
continue
@@ -819,7 +840,7 @@ def load_config(self, filename, replace=True):
819840
self._warn_for_no_prompt = True
820841

821842
def _load_config(self, filename, replace):
822-
with self._open(filename) as f:
843+
with self._open_config(filename) as f:
823844
if replace:
824845
# If we're replacing the configuration, keep track of which
825846
# symbols and choices got set so that we can unset the rest
@@ -1428,7 +1449,7 @@ def __repr__(self):
14281449
return "<{}>".format(", ".join((
14291450
"configuration with {} symbols".format(len(self.syms)),
14301451
'main menu prompt "{}"'.format(self.mainmenu_text),
1431-
"srctree not set" if self.srctree is None else
1452+
"srctree is current directory" if not self.srctree else
14321453
'srctree "{}"'.format(self.srctree),
14331454
'config symbol prefix "{}"'.format(self.config_prefix),
14341455
"warnings " +
@@ -1450,33 +1471,30 @@ def __repr__(self):
14501471
# File reading
14511472
#
14521473

1453-
def _open(self, filename):
1454-
# First tries to open 'filename', then '$srctree/filename' if $srctree
1455-
# was set when the configuration was loaded
1474+
def _open_config(self, filename):
1475+
# Opens a .config file. First tries to open 'filename', then
1476+
# '$srctree/filename' if $srctree was set when the configuration was
1477+
# loaded.
14561478

14571479
try:
14581480
return self._open_enc(filename, _UNIVERSAL_NEWLINES_MODE)
14591481
except IOError as e:
1460-
if not os.path.isabs(filename) and self.srctree is not None:
1461-
filename = os.path.join(self.srctree, filename)
1462-
try:
1463-
return self._open_enc(filename, _UNIVERSAL_NEWLINES_MODE)
1464-
except IOError as e2:
1465-
# This is needed for Python 3, because e2 is deleted after
1466-
# the try block:
1467-
#
1468-
# https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
1469-
e = e2
1470-
1471-
raise IOError(textwrap.fill(
1472-
"Could not open '{}' ({}: {}). Perhaps the $srctree "
1473-
"environment variable (which was {}) is set incorrectly. Note "
1474-
"that the current value of $srctree is saved when the Kconfig "
1475-
"instance is created (for consistency and to cleanly "
1476-
"separate instances)."
1477-
.format(filename, errno.errorcode[e.errno], e.strerror,
1478-
"unset" if self.srctree is None else
1479-
'"{}"'.format(self.srctree)),
1482+
# This will try opening the same file twice if $srctree is unset,
1483+
# but it's not a big deal
1484+
try:
1485+
return self._open_enc(os.path.join(self.srctree, filename),
1486+
_UNIVERSAL_NEWLINES_MODE)
1487+
except IOError as e2:
1488+
# This is needed for Python 3, because e2 is deleted after
1489+
# the try block:
1490+
#
1491+
# https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
1492+
e = e2
1493+
1494+
raise IOError("\n" + textwrap.fill(
1495+
"Could not open '{}' ({}: {}){}".format(
1496+
filename, errno.errorcode[e.errno], e.strerror,
1497+
self._srctree_hint()),
14801498
80))
14811499

14821500
def _enter_file(self, filename):
@@ -1497,23 +1515,23 @@ def _enter_file(self, filename):
14971515
for name, linenr, _
14981516
in reversed(self._filestack))))
14991517

1518+
# Open 'filename' relative to $srctree
1519+
#
1520+
# Note: We already know that the file exists
1521+
1522+
full_filename = os.path.join(self.srctree, filename)
15001523
try:
1501-
self._file = self._open(filename)
1524+
self._file = self._open_enc(
1525+
full_filename, _UNIVERSAL_NEWLINES_MODE)
15021526
except IOError as e:
1503-
# Extend the error message a bit in this case
1504-
raise IOError(textwrap.fill(
1505-
"{}:{}: {} Also note that Kconfiglib expands references to "
1506-
"environment variables directly, "
1507-
"meaning you do not need \"bounce\" symbols with "
1508-
"'option env=\"FOO\"'. For compatibility with the C tools, "
1509-
"name the bounce symbols the same as the environment variable "
1510-
"they reference (like the Linux kernel does)."
1511-
.format(self._filename, self._linenr, e),
1512-
80))
1527+
raise IOError("{}:{}: Could not open '{}' ({}: {})".format(
1528+
self._filename, self._linenr, full_filename,
1529+
errno.errorcode[e.errno], e.strerror))
15131530

15141531
self._filename = filename
15151532
self._linenr = 0
15161533

1534+
15171535
def _leave_file(self):
15181536
# Returns from a Kconfig file to the file that sourced it
15191537

@@ -2158,40 +2176,34 @@ def _parse_block(self, end_token, parent, prev):
21582176
# Tricky Python semantics: This assign prev.next before prev
21592177
prev.next = prev = node
21602178

2161-
elif t0 == _T_SOURCE:
2162-
self._enter_file(self._expect_str_and_eol())
2163-
prev = self._parse_block(None, parent, prev)
2164-
self._leave_file()
2179+
elif t0 in (_T_SOURCE, _T_RSOURCE, _T_OSOURCE, _T_ORSOURCE):
2180+
pattern = self._expect_str_and_eol()
21652181

2166-
elif t0 == _T_RSOURCE:
2167-
self._enter_file(os.path.join(
2168-
os.path.dirname(self._filename),
2169-
self._expect_str_and_eol()
2170-
))
2171-
prev = self._parse_block(None, parent, prev)
2172-
self._leave_file()
2182+
# Check if the pattern is absolute and avoid stripping srctree
2183+
# from it below in that case. We must do the check before
2184+
# join()'ing, as srctree might be an absolute path.
2185+
isabs = os.path.isabs(pattern)
21732186

2174-
elif t0 in (_T_GSOURCE, _T_GRSOURCE):
2175-
pattern = self._expect_str_and_eol()
2176-
if t0 == _T_GRSOURCE:
2177-
# Relative gsource
2187+
if t0 in (_T_RSOURCE, _T_ORSOURCE):
2188+
# Relative source
21782189
pattern = os.path.join(os.path.dirname(self._filename),
21792190
pattern)
21802191

2181-
if self.srctree is None:
2182-
strip_srctree = False
2183-
else:
2184-
# $srctree set and pattern not absolute?
2185-
strip_srctree = not os.path.isabs(pattern)
2186-
2187-
# If $srctree is set, glob relative to it
2188-
pattern = os.path.join(self.srctree, pattern)
2189-
21902192
# Sort the glob results to ensure a consistent ordering of
21912193
# Kconfig symbols, which indirectly ensures a consistent
21922194
# ordering in e.g. .config files
2193-
for filename in sorted(glob.iglob(pattern)):
2194-
if strip_srctree:
2195+
filenames = \
2196+
sorted(glob.iglob(os.path.join(self.srctree, pattern)))
2197+
2198+
if not filenames and t0 in (_T_SOURCE, _T_RSOURCE):
2199+
raise KconfigError("\n" + textwrap.fill(
2200+
"{}:{}: '{}' does not exist{}".format(
2201+
self._filename, self._linenr, pattern,
2202+
self._srctree_hint()),
2203+
80))
2204+
2205+
for filename in filenames:
2206+
if not isabs:
21952207
# Strip the $srctree prefix from the filename and let
21962208
# the normal $srctree logic find the file. This makes
21972209
# the globbed filenames appear without a $srctree
@@ -2965,6 +2977,16 @@ def _warn_redun_assign(self, msg, filename=None, linenr=None):
29652977
if self._warn_for_redun_assign:
29662978
self._warn(msg, filename, linenr)
29672979

2980+
def _srctree_hint(self):
2981+
# Hint printed when Kconfig files can't be found or .config files can't
2982+
# be opened
2983+
2984+
return ". Perhaps the $srctree environment variable (set to '{}') " \
2985+
"is set incorrectly. Note that the current value of $srctree " \
2986+
"is saved when the Kconfig instance is created (for " \
2987+
"consistency and to cleanly separate instances)." \
2988+
.format(self.srctree if self.srctree else "unset or blank")
2989+
29682990
class Symbol(object):
29692991
"""
29702992
Represents a configuration symbol:
@@ -5593,8 +5615,6 @@ def _shell_fn(kconf, args):
55935615
_T_EQUAL,
55945616
_T_GREATER,
55955617
_T_GREATER_EQUAL,
5596-
_T_GRSOURCE,
5597-
_T_GSOURCE,
55985618
_T_HELP,
55995619
_T_HEX,
56005620
_T_IF,
@@ -5612,6 +5632,8 @@ def _shell_fn(kconf, args):
56125632
_T_OPTION,
56135633
_T_OPTIONAL,
56145634
_T_OR,
5635+
_T_ORSOURCE,
5636+
_T_OSOURCE,
56155637
_T_PROMPT,
56165638
_T_RANGE,
56175639
_T_RSOURCE,
@@ -5656,8 +5678,8 @@ def _shell_fn(kconf, args):
56565678
"endif": _T_ENDIF,
56575679
"endmenu": _T_ENDMENU,
56585680
"env": _T_ENV,
5659-
"grsource": _T_GRSOURCE,
5660-
"gsource": _T_GSOURCE,
5681+
"grsource": _T_ORSOURCE, # Backwards compatibility
5682+
"gsource": _T_OSOURCE, # Backwards compatibility
56615683
"help": _T_HELP,
56625684
"hex": _T_HEX,
56635685
"if": _T_IF,
@@ -5670,6 +5692,8 @@ def _shell_fn(kconf, args):
56705692
"on": _T_ON,
56715693
"option": _T_OPTION,
56725694
"optional": _T_OPTIONAL,
5695+
"orsource": _T_ORSOURCE,
5696+
"osource": _T_OSOURCE,
56735697
"prompt": _T_PROMPT,
56745698
"range": _T_RANGE,
56755699
"rsource": _T_RSOURCE,
@@ -5691,12 +5715,12 @@ def _shell_fn(kconf, args):
56915715
_T_BOOL,
56925716
_T_CHOICE,
56935717
_T_COMMENT,
5694-
_T_GRSOURCE,
5695-
_T_GSOURCE,
56965718
_T_HEX,
56975719
_T_INT,
56985720
_T_MAINMENU,
56995721
_T_MENU,
5722+
_T_ORSOURCE,
5723+
_T_OSOURCE,
57005724
_T_PROMPT,
57015725
_T_RSOURCE,
57025726
_T_SOURCE,

0 commit comments

Comments
 (0)