From daac69dc05217d024fbe021447629fa2b3d95b70 Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Tue, 13 Mar 2018 03:24:41 +0100 Subject: [PATCH] Add a globbing source statement 'gsource' works like 'source', but takes a glob pattern and sources all matching files. Works as a no-op if no files match, and hence doubles as an include-if-exists function, similar to '-include' in 'make'. Add a 'grsource' statement as well, mirroring 'rsource'. Came up in https://github.com/ulfalizer/Kconfiglib/pull/40. --- kconfiglib.py | 72 ++++++++++++++++++++++++++++++++-- tests/Klocation | 20 ++++++++++ tests/sub/Klocation_grsourced1 | 1 + tests/sub/Klocation_grsourced2 | 1 + tests/sub/Klocation_gsourced1 | 1 + tests/sub/Klocation_gsourced2 | 1 + testsuite.py | 8 +++- 7 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 tests/sub/Klocation_grsourced1 create mode 100644 tests/sub/Klocation_grsourced2 create mode 100644 tests/sub/Klocation_gsourced1 create mode 100644 tests/sub/Klocation_gsourced2 diff --git a/kconfiglib.py b/kconfiglib.py index 85a0054b..99bdf808 100644 --- a/kconfiglib.py +++ b/kconfiglib.py @@ -313,8 +313,13 @@ functions just avoid printing 'if y' conditions to give cleaner output. +Kconfig extensions +================== + +Kconfiglib implements two Kconfig extensions related to 'source': + 'source' with relative path -=========================== +--------------------------- The library implements a custom 'rsource' statement that allows to import Kconfig file by specifying path relative to directory of the currently parsed @@ -347,6 +352,26 @@ If absolute path is given to 'rsource' then it follows behavior of 'source'. +Globbed sourcing with 'gsource' and 'grsource' +---------------------------------------------- + +The 'gsource' statement works like 'source', but takes a glob pattern and +sources all matching Kconfig files. For example, the following statement might +source 'sub1/foofoofoo' and 'sub2/foobarfoo': + + gsource "sub[12]/foo*foo" + +The glob patterns accepted are the same as for the standard glob.glob() +function. + +If no file matches the pattern, gsource is a no-op, and hence doubles as an +include-if-exists function when given a plain filename (similar to '-include' +in 'make'). It might help to think of the 'g' as "generalized" in that case. + +'grsource' is the 'rsource' version of 'gsource' and globs relative to the +directory of the current Kconfig file. + + Feedback ======== @@ -354,6 +379,7 @@ service, or open a ticket on the GitHub page. """ import errno +import glob import os import platform import re @@ -1822,7 +1848,7 @@ def _parse_block(self, end_token, parent, visible_if_deps, prev_node): elif t0 == _T_SOURCE: self._enter_file(self._expand_syms(self._expect_str_and_eol())) - prev_node = self._parse_block(None, # end_token + prev_node = self._parse_block(None, # end_token parent, visible_if_deps, prev_node) @@ -1833,12 +1859,44 @@ def _parse_block(self, end_token, parent, visible_if_deps, prev_node): os.path.dirname(self._filename), self._expand_syms(self._expect_str_and_eol()) )) - prev_node = self._parse_block(None, # end_token + prev_node = self._parse_block(None, # end_token parent, visible_if_deps, prev_node) self._leave_file() + elif t0 in (_T_GSOURCE, _T_GRSOURCE): + pattern = self._expand_syms(self._expect_str_and_eol()) + if t0 == _T_GRSOURCE: + # Relative gsource + pattern = os.path.join(os.path.dirname(self._filename), + pattern) + + # If $srctree is set, glob relative to it + if self.srctree is not None: + pattern = os.path.join(self.srctree, pattern) + + # Sort the glob results to ensure a consistent ordering of + # Kconfig symbols, which indirectly ensures a consistent + # ordering in e.g. .config files + for filename in sorted(glob.glob(pattern)): + if self.srctree is not None and not os.path.isabs(filename): + # Strip the $srctree prefix from the filename and let + # the normal $srctree logic find the file. This makes + # the globbed filenames appear without a $srctree + # prefix in MenuNode.filename, which is consistent with + # how 'source' and 'rsource' work. We get the same + # behavior as if the files had been 'source'd one by + # one. + filename = os.path.relpath(filename, self.srctree) + + self._enter_file(filename) + prev_node = self._parse_block(None, # end_token + parent, + visible_if_deps, + prev_node) + self._leave_file() + elif t0 == end_token: # We have reached the end of the block. Terminate the final # node and return it. @@ -4699,6 +4757,8 @@ def _check_choice_sanity(choice): _T_EQUAL, _T_GREATER, _T_GREATER_EQUAL, + _T_GRSOURCE, + _T_GSOURCE, _T_HELP, _T_HEX, _T_IF, @@ -4725,7 +4785,7 @@ def _check_choice_sanity(choice): _T_TRISTATE, _T_UNEQUAL, _T_VISIBLE, -) = range(45) +) = range(47) # Public integers representing expression types # @@ -4759,6 +4819,8 @@ def _check_choice_sanity(choice): "endif": _T_ENDIF, "endmenu": _T_ENDMENU, "env": _T_ENV, + "grsource": _T_GRSOURCE, + "gsource": _T_GSOURCE, "help": _T_HELP, "hex": _T_HEX, "if": _T_IF, @@ -4792,6 +4854,8 @@ def _check_choice_sanity(choice): _T_BOOL, _T_CHOICE, _T_COMMENT, + _T_GRSOURCE, + _T_GSOURCE, _T_HEX, _T_INT, _T_MAINMENU, diff --git a/tests/Klocation b/tests/Klocation index aa176b26..99a8ee01 100644 --- a/tests/Klocation +++ b/tests/Klocation @@ -49,9 +49,29 @@ config _RSOURCED string default "_rsourced" +config _GSOURCED + string + default "_gsourced" + +config _GRSOURCED + string + default "_grsourced" + # Expands to "tests/Klocation_sourced" source "$TESTS_DIR_FROM_ENV/Klocation$_SOURCED" + # Expands to "sub/Klocation_rsourced" rsource "$SUB_DIR_FROM_ENV/Klocation$_RSOURCED" +# Expands to "tests/*ub/Klocation_gsourced[12]", matching +# tests/sub/Klocation_gsourced{1,2} +gsource "$TESTS_DIR_FROM_ENV/*ub/Klocation$_GSOURCED[12]" + +# Expands to "sub/Klocation_grsourced[12]", matching +# tests/sub/Klocation_grsourced{1,2} +grsource "$SUB_DIR_FROM_ENV/Klocation$_GRSOURCED[12]" + +# No-op +gsource "nonexisting file" + config MULTI_DEF diff --git a/tests/sub/Klocation_grsourced1 b/tests/sub/Klocation_grsourced1 new file mode 100644 index 00000000..49dac36e --- /dev/null +++ b/tests/sub/Klocation_grsourced1 @@ -0,0 +1 @@ +config MULTI_DEF diff --git a/tests/sub/Klocation_grsourced2 b/tests/sub/Klocation_grsourced2 new file mode 100644 index 00000000..49dac36e --- /dev/null +++ b/tests/sub/Klocation_grsourced2 @@ -0,0 +1 @@ +config MULTI_DEF diff --git a/tests/sub/Klocation_gsourced1 b/tests/sub/Klocation_gsourced1 new file mode 100644 index 00000000..49dac36e --- /dev/null +++ b/tests/sub/Klocation_gsourced1 @@ -0,0 +1 @@ +config MULTI_DEF diff --git a/tests/sub/Klocation_gsourced2 b/tests/sub/Klocation_gsourced2 new file mode 100644 index 00000000..49dac36e --- /dev/null +++ b/tests/sub/Klocation_gsourced2 @@ -0,0 +1 @@ +config MULTI_DEF diff --git a/testsuite.py b/testsuite.py index 4b3d0f20..b3f43b49 100644 --- a/testsuite.py +++ b/testsuite.py @@ -792,7 +792,7 @@ def verify_help(node, s): """) - print("Testing locations and 'source', 'rsource'") + print("Testing locations and source/rsource/gsource/grsource") def verify_locations(nodes, *expected_locs): verify(len(nodes) == len(expected_locs), @@ -823,7 +823,11 @@ def verify_locations(nodes, *expected_locs): "tests/Klocation:31", "tests/Klocation_sourced:3", "tests/sub/Klocation_rsourced:2", - "tests/Klocation:57") + "tests/sub/Klocation_gsourced1:1", + "tests/sub/Klocation_gsourced2:1", + "tests/sub/Klocation_grsourced1:1", + "tests/sub/Klocation_grsourced2:1", + "tests/Klocation:77") verify_locations(c.named_choices["CHOICE"].nodes, "tests/Klocation_sourced:5")