[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Minios-devel] [UNIKRAFT PATCH v3 14/23] kconfig: Import kconfiglib



Hi Sharan,

This patch looks ok.

-- Felipe

Reviewed-by: Felipe Huici <felipe.huici@xxxxxxxxx>

On 19.09.19, 14:08, "Minios-devel on behalf of Sharan Santhanam" 
<minios-devel-bounces@xxxxxxxxxxxxxxxxxxxx on behalf of 
Sharan.Santhanam@xxxxxxxxx> wrote:

    Import Kconfiglib for providing python based front end to the kconfig.
    
    URL: https://github.com/ulfalizer/Kconfiglib
    TAG: v12.13.0
    
    Signed-off-by: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
    ---
     support/kconfiglib/LICENSE.txt                     |    5 +
     support/kconfiglib/MANIFEST.in                     |    2 +
     support/kconfiglib/README.rst                      |  806 +++
     support/kconfiglib/alldefconfig.py                 |   27 +
     support/kconfiglib/allmodconfig.py                 |   46 +
     support/kconfiglib/allnoconfig.py                  |   45 +
     support/kconfiglib/allyesconfig.py                 |   56 +
     support/kconfiglib/defconfig.py                    |   43 +
     support/kconfiglib/examples/Kmenuconfig            |  102 +
     support/kconfiglib/examples/allnoconfig_walk.py    |   66 +
     support/kconfiglib/examples/defconfig_oldconfig.py |   39 +
     support/kconfiglib/examples/dumpvars.py            |   15 +
     support/kconfiglib/examples/eval_expr.py           |   24 +
     support/kconfiglib/examples/find_symbol.py         |  112 +
     support/kconfiglib/examples/help_grep.py           |   64 +
     support/kconfiglib/examples/kconfiglib.py          |    1 +
     support/kconfiglib/examples/list_undefined.py      |  156 +
     support/kconfiglib/examples/menuconfig_example.py  |  341 +
     support/kconfiglib/examples/merge_config.py        |  133 +
     support/kconfiglib/examples/print_config_tree.py   |  199 +
     support/kconfiglib/examples/print_sym_info.py      |   54 +
     support/kconfiglib/examples/print_tree.py          |   75 +
     support/kconfiglib/genconfig.py                    |  132 +
     support/kconfiglib/guiconfig.py                    | 2316 +++++++
     support/kconfiglib/kconfiglib.py                   | 7018 
++++++++++++++++++++
     support/kconfiglib/listnewconfig.py                |   47 +
     support/kconfiglib/makefile.patch                  |   48 +
     support/kconfiglib/menuconfig.py                   | 3264 +++++++++
     support/kconfiglib/oldconfig.py                    |  246 +
     support/kconfiglib/olddefconfig.py                 |   28 +
     support/kconfiglib/savedefconfig.py                |   49 +
     support/kconfiglib/setconfig.py                    |   92 +
     support/kconfiglib/setup.cfg                       |    7 +
     support/kconfiglib/setup.py                        |  100 +
     support/kconfiglib/tests/Kappend                   |   12 +
     support/kconfiglib/tests/Kassignable               |  230 +
     support/kconfiglib/tests/Kchoice                   |  198 +
     support/kconfiglib/tests/Kdefconfig_existent       |    8 +
     support/kconfiglib/tests/Kdefconfig_existent_but_n |   10 +
     support/kconfiglib/tests/Kdefconfig_nonexistent    |    5 +
     support/kconfiglib/tests/Kdefconfig_srctree        |    5 +
     support/kconfiglib/tests/Kdepcopy                  |  173 +
     support/kconfiglib/tests/Kdeploop0                 |    3 +
     support/kconfiglib/tests/Kdeploop1                 |    3 +
     support/kconfiglib/tests/Kdeploop10                |   48 +
     support/kconfiglib/tests/Kdeploop2                 |    3 +
     support/kconfiglib/tests/Kdeploop3                 |    3 +
     support/kconfiglib/tests/Kdeploop4                 |    7 +
     support/kconfiglib/tests/Kdeploop5                 |    7 +
     support/kconfiglib/tests/Kdeploop6                 |    6 +
     support/kconfiglib/tests/Kdeploop7                 |   11 +
     support/kconfiglib/tests/Kdeploop8                 |    8 +
     support/kconfiglib/tests/Kdeploop9                 |    7 +
     support/kconfiglib/tests/Kdirdep                   |   30 +
     support/kconfiglib/tests/Kescape                   |    3 +
     support/kconfiglib/tests/Keval                     |   34 +
     support/kconfiglib/tests/Kexpr_items               |   11 +
     support/kconfiglib/tests/Khelp                     |   50 +
     support/kconfiglib/tests/Kifremoval                |   99 +
     support/kconfiglib/tests/Kimply                    |  145 +
     support/kconfiglib/tests/Kinclude_path             |   12 +
     support/kconfiglib/tests/Kinclude_path_sourced_1   |   12 +
     support/kconfiglib/tests/Kinclude_path_sourced_2   |   11 +
     support/kconfiglib/tests/Kitemlists                |   36 +
     support/kconfiglib/tests/Klocation                 |   72 +
     support/kconfiglib/tests/Klocation_sourced         |   18 +
     support/kconfiglib/tests/Kmainmenu                 |    5 +
     support/kconfiglib/tests/Kmenuconfig               |   37 +
     support/kconfiglib/tests/Kmisc                     |   82 +
     support/kconfiglib/tests/Kmissingrsource           |    1 +
     support/kconfiglib/tests/Kmissingsource            |    1 +
     support/kconfiglib/tests/Korder                    |   35 +
     support/kconfiglib/tests/Kpreprocess               |  147 +
     support/kconfiglib/tests/Krange                    |  133 +
     support/kconfiglib/tests/Krecursive1               |    1 +
     support/kconfiglib/tests/Krecursive2               |    1 +
     support/kconfiglib/tests/Kreferenced               |   63 +
     support/kconfiglib/tests/Krelation                 |   36 +
     support/kconfiglib/tests/Krepr                     |   64 +
     support/kconfiglib/tests/Kstr                      |  293 +
     support/kconfiglib/tests/Kundef                    |   23 +
     support/kconfiglib/tests/Kuserfunctions            |   11 +
     support/kconfiglib/tests/Kvisibility               |  342 +
     support/kconfiglib/tests/config_indented           |    3 +
     support/kconfiglib/tests/config_set_bool           |    1 +
     support/kconfiglib/tests/config_set_string         |    1 +
     support/kconfiglib/tests/defconfig_1               |    0
     support/kconfiglib/tests/defconfig_2               |    0
     support/kconfiglib/tests/empty                     |    0
     support/kconfiglib/tests/kconfigfunctions.py       |   14 +
     support/kconfiglib/tests/reltest                   |   51 +
     support/kconfiglib/tests/sub/Kconfig_symlink_2     |    1 +
     support/kconfiglib/tests/sub/Kconfig_symlink_3     |    2 +
     support/kconfiglib/tests/sub/Klocation_grsourced1  |    1 +
     support/kconfiglib/tests/sub/Klocation_grsourced2  |    1 +
     support/kconfiglib/tests/sub/Klocation_gsourced1   |    1 +
     support/kconfiglib/tests/sub/Klocation_gsourced2   |    1 +
     support/kconfiglib/tests/sub/Klocation_rsourced    |    2 +
     support/kconfiglib/tests/sub/defconfig_in_sub      |    0
     support/kconfiglib/tests/sub/sub/Kconfig_symlink_1 |    2 +
     support/kconfiglib/tests/symlink                   |    1 +
     support/kconfiglib/testsuite.py                    | 3136 +++++++++
     102 files changed, 21531 insertions(+)
     create mode 100644 support/kconfiglib/LICENSE.txt
     create mode 100644 support/kconfiglib/MANIFEST.in
     create mode 100644 support/kconfiglib/README.rst
     create mode 100755 support/kconfiglib/alldefconfig.py
     create mode 100755 support/kconfiglib/allmodconfig.py
     create mode 100755 support/kconfiglib/allnoconfig.py
     create mode 100755 support/kconfiglib/allyesconfig.py
     create mode 100755 support/kconfiglib/defconfig.py
     create mode 100644 support/kconfiglib/examples/Kmenuconfig
     create mode 100644 support/kconfiglib/examples/allnoconfig_walk.py
     create mode 100644 support/kconfiglib/examples/defconfig_oldconfig.py
     create mode 100644 support/kconfiglib/examples/dumpvars.py
     create mode 100644 support/kconfiglib/examples/eval_expr.py
     create mode 100644 support/kconfiglib/examples/find_symbol.py
     create mode 100644 support/kconfiglib/examples/help_grep.py
     create mode 120000 support/kconfiglib/examples/kconfiglib.py
     create mode 100644 support/kconfiglib/examples/list_undefined.py
     create mode 100755 support/kconfiglib/examples/menuconfig_example.py
     create mode 100755 support/kconfiglib/examples/merge_config.py
     create mode 100644 support/kconfiglib/examples/print_config_tree.py
     create mode 100644 support/kconfiglib/examples/print_sym_info.py
     create mode 100644 support/kconfiglib/examples/print_tree.py
     create mode 100755 support/kconfiglib/genconfig.py
     create mode 100755 support/kconfiglib/guiconfig.py
     create mode 100644 support/kconfiglib/kconfiglib.py
     create mode 100755 support/kconfiglib/listnewconfig.py
     create mode 100644 support/kconfiglib/makefile.patch
     create mode 100755 support/kconfiglib/menuconfig.py
     create mode 100755 support/kconfiglib/oldconfig.py
     create mode 100755 support/kconfiglib/olddefconfig.py
     create mode 100755 support/kconfiglib/savedefconfig.py
     create mode 100755 support/kconfiglib/setconfig.py
     create mode 100644 support/kconfiglib/setup.cfg
     create mode 100644 support/kconfiglib/setup.py
     create mode 100644 support/kconfiglib/tests/Kappend
     create mode 100644 support/kconfiglib/tests/Kassignable
     create mode 100644 support/kconfiglib/tests/Kchoice
     create mode 100644 support/kconfiglib/tests/Kdefconfig_existent
     create mode 100644 support/kconfiglib/tests/Kdefconfig_existent_but_n
     create mode 100644 support/kconfiglib/tests/Kdefconfig_nonexistent
     create mode 100644 support/kconfiglib/tests/Kdefconfig_srctree
     create mode 100644 support/kconfiglib/tests/Kdepcopy
     create mode 100644 support/kconfiglib/tests/Kdeploop0
     create mode 100644 support/kconfiglib/tests/Kdeploop1
     create mode 100644 support/kconfiglib/tests/Kdeploop10
     create mode 100644 support/kconfiglib/tests/Kdeploop2
     create mode 100644 support/kconfiglib/tests/Kdeploop3
     create mode 100644 support/kconfiglib/tests/Kdeploop4
     create mode 100644 support/kconfiglib/tests/Kdeploop5
     create mode 100644 support/kconfiglib/tests/Kdeploop6
     create mode 100644 support/kconfiglib/tests/Kdeploop7
     create mode 100644 support/kconfiglib/tests/Kdeploop8
     create mode 100644 support/kconfiglib/tests/Kdeploop9
     create mode 100644 support/kconfiglib/tests/Kdirdep
     create mode 100644 support/kconfiglib/tests/Kescape
     create mode 100644 support/kconfiglib/tests/Keval
     create mode 100644 support/kconfiglib/tests/Kexpr_items
     create mode 100644 support/kconfiglib/tests/Khelp
     create mode 100644 support/kconfiglib/tests/Kifremoval
     create mode 100644 support/kconfiglib/tests/Kimply
     create mode 100644 support/kconfiglib/tests/Kinclude_path
     create mode 100644 support/kconfiglib/tests/Kinclude_path_sourced_1
     create mode 100644 support/kconfiglib/tests/Kinclude_path_sourced_2
     create mode 100644 support/kconfiglib/tests/Kitemlists
     create mode 100644 support/kconfiglib/tests/Klocation
     create mode 100644 support/kconfiglib/tests/Klocation_sourced
     create mode 100644 support/kconfiglib/tests/Kmainmenu
     create mode 100644 support/kconfiglib/tests/Kmenuconfig
     create mode 100644 support/kconfiglib/tests/Kmisc
     create mode 100644 support/kconfiglib/tests/Kmissingrsource
     create mode 100644 support/kconfiglib/tests/Kmissingsource
     create mode 100644 support/kconfiglib/tests/Korder
     create mode 100644 support/kconfiglib/tests/Kpreprocess
     create mode 100644 support/kconfiglib/tests/Krange
     create mode 100644 support/kconfiglib/tests/Krecursive1
     create mode 100644 support/kconfiglib/tests/Krecursive2
     create mode 100644 support/kconfiglib/tests/Kreferenced
     create mode 100644 support/kconfiglib/tests/Krelation
     create mode 100644 support/kconfiglib/tests/Krepr
     create mode 100644 support/kconfiglib/tests/Kstr
     create mode 100644 support/kconfiglib/tests/Kundef
     create mode 100644 support/kconfiglib/tests/Kuserfunctions
     create mode 100644 support/kconfiglib/tests/Kvisibility
     create mode 100644 support/kconfiglib/tests/config_indented
     create mode 100644 support/kconfiglib/tests/config_set_bool
     create mode 100644 support/kconfiglib/tests/config_set_string
     create mode 100644 support/kconfiglib/tests/defconfig_1
     create mode 100644 support/kconfiglib/tests/defconfig_2
     create mode 100644 support/kconfiglib/tests/empty
     create mode 100644 support/kconfiglib/tests/kconfigfunctions.py
     create mode 100755 support/kconfiglib/tests/reltest
     create mode 100644 support/kconfiglib/tests/sub/Kconfig_symlink_2
     create mode 100644 support/kconfiglib/tests/sub/Kconfig_symlink_3
     create mode 100644 support/kconfiglib/tests/sub/Klocation_grsourced1
     create mode 100644 support/kconfiglib/tests/sub/Klocation_grsourced2
     create mode 100644 support/kconfiglib/tests/sub/Klocation_gsourced1
     create mode 100644 support/kconfiglib/tests/sub/Klocation_gsourced2
     create mode 100644 support/kconfiglib/tests/sub/Klocation_rsourced
     create mode 100644 support/kconfiglib/tests/sub/defconfig_in_sub
     create mode 100644 support/kconfiglib/tests/sub/sub/Kconfig_symlink_1
     create mode 120000 support/kconfiglib/tests/symlink
     create mode 100644 support/kconfiglib/testsuite.py
    
    diff --git a/support/kconfiglib/LICENSE.txt b/support/kconfiglib/LICENSE.txt
    new file mode 100644
    index 0000000..8b31efc
    --- /dev/null
    +++ b/support/kconfiglib/LICENSE.txt
    @@ -0,0 +1,5 @@
    +Copyright (c) 2011-2019, Ulf Magnusson <ulfalizer@xxxxxxxxx>
    +
    +Permission to use, copy, modify, and/or distribute this software for any 
purpose with or without fee is hereby granted, provided that the above 
copyright notice and this permission notice appear in all copies.
    +
    +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 
USE OR PERFORMANCE OF THIS SOFTWARE.
    diff --git a/support/kconfiglib/MANIFEST.in b/support/kconfiglib/MANIFEST.in
    new file mode 100644
    index 0000000..881a794
    --- /dev/null
    +++ b/support/kconfiglib/MANIFEST.in
    @@ -0,0 +1,2 @@
    +# Include the license file in source distributions
    +include LICENSE.txt
    diff --git a/support/kconfiglib/README.rst b/support/kconfiglib/README.rst
    new file mode 100644
    index 0000000..bdbd457
    --- /dev/null
    +++ b/support/kconfiglib/README.rst
    @@ -0,0 +1,806 @@
    +.. contents:: Table of contents
    +   :backlinks: none
    +
    +Overview
    +--------
    +
    +Kconfiglib is a `Kconfig
    
+<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-language.rst>`__
    +implementation in Python 2/3. It started out as a helper library, but now 
has a
    +enough functionality to also work well as a standalone Kconfig 
implementation
    +(including `terminal and GUI menuconfig interfaces <Menuconfig 
interfaces_>`_
    +and `Kconfig extensions`_).
    +
    +The entire library is contained in `kconfiglib.py
    +<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_. The
    +bundled scripts are implemented on top of it. Implementing your own scripts
    +should be relatively easy, if needed.
    +
    +Kconfiglib is used exclusively by e.g. the `Zephyr 
<https://www.zephyrproject.org/>`_ 
    +and `ACRN <https://projectacrn.org/>`_ projects. It is also used for many 
small helper
    +scripts in various projects.
    +
    +Since Kconfiglib is based around a library, it can be used e.g. to 
generate a
    +`Kconfig cross-reference 
<https://docs.zephyrproject.org/latest/reference/kconfig/index.html>`_ (note: 
heavy page),
    +using the same robust Kconfig parser used for other Kconfig tools, instead 
of brittle ad-hoc parsing. The documentation generation script can be found 
`here 
<https://github.com/zephyrproject-rtos/zephyr/blob/master/doc/scripts/genrest.py>`__.
    +
    +Kconfiglib implements the recently added `Kconfig preprocessor
    
+<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-macro-language.rst>`__.
    +For backwards compatibility, environment variables can be referenced both 
as
    +``$(FOO)`` (the new syntax) and as ``$FOO`` (the old syntax). The old 
syntax is
    +deprecated, but will probably be supported for a long time, as its needed 
to
    +stay compatible with older Linux kernels. The major version will be 
increased
    +if support is ever dropped. Using the old syntax with an undefined 
environment
    +variable keeps the string as is.
    +
    +Note: See `this issue 
<https://github.com/ulfalizer/Kconfiglib/issues/47>`_ if you run into
    +a "macro expanded to blank string" error with kernel 4.18+.
    +
    +See `this page
    +<https://docs.zephyrproject.org/latest/guides/kconfig/index.html>`__ for 
some
    +Kconfig tips and best practices.
    +
    +Installation
    +------------
    +
    +Installation with pip
    +~~~~~~~~~~~~~~~~~~~~~
    +
    +Kconfiglib is available on `PyPI 
<https://pypi.python.org/pypi/kconfiglib/>`_ and can be
    +installed with e.g.
    +
    +.. code::
    +
    +    $ pip(3) install kconfiglib
    +
    +Microsoft Windows is supported.
    +
    +The ``pip`` installation will give you both the base library and the 
following
    +executables. All but two (``genconfig`` and ``setconfig``) mirror 
functionality
    +available in the C tools.
    +
    +- `menuconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_
    +
    +- `guiconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_
    +
    +- `oldconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/oldconfig.py>`_
    +
    +- `olddefconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/olddefconfig.py>`_
    +
    +- `savedefconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/savedefconfig.py>`_
    +
    +- `defconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/defconfig.py>`_
    +
    +- `alldefconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/alldefconfig.py>`_
    +
    +- `allnoconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/allnoconfig.py>`_
    +
    +- `allmodconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/allmodconfig.py>`_
    +
    +- `allyesconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_
    +
    +- `listnewconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/listnewconfig.py>`_
    +
    +- `genconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/genconfig.py>`_
    +
    +- `setconfig 
<https://github.com/ulfalizer/Kconfiglib/blob/master/setconfig.py>`_
    +
    +``genconfig`` is intended to be run at build time. It generates a C header 
from
    +the configuration and (optionally) information that can be used to rebuild 
only
    +files that reference Kconfig symbols that have changed value.
    +
    +Starting with Kconfiglib version 12.2.0, all utilities are compatible with 
both
    +Python 2 and Python 3. Previously, ``menuconfig.py`` only ran under Python 
3
    +(i.e., it's now more backwards compatible than before).
    +
    +**Note:** If you install Kconfiglib with ``pip``'s ``--user`` flag, make 
sure
    +that your ``PATH`` includes the directory where the executables end up. 
You can
    +list the installed files with ``pip(3) show -f kconfiglib``.
    +
    +All releases have a corresponding tag in the git repository, e.g. 
``v12.13.0``
    +(the latest version).
    +
    +`Semantic versioning <http://semver.org/>`_ is used. There's been
    +ten small changes (`1 
<https://github.com/ulfalizer/Kconfiglib/commit/e8b4ecb6ff6ccc1c7be0818314fbccda2ef2b2ee>`_,
    +`2 
<https://github.com/ulfalizer/Kconfiglib/commit/db633015a4d7b0ba1e882f665e191f350932b2af>`_,
    +`3 
<https://github.com/ulfalizer/Kconfiglib/commit/8983f7eb297dd614faf0beee3129559bc8ba338e>`_,
    +`4 
<https://github.com/ulfalizer/Kconfiglib/commit/cbf32e29a130d22bc734b7778e6304ac9df2a3e8>`_,
    +`5 
<https://github.com/ulfalizer/Kconfiglib/commit/eb6c21a9b33a2d6e2bed9882d4f930d0cab2f03b>`_,
    +`6 
<https://github.com/ulfalizer/Kconfiglib/commit/c19fc11355b13d75d97286402c7a933fb23d3b70>`_,
    +`7 
<https://github.com/ulfalizer/Kconfiglib/commit/7a428aa415606820a44291f475248b08e3952c4b>`_,
    +`8 
<https://github.com/ulfalizer/Kconfiglib/commit/f247ddf618ad29718e5efd3e69f8baf75d4d347b>`_,
    +`9 
<https://github.com/ulfalizer/Kconfiglib/commit/4fed39d9271ceb68be4157ab3f96a45b94f77dc0>`_,
    +`10 
<https://github.com/ulfalizer/Kconfiglib/commit/55bc8c380869ea663092212e8fe388ad7abae596>`_)
    +to the behavior of the API, which is why the major version is at 12 rather 
than
    +2. I do major version bumps for all behavior changes, even tiny ones, and 
most of these were
    +fixes for baby issues in the early days of the Kconfiglib 2 API.
    +
    +Manual installation
    +~~~~~~~~~~~~~~~~~~~
    +
    +Just drop ``kconfiglib.py`` and the scripts you want somewhere. There are 
no
    +third-party dependencies (except for the `windows-curses
    +<https://github.com/zephyrproject-rtos/windows-curses>`_ package on 
Windows,
    +when running the terminal ``menuconfig`` implementation).
    +
    +Installation for the Linux kernel
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +See the module docstring at the top of `kconfiglib.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
    +
    +Python version compatibility (2.7/3.2+)
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +Kconfiglib and all utilities run under both Python 2.7 and Python 3.2 and
    +later. The code mostly uses basic Python features and has no third-party
    +dependencies, so keeping it backwards-compatible is pretty low effort.
    +
    +The 3.2 requirement comes from ``argparse``. ``format()`` with unnumbered
    +``{}`` is used as well.
    +
    +A recent Python 3 version is recommended if you have a choice, as it'll 
give
    +you better Unicode handling.
    +
    +Getting started
    +---------------
    +
    +1. `Install <Installation_>`_ the library and the utilities.
    +
    +2. Write `Kconfig
    +   
<https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt>`__
    +   files that describe the available configuration options.
    +
    +3. Generate an initial configuration with e.g. 
``menuconfig``/``guiconfig`` or
    +   ``alldefconfig``. The configuration is saved as ``.config`` by default.
    +
    +   For more advanced projects, the ``defconfig`` utility can be used to
    +   generate the initial configuration from an existing configuration file.
    +   Usually, this existing configuration file would be a minimal 
configuration
    +   file, as generated by e.g. ``savedefconfig``.
    +
    +4. Run ``genconfig`` to generate a header file. By default, it is saved as
    +   ``config.h``.
    +
    +   Normally, ``genconfig`` would be run automatically as part of the build.
    +
    +   Before writing a header file or other configuration output, Kconfiglib
    +   compares the old contents of the file against the new contents. If 
there's
    +   no change, the write is skipped. This avoids updating file metadata 
like the
    +   modification time, and might save work depending on your build setup.
    +   
    +   Adding new configuration output formats should be relatively 
straightforward.
    +   See the implementation of ``write_config()`` in `kconfiglib.py
    +   <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
    +   The documentation for the ``Symbol.config_string`` property has some 
tips as
    +   well.
    +   
    +5. To update an old ``.config`` file after the Kconfig files have changed 
(e.g.
    +   to add new options), run ``oldconfig`` (prompts for values for new 
options)
    +   or ``olddefconfig`` (gives new options their default value). Entering 
the
    +   ``menuconfig`` or ``guiconfig`` interface and saving the configuration 
will
    +   also update it (the configuration interfaces always prompt for saving
    +   on exit if it would modify the contents of the ``.config`` file).
    +
    +   Due to Kconfig semantics, simply loading an old ``.config`` file 
performs an
    +   implicit ``olddefconfig``, so building will normally not be affected by
    +   having an outdated configuration.
    +
    +Whenever ``.config`` is overwritten, the previous version of the file is 
saved
    +to ``.config.old`` (or, more generally, to ``$KCONFIG_CONFIG.old``).
    +
    +For some general Kconfig advice, see `this page
    +<https://docs.zephyrproject.org/latest/guides/kconfig/index.html>`__.
    +
    +Using ``.config`` files as Make input
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +``.config`` files use Make syntax and can be included directly in 
Makefiles to
    +read configuration values from there. This is why ``n``-valued
    +``bool``/``tristate`` values are written out as ``# CONFIG_FOO is not 
set`` (a
    +Make comment) in ``.config``, allowing them to be tested with ``ifdef`` in
    +Make.
    +
    +If you make use of this, you might want to pass ``--config-out 
<filename>`` to
    +``genconfig`` and include the configuration file it generates instead of
    +including ``.config`` directly. This has the advantage that the generated
    +configuration file will always be a "full" configuration file, even if
    +``.config`` is outdated. Otherwise, it might be necessary to run
    +``old(def)config`` or ``menuconfig``/``guiconfig`` before rebuilding with 
an
    +outdated ``.config``.
    +
    +If you use ``--sync-deps`` to generate incremental build information, you 
can
    +include ``deps/auto.conf`` instead, which is also a full configuration 
file.
    +
    +Useful helper macros
    +~~~~~~~~~~~~~~~~~~~~
    +
    +The `include/linux/kconfig.h
    +<https://github.com/torvalds/linux/blob/master/include/linux/kconfig.h>`_
    +header in the Linux kernel defines some useful helper macros for testing
    +Kconfig configuration values.
    +
    +``IS_ENABLED()`` is generally useful, allowing configuration values to be
    +tested in ``if`` statements with no runtime overhead.
    +
    +Incremental building
    +~~~~~~~~~~~~~~~~~~~~
    +
    +See the docstring for ``Kconfig.sync_deps()`` in `kconfiglib.py
    +<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_ for 
hints
    +on implementing incremental builds (rebuilding just source files that 
reference
    +changed configuration values).
    +
    +Running the ``scripts/basic/fixdep.c`` tool from the kernel on the output 
of
    +``gcc -MD <source file>`` might give you an idea of how it all fits 
together.
    +
    +Library documentation
    +---------------------
    +
    +Kconfiglib comes with extensive documentation in the form of docstrings. 
To view it, run e.g.
    +the following command:
    +
    +.. code:: sh
    +
    +    $ pydoc(3) kconfiglib
    +
    +For HTML output, add ``-w``:
    +
    +.. code:: sh
    +
    +    $ pydoc(3) -w kconfiglib
    +
    +This will also work after installing Kconfiglib with ``pip(3)``.
    +
    +Documentation for the ``menuconfig`` and ``guiconfig`` interfaces can be 
viewed
    +in the same way:
    +
    +.. code:: sh
    +
    +    $ pydoc3 menuconfig/guiconfig
    +
    +A good starting point for learning the library is to read the module 
docstring
    +(which you could also just read directly at the beginning of `kconfiglib.py
    +<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_). It
    +gives an introduction to symbol values, the menu tree, and expressions.
    +
    +After reading the module docstring, a good next step is to read the 
``Kconfig``
    +class documentation, and then the documentation for the ``Symbol``, 
``Choice``,
    +and ``MenuNode`` classes.
    +
    +Please tell me if something is unclear or can be explained better.
    +
    +Library features
    +----------------
    +
    +Kconfiglib can do the following, among other things:
    +
    +- **Programmatically get and set symbol values**
    +
    +  See `allnoconfig.py
    +  <https://github.com/ulfalizer/Kconfiglib/blob/master/allnoconfig.py>`_ 
and
    +  `allyesconfig.py
    +  <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_,
    +  which are automatically verified to produce identical output to the 
standard
    +  ``make allnoconfig`` and ``make allyesconfig``.
    +
    +- **Read and write .config and defconfig files**
    +
    +  The generated ``.config`` and ``defconfig`` (minimal configuration) 
files are
    +  character-for-character identical to what the C implementation would 
generate
    +  (except for the header comment). The test suite relies on this, as it
    +  compares the generated files.
    +  
    +- **Write C headers**
    +
    +  The generated headers use the same format as 
``include/generated/autoconf.h``
    +  from the Linux kernel.
    +
    +- **Implement incremental builds**
    +
    +  This uses the same scheme as the ``include/config`` directory in the 
kernel:
    +  Symbols are translated into files that are touched when the symbol's 
value
    +  changes between builds, which can be used to avoid having to do a full
    +  rebuild whenever the configuration is changed.
    +
    +  See the ``sync_deps()`` function for more information.
    +
    +- **Inspect symbols**
    +
    +  Printing a symbol or other item (which calls ``__str__()``) returns its
    +  definition in Kconfig format. This also works for symbols defined in 
multiple
    +  locations.
    +
    +  A helpful ``__repr__()`` is  on all objects too.
    +
    +  All ``__str__()`` and ``__repr__()`` methods are deliberately implemented
    +  with just public APIs, so all symbol information can be fetched 
separately as
    +  well.
    +
    +- **Inspect expressions**
    +
    +  Expressions use a simple tuple-based format that can be processed 
manually
    +  if needed. Expression printing and evaluation functions are provided,
    +  implemented with public APIs.
    +
    +- **Inspect the menu tree**
    +
    +  The underlying menu tree is exposed, including submenus created 
implicitly
    +  from symbols depending on preceding symbols. This can be used e.g. to
    +  implement menuconfig-like functionality.
    +  
    +  See `menuconfig.py
    +  
<https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_/`guiconfig.py
    +  <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_ and 
the
    +  minimalistic `menuconfig_example.py
    +  
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/menuconfig_example.py>`_
    +  example.
    +
    +Kconfig extensions
    +~~~~~~~~~~~~~~~~~~
    +
    +The following Kconfig extensions are available:
    +
    +- ``source`` supports glob patterns and includes each matching file. A 
pattern
    +  is required to match at least one file.
    +
    +  A separate ``osource`` statement is available for cases where it's okay 
for
    +  the pattern to match no files (in which case ``osource`` turns into a 
no-op).
    +  
    +- A relative ``source`` statement (``rsource``) is available, where file 
paths
    +  are specified relative to the directory of the current Kconfig file. An
    +  ``orsource`` statement is available as well, analogous to ``osource``.
    +
    +- Preprocessor user functions can be defined in Python, which makes it 
simple
    +  to integrate information from existing Python tools into Kconfig (e.g. to
    +  have Kconfig symbols depend on hardware information stored in some other
    +  format).
    +
    +  See the *Kconfig extensions* section in the
    +  `kconfiglib.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_
    +  module docstring for more information.
    +
    +- ``def_int``, ``def_hex``, and ``def_string`` are available in addition to
    +  ``def_bool`` and ``def_tristate``, allowing ``int``, ``hex``, and 
``string``
    +  symbols to be given a type and a default at the same time.
    +
    +  These can be useful in projects that make use of symbols defined in 
multiple
    +  locations, and remove some Kconfig inconsistency.
    +  
    +- Environment variables are expanded directly in e.g. ``source`` and
    +  ``mainmenu`` statements, meaning ``option env`` symbols are redundant.
    +
    +  This is the standard behavior with the new `Kconfig preprocessor
    +  
<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-macro-language.txt>`__,
    +  which Kconfiglib implements.
    +
    +  ``option env`` symbols are accepted but ignored, which leads the caveat 
that
    +  they must have the same name as the environment variables they reference
    +  (Kconfiglib warns if the names differ). This keeps Kconfiglib compatible 
with
    +  older Linux kernels, where the name of the ``option env`` symbol always
    +  matched the environment variable. Compatibility with older Linux kernels 
is
    +  the main reason ``option env`` is still supported.
    +
    +  The C tools have dropped support for ``option env``.
    +
    +- Two extra optional warnings can be enabled by setting environment 
variables,
    +  covering cases that are easily missed when making changes to Kconfig 
files:
    +
    +  * ``KCONFIG_WARN_UNDEF``: If set to ``y``, warnings will be generated 
for all
    +    references to undefined symbols within Kconfig files. The only gotcha 
is
    +    that all hex literals must be prefixed with ``0x`` or ``0X``, to make 
it
    +    possible to distinguish them from symbol references.
    +
    +    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with 
many
    +    shared Kconfig files, leading to some safe undefined symbol references.
    +    ``KCONFIG_WARN_UNDEF`` is useful in projects that only have a single
    +    Kconfig tree though.
    +
    +    ``KCONFIG_STRICT`` is an older alias for this environment variable,
    +    supported for backwards compatibility.
    +
    +  * ``KCONFIG_WARN_UNDEF_ASSIGN``: If set to ``y``, warnings will be 
generated
    +    for all assignments to undefined symbols within ``.config`` files. By
    +    default, no such warnings are generated.
    +
    +    This warning can also be enabled/disabled by setting
    +    ``Kconfig.warn_assign_undef`` to ``True``/``False``.
    +
    +Other features
    +--------------
    +
    +- **Single-file implementation**
    +  
    +  The entire library is contained in `kconfiglib.py
    +  <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
    +
    +  The tools implemented on top of it are one file each.
    +
    +- **Robust and highly compatible with the C Kconfig tools**
    +  
    +  The `test suite 
<https://github.com/ulfalizer/Kconfiglib/blob/master/testsuite.py>`_
    +  automatically compares output from Kconfiglib and the C tools
    +  by diffing the generated ``.config`` files for the real kernel Kconfig 
and
    +  defconfig files, for all ARCHes.
    +  
    +  This currently involves comparing the output for 36 ARCHes and 498 
defconfig
    +  files (or over 18000 ARCH/defconfig combinations in "obsessive" test 
suite
    +  mode). All tests are expected to pass.
    +
    +  A comprehensive suite of selftests is included as well.
    +
    +- **Not horribly slow despite being a pure Python implementation**
    +  
    +  The `allyesconfig.py
    +  <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_
    +  script currently runs in about 1.3 seconds on the Linux kernel on a Core 
i7
    +  2600K (with a warm file cache), including the ``make`` overhead from 
``make
    +  scriptconfig``. Note that the Linux kernel Kconfigs are absolutely 
massive
    +  (over 14k symbols for x86) compared to most projects, and also have 
overhead
    +  from running shell commands via the Kconfig preprocessor.
    +  
    +  Kconfiglib is especially speedy in cases where multiple ``.config`` files
    +  need to be processed, because the ``Kconfig`` files will only need to be 
parsed
    +  once.
    +
    +  For long-running jobs, `PyPy <https://pypy.org/>`_ gives a big 
performance
    +  boost. CPython is faster for short-running jobs as PyPy needs some time 
to
    +  warm up.
    +  
    +  Kconfiglib also works well with the
    +  `multiprocessing 
<https://docs.python.org/3/library/multiprocessing.html>`_
    +  module. No global state is kept.
    +
    +- **Generates more warnings than the C implementation**
    +
    +  Generates the same warnings as the C implementation, plus additional 
ones.
    +  Also detects dependency and ``source`` loops.
    +
    +  All warnings point out the location(s) in the ``Kconfig`` files where a
    +  symbol is defined, where applicable.
    +
    +- **Unicode support**
    +
    +  Unicode characters in string literals in ``Kconfig`` and ``.config`` 
files are
    +  correctly handled. This support mostly comes for free from Python.
    +
    +- **Windows support**
    +
    +  Nothing Linux-specific is used. Universal newlines mode is used for both
    +  Python 2 and Python 3.
    +  
    +  The `Zephyr <https://www.zephyrproject.org/>`_ project uses Kconfiglib to
    +  generate ``.config`` files and C headers on Linux as well as Windows.
    +
    +- **Internals that (mostly) mirror the C implementation**
    +  
    +  While being simpler to understand and tweak.
    +
    +Menuconfig interfaces
    +---------------------
    +
    +Three configuration interfaces are currently available:
    +
    +- `menuconfig.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_
    +  is a terminal-based configuration interface implemented using the 
standard
    +  Python ``curses`` module. ``xconfig`` features like showing invisible 
symbols and
    +  showing symbol names are included, and it's possible to jump directly to 
a symbol
    +  in the menu tree (even if it's currently invisible).
    +  
    +  .. image:: 
https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/menuconfig.gif
    +
    +  *There is now also a show-help mode that shows the help text of the 
currently
    +  selected symbol in the help window at the bottom.*
    +
    +  Starting with Kconfiglib 12.2.0, ``menuconfig.py`` runs under both 
Python 2
    +  and Python 3 (previously, it only ran under Python 3, so this was a
    +  backport). Running it under Python 3 provides better support for Unicode 
text
    +  entry (``get_wch()`` is not available in the ``curses`` module on Python 
2).
    +
    +  There are no third-party dependencies on \*nix. On Windows,
    +  the ``curses`` modules is not available by default, but support
    +  can be added by installing the ``windows-curses`` package (which is
    +  installed automatically when Kconfiglib is installed via ``pip``
    +  on Windows):
    +  
    +  .. code-block:: shell
    +
    +      $ pip install windows-curses
    +
    +  This uses wheels built from `this repository
    +  <https://github.com/zephyrproject-rtos/windows-curses>`_, which is in 
turn
    +  based on Christoph Gohlke's `Python Extension Packages for Windows
    +  <https://www.lfd.uci.edu/~gohlke/pythonlibs/#curses>`_.
    +
    +  See the docstring at the top of `menuconfig.py
    +  <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_ for
    +  more information about the terminal menuconfig implementation.
    +
    +- `guiconfig.py
    +  <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_ is a
    +  graphical configuration interface written in `Tkinter
    +  <https://docs.python.org/3/library/tkinter.html>`_. Like 
``menuconfig.py``,
    +  it supports showing all symbols (with invisible symbols in red) and 
jumping
    +  directly to symbols. Symbol values can also be changed directly from the
    +  jump-to dialog.
    +
    +  When single-menu mode is enabled, a single menu is shown at a time, like 
in
    +  the terminal menuconfig. Only this mode distinguishes between symbols 
defined
    +  with ``config`` and symbols defined with ``menuconfig``.
    +
    +  ``guiconfig.py`` has been tested on X11, Windows, and macOS, and is
    +  compatible with both Python 2 and Python 3.
    +
    +  Despite being part of the Python standard library, ``tkinter`` often 
isn't
    +  included by default in Python installations on Linux. These commands will
    +  install it on a few different distributions:
    +
    +  - Ubuntu: ``sudo apt install python-tk``/``sudo apt install python3-tk``
    +
    +  - Fedora: ``dnf install python2-tkinter``/``dnf install python3-tkinter``
    +
    +  - Arch: ``sudo pacman -S tk``
    +
    +  - Clear Linux: ``sudo swupd bundle-add python3-tcl``
    +
    +  Screenshot below, with show-all mode enabled and the jump-to dialog open:
    +
    +  .. image:: 
https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/guiconfig.png
    +
    +  To avoid having to carry around a bunch of GIFs, the image data is 
embedded
    +  in ``guiconfig.py``. To use separate GIF files instead, change
    +  ``_USE_EMBEDDED_IMAGES`` to ``False`` in ``guiconfig.py``. The image 
files
    +  can be found in the `screenshots
    +  <https://github.com/ulfalizer/Kconfiglib/tree/screenshots/guiconfig>`_
    +  branch.
    +
    +  I did my best with the images, but some are definitely only art adjacent.
    +  Touch-ups are welcome. :)
    +
    +- `pymenuconfig <https://github.com/RomaVis/pymenuconfig>`_, built by 
`RomaVis
    +  <https://github.com/RomaVis>`_, is an older portable Python 2/3 TkInter
    +  menuconfig implementation.
    +
    +  Screenshot below:
    +
    +  .. image:: 
https://raw.githubusercontent.com/RomaVis/pymenuconfig/master/screenshot.PNG
    +
    +  While working on the terminal menuconfig implementation, I added a few 
APIs
    +  to Kconfiglib that turned out to be handy. ``pymenuconfig`` predates
    +  ``menuconfig.py`` and ``guiconfig.py``, and so didn't have them 
available.
    +  Blame me for any workarounds.
    +
    +Examples
    +--------
    +
    +Example scripts
    +~~~~~~~~~~~~~~~
    +
    +The `examples/ 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples>`_ directory 
contains some simple example scripts. Among these are the following ones. Make 
sure you run them with the latest version of Kconfiglib, as they might make use 
of newly added features.
    +
    +- `eval_expr.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/eval_expr.py>`_ 
evaluates an expression in the context of a configuration.
    +
    +- `find_symbol.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/find_symbol.py>`_ 
searches through expressions to find references to a symbol, also printing a 
"backtrace" with parents for each reference found.
    +
    +- `help_grep.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/help_grep.py>`_ 
searches for a string in all help texts.
    +
    +- `print_tree.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/print_tree.py>`_ 
prints a tree of all configuration items.
    +
    +- `print_config_tree.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/print_config_tree.py>`_
 is similar to ``print_tree.py``, but dumps the tree as it would appear in 
``menuconfig``, including values. This can be handy for visually diffing 
between ``.config`` files and different versions of ``Kconfig`` files.
    +
    +- `list_undefined.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/list_undefined.py>`_
 finds references to symbols that are not defined by any architecture in the 
Linux kernel.
    +
    +- `merge_config.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/merge_config.py>`_
 merges configuration fragments to produce a complete .config, similarly to 
``scripts/kconfig/merge_config.sh`` from the kernel.
    +
    +- `menuconfig_example.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/examples/menuconfig_example.py>`_
 implements a configuration interface that uses notation similar to ``make 
menuconfig``. It's deliberately kept as simple as possible to demonstrate just 
the core concepts.
    +
    +Real-world examples
    +~~~~~~~~~~~~~~~~~~~
    +
    +- `kconfig.py
    +  
<https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py>`_
    +  from the `Zephyr <https://www.zephyrproject.org/>`_ project handles
    +  ``.config`` and header file generation, also doing configuration fragment
    +  merging
    +
    +- `genrest.py
    +  
<https://github.com/zephyrproject-rtos/zephyr/blob/master/doc/scripts/genrest.py>`_
    +  generates a Kconfig symbol cross-reference, which can be viewed `here
    +  <http://docs.zephyrproject.org/reference/kconfig/index.html>`__
    +
    +- `CMake and IDE integration
    +  <https://github.com/espressif/esp-idf/tree/master/tools/kconfig_new>`_ 
from
    +  the ESP-IDF project, via a configuration server program.
    +
    +- `A script for turning on USB-related options
    +  
<https://github.com/google/syzkaller/blob/master/dashboard/config/kconfiglib-merge-usb-configs.py>`_,
    +  from the `syzkaller <https://github.com/google/syzkaller>`_ project.
    +
    +- `Various automated checks
    +  
<https://github.com/zephyrproject-rtos/ci-tools/blob/master/scripts/check_compliance.py>`_,
    +  including a check for references to undefined Kconfig symbols in source 
code.
    +  See the ``KconfigCheck`` class.
    +
    +- `Various utilities
    +  
<https://github.com/projectacrn/acrn-hypervisor/tree/master/scripts/kconfig>`_
    +  from the `ACRN <https://projectacrn.org/>`_ project
    +
    +These use the older Kconfiglib 1 API, which was clunkier and not as general
    +(functions instead of properties, no direct access to the menu structure or
    +properties, uglier ``__str__()`` output):
    +
    +- `genboardscfg.py 
<http://git.denx.de/?p=u-boot.git;a=blob;f=tools/genboardscfg.py;hb=HEAD>`_ 
from `Das U-Boot <http://www.denx.de/wiki/U-Boot>`_ generates some sort of 
legacy board database by pulling information from a newly added Kconfig-based 
configuration system (as far as I understand it :).
    +
    +- `gen-manual-lists.py 
<https://git.busybox.net/buildroot/tree/support/scripts/gen-manual-lists.py?id=5676a2deea896f38123b99781da0a612865adeb0>`_
 generated listings for an appendix in the `Buildroot <https://buildroot.org>`_ 
manual. (The listing has since been removed.)
    +
    +- `gen_kconfig_doc.py 
<https://github.com/espressif/esp-idf/blob/master/docs/gen-kconfig-doc.py>`_ 
from the `esp-idf <https://github.com/espressif/esp-idf>`_ project generates 
documentation from Kconfig files.
    +
    +- `SConf <https://github.com/CoryXie/SConf>`_ builds an interactive 
configuration interface (like ``menuconfig``) on top of Kconfiglib, for use 
e.g. with `SCons <scons.org>`_.
    +
    +- `kconfig-diff.py <https://gist.github.com/dubiousjim/5638961>`_ -- a 
script by `dubiousjim <https://github.com/dubiousjim>`_ that compares kernel 
configurations.
    +
    +- Originally, Kconfiglib was used in chapter 4 of my `master's thesis 
<http://liu.diva-portal.org/smash/get/diva2:473038/FULLTEXT01.pdf>`_ to 
automatically generate a "minimal" kernel for a given system. Parts of it 
bother me a bit now, but that's how it goes with old work.
    +
    +Sample ``make iscriptconfig`` session
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +The following log should give some idea of the functionality available in 
the API:
    +
    +.. code-block::
    +
    +    $ make iscriptconfig
    +    A Kconfig instance 'kconf' for the architecture x86 has been created.
    +    >>> kconf  # Calls Kconfig.__repr__()
    +    <configuration with 13711 symbols, main menu prompt "Linux/x86 
4.14.0-rc7 Kernel Configuration", srctree ".", config symbol prefix "CONFIG_", 
warnings enabled, undef. symbol assignment warnings disabled>
    +    >>> kconf.mainmenu_text  # Expanded main menu text
    +    'Linux/x86 4.14.0-rc7 Kernel Configuration'
    +    >>> kconf.top_node  # The implicit top-level menu
    +    <menu node for menu, prompt "Linux/x86 4.14.0-rc7 Kernel 
Configuration" (visibility y), deps y, 'visible if' deps y, has child, 
Kconfig:5>
    +    >>> kconf.top_node.list  # First child menu node
    +    <menu node for symbol SRCARCH, deps y, has next, Kconfig:7>
    +    >>> print(kconf.top_node.list)  # Calls MenuNode.__str__()
    +    config SRCARCH
    +           string
    +           option env="SRCARCH"
    +           default "x86"
    +    >>> sym = kconf.top_node.list.next.item  # Item contained in next menu 
node
    +    >>> print(sym)  # Calls Symbol.__str__()
    +    config 64BIT
    +           bool "64-bit kernel" if ARCH = "x86"
    +           default ARCH != "i386"
    +           help
    +             Say yes to build a 64-bit kernel - formerly known as x86_64
    +             Say no to build a 32-bit kernel - formerly known as i386
    +    >>> sym  # Calls Symbol.__repr__()
    +    <symbol 64BIT, bool, "64-bit kernel", value y, visibility y, direct 
deps y, arch/x86/Kconfig:2>
    +    >>> sym.assignable  # Currently assignable values (0, 1, 2 = n, m, y)
    +    (0, 2)
    +    >>> sym.set_value(0)  # Set it to n
    +    True
    +    >>> sym.tri_value  # Check the new value
    +    0
    +    >>> sym = kconf.syms["X86_MPPARSE"]  # Look up symbol by name
    +    >>> print(sym)
    +    config X86_MPPARSE
    +           bool "Enable MPS table" if (ACPI || SFI) && X86_LOCAL_APIC
    +           default y if X86_LOCAL_APIC
    +           help
    +             For old smp systems that do not have proper acpi support. 
Newer systems
    +             (esp with 64bit cpus) with acpi support, MADT and DSDT will 
override it
    +    >>> default = sym.defaults[0]  # Fetch its first default
    +    >>> sym = default[1]  # Fetch the default's condition (just a Symbol 
here)
    +    >>> print(sym)
    +    config X86_LOCAL_APIC
    +           bool
    +           default y
    +           select IRQ_DOMAIN_HIERARCHY
    +           select PCI_MSI_IRQ_DOMAIN if PCI_MSI
    +           depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC 
|| PCI_MSI
    +    >>> sym.nodes  # Show the MenuNode(s) associated with it
    +    [<menu node for symbol X86_LOCAL_APIC, deps n, has next, 
arch/x86/Kconfig:1015>]
    +    >>> kconfiglib.expr_str(sym.defaults[0][1])  # Print the default's 
condition
    +    'X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI'
    +    >>> kconfiglib.expr_value(sym.defaults[0][1])  # Evaluate it (0 = n)
    +    0
    +    >>> kconf.syms["64BIT"].set_value(2)
    +    True
    +    >>> kconfiglib.expr_value(sym.defaults[0][1])  # Evaluate it again (2 
= y)
    +    2
    +    >>> kconf.write_config("myconfig")  # Save a .config
    +    >>> ^D
    +    $ cat myconfig
    +    # Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
    +    CONFIG_64BIT=y
    +    CONFIG_X86_64=y
    +    CONFIG_X86=y
    +    CONFIG_INSTRUCTION_DECODER=y
    +    CONFIG_OUTPUT_FORMAT="elf64-x86-64"
    +    CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig"
    +    CONFIG_LOCKDEP_SUPPORT=y
    +    CONFIG_STACKTRACE_SUPPORT=y
    +    CONFIG_MMU=y
    +    ...
    + 
    +Test suite
    +----------
    +
    +The test suite is run with
    +
    +.. code::
    +
    +    $ python(3) Kconfiglib/testsuite.py
    +    
    +`pypy <https://pypy.org/>`_ works too, and is much speedier for everything 
except ``allnoconfig.py``/``allnoconfig_simpler.py``/``allyesconfig.py``, where 
it doesn't have time to warm up since
    +the scripts are run via ``make scriptconfig``.
    +
    +The test suite must be run from the top-level kernel directory. It 
requires that the
    +Kconfiglib git repository has been cloned into it and that the makefile 
patch has been applied.
    +
    +To get rid of warnings generated for the kernel ``Kconfig`` files, add 
``2>/dev/null`` to the command to
    +discard ``stderr``.
    +
    +**NOTE: Forgetting to apply the Makefile patch will cause some tests that 
compare generated configurations to fail**
    +
    +**NOTE: The test suite overwrites .config in the kernel root, so make sure 
to back it up.**
    +
    +The test suite consists of a set of selftests and a set of compatibility 
tests that
    +compare configurations generated by Kconfiglib with
    +configurations generated by the C tools, for a number of cases. See
    +`testsuite.py 
<https://github.com/ulfalizer/Kconfiglib/blob/master/testsuite.py>`_
    +for the available options.
    +
    +The `tests/reltest 
<https://github.com/ulfalizer/Kconfiglib/blob/master/tests/reltest>`_ script 
runs the test suite
    +and all the example scripts for both Python 2 and Python 3, verifying that 
everything works.
    +
    +Rarely, the output from the C tools is changed slightly (most recently due 
to a
    +`change <https://www.spinics.net/lists/linux-kbuild/msg17074.html>`_ I 
added).
    +If you get test suite failures, try running the test suite again against 
the
    +`linux-next tree <https://www.kernel.org/doc/man-pages/linux-next.html>`_,
    +which has all the latest changes. I will make it clear if any
    +non-backwards-compatible changes appear.
    +
    +A lot of time is spent waiting around for ``make`` and the C utilities 
(which need to reparse all the
    +Kconfig files for each defconfig test). Adding some multiprocessing to the 
test suite would make sense
    +too.
    +
    +Notes
    +-----
    +
    +* This is version 2 of Kconfiglib, which is not backwards-compatible with
    +  Kconfiglib 1. A summary of changes between Kconfiglib 1 and Kconfiglib
    +  2 can be found `here
    +  
<https://github.com/ulfalizer/Kconfiglib/blob/screenshots/kconfiglib-2-changes.txt>`__.
    +
    +* I sometimes see people add custom output formats, which is pretty
    +  straightforward to do (see the implementations of ``write_autoconf()`` 
and
    +  ``write_config()`` for a template, and also the documentation of the
    +  ``Symbol.config_string`` property). If you come up with something you 
think
    +  might be useful to other people, I'm happy to take it in upstream. 
Batteries
    +  included and all that.
    +
    +* Kconfiglib assumes the modules symbol is ``MODULES``, which is 
backwards-compatible.
    +  A warning is printed by default if ``option modules`` is set on some 
other symbol.
    +  
    +  Let me know if you need proper ``option modules`` support. It wouldn't 
be that
    +  hard to add.
    +
    +Thanks
    +------
    +
    +- To `RomaVis <https://github.com/RomaVis>`_, for making
    +  `pymenuconfig <https://github.com/RomaVis/pymenuconfig>`_ and suggesting
    +  the ``rsource`` keyword.
    +
    +- To `Mitja Horvat <https://github.com/pinkfluid>`_, for adding support
    +  for user-defined styles to the terminal menuconfig.
    +
    +- To `Philip Craig <https://github.com/philipc>`_ for adding
    +  support for the ``allnoconfig_y`` option and fixing an obscure issue
    +  with ``comment``\s inside ``choice``\s (that didn't affect correctness 
but
    +  made outputs differ). ``allnoconfig_y`` is used to force certain symbols
    +  to ``y`` during ``make allnoconfig`` to improve coverage.
    +
    +License
    +-------
    +
    +See `LICENSE.txt 
<https://github.com/ulfalizer/Kconfiglib/blob/master/LICENSE.txt>`_. SPDX 
license identifiers are used in the
    +source code.
    diff --git a/support/kconfiglib/alldefconfig.py 
b/support/kconfiglib/alldefconfig.py
    new file mode 100755
    index 0000000..5082fcb
    --- /dev/null
    +++ b/support/kconfiglib/alldefconfig.py
    @@ -0,0 +1,27 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Writes a configuration file where all symbols are set to their their 
default
    +values.
    +
    +The default output filename is '.config'. A different filename can be 
passed in
    +the KCONFIG_CONFIG environment variable.
    +
    +Usage for the Linux kernel:
    +
    +  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/alldefconfig.py
    +"""
    +import kconfiglib
    +
    +
    +def main():
    +    kconf = kconfiglib.standard_kconfig()
    +    kconfiglib.load_allconfig(kconf, "alldef.config")
    +    print(kconf.write_config())
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/allmodconfig.py 
b/support/kconfiglib/allmodconfig.py
    new file mode 100755
    index 0000000..9ae0b3b
    --- /dev/null
    +++ b/support/kconfiglib/allmodconfig.py
    @@ -0,0 +1,46 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Writes a configuration file where as many symbols as possible are set to 
'm'.
    +
    +The default output filename is '.config'. A different filename can be 
passed
    +in the KCONFIG_CONFIG environment variable.
    +
    +Usage for the Linux kernel:
    +
    +  $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/allmodconfig.py
    +"""
    +import kconfiglib
    +
    +
    +def main():
    +    kconf = kconfiglib.standard_kconfig()
    +
    +    # See allnoconfig.py
    +    kconf.warn = False
    +
    +    for sym in kconf.unique_defined_syms:
    +        if sym.orig_type == kconfiglib.BOOL:
    +            # 'bool' choice symbols get their default value, as determined 
by
    +            # e.g. 'default's on the choice
    +            if not sym.choice:
    +                # All other bool symbols get set to 'y', like for 
allyesconfig
    +                sym.set_value(2)
    +        elif sym.orig_type == kconfiglib.TRISTATE:
    +            sym.set_value(1)
    +
    +    for choice in kconf.unique_choices:
    +        choice.set_value(2 if choice.orig_type == kconfiglib.BOOL else 1)
    +
    +    kconf.warn = True
    +
    +    kconfiglib.load_allconfig(kconf, "allmod.config")
    +
    +    print(kconf.write_config())
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/allnoconfig.py 
b/support/kconfiglib/allnoconfig.py
    new file mode 100755
    index 0000000..e162ccb
    --- /dev/null
    +++ b/support/kconfiglib/allnoconfig.py
    @@ -0,0 +1,45 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Writes a configuration file where as many symbols as possible are set to 
'n'.
    +
    +The default output filename is '.config'. A different filename can be 
passed
    +in the KCONFIG_CONFIG environment variable.
    +
    +Usage for the Linux kernel:
    +
    +  $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/allmodconfig.py
    +
    +See the examples/allnoconfig_walk.py example script for another way to
    +implement this script.
    +"""
    +import kconfiglib
    +
    +
    +def main():
    +    kconf = kconfiglib.standard_kconfig()
    +
    +    # Avoid warnings that would otherwise get printed by Kconfiglib for the
    +    # following:
    +    #
    +    # 1. Assigning a value to a symbol without a prompt, which never has 
any
    +    #    effect
    +    #
    +    # 2. Assigning values invalid for the type (only bool/tristate symbols
    +    #    accept 0/1/2, for n/m/y). The assignments will be ignored for 
other
    +    #    symbol types, which is what we want.
    +    kconf.warn = False
    +    for sym in kconf.unique_defined_syms:
    +        sym.set_value(2 if sym.is_allnoconfig_y else 0)
    +    kconf.warn = True
    +
    +    kconfiglib.load_allconfig(kconf, "allno.config")
    +
    +    print(kconf.write_config())
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/allyesconfig.py 
b/support/kconfiglib/allyesconfig.py
    new file mode 100755
    index 0000000..e19ab31
    --- /dev/null
    +++ b/support/kconfiglib/allyesconfig.py
    @@ -0,0 +1,56 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Writes a configuration file where as many symbols as possible are set to 
'y'.
    +
    +The default output filename is '.config'. A different filename can be 
passed
    +in the KCONFIG_CONFIG environment variable.
    +
    +Usage for the Linux kernel:
    +
    +  $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/allmodconfig.py
    +"""
    +import kconfiglib
    +
    +
    +def main():
    +    kconf = kconfiglib.standard_kconfig()
    +
    +    # See allnoconfig.py
    +    kconf.warn = False
    +
    +    # Try to set all symbols to 'y'. Dependencies might truncate the value 
down
    +    # later, but this will at least give the highest possible value.
    +    #
    +    # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex
    +    # symbols still take a string, because they preserve formatting).
    +    for sym in kconf.unique_defined_syms:
    +        # Set choice symbols to 'm'. This value will be ignored for 
choices in
    +        # 'y' mode (the "normal" mode), which will instead just get their
    +        # default selection, but will set all symbols in m-mode choices to 
'm',
    +        # which is as high as they can go.
    +        #
    +        # Here's a convoluted example of how you might get an m-mode choice
    +        # even during allyesconfig:
    +        #
    +        #   choice
    +        #           tristate "weird choice"
    +        #           depends on m
    +        sym.set_value(1 if sym.choice else 2)
    +
    +    # Set all choices to the highest possible mode
    +    for choice in kconf.unique_choices:
    +        choice.set_value(2)
    +
    +    kconf.warn = True
    +
    +    kconfiglib.load_allconfig(kconf, "allyes.config")
    +
    +    print(kconf.write_config())
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/defconfig.py 
b/support/kconfiglib/defconfig.py
    new file mode 100755
    index 0000000..d1b1e4e
    --- /dev/null
    +++ b/support/kconfiglib/defconfig.py
    @@ -0,0 +1,43 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Reads a specified configuration file, then writes a new configuration file.
    +This can be used to initialize the configuration from e.g. an arch-specific
    +configuration file. This input configuration file would usually be a 
minimal
    +configuration file, as generated by e.g. savedefconfig.
    +
    +The default output filename is '.config'. A different filename can be 
passed in
    +the KCONFIG_CONFIG environment variable.
    +"""
    +import argparse
    +
    +import kconfiglib
    +
    +
    +def main():
    +    parser = argparse.ArgumentParser(
    +        formatter_class=argparse.RawDescriptionHelpFormatter,
    +        description=__doc__)
    +
    +    parser.add_argument(
    +        "--kconfig",
    +        default="Kconfig",
    +        help="Base Kconfig file (default: Kconfig)")
    +
    +    parser.add_argument(
    +        "config",
    +        metavar="CONFIGURATION",
    +        help="Input configuration file")
    +
    +    args = parser.parse_args()
    +
    +    kconf = kconfiglib.Kconfig(args.kconfig)
    +    print(kconf.load_config(args.config))
    +    print(kconf.write_config())
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/examples/Kmenuconfig 
b/support/kconfiglib/examples/Kmenuconfig
    new file mode 100644
    index 0000000..f1cb67b
    --- /dev/null
    +++ b/support/kconfiglib/examples/Kmenuconfig
    @@ -0,0 +1,102 @@
    +mainmenu "Example Kconfig configuration"
    +
    +config MODULES
    +   bool "Enable loadable module support"
    +   option modules
    +   default y
    +
    +menu "Bool and tristate symbols"
    +
    +config BOOL
    +   bool "Bool symbol"
    +   default y
    +
    +config BOOL_DEP
    +   bool "Dependent bool symbol"
    +   depends on BOOL
    +
    +# Mix it up a bit with an 'if' instead of a 'depends on'
    +if BOOL
    +
    +config TRI_DEP
    +   tristate "Dependent tristate symbol"
    +   select SELECTED_BY_TRI_DEP
    +   imply IMPLIED_BY_TRI_DEP
    +
    +endif
    +
    +config TWO_MENU_NODES
    +   bool "First prompt"
    +   depends on BOOL
    +
    +config TRI
    +   tristate "Tristate symbol"
    +
    +config TWO_MENU_NODES
    +   bool "Second prompt"
    +
    +comment "These are selected by TRI_DEP"
    +
    +config SELECTED_BY_TRI_DEP
    +   tristate "Tristate selected by TRI_DEP"
    +
    +config IMPLIED_BY_TRI_DEP
    +   tristate "Tristate implied by TRI_DEP"
    +
    +endmenu
    +
    +
    +menu "String, int, and hex symbols"
    +
    +config STRING
    +   string "String symbol"
    +   default "foo"
    +
    +config INT
    +   int "Int symbol"
    +   default 747
    +
    +config HEX
    +   hex "Hex symbol"
    +   default 0xABC
    +
    +endmenu
    +
    +
    +menu "Various choices"
    +
    +choice BOOL_CHOICE
    +   bool "Bool choice"
    +
    +config BOOL_CHOICE_SYM_1
    +   bool "Bool choice sym 1"
    +
    +config BOOL_CHOICE_SYM_2
    +   bool "Bool choice sym 2"
    +
    +endchoice
    +
    +choice TRI_CHOICE
    +   tristate "Tristate choice"
    +
    +config TRI_CHOICE_SYM_1
    +   tristate "Tristate choice sym 1"
    +
    +config TRI_CHOICE_SYM_2
    +   tristate "Tristate choice sym 2"
    +
    +endchoice
    +
    +choice OPT_BOOL_CHOICE
    +   bool "Optional bool choice"
    +   optional
    +
    +config OPT_BOOL_CHOICE_SYM_1
    +   bool "Optional bool choice sym 1"
    +
    +config OPT_BOOL_CHOICE_SYM_2
    +   bool "Optional bool choice sym 2"
    +
    +endchoice
    +
    +endmenu
    diff --git a/support/kconfiglib/examples/allnoconfig_walk.py 
b/support/kconfiglib/examples/allnoconfig_walk.py
    new file mode 100644
    index 0000000..5a8cc23
    --- /dev/null
    +++ b/support/kconfiglib/examples/allnoconfig_walk.py
    @@ -0,0 +1,66 @@
    +# This is tree-walking version of allnoconfig.py, for demonstration 
purposes.
    +# Verified by the test suite to generate identical output to 'make 
allnoconfig'
    +# for all ARCHes.
    +#
    +# Note: A more practical version would use Kconfig.node_iter(). The manual 
tree
    +# walking is for demonstration purposes.
    +#
    +# Usage for the Linux kernel:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/allnoconfig_walk.py
    +
    +import sys
    +
    +from kconfiglib import Kconfig, Symbol
    +
    +
    +def do_allnoconfig(node):
    +    global changed
    +
    +    # Walk the tree of menu nodes. You can imagine this as going down/into 
menu
    +    # entries in the menuconfig interface, setting each to n (or the lowest
    +    # assignable value).
    +
    +    while node:
    +        if isinstance(node.item, Symbol):
    +            sym = node.item
    +
    +            # Is the symbol a non-allnoconfig_y symbol that can be set to a
    +            # lower value than its current value?
    +            if (not sym.is_allnoconfig_y and
    +                sym.assignable and
    +                sym.assignable[0] < sym.tri_value):
    +
    +                # Yup, lower it
    +                sym.set_value(sym.assignable[0])
    +                changed = True
    +
    +        # Recursively lower children
    +        if node.list:
    +            do_allnoconfig(node.list)
    +
    +        node = node.next
    +
    +
    +# Parse the Kconfig files
    +kconf = Kconfig(sys.argv[1])
    +
    +# Do an initial pass to set 'option allnoconfig_y' symbols to y
    +for sym in kconf.unique_defined_syms:
    +    if sym.is_allnoconfig_y:
    +        sym.set_value(2)
    +
    +while True:
    +    # Changing later symbols in the configuration can sometimes allow 
earlier
    +    # symbols to be lowered, e.g. if a later symbol 'select's an earlier
    +    # symbol. To handle such situations, we do additional passes over the 
tree
    +    # until we're no longer able to change the value of any symbol in a 
pass.
    +    changed = False
    +
    +    do_allnoconfig(kconf.top_node)
    +
    +    # Did the pass change any symbols?
    +    if not changed:
    +        break
    +
    +print(kconf.write_config())
    diff --git a/support/kconfiglib/examples/defconfig_oldconfig.py 
b/support/kconfiglib/examples/defconfig_oldconfig.py
    new file mode 100644
    index 0000000..68336c6
    --- /dev/null
    +++ b/support/kconfiglib/examples/defconfig_oldconfig.py
    @@ -0,0 +1,39 @@
    +# Produces exactly the same output as the following script:
    +#
    +# make defconfig
    +# echo CONFIG_ETHERNET=n >> .config
    +# make oldconfig
    +# echo CONFIG_ETHERNET=y >> .config
    +# yes n | make oldconfig
    +#
    +# This came up in https://github.com/ulfalizer/Kconfiglib/issues/15.
    +#
    +# Usage:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/defconfig_oldconfig.py
    +
    +import sys
    +
    +import kconfiglib
    +
    +
    +kconf = kconfiglib.Kconfig(sys.argv[1])
    +
    +# Mirrors defconfig
    +kconf.load_config("arch/x86/configs/x86_64_defconfig")
    +kconf.write_config()
    +
    +# Mirrors the first oldconfig
    +kconf.load_config()
    +kconf.syms["ETHERNET"].set_value(0)
    +kconf.write_config()
    +
    +# Mirrors the second oldconfig
    +kconf.load_config()
    +kconf.syms["ETHERNET"].set_value(2)
    +for s in kconf.unique_defined_syms:
    +    if s.user_value is None and 0 in s.assignable:
    +        s.set_value(0)
    +
    +# Write the final configuration
    +print(kconf.write_config())
    diff --git a/support/kconfiglib/examples/dumpvars.py 
b/support/kconfiglib/examples/dumpvars.py
    new file mode 100644
    index 0000000..0f8ab43
    --- /dev/null
    +++ b/support/kconfiglib/examples/dumpvars.py
    @@ -0,0 +1,15 @@
    +# Prints all (set) environment variables referenced in the Kconfig files
    +# together with their values, as a list of assignments.
    +#
    +# Note: This only works for environment variables referenced via the $(FOO)
    +# preprocessor syntax. The older $FOO syntax is maintained for backwards
    +# compatibility.
    +
    +import os
    +import sys
    +
    +import kconfiglib
    +
    +
    +print(" ".join("{}='{}'".format(var, os.environ[var])
    +               for var in kconfiglib.Kconfig(sys.argv[1]).env_vars))
    diff --git a/support/kconfiglib/examples/eval_expr.py 
b/support/kconfiglib/examples/eval_expr.py
    new file mode 100644
    index 0000000..23eedb4
    --- /dev/null
    +++ b/support/kconfiglib/examples/eval_expr.py
    @@ -0,0 +1,24 @@
    +# Evaluates an expression (e.g. "X86_64 || (X86_32 && X86_LOCAL_APIC)") in 
the
    +# context of a configuration. Note that this always yields a tristate 
value (n,
    +# m, or y).
    +#
    +# Usage:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/eval_expr.py SCRIPT_ARG=<expr>
    +
    +import sys
    +
    +import kconfiglib
    +
    +
    +if len(sys.argv) < 3:
    +    sys.exit("Pass the expression to evaluate with 
SCRIPT_ARG=<expression>")
    +
    +kconf = kconfiglib.Kconfig(sys.argv[1])
    +expr = sys.argv[2]
    +
    +# Enable modules so that m doesn't get demoted to n
    +kconf.modules.set_value(2)
    +
    +print("the expression '{}' evaluates to {}"
    +      .format(expr, kconf.eval_string(expr)))
    diff --git a/support/kconfiglib/examples/find_symbol.py 
b/support/kconfiglib/examples/find_symbol.py
    new file mode 100644
    index 0000000..f747103
    --- /dev/null
    +++ b/support/kconfiglib/examples/find_symbol.py
    @@ -0,0 +1,112 @@
    +# Prints all menu nodes that reference a given symbol any of their 
properties
    +# or property conditions, along with their parent menu nodes.
    +#
    +# Usage:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/find_symbol.py SCRIPT_ARG=<name>
    +#
    +# Example output for SCRIPT_ARG=X86:
    +#
    +#   Found 470 locations that reference X86:
    +#
    +#   ========== Location 1 (init/Kconfig:1108) ==========
    +#
    +#   config SGETMASK_SYSCALL
    +#          bool
    +#          prompt "sgetmask/ssetmask syscalls support" if EXPERT
    +#          default PARISC || M68K || PPC || MIPS || X86 || SPARC || 
MICROBLAZE || SUPERH
    +#          help
    +#            sys_sgetmask and sys_ssetmask are obsolete system calls
    +#            no longer supported in libc but still enabled by default in 
some
    +#            architectures.
    +#
    +#            If unsure, leave the default option here.
    +#
    +#   ---------- Parent 1 (init/Kconfig:1077)  ----------
    +#
    +#   menuconfig EXPERT
    +#          bool
    +#          prompt "Configure standard kernel features (expert users)"
    +#          select DEBUG_KERNEL
    +#          help
    +#            This option allows certain base kernel options and settings
    +#            to be disabled or tweaked. This is for specialized
    +#            environments which can tolerate a "non-standard" kernel.
    +#            Only use this if you really know what you are doing.
    +#
    +#   ---------- Parent 2 (init/Kconfig:39)  ----------
    +#
    +#   menu "General setup"
    +#
    +#   ========== Location 2 (arch/Kconfig:29) ==========
    +#
    +#   config OPROFILE_EVENT_MULTIPLEX
    +#          bool
    +#          prompt "OProfile multiplexing support (EXPERIMENTAL)"
    +#          default "n"
    +#          depends on OPROFILE && X86
    +#          help
    +#            The number of hardware counters is limited. The multiplexing
    +#            feature enables OProfile to gather more events than counters
    +#            are provided by the hardware. This is realized by switching
    +#            between events at a user specified time interval.
    +#
    +#            If unsure, say N.
    +#
    +#   ---------- Parent 1 (arch/Kconfig:16)  ----------
    +#
    +#   config OPROFILE
    +#          tristate
    +#          prompt "OProfile system profiling"
    +#          select RING_BUFFER
    +#          select RING_BUFFER_ALLOW_SWAP
    +#          depends on PROFILING && HAVE_OPROFILE
    +#          help
    +#            OProfile is a profiling system capable of profiling the
    +#            whole system, include the kernel, kernel modules, libraries,
    +#            and applications.
    +#
    +#            If unsure, say N.
    +#
    +#   ---------- Parent 2 (init/Kconfig:39)  ----------
    +#
    +#   menu "General setup"
    +#
    +#   ... (tons more)
    +
    +import sys
    +
    +import kconfiglib
    +
    +
    +if len(sys.argv) < 3:
    +    sys.exit('Pass symbol name (without "CONFIG_" prefix) with 
SCRIPT_ARG=<name>')
    +
    +kconf = kconfiglib.Kconfig(sys.argv[1])
    +sym_name = sys.argv[2]
    +if sym_name not in kconf.syms:
    +    print("No symbol {} exists in the configuration".format(sym_name))
    +    sys.exit(0)
    +
    +referencing = [node for node in kconf.node_iter()
    +               if kconf.syms[sym_name] in node.referenced]
    +if not referencing:
    +    print("No references to {} found".format(sym_name))
    +    sys.exit(0)
    +
    +print("Found {} locations that reference {}:\n"
    +      .format(len(referencing), sym_name))
    +
    +for i, node in enumerate(referencing, 1):
    +    print("========== Location {} ({}:{}) ==========\n\n{}"
    +          .format(i, node.filename, node.linenr, node))
    +
    +    # Print the parents of the menu node too
    +
    +    node = node.parent
    +    parent_i = 1
    +    while node is not kconf.top_node:
    +        print("---------- Parent {} ({}:{})  ----------\n\n{}"
    +              .format(parent_i, node.filename, node.linenr, node))
    +        node = node.parent
    +        parent_i += 1
    diff --git a/support/kconfiglib/examples/help_grep.py 
b/support/kconfiglib/examples/help_grep.py
    new file mode 100644
    index 0000000..157d8f2
    --- /dev/null
    +++ b/support/kconfiglib/examples/help_grep.py
    @@ -0,0 +1,64 @@
    +# Does a case-insensitive search for a regular expression in the help 
texts of
    +# symbols and choices and the prompts of menus and comments. Prints the
    +# matching items together with their locations and the matching text.
    +#
    +# Usage:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=<regex>
    +#
    +# Shortened example output for SCRIPT_ARG=general:
    +#
    +#   menu "General setup"
    +#   location: init/Kconfig:39
    +#
    +#   config SYSVIPC
    +#          bool
    +#          prompt "System V IPC"
    +#          help
    +#            ...
    +#            exchange information. It is generally considered to be a good 
thing,
    +#            ...
    +#
    +#   location: init/Kconfig:233
    +#
    +#   config BSD_PROCESS_ACCT
    +#          bool
    +#          prompt "BSD Process Accounting" if MULTIUSER
    +#          help
    +#            ...
    +#            information.  This is generally a good idea, so say Y.
    +#
    +#   location: init/Kconfig:403
    +#
    +#   ...
    +
    +
    +import re
    +import sys
    +
    +from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
    +
    +
    +if len(sys.argv) < 3:
    +    sys.exit("Pass the regex with SCRIPT_ARG=<regex>")
    +
    +search = re.compile(sys.argv[2], re.IGNORECASE).search
    +
    +for node in Kconfig(sys.argv[1]).node_iter():
    +    match = False
    +
    +    if isinstance(node.item, (Symbol, Choice)) and \
    +       node.help is not None and search(node.help):
    +        print(node.item)
    +        match = True
    +
    +    elif node.item == MENU and search(node.prompt[0]):
    +        print('menu "{}"'.format(node.prompt[0]))
    +        match = True
    +
    +    elif node.item == COMMENT and search(node.prompt[0]):
    +        print('comment "{}"'.format(node.prompt[0]))
    +        match = True
    +
    +    if match:
    +        print("location: {}:{}\n".format(node.filename, node.linenr))
    diff --git a/support/kconfiglib/examples/kconfiglib.py 
b/support/kconfiglib/examples/kconfiglib.py
    new file mode 120000
    index 0000000..b9dfb64
    --- /dev/null
    +++ b/support/kconfiglib/examples/kconfiglib.py
    @@ -0,0 +1 @@
    +../kconfiglib.py
    \ No newline at end of file
    diff --git a/support/kconfiglib/examples/list_undefined.py 
b/support/kconfiglib/examples/list_undefined.py
    new file mode 100644
    index 0000000..4a3bc9b
    --- /dev/null
    +++ b/support/kconfiglib/examples/list_undefined.py
    @@ -0,0 +1,156 @@
    +# Prints a list of symbols that are referenced in the Kconfig files of some
    +# architecture but not defined by the Kconfig files of any architecture.
    +#
    +# A Kconfig file might be shared between many architectures and 
legitimately
    +# reference undefined symbols for some of them, but if no architecture 
defines
    +# the symbol, it usually indicates a problem or potential cleanup.
    +#
    +# This script could be sped up a lot if needed. See the comment near the
    +# referencing_nodes() call.
    +#
    +# Run with the following command in the kernel root:
    +#
    +#   $ python(3) Kconfiglib/examples/list_undefined.py
    +#
    +# Example output:
    +#
    +#   Registering defined and undefined symbols for all arches
    +#     Processing mips
    +#     Processing ia64
    +#     Processing metag
    +#     ...
    +#
    +#   Finding references to each undefined symbol
    +#     Processing mips
    +#     Processing ia64
    +#     Processing metag
    +#     ...
    +#
    +#   The following globally undefined symbols were found, listed here
    +#   together with the locations of the items that reference them.
    +#   References might come from enclosing menus and ifs.
    +#
    +#     ARM_ERRATA_753970: arch/arm/mach-mvebu/Kconfig:56, 
arch/arm/mach-mvebu/Kconfig:39
    +#     SUNXI_CCU_MP: drivers/clk/sunxi-ng/Kconfig:14
    +#     SUNXI_CCU_DIV: drivers/clk/sunxi-ng/Kconfig:14
    +#     AC97: sound/ac97/Kconfig:6
    +#     ...
    +
    +import os
    +import subprocess
    +
    +from kconfiglib import Kconfig
    +
    +
    +# Referenced inside the Kconfig files
    +os.environ["KERNELVERSION"] = str(
    +    subprocess.check_output(("make", 
"kernelversion")).decode("utf-8").rstrip()
    +)
    +
    +
    +def all_arch_srcarch_pairs():
    +    """
    +    Generates all valid (ARCH, SRCARCH) tuples for the kernel, 
corresponding to
    +    different architectures. SRCARCH holds the arch/ subdirectory.
    +    """
    +    for srcarch in os.listdir("arch"):
    +        # Each subdirectory of arch/ containing a Kconfig file corresponds 
to
    +        # an architecture
    +        if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
    +            yield (srcarch, srcarch)
    +
    +    # Some architectures define additional ARCH settings with ARCH != 
SRCARCH
    +    # (search for "Additional ARCH settings for" in the top-level Makefile)
    +
    +    yield ("i386", "x86")
    +    yield ("x86_64", "x86")
    +
    +    yield ("sparc32", "sparc")
    +    yield ("sparc64", "sparc")
    +
    +    yield ("sh64", "sh")
    +
    +    yield ("um", "um")
    +
    +
    +def all_arch_srcarch_kconfigs():
    +    """
    +    Generates Kconfig instances for all the architectures in the kernel
    +    """
    +
    +    os.environ["srctree"] = "."
    +    os.environ["HOSTCC"] = "gcc"
    +    os.environ["HOSTCXX"] = "g++"
    +    os.environ["CC"] = "gcc"
    +    os.environ["LD"] = "ld"
    +
    +    for arch, srcarch in all_arch_srcarch_pairs():
    +        print("  Processing " + arch)
    +
    +        os.environ["ARCH"] = arch
    +        os.environ["SRCARCH"] = srcarch
    +
    +        # um (User Mode Linux) uses a different base Kconfig file
    +        yield Kconfig("Kconfig" if arch != "um" else "arch/x86/um/Kconfig",
    +                      warn=False)
    +
    +
    +print("Registering defined and undefined symbols for all arches")
    +
    +# Sets holding the names of all defined and undefined symbols, for all
    +# architectures
    +defined = set()
    +undefined = set()
    +
    +for kconf in all_arch_srcarch_kconfigs():
    +    for name, sym in kconf.syms.items():
    +        if sym.nodes:
    +            # If the symbol has a menu node, it is defined
    +            defined.add(name)
    +        else:
    +            # Undefined symbol. We skip some of the uninteresting ones.
    +
    +            # Due to how Kconfig works, integer literals show up as symbols
    +            # (from e.g. 'default 1'). Skip those.
    +            try:
    +                int(name, 0)
    +                continue
    +            except ValueError:
    +                # Interesting undefined symbol
    +                undefined.add(name)
    +
    +
    +print("\nFinding references to each undefined symbol")
    +
    +def referencing_nodes(kconf, name):
    +    # Returns a list of all menu nodes that reference a symbol named 
'name' in
    +    # any of their properties or property conditions
    +    res = []
    +
    +    for node in kconf.node_iter():
    +        for ref in node.referenced:
    +            if ref.name == name:
    +                res.append(node)
    +
    +    return res
    +
    +
    +# Maps each globally undefined symbol to the menu nodes that reference it
    +undef_sym_refs = [(name, set()) for name in undefined - defined]
    +
    +for kconf in all_arch_srcarch_kconfigs():
    +    for name, refs in undef_sym_refs:
    +        # This means that we search the entire configuration tree for each
    +        # undefined symbol, which is terribly inefficient. We could speed
    +        # things up by tweaking referencing_nodes() to compare each symbol 
to
    +        # multiple symbols while walking the configuration tree.
    +        for node in referencing_nodes(kconf, name):
    +            refs.add("{}:{}".format(node.filename, node.linenr))
    +
    +
    +print("\nThe following globally undefined symbols were found, listed 
here\n"
    +      "together with the locations of the items that reference them.\n"
    +      "References might come from enclosing menus and ifs.\n")
    +
    +for name, refs in undef_sym_refs:
    +    print("  {}: {}".format(name, ", ".join(refs)))
    diff --git a/support/kconfiglib/examples/menuconfig_example.py 
b/support/kconfiglib/examples/menuconfig_example.py
    new file mode 100755
    index 0000000..a06b05e
    --- /dev/null
    +++ b/support/kconfiglib/examples/menuconfig_example.py
    @@ -0,0 +1,341 @@
    +#!/usr/bin/env python
    +
    +# Implements a simple configuration interface on top of Kconfiglib to
    +# demonstrate concepts for building a menuconfig-like. Emulates how the
    +# standard menuconfig prints menu entries.
    +#
    +# Always displays the entire Kconfig tree to keep things as simple as 
possible
    +# (all symbols, choices, menus, and comments).
    +#
    +# Usage:
    +#
    +#   $ python(3) Kconfiglib/examples/menuconfig.py <Kconfig file>
    +#
    +# A sample Kconfig is available in Kconfiglib/examples/Kmenuconfig.
    +#
    +# Here's a notation guide. The notation matches the one used by menuconfig
    +# (scripts/kconfig/mconf):
    +#
    +#   [ ] prompt      - Bool
    +#   < > prompt      - Tristate
    +#   {M} prompt      - Tristate selected to m. Can only be set to m or y.
    +#   -*- prompt      - Bool/tristate selected to y, pinning it
    +#   -M- prompt      - Tristate selected to m that also has m visibility,
    +#                     pinning it to m
    +#   (foo) prompt    - String/int/hex symbol with value "foo"
    +#   --> prompt      - The selected symbol in a choice in y mode. This
    +#                     syntax is unique to this example.
    +#
    +# When modules are disabled, the .type attribute of TRISTATE symbols and
    +# choices automatically changes to BOOL. This trick is used by the C
    +# implementation as well, and gives the expected behavior without having 
to do
    +# anything extra here. The original type is available in .orig_type if 
needed.
    +#
    +# The Kconfiglib/examples/Kmenuconfig example uses named choices to be 
able to
    +# refer to choices by name. Named choices are supported in the C tools 
too, but
    +# I don't think I've ever seen them used in the wild.
    +#
    +# Sample session:
    +#
    +#   $ python Kconfiglib/examples/menuconfig.py 
Kconfiglib/examples/Kmenuconfig
    +#
    +#   ======== Example Kconfig configuration ========
    +#
    +#   [*] Enable loadable module support (MODULES)
    +#       Bool and tristate symbols
    +#           [*] Bool symbol (BOOL)
    +#                   [ ] Dependent bool symbol (BOOL_DEP)
    +#                   < > Dependent tristate symbol (TRI_DEP)
    +#                   [ ] First prompt (TWO_MENU_NODES)
    +#           < > Tristate symbol (TRI)
    +#           [ ] Second prompt (TWO_MENU_NODES)
    +#               *** These are selected by TRI_DEP ***
    +#           < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
    +#           < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
    +#       String, int, and hex symbols
    +#           (foo) String symbol (STRING)
    +#           (747) Int symbol (INT)
    +#           (0xABC) Hex symbol (HEX)
    +#       Various choices
    +#           -*- Bool choice (BOOL_CHOICE)
    +#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
    +#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
    +#           {M} Tristate choice (TRI_CHOICE)
    +#                   < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
    +#                   < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
    +#           [ ] Optional bool choice (OPT_BOOL_CHOICE)
    +#
    +#   Enter a symbol/choice name, "load_config", or "write_config" (or press 
CTRL+D to exit): BOOL
    +#   Value for BOOL (available: n, y): n
    +#
    +#   ======== Example Kconfig configuration ========
    +#
    +#   [*] Enable loadable module support (MODULES)
    +#       Bool and tristate symbols
    +#           [ ] Bool symbol (BOOL)
    +#           < > Tristate symbol (TRI)
    +#           [ ] Second prompt (TWO_MENU_NODES)
    +#               *** These are selected by TRI_DEP ***
    +#           < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
    +#           < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
    +#       String, int, and hex symbols
    +#           (foo) String symbol (STRING)
    +#           (747) Int symbol (INT)
    +#           (0xABC) Hex symbol (HEX)
    +#       Various choices
    +#           -*- Bool choice (BOOL_CHOICE)
    +#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
    +#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
    +#           {M} Tristate choice (TRI_CHOICE)
    +#                   < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
    +#                   < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
    +#          [ ] Optional bool choice (OPT_BOOL_CHOICE)
    +#
    +#   Enter a symbol/choice name, "load_config", or "write_config" (or press 
CTRL+D to exit): MODULES
    +#   Value for MODULES (available: n, y): n
    +#
    +#   ======== Example Kconfig configuration ========
    +#
    +#   [ ] Enable loadable module support (MODULES)
    +#       Bool and tristate symbols
    +#           [ ] Bool symbol (BOOL)
    +#           [ ] Tristate symbol (TRI)
    +#           [ ] Second prompt (TWO_MENU_NODES)
    +#               *** These are selected by TRI_DEP ***
    +#           [ ] Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
    +#           [ ] Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
    +#       String, int, and hex symbols
    +#           (foo) String symbol (STRING)
    +#           (747) Int symbol (INT)
    +#           (0xABC) Hex symbol (HEX)
    +#       Various choices
    +#           -*- Bool choice (BOOL_CHOICE)
    +#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
    +#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
    +#           -*- Tristate choice (TRI_CHOICE)
    +#                   --> Tristate choice sym 1 (TRI_CHOICE_SYM_1)
    +#                       Tristate choice sym 2 (TRI_CHOICE_SYM_2)
    +#           [ ] Optional bool choice (OPT_BOOL_CHOICE)
    +#
    +#   Enter a symbol/choice name, "load_config", or "write_config" (or press 
CTRL+D to exit): ^D
    +
    +from __future__ import print_function
    +import readline
    +import sys
    +
    +from kconfiglib import Kconfig, \
    +                       Symbol, MENU, COMMENT, \
    +                       BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
    +                       expr_value, \
    +                       TRI_TO_STR
    +
    +
    +# Python 2/3 compatibility hack
    +if sys.version_info[0] < 3:
    +    input = raw_input
    +
    +
    +def indent_print(s, indent):
    +    print(indent*" " + s)
    +
    +
    +def value_str(sc):
    +    """
    +    Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
    +
    +    sc: Symbol or Choice.
    +    """
    +    if sc.type in (STRING, INT, HEX):
    +        return "({})".format(sc.str_value)
    +
    +    # BOOL or TRISTATE
    +
    +    # The choice mode is an upper bound on the visibility of choice 
symbols, so
    +    # we can check the choice symbols' own visibility to see if the choice 
is
    +    # in y mode
    +    if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
    +        # For choices in y mode, print '-->' next to the selected symbol
    +        return "-->" if sc.choice.selection is sc else "   "
    +
    +    tri_val_str = (" ", "M", "*")[sc.tri_value]
    +
    +    if len(sc.assignable) == 1:
    +        # Pinned to a single value
    +        return "-{}-".format(tri_val_str)
    +
    +    if sc.type == BOOL:
    +        return "[{}]".format(tri_val_str)
    +
    +    if sc.type == TRISTATE:
    +        if sc.assignable == (1, 2):
    +            # m and y available
    +            return "{" + tri_val_str + "}"  # Gets a bit confusing with 
.format()
    +        return "<{}>".format(tri_val_str)
    +
    +
    +def node_str(node):
    +    """
    +    Returns the complete menu entry text for a menu node, or "" for 
invisible
    +    menu nodes. Invisible menu nodes are those that lack a prompt or that 
do
    +    not have a satisfied prompt condition.
    +
    +    Example return value: "[*] Bool symbol (BOOL)"
    +
    +    The symbol name is printed in parentheses to the right of the prompt. 
This
    +    is so that symbols can easily be referred to in the configuration
    +    interface.
    +    """
    +    if not node.prompt:
    +        return ""
    +
    +    # Even for menu nodes for symbols and choices, it's wrong to check
    +    # Symbol.visibility / Choice.visibility here. The reason is that a 
symbol
    +    # (and a choice, in theory) can be defined in multiple locations, 
giving it
    +    # multiple menu nodes, which do not necessarily all have the same 
prompt
    +    # visibility. Symbol.visibility / Choice.visibility is calculated as 
the OR
    +    # of the visibility of all the prompts.
    +    prompt, prompt_cond = node.prompt
    +    if not expr_value(prompt_cond):
    +        return ""
    +
    +    if node.item == MENU:
    +        return "    " + prompt
    +
    +    if node.item == COMMENT:
    +        return "    *** {} ***".format(prompt)
    +
    +    # Symbol or Choice
    +
    +    sc = node.item
    +
    +    if sc.type == UNKNOWN:
    +        # Skip symbols defined without a type (these are obscure and 
generate
    +        # a warning)
    +        return ""
    +
    +    # {:3} sets the field width to three. Gives nice alignment for empty 
string
    +    # values.
    +    res = "{:3} {}".format(value_str(sc), prompt)
    +
    +    # Don't print the name for unnamed choices (the normal kind)
    +    if sc.name is not None:
    +        res += " ({})".format(sc.name)
    +
    +    return res
    +
    +
    +def print_menuconfig_nodes(node, indent):
    +    """
    +    Prints a tree with all the menu entries rooted at 'node'. Child menu
    +    entries are indented.
    +    """
    +    while node:
    +        string = node_str(node)
    +        if string:
    +            indent_print(string, indent)
    +
    +        if node.list:
    +            print_menuconfig_nodes(node.list, indent + 8)
    +
    +        node = node.next
    +
    +
    +def print_menuconfig(kconf):
    +    """
    +    Prints all menu entries for the configuration.
    +    """
    +    # Print the expanded mainmenu text at the top. This is the same as
    +    # kconf.top_node.prompt[0], but with variable references expanded.
    +    print("\n======== {} ========\n".format(kconf.mainmenu_text))
    +
    +    print_menuconfig_nodes(kconf.top_node.list, 0)
    +    print("")
    +
    +
    +def get_value_from_user(sc):
    +    """
    +    Prompts the user for a value for the symbol or choice 'sc'. For
    +    bool/tristate symbols and choices, provides a list of all the 
assignable
    +    values.
    +    """
    +    if not sc.visibility:
    +        print(sc.name + " is not currently visible")
    +        return False
    +
    +    prompt = "Value for {}".format(sc.name)
    +    if sc.type in (BOOL, TRISTATE):
    +        prompt += " (available: {})" \
    +                  .format(", ".join(TRI_TO_STR[val] for val in 
sc.assignable))
    +    prompt += ": "
    +
    +    val = input(prompt)
    +
    +    # Automatically add a "0x" prefix for hex symbols, like the menuconfig
    +    # interface does. This isn't done when loading .config files, hence why
    +    # set_value() doesn't do it automatically.
    +    if sc.type == HEX and not val.startswith(("0x", "0X")):
    +        val = "0x" + val
    +
    +    # Let Kconfiglib itself print a warning here if the value is invalid. 
We
    +    # could also disable warnings temporarily with 'kconf.warn = False' and
    +    # print our own warning.
    +    return sc.set_value(val)
    +
    +
    +if __name__ == "__main__":
    +    if len(sys.argv) != 2:
    +        sys.exit("usage: menuconfig.py <Kconfig file>")
    +
    +    # Load Kconfig configuration files
    +    kconf = Kconfig(sys.argv[1])
    +
    +    # Print the initial configuration tree
    +    print_menuconfig(kconf)
    +
    +    while True:
    +        try:
    +            cmd = input('Enter a symbol/choice name, "load_config", or '
    +                        '"write_config" (or press CTRL+D to exit): 
').strip()
    +        except EOFError:
    +            print("")
    +            break
    +
    +        if cmd == "load_config":
    +            config_filename = input(".config file to load: ")
    +            try:
    +                # Returns a message telling which file got loaded
    +                print(kconf.load_config(config_filename))
    +            except EnvironmentError as e:
    +                print(e, file=sys.stderr)
    +
    +            print_menuconfig(kconf)
    +            continue
    +
    +        if cmd == "write_config":
    +            config_filename = input("To this file: ")
    +            try:
    +                # Returns a message telling which file got saved
    +                print(kconf.write_config(config_filename))
    +            except EnvironmentError as e:
    +                print(e, file=sys.stderr)
    +
    +            continue
    +
    +        # Assume 'cmd' is the name of a symbol or choice if it isn't one 
of the
    +        # commands above, prompt the user for a value for it, and print 
the new
    +        # configuration tree
    +
    +        if cmd in kconf.syms:
    +            if get_value_from_user(kconf.syms[cmd]):
    +                print_menuconfig(kconf)
    +
    +            continue
    +
    +        if cmd in kconf.named_choices:
    +            if get_value_from_user(kconf.named_choices[cmd]):
    +                print_menuconfig(kconf)
    +
    +            continue
    +
    +        print("No symbol/choice named '{}' in the 
configuration".format(cmd),
    +              file=sys.stderr)
    diff --git a/support/kconfiglib/examples/merge_config.py 
b/support/kconfiglib/examples/merge_config.py
    new file mode 100755
    index 0000000..ef11d79
    --- /dev/null
    +++ b/support/kconfiglib/examples/merge_config.py
    @@ -0,0 +1,133 @@
    +#!/usr/bin/env python
    +
    +# This script functions similarly to scripts/kconfig/merge_config.sh from 
the
    +# kernel tree, merging multiple configurations fragments to produce a 
complete
    +# .config, with unspecified values filled in as for alldefconfig.
    +#
    +# The generated .config respects symbol dependencies, and a warning is 
printed
    +# if any symbol gets a different value from the assigned value.
    +#
    +# For a real-world merging example based on this script, see
    +# 
https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py.
    +#
    +# Here's a demo:
    +#
    +# Kconfig contents:
    +#
    +#     config FOO
    +#         bool "FOO"
    +#
    +#     config BAR
    +#         bool "BAR"
    +#
    +#     config BAZ
    +#         string "BAZ"
    +#
    +#     config QAZ
    +#         bool "QAZ" if n
    +#
    +#
    +# conf1 contents:
    +#
    +#     CONFIG_FOO=y
    +#
    +#
    +# conf2 contents:
    +#
    +#     CONFIG_BAR=y
    +#
    +#
    +# conf3 contents:
    +#
    +#     # Assigned twice (would generate warning if 'warn_assign_override' 
was
    +#     # True)
    +#     # CONFIG_FOO is not set
    +#
    +#     # Ops... this symbol doesn't exist
    +#     CONFIG_OPS=y
    +#
    +#     CONFIG_BAZ="baz string"
    +#
    +#
    +# conf4 contents:
    +#
    +#     CONFIG_QAZ=y
    +#
    +#
    +# Running:
    +#
    +#     $ python(3) merge_config.py Kconfig merged conf1 conf2 conf3 conf4
    +#     Merged configuration 'conf1'
    +#     Merged configuration 'conf2'
    +#     conf3:5: warning: attempt to assign the value 'y' to the undefined 
symbol OPS
    +#     Merged configuration 'conf3'
    +#     Merged configuration 'conf4'
    +#     Configuration saved to 'merged'
    +#     warning: QAZ (defined at Kconfig:10) was assigned the value 'y' but 
got the value 'n' -- check dependencies
    +#     $ cat merged
    +#     Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
    +#     # CONFIG_FOO is not set
    +#     CONFIG_BAR=y
    +#     CONFIG_BAZ="baz string"
    +
    +from __future__ import print_function
    +import sys
    +
    +from kconfiglib import Kconfig, BOOL, TRISTATE, TRI_TO_STR
    +
    +
    +if len(sys.argv) < 4:
    +    sys.exit("usage: merge_config.py Kconfig merged_config config1 
[config2 ...]")
    +
    +kconf = Kconfig(sys.argv[1])
    +
    +# Enable warnings for assignments to undefined symbols
    +kconf.warn_assign_undef = True
    +
    +# (This script uses alldefconfig as the base. Other starting states could 
be
    +# set up here as well. The approach in examples/allnoconfig_simpler.py 
could
    +# provide an allnoconfig starting state for example.)
    +
    +# Disable warnings generated for multiple assignments to the same symbol 
within
    +# a (set of) configuration files. Assigning a symbol multiple times might 
be
    +# done intentionally when merging configuration files.
    +kconf.warn_assign_override = False
    +kconf.warn_assign_redun = False
    +
    +# Create a merged configuration by loading the fragments with 
replace=False.
    +# load_config() and write_config() returns a message to print.
    +for config in sys.argv[3:]:
    +    print(kconf.load_config(config, replace=False))
    +
    +# Write the merged configuration
    +print(kconf.write_config(sys.argv[2]))
    +
    +# Print warnings for symbols whose actual value doesn't match the assigned
    +# value
    +
    +def name_and_loc(sym):
    +    # Helper for printing symbol names and Kconfig file location(s) in 
warnings
    +
    +    if not sym.nodes:
    +        return sym.name + " (undefined)"
    +
    +    return "{} (defined at {})".format(
    +        sym.name,
    +        ", ".join("{}:{}".format(node.filename, node.linenr)
    +                  for node in sym.nodes))
    +
    +for sym in kconf.defined_syms:
    +    # Was the symbol assigned to?
    +    if sym.user_value is not None:
    +        # Tristate values are represented as 0, 1, 2. Having them as
    +        # "n", "m", "y" is more convenient here, so convert.
    +        if sym.type in (BOOL, TRISTATE):
    +            user_value = TRI_TO_STR[sym.user_value]
    +        else:
    +            user_value = sym.user_value
    +
    +        if user_value != sym.str_value:
    +            print("warning: {} was assigned the value '{}' but got the "
    +                  "value '{}' -- check dependencies".format(
    +                      name_and_loc(sym), user_value, sym.str_value),
    +                  file=sys.stderr)
    diff --git a/support/kconfiglib/examples/print_config_tree.py 
b/support/kconfiglib/examples/print_config_tree.py
    new file mode 100644
    index 0000000..dc81d9d
    --- /dev/null
    +++ b/support/kconfiglib/examples/print_config_tree.py
    @@ -0,0 +1,199 @@
    +# Prints menu entries as a tree with its value in the .config file. This 
can be
    +# handy e.g. for diffing between different .config files or versions of 
Kconfig files.
    +#
    +# Usage:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig SCRIPT=print_config_tree.py 
[SCRIPT_ARG=<.config>]
    +#
    +#   If the variable WITH_HELP_DESC is modified to 'True', the help is added
    +#   to the symbols.
    +#
    +# Here's a notation guide. The notation matches the one used by menuconfig
    +# (scripts/kconfig/mconf):
    +#
    +#   [ ] prompt      - Bool
    +#   < > prompt      - Tristate
    +#   {M} prompt      - Tristate selected to m. Can only be set to m or y.
    +#   -*- prompt      - Bool/tristate selected to y, pinning it
    +#   -M- prompt      - Tristate selected to m that also has m visibility,
    +#                     pinning it to m
    +#   (foo) prompt    - String/int/hex symbol with value "foo"
    +#   --> prompt      - The selected symbol in a choice in y mode. This
    +#                     syntax is unique to this example.
    +#
    +# When modules are disabled, the .type attribute of TRISTATE symbols and
    +# choices automatically changes to BOOL. This trick is used by the C
    +# implementation as well, and gives the expected behavior without having 
to do
    +# anything extra here. The original type is available in .orig_type if 
needed.
    +#
    +# Example output:
    +#
    +#   $ make scriptconfig SCRIPT=Kconfiglib/examples/print_config_tree.py 
[SCRIPT_ARG=<.config file>]
    +#
    +#   ======== Linux/x86 4.9.82 Kernel Configuration ========
    +#
    +#   [*] 64-bit kernel (64BIT)
    +#       General setup
    +#          ()  Cross-compiler tool prefix (CROSS_COMPILE)
    +#          [ ] Compile also drivers which will not load (COMPILE_TEST)
    +#          ()  Local version - append to kernel release (LOCALVERSION)
    +#          [*] Automatically append version information to the version 
string (LOCALVERSION_AUTO)
    +#          -*- Kernel compression mode
    +#          ...
    +#
    +# With the variable WITH_HELP_DESC modified to 'True':
    +#
    +#   ======== Linux/x86 4.9.82 Kernel Configuration ========
    +#
    +#   [*] 64-bit kernel - Say yes to build a 64-bit kernel - formerly known 
as x86_64 Say no to build a 32-bit kernel - formerly known as i386  (64BIT)
    +#       General setup
    +#           ()  Cross-compiler tool prefix - Same as running 'make 
CROSS_COMPILE=prefix-' but stored for default make runs in this kernel build 
directory.  You don't need to set this unless you want the configured kernel 
build directory to select the cross-compiler automatically.  (CROSS_COMPILE)
    +#           [ ] Compile also drivers which will not load - Some drivers 
can be compiled on a different platform than they are intended to be run on. 
Despite they cannot be loaded there (or even when they load they cannot be used 
due to missing HW support), developers still, opposing to distributors, might 
want to build such drivers to compile-test them.  If you are a developer and 
want to build everything available, say Y here. If you are a user/distributor, 
say N here to exclude useless drivers to be distributed.  (COMPILE_TEST)
    +#           ...
    +
    +import sys
    +
    +from kconfiglib import Kconfig, \
    +                       Symbol, MENU, COMMENT, \
    +                       BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
    +                       expr_value
    +
    +
    +# Add help description to output
    +WITH_HELP_DESC = False
    +
    +
    +def indent_print(s, indent):
    +    print(indent*" " + s)
    +
    +
    +def value_str(sc):
    +    """
    +    Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
    +
    +    sc: Symbol or Choice.
    +    """
    +    if sc.type in (STRING, INT, HEX):
    +        return "({})".format(sc.str_value)
    +
    +    # BOOL or TRISTATE
    +
    +    # The choice mode is an upper bound on the visibility of choice 
symbols, so
    +    # we can check the choice symbols' own visibility to see if the choice 
is
    +    # in y mode
    +    if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
    +        # For choices in y mode, print '-->' next to the selected symbol
    +        return "-->" if sc.choice.selection is sc else "   "
    +
    +    tri_val_str = (" ", "M", "*")[sc.tri_value]
    +
    +    if len(sc.assignable) == 1:
    +        # Pinned to a single value
    +        return "-{}-".format(tri_val_str)
    +
    +    if sc.type == BOOL:
    +        return "[{}]".format(tri_val_str)
    +
    +    if sc.type == TRISTATE:
    +        if sc.assignable == (1, 2):
    +            # m and y available
    +            return "{" + tri_val_str + "}"  # Gets a bit confusing with 
.format()
    +        return "<{}>".format(tri_val_str)
    +
    +
    +def node_str(node):
    +    """
    +    Returns the complete menu entry text for a menu node, or "" for 
invisible
    +    menu nodes. Invisible menu nodes are those that lack a prompt or that 
do
    +    not have a satisfied prompt condition.
    +
    +    Example return value: "[*] Bool symbol (BOOL)"
    +
    +    The symbol name is printed in parentheses to the right of the prompt.
    +    """
    +    if not node.prompt:
    +        return ""
    +
    +    # Even for menu nodes for symbols and choices, it's wrong to check
    +    # Symbol.visibility / Choice.visibility here. The reason is that a 
symbol
    +    # (and a choice, in theory) can be defined in multiple locations, 
giving it
    +    # multiple menu nodes, which do not necessarily all have the same 
prompt
    +    # visibility. Symbol.visibility / Choice.visibility is calculated as 
the OR
    +    # of the visibility of all the prompts.
    +    prompt, prompt_cond = node.prompt
    +    if not expr_value(prompt_cond):
    +        return ""
    +
    +    if node.item == MENU:
    +        return "    " + prompt
    +
    +    if node.item == COMMENT:
    +        return "    *** {} ***".format(prompt)
    +
    +    # Symbol or Choice
    +
    +    sc = node.item
    +
    +    if sc.type == UNKNOWN:
    +        # Skip symbols defined without a type (these are obscure and 
generate
    +        # a warning)
    +        return ""
    +
    +    # Add help text
    +    if WITH_HELP_DESC:
    +        prompt += ' - ' + str(node.help).replace('\n', ' ').replace('\r', 
'')
    +
    +    # {:3} sets the field width to three. Gives nice alignment for empty 
string
    +    # values.
    +    res = "{:3} {}".format(value_str(sc), prompt)
    +
    +    # Don't print the name for unnamed choices (the normal kind)
    +    if sc.name is not None:
    +        res += " ({})".format(sc.name)
    +
    +    return res
    +
    +
    +def print_menuconfig_nodes(node, indent):
    +    """
    +    Prints a tree with all the menu entries rooted at 'node'. Child menu
    +    entries are indented.
    +    """
    +    while node:
    +        string = node_str(node)
    +        if string:
    +            indent_print(string, indent)
    +
    +        if node.list:
    +            print_menuconfig_nodes(node.list, indent + 8)
    +
    +        node = node.next
    +
    +
    +def print_menuconfig(kconf):
    +    """
    +    Prints all menu entries for the configuration.
    +    """
    +    # Print the expanded mainmenu text at the top. This is the same as
    +    # kconf.top_node.prompt[0], but with variable references expanded.
    +    print("\n======== {} ========\n".format(kconf.mainmenu_text))
    +
    +    print_menuconfig_nodes(kconf.top_node.list, 0)
    +    print("")
    +
    +
    +if __name__ == "__main__":
    +
    +    # Load Kconfig configuration files
    +    kconf = Kconfig(sys.argv[1])
    +
    +    # Set default .config file or load it from argv
    +    if len(sys.argv) == 2:
    +        config_filename = '.config'
    +    else:
    +        config_filename = sys.argv[2]
    +
    +    kconf.load_config(config_filename)
    +
    +    # Print the configuration tree
    +    print_menuconfig(kconf)
    diff --git a/support/kconfiglib/examples/print_sym_info.py 
b/support/kconfiglib/examples/print_sym_info.py
    new file mode 100644
    index 0000000..ea6fc72
    --- /dev/null
    +++ b/support/kconfiglib/examples/print_sym_info.py
    @@ -0,0 +1,54 @@
    +# Loads a Kconfig and a .config and prints a symbol.
    +#
    +# Usage:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/print_sym_info.py SCRIPT_ARG=<name>
    +#
    +# Example output for SCRIPT_ARG=MODULES:
    +#
    +# menuconfig MODULES
    +#  bool
    +#  prompt "Enable loadable module support"
    +#  option modules
    +#  help
    +#    Kernel modules are small pieces of compiled code which can
    +#    be inserted in the running kernel, rather than being
    +#    permanently built into the kernel.  You use the "modprobe"
    +#    tool to add (and sometimes remove) them.  If you say Y here,
    +#    many parts of the kernel can be built as modules (by
    +#    answering M instead of Y where indicated): this is most
    +#    useful for infrequently used options which are not required
    +#    for booting.  For more information, see the man pages for
    +#    modprobe, lsmod, modinfo, insmod and rmmod.
    +#
    +#    If you say Y here, you will need to run "make
    +#    modules_install" to put the modules under /lib/modules/
    +#    where modprobe can find them (you may need to be root to do
    +#    this).
    +#
    +#    If unsure, say Y.
    +#
    +# value = n
    +# visibility = y
    +# currently assignable values: n, y
    +# defined at init/Kconfig:1674
    +
    +import sys
    +
    +from kconfiglib import Kconfig, TRI_TO_STR
    +
    +
    +if len(sys.argv) < 3:
    +    sys.exit('Pass symbol name (without "CONFIG_" prefix) with 
SCRIPT_ARG=<name>')
    +
    +kconf = Kconfig(sys.argv[1])
    +sym = kconf.syms[sys.argv[2]]
    +
    +print(sym)
    +print("value = " + sym.str_value)
    +print("visibility = " + TRI_TO_STR[sym.visibility])
    +print("currently assignable values: " +
    +      ", ".join([TRI_TO_STR[v] for v in sym.assignable]))
    +
    +for node in sym.nodes:
    +    print("defined at {}:{}".format(node.filename, node.linenr))
    diff --git a/support/kconfiglib/examples/print_tree.py 
b/support/kconfiglib/examples/print_tree.py
    new file mode 100644
    index 0000000..49cb954
    --- /dev/null
    +++ b/support/kconfiglib/examples/print_tree.py
    @@ -0,0 +1,75 @@
    +# Prints the menu tree of the configuration. Dependencies between symbols 
can
    +# sometimes implicitly alter the menu structure (see 
kconfig-language.txt), and
    +# that's implemented too.
    +#
    +# Note: See the Kconfig.node_iter() function as well, which provides a 
simpler
    +# interface for walking the menu tree.
    +#
    +# Usage:
    +#
    +#   $ make [ARCH=<arch>] scriptconfig 
SCRIPT=Kconfiglib/examples/print_tree.py
    +#
    +# Example output:
    +#
    +#   ...
    +#   config HAVE_KERNEL_LZO
    +#   config HAVE_KERNEL_LZ4
    +#   choice
    +#     config KERNEL_GZIP
    +#     config KERNEL_BZIP2
    +#     config KERNEL_LZMA
    +#     config KERNEL_XZ
    +#     config KERNEL_LZO
    +#     config KERNEL_LZ4
    +#   config DEFAULT_HOSTNAME
    +#   config SWAP
    +#   config SYSVIPC
    +#     config SYSVIPC_SYSCTL
    +#   config POSIX_MQUEUE
    +#     config POSIX_MQUEUE_SYSCTL
    +#   config CROSS_MEMORY_ATTACH
    +#   config FHANDLE
    +#   config USELIB
    +#   config AUDIT
    +#   config HAVE_ARCH_AUDITSYSCALL
    +#   config AUDITSYSCALL
    +#   config AUDIT_WATCH
    +#   config AUDIT_TREE
    +#   menu "IRQ subsystem"
    +#     config MAY_HAVE_SPARSE_IRQ
    +#     config GENERIC_IRQ_LEGACY
    +#     config GENERIC_IRQ_PROBE
    +#   ...
    +
    +import sys
    +
    +from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
    +
    +
    +def indent_print(s, indent):
    +    print(indent*" " + s)
    +
    +
    +def print_items(node, indent):
    +    while node:
    +        if isinstance(node.item, Symbol):
    +            indent_print("config " + node.item.name, indent)
    +
    +        elif isinstance(node.item, Choice):
    +            indent_print("choice", indent)
    +
    +        elif node.item == MENU:
    +            indent_print('menu "{}"'.format(node.prompt[0]), indent)
    +
    +        elif node.item == COMMENT:
    +            indent_print('comment "{}"'.format(node.prompt[0]), indent)
    +
    +
    +        if node.list:
    +            print_items(node.list, indent + 2)
    +
    +        node = node.next
    +
    +
    +kconf = Kconfig(sys.argv[1])
    +print_items(kconf.top_node, 0)
    diff --git a/support/kconfiglib/genconfig.py 
b/support/kconfiglib/genconfig.py
    new file mode 100755
    index 0000000..bb3e6ff
    --- /dev/null
    +++ b/support/kconfiglib/genconfig.py
    @@ -0,0 +1,132 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Generates a header file with #defines from the configuration, matching the
    +format of include/generated/autoconf.h in the Linux kernel.
    +
    +Optionally, also writes the configuration output as a .config file. See
    +--config-out.
    +
    +The --sync-deps, --file-list, and --env-list options generate information 
that
    +can be used to avoid needless rebuilds/reconfigurations.
    +
    +Before writing a header or configuration file, Kconfiglib compares the old
    +contents of the file against the new contents. If there's no change, the 
write
    +is skipped. This avoids updating file metadata like the modification time, 
and
    +might save work depending on your build setup.
    +
    +By default, the configuration is generated from '.config'. A different
    +configuration file can be passed in the KCONFIG_CONFIG environment 
variable.
    +"""
    +import argparse
    +import os
    +import sys
    +
    +import kconfiglib
    +
    +
    +DEFAULT_HEADER_PATH = "config.h"
    +DEFAULT_SYNC_DEPS_PATH = "deps/"
    +
    +
    +def main():
    +    parser = argparse.ArgumentParser(
    +        formatter_class=argparse.RawDescriptionHelpFormatter,
    +        description=__doc__)
    +
    +    parser.add_argument(
    +        "--header-path",
    +        metavar="HEADER_FILE",
    +        default=DEFAULT_HEADER_PATH,
    +        help="Path for the generated header file (default: {})"
    +             .format(DEFAULT_HEADER_PATH))
    +
    +    parser.add_argument(
    +        "--config-out",
    +        metavar="CONFIG_FILE",
    +        help="""
    +Write the configuration to CONFIG_FILE. This is useful if you include 
.config
    +files in Makefiles, as the generated configuration file will be a full 
.config
    +file even if .config is outdated. The generated configuration matches what
    +olddefconfig would produce. If you use sync-deps, you can include
    +deps/auto.conf instead. --config-out is meant for cases where incremental 
build
    +information isn't needed.
    +""")
    +
    +    parser.add_argument(
    +        "--sync-deps",
    +        metavar="OUTPUT_DIR",
    +        nargs="?",
    +        const=DEFAULT_SYNC_DEPS_PATH,
    +        help="""
    +Enable generation of symbol dependency information for incremental builds,
    +optionally specifying the output directory (default: {}). See the 
docstring of
    +Kconfig.sync_deps() in Kconfiglib for more information.
    +""".format(DEFAULT_SYNC_DEPS_PATH))
    +
    +    parser.add_argument(
    +        "--file-list",
    +        metavar="OUTPUT_FILE",
    +        help="""
    +Write a list of all Kconfig files to OUTPUT_FILE, with one file per line. 
The
    +paths are relative to $srctree (or to the current directory if $srctree is
    +unset). Files appear in the order they're 'source'd.
    +""")
    +
    +    parser.add_argument(
    +        "--env-list",
    +        metavar="OUTPUT_FILE",
    +        help="""
    +Write a list of all environment variables referenced in Kconfig files to
    +OUTPUT_FILE, with one variable per line. Each line has the format 
NAME=VALUE.
    +Only environment variables referenced with the preprocessor $(VAR) syntax 
are
    +included, and not variables referenced with the older $VAR syntax (which is
    +only supported for backwards compatibility).
    +""")
    +
    +    parser.add_argument(
    +        "kconfig_filename",
    +        metavar="KCONFIG_FILENAME",
    +        nargs="?",
    +        default="Kconfig",
    +        help="Top-level Kconfig file (default: Kconfig)")
    +
    +    args = parser.parse_args()
    +
    +
    +    kconf = kconfiglib.Kconfig(args.kconfig_filename)
    +    kconf.load_config()
    +
    +    kconf.write_autoconf(args.header_path)
    +
    +    if args.config_out is not None:
    +        kconf.write_config(args.config_out, save_old=False)
    +
    +    if args.sync_deps is not None:
    +        kconf.sync_deps(args.sync_deps)
    +
    +    if args.file_list is not None:
    +        with _open_write(args.file_list) as f:
    +            for path in kconf.kconfig_filenames:
    +                f.write(path + "\n")
    +
    +    if args.env_list is not None:
    +        with _open_write(args.env_list) as f:
    +            for env_var in kconf.env_vars:
    +                f.write("{}={}\n".format(env_var, os.environ[env_var]))
    +
    +
    +def _open_write(path):
    +    # Python 2/3 compatibility. io.open() is available on both, but makes
    +    # write() expect 'unicode' strings on Python 2.
    +
    +    if sys.version_info[0] < 3:
    +        return open(path, "w")
    +    return open(path, "w", encoding="utf-8")
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/guiconfig.py 
b/support/kconfiglib/guiconfig.py
    new file mode 100755
    index 0000000..050789a
    --- /dev/null
    +++ b/support/kconfiglib/guiconfig.py
    @@ -0,0 +1,2316 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Overview
    +========
    +
    +A Tkinter-based menuconfig implementation, based around a treeview control 
and
    +a help display. The interface should feel familiar to people used to qconf
    +('make xconfig'). Compatible with both Python 2 and Python 3.
    +
    +The display can be toggled between showing the full tree and showing just a
    +single menu (like menuconfig.py). Only single-menu mode distinguishes 
between
    +symbols defined with 'config' and symbols defined with 'menuconfig'.
    +
    +A show-all mode is available that shows invisible items in red.
    +
    +Supports both mouse and keyboard controls. The following keyboard 
shortcuts are
    +available:
    +
    +  Ctrl-S   : Save configuration
    +  Ctrl-O   : Open configuration
    +  Ctrl-A   : Toggle show-all mode
    +  Ctrl-N   : Toggle show-name mode
    +  Ctrl-M   : Toggle single-menu mode
    +  Ctrl-F, /: Open jump-to dialog
    +  ESC      : Close
    +
    +Running
    +=======
    +
    +guiconfig.py can be run either as a standalone executable or by calling the
    +menuconfig() function with an existing Kconfig instance. The second option 
is a
    +bit inflexible in that it will still load and save .config, etc.
    +
    +When run in standalone mode, the top-level Kconfig file to load can be 
passed
    +as a command-line argument. With no argument, it defaults to "Kconfig".
    +
    +The KCONFIG_CONFIG environment variable specifies the .config file to load 
(if
    +it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
    +
    +When overwriting a configuration file, the old version is saved to
    +<filename>.old (e.g. .config.old).
    +
    +$srctree is supported through Kconfiglib.
    +"""
    +
    +# Note: There's some code duplication with menuconfig.py below, especially 
for
    +# the help text. Maybe some of it could be moved into kconfiglib.py or a 
shared
    +# helper script, but OTOH it's pretty nice to have things standalone and
    +# customizable.
    +
    +import errno
    +import os
    +import sys
    +
    +_PY2 = sys.version_info[0] < 3
    +
    +if _PY2:
    +    # Python 2
    +    from Tkinter import *
    +    import ttk
    +    import tkFont as font
    +    import tkFileDialog as filedialog
    +    import tkMessageBox as messagebox
    +else:
    +    # Python 3
    +    from tkinter import *
    +    import tkinter.ttk as ttk
    +    import tkinter.font as font
    +    from tkinter import filedialog, messagebox
    +
    +from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
    +                       BOOL, TRISTATE, STRING, INT, HEX, \
    +                       AND, OR, \
    +                       expr_str, expr_value, split_expr, \
    +                       standard_sc_expr_str, \
    +                       TRI_TO_STR, TYPE_TO_STR, \
    +                       standard_kconfig, standard_config_filename
    +
    +
    +# If True, use GIF image data embedded in this file instead of separate GIF
    +# files. See _load_images().
    +_USE_EMBEDDED_IMAGES = True
    +
    +
    +# Help text for the jump-to dialog
    +_JUMP_TO_HELP = """\
    +Type one or more strings/regexes and press Enter to list items that match 
all
    +of them. Python's regex flavor is used (see the 're' module). 
Double-clicking
    +an item will jump to it. Item values can be toggled directly within the 
dialog.\
    +"""
    +
    +
    +def _main():
    +    menuconfig(standard_kconfig())
    +
    +
    +# Global variables used below:
    +#
    +#   _root:
    +#     The Toplevel instance for the main window
    +#
    +#   _tree:
    +#     The Treeview in the main window
    +#
    +#   _jump_to_tree:
    +#     The Treeview in the jump-to dialog. None if the jump-to dialog isn't
    +#     open. Doubles as a flag.
    +#
    +#   _jump_to_matches:
    +#     List of Nodes shown in the jump-to dialog
    +#
    +#   _menupath:
    +#     The Label that shows the menu path of the selected item
    +#
    +#   _backbutton:
    +#     The button shown in single-menu mode for jumping to the parent menu
    +#
    +#   _status_label:
    +#     Label with status text shown at the bottom of the main window
    +#     ("Modified", "Saved to ...", etc.)
    +#
    +#   _id_to_node:
    +#     We can't use Node objects directly as Treeview item IDs, so we use 
their
    +#     id()s instead. This dictionary maps Node id()s back to Nodes. (The 
keys
    +#     are actually str(id(node)), just to simplify lookups.)
    +#
    +#   _cur_menu:
    +#     The current menu. Ignored outside single-menu mode.
    +#
    +#   _show_all_var/_show_name_var/_single_menu_var:
    +#     Tkinter Variable instances bound to the corresponding checkboxes
    +#
    +#   _show_all/_single_menu:
    +#     Plain Python bools that track _show_all_var and _single_menu_var, to
    +#     speed up and simplify things a bit
    +#
    +#   _conf_filename:
    +#     File to save the configuration to
    +#
    +#   _minconf_filename:
    +#     File to save minimal configurations to
    +#
    +#   _conf_changed:
    +#     True if the configuration has been changed. If False, we don't bother
    +#     showing the save-and-quit dialog.
    +#
    +#     We reset this to False whenever the configuration is saved.
    +#
    +#   _*_img:
    +#     PhotoImage instances for images
    +
    +
    +def menuconfig(kconf):
    +    """
    +    Launches the configuration interface, returning after the user exits.
    +
    +    kconf:
    +      Kconfig instance to be configured
    +    """
    +    global _kconf
    +    global _conf_filename
    +    global _minconf_filename
    +    global _jump_to_tree
    +    global _cur_menu
    +
    +    _kconf = kconf
    +
    +    _jump_to_tree = None
    +
    +    _create_id_to_node()
    +
    +    _create_ui()
    +
    +    # Filename to save configuration to
    +    _conf_filename = standard_config_filename()
    +
    +    # Load existing configuration and check if it's outdated
    +    _set_conf_changed(_load_config())
    +
    +    # Filename to save minimal configuration to
    +    _minconf_filename = "defconfig"
    +
    +    # Current menu in single-menu mode
    +    _cur_menu = _kconf.top_node
    +
    +    # Any visible items in the top menu?
    +    if not _shown_menu_nodes(kconf.top_node):
    +        # Nothing visible. Start in show-all mode and try again.
    +        _show_all_var.set(True)
    +        if not _shown_menu_nodes(kconf.top_node):
    +            # Give up and show an error. It's nice to be able to assume 
that
    +            # the tree is non-empty in the rest of the code.
    +            _root.wait_visibility()
    +            messagebox.showerror(
    +                "Error",
    +                "Empty configuration -- nothing to configure.\n\n"
    +                "Check that environment variables are set properly.")
    +            _root.destroy()
    +            return
    +
    +    # Build the initial tree
    +    _update_tree()
    +
    +    # Select the first item and focus the Treeview, so that keyboard 
controls
    +    # work immediately
    +    _select(_tree, _tree.get_children()[0])
    +    _tree.focus_set()
    +
    +    # Make geometry information available for centering the window. This
    +    # indirectly creates the window, so hide it so that it's never shown 
at the
    +    # old location.
    +    _root.withdraw()
    +    _root.update_idletasks()
    +
    +    # Center the window
    +    _root.geometry("+{}+{}".format(
    +        (_root.winfo_screenwidth() - _root.winfo_reqwidth())//2,
    +        (_root.winfo_screenheight() - _root.winfo_reqheight())//2))
    +
    +    # Show it
    +    _root.deiconify()
    +
    +    # Prevent the window from being automatically resized. Otherwise, it
    +    # changes size when scrollbars appear/disappear before the user has
    +    # manually resized it.
    +    _root.geometry(_root.geometry())
    +
    +    _root.mainloop()
    +
    +
    +def _load_config():
    +    # Loads any existing .config file. See the Kconfig.load_config() 
docstring.
    +    #
    +    # Returns True if .config is missing or outdated. We always prompt for
    +    # saving the configuration in that case.
    +
    +    print(_kconf.load_config())
    +    if not os.path.exists(_conf_filename):
    +        # No .config
    +        return True
    +
    +    return _needs_save()
    +
    +
    +def _needs_save():
    +    # Returns True if a just-loaded .config file is outdated (would get
    +    # modified when saving)
    +
    +    if _kconf.missing_syms:
    +        # Assignments to undefined symbols in the .config
    +        return True
    +
    +    for sym in _kconf.unique_defined_syms:
    +        if sym.user_value is None:
    +            if sym.config_string:
    +                # Unwritten symbol
    +                return True
    +        elif sym.orig_type in (BOOL, TRISTATE):
    +            if sym.tri_value != sym.user_value:
    +                # Written bool/tristate symbol, new value
    +                return True
    +        elif sym.str_value != sym.user_value:
    +            # Written string/int/hex symbol, new value
    +            return True
    +
    +    # No need to prompt for save
    +    return False
    +
    +
    +def _create_id_to_node():
    +    global _id_to_node
    +
    +    _id_to_node = {str(id(node)): node for node in _kconf.node_iter()}
    +
    +
    +def _create_ui():
    +    # Creates the main window UI
    +
    +    global _root
    +    global _tree
    +
    +    # Create the root window. This initializes Tkinter and makes e.g.
    +    # PhotoImage available, so do it early.
    +    _root = Tk()
    +
    +    _load_images()
    +    _init_misc_ui()
    +    _fix_treeview_issues()
    +
    +    _create_top_widgets()
    +    # Create the pane with the Kconfig tree and description text
    +    panedwindow, _tree = _create_kconfig_tree_and_desc(_root)
    +    panedwindow.grid(column=0, row=1, sticky="nsew")
    +    _create_status_bar()
    +
    +    _root.columnconfigure(0, weight=1)
    +    # Only the pane with the Kconfig tree and description grows vertically
    +    _root.rowconfigure(1, weight=1)
    +
    +    # Start with show-name disabled
    +    _do_showname()
    +
    +    _tree.bind("<Left>", _tree_left_key)
    +    _tree.bind("<Right>", _tree_right_key)
    +    # Note: Binding this for the jump-to tree as well would cause issues 
due to
    +    # the Tk bug mentioned in _tree_open()
    +    _tree.bind("<<TreeviewOpen>>", _tree_open)
    +    # add=True to avoid overriding the description text update
    +    _tree.bind("<<TreeviewSelect>>", _update_menu_path, add=True)
    +
    +    _root.bind("<Control-s>", _save)
    +    _root.bind("<Control-o>", _open)
    +    _root.bind("<Control-a>", _toggle_showall)
    +    _root.bind("<Control-n>", _toggle_showname)
    +    _root.bind("<Control-m>", _toggle_tree_mode)
    +    _root.bind("<Control-f>", _jump_to_dialog)
    +    _root.bind("/", _jump_to_dialog)
    +    _root.bind("<Escape>", _on_quit)
    +
    +
    +def _load_images():
    +    # Loads GIF images, creating the global _*_img PhotoImage variables.
    +    # Base64-encoded images embedded in this script are used if
    +    # _USE_EMBEDDED_IMAGES is True, and separate image files in the same
    +    # directory as the script otherwise.
    +    #
    +    # Using a global variable indirectly prevents the image from being
    +    # garbage-collected. Passing an image to a Tkinter function isn't 
enough to
    +    # keep it alive.
    +
    +    def load_image(name, data):
    +        var_name = "_{}_img".format(name)
    +
    +        if _USE_EMBEDDED_IMAGES:
    +            globals()[var_name] = PhotoImage(data=data, format="gif")
    +        else:
    +            globals()[var_name] = PhotoImage(
    +                file=os.path.join(os.path.dirname(__file__), name + 
".gif"),
    +                format="gif")
    +
    +    # Note: Base64 data can be put on the clipboard with
    +    #   $ base64 -w0 foo.gif | xclip
    +
    +    load_image("icon", 
"R0lGODlhMAAwAPEDAAAAAADQAO7u7v///yH5BAUKAAMALAAAAAAwADAAAAL/nI+gy+2Pokyv2jazuZxryQjiSJZmyXxHeLbumH6sEATvW8OLNtf5bfLZRLFITzgEipDJ4mYxYv6A0ubuqYhWk66tVTE4enHer7jcKvt0LLUw6P45lvEprT6c0+v7OBuqhYdHohcoqIbSAHc4ljhDwrh1UlgSydRCWWlp5wiYZvmSuSh4IzrqV6p4cwhkCsmY+nhK6uJ6t1mrOhuJqfu6+WYiCiwl7HtLjNSZZZis/MeM7NY3TaRKS40ooDeoiVqIultsrav92bi9c3a5KkkOsOJZpSS99m4k/0zPng4Gks9JSbB+8DIcoQfnjwpZCHv5W+ip4aQrKrB0uOikYhiMCBw1/uPoQUMBADs=")
    +    load_image("n_bool", 
"R0lGODdhEAAQAPAAAAgICP///ywAAAAAEAAQAAACIISPacHtvp5kcb5qG85hZ2+BkyiRF8BBaEqtrKkqslEAADs=")
    +    load_image("y_bool", 
"R0lGODdhEAAQAPEAAAgICADQAP///wAAACwAAAAAEAAQAAACMoSPacLtvlh4YrIYsst2cV19AvaVF9CUXBNJJoum7ymrsKuCnhiupIWjSSjAFuWhSCIKADs=")
    +    load_image("n_tri", 
"R0lGODlhEAAQAPD/AAEBAf///yH5BAUKAAIALAAAAAAQABAAAAInlI+pBrAKQnCPSUlXvFhznlkfeGwjKZhnJ65h6nrfi6h0st2QXikFADs=")
    +    load_image("m_tri", 
"R0lGODlhEAAQAPEDAAEBAeQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nI+pBrAWAhPCjYhiAJQCnWmdoElHGVBoiK5M21ofXFpXRIrgiecqxkuNciZIhNOZFRNI24PhfEoLADs=")
    +    load_image("y_tri", 
"R0lGODlhEAAQAPEDAAICAgDQAP///wAAACH5BAUKAAMALAAAAAAQABAAAAI0nI+pBrAYBhDCRRUypfmergmgZ4xjMpmaw2zmxk7cCB+pWiVqp4MzDwn9FhGZ5WFjIZeGAgA7")
    +    load_image("m_my", 
"R0lGODlhEAAQAPEDAAAAAOQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nIGpxiAPI2ghxFinq/ZygQhc94zgZopmOLYf67anGr+oZdp02emfV5n9MEHN5QhqICETxkABbQ4KADs=")
    +    load_image("y_my", 
"R0lGODlhEAAQAPH/AAAAAADQAAPRA////yH5BAUKAAQALAAAAAAQABAAAAM+SArcrhCMSSuIM9Q8rxxBWIXawIBkmWonupLd565Um9G1PIs59fKmzw8WnAlusBYR2SEIN6DmAmqBLBxYSAIAOw==")
    +    load_image("n_locked", 
"R0lGODlhEAAQAPABAAAAAP///yH5BAUKAAEALAAAAAAQABAAAAIgjB8AyKwN04pu0vMutpqqz4Hih4ydlnUpyl2r23pxUAAAOw==")
    +    load_image("m_locked", 
"R0lGODlhEAAQAPD/AAAAAOQMuiH5BAUKAAIALAAAAAAQABAAAAIylC8AyKwN04ohnGcqqlZmfXDWI26iInZoyiore05walolV39ftxsYHgL9QBBMBGFEFAAAOw==")
    +    load_image("y_locked", 
"R0lGODlhEAAQAPD/AAAAAADQACH5BAUKAAIALAAAAAAQABAAAAIylC8AyKzNgnlCtoDTwvZwrHydIYpQmR3KWq4uK74IOnp0HQPmnD3cOVlUIAgKsShkFAAAOw==")
    +    load_image("not_selected", 
"R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIrlA2px6IBw2IpWglOvTYhzmUbGD3kNZ5QqrKn2YrqigCxZoMelU6No9gdCgA7")
    +    load_image("selected", 
"R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIzlA2px6IBw2IpWglOvTah/kTZhimASJomiqonlLov1qptHTsgKSEzh9H8QI0QzNPwmRoFADs=")
    +    load_image("edit", 
"R0lGODlhEAAQAPIFAAAAAKOLAMuuEPvXCvrxvgAAAAAAAAAAACH5BAUKAAUALAAAAAAQABAAAANCWLqw/gqMBp8cszJxcwVC2FEOEIAi5kVBi3IqWZhuCGMyfdpj2e4pnK+WAshmvxeAcETWlsxPkkBtsqBMa8TIBSQAADs=")
    +
    +
    +def _fix_treeview_issues():
    +    # Fixes some Treeview issues
    +
    +    global _treeview_rowheight
    +
    +    style = ttk.Style()
    +
    +    # The treeview rowheight isn't adjusted automatically on high-DPI 
displays,
    +    # so do it ourselves. The font will probably always be TkDefaultFont, 
but
    +    # play it safe and look it up.
    +
    +    _treeview_rowheight = font.Font(font=style.lookup("Treeview", "font")) 
\
    +        .metrics("linespace") + 2
    +
    +    style.configure("Treeview", rowheight=_treeview_rowheight)
    +
    +    # Work around regression in 
https://core.tcl.tk/tk/tktview?name=509cafafae,
    +    # which breaks tag background colors
    +
    +    for option in "foreground", "background":
    +        # Filter out any styles starting with ("!disabled", "!selected", 
...).
    +        # style.map() returns an empty list for missing options, so this 
should
    +        # be future-safe.
    +        style.map(
    +            "Treeview",
    +            **{option: [elm for elm in style.map("Treeview", 
query_opt=option)
    +                        if elm[:2] != ("!disabled", "!selected")]})
    +
    +
    +def _init_misc_ui():
    +    # Does misc. UI initialization, like setting the title, icon, and theme
    +
    +    _root.title(_kconf.mainmenu_text)
    +    # iconphoto() isn't available in Python 2's Tkinter
    +    _root.tk.call("wm", "iconphoto", _root._w, "-default", _icon_img)
    +    # Reducing the width of the window to 1 pixel makes it move around, at
    +    # least on GNOME. Prevent weird stuff like that.
    +    _root.minsize(128, 128)
    +    _root.protocol("WM_DELETE_WINDOW", _on_quit)
    +
    +    # Use the 'clam' theme on *nix if it's available. It looks nicer than 
the
    +    # 'default' theme.
    +    if _root.tk.call("tk", "windowingsystem") == "x11":
    +        style = ttk.Style()
    +        if "clam" in style.theme_names():
    +            style.theme_use("clam")
    +
    +
    +def _create_top_widgets():
    +    # Creates the controls above the Kconfig tree in the main window
    +
    +    global _show_all_var
    +    global _show_name_var
    +    global _single_menu_var
    +    global _menupath
    +    global _backbutton
    +
    +    topframe = ttk.Frame(_root)
    +    topframe.grid(column=0, row=0, sticky="ew")
    +
    +    ttk.Button(topframe, text="Save", command=_save) \
    +        .grid(column=0, row=0, sticky="ew", padx=".05c", pady=".05c")
    +
    +    ttk.Button(topframe, text="Save as...", command=_save_as) \
    +        .grid(column=1, row=0, sticky="ew")
    +
    +    ttk.Button(topframe, text="Save minimal (advanced)...",
    +               command=_save_minimal) \
    +        .grid(column=2, row=0, sticky="ew", padx=".05c")
    +
    +    ttk.Button(topframe, text="Open...", command=_open) \
    +        .grid(column=3, row=0)
    +
    +    ttk.Button(topframe, text="Jump to...", command=_jump_to_dialog) \
    +        .grid(column=4, row=0, padx=".05c")
    +
    +    _show_name_var = BooleanVar()
    +    ttk.Checkbutton(topframe, text="Show name", command=_do_showname,
    +                    variable=_show_name_var) \
    +        .grid(column=0, row=1, sticky="nsew", padx=".05c", pady="0 .05c",
    +              ipady=".2c")
    +
    +    _show_all_var = BooleanVar()
    +    ttk.Checkbutton(topframe, text="Show all", command=_do_showall,
    +                    variable=_show_all_var) \
    +        .grid(column=1, row=1, sticky="nsew", pady="0 .05c")
    +
    +    # Allow the show-all and single-menu status to be queried via plain 
global
    +    # Python variables, which is faster and simpler
    +
    +    def show_all_updated(*_):
    +        global _show_all
    +        _show_all = _show_all_var.get()
    +
    +    _trace_write(_show_all_var, show_all_updated)
    +    _show_all_var.set(False)
    +
    +    _single_menu_var = BooleanVar()
    +    ttk.Checkbutton(topframe, text="Single-menu mode", 
command=_do_tree_mode,
    +                    variable=_single_menu_var) \
    +        .grid(column=2, row=1, sticky="nsew", padx=".05c", pady="0 .05c")
    +
    +    _backbutton = ttk.Button(topframe, text="<--", command=_leave_menu,
    +                             state="disabled")
    +    _backbutton.grid(column=0, row=4, sticky="nsew", padx=".05c", pady="0 
.05c")
    +
    +    def tree_mode_updated(*_):
    +        global _single_menu
    +        _single_menu = _single_menu_var.get()
    +
    +        if _single_menu:
    +            _backbutton.grid()
    +        else:
    +            _backbutton.grid_remove()
    +
    +    _trace_write(_single_menu_var, tree_mode_updated)
    +    _single_menu_var.set(False)
    +
    +    # Column to the right of the buttons that the menu path extends into, 
so
    +    # that it can grow wider than the buttons
    +    topframe.columnconfigure(5, weight=1)
    +
    +    _menupath = ttk.Label(topframe)
    +    _menupath.grid(column=0, row=3, columnspan=6, sticky="w", padx="0.05c",
    +                   pady="0 .05c")
    +
    +
    +def _create_kconfig_tree_and_desc(parent):
    +    # Creates a Panedwindow with a Treeview that shows Kconfig nodes and a 
Text
    +    # that shows a description of the selected node. Returns a tuple with 
the
    +    # Panedwindow and the Treeview. This code is shared between the main 
window
    +    # and the jump-to dialog.
    +
    +    panedwindow = ttk.Panedwindow(parent, orient=VERTICAL)
    +
    +    tree_frame, tree = _create_kconfig_tree(panedwindow)
    +    desc_frame, desc = _create_kconfig_desc(panedwindow)
    +
    +    panedwindow.add(tree_frame, weight=1)
    +    panedwindow.add(desc_frame)
    +
    +    def tree_select(_):
    +        # The Text widget does not allow editing the text in its disabled
    +        # state. We need to temporarily enable it.
    +        desc["state"] = "normal"
    +
    +        sel = tree.selection()
    +        if not sel:
    +            desc.delete("1.0", "end")
    +            desc["state"] = "disabled"
    +            return
    +
    +        # Text.replace() is not available in Python 2's Tkinter
    +        desc.delete("1.0", "end")
    +        desc.insert("end", _info_str(_id_to_node[sel[0]]))
    +
    +        desc["state"] = "disabled"
    +
    +    tree.bind("<<TreeviewSelect>>", tree_select)
    +    tree.bind("<1>", _tree_click)
    +    tree.bind("<Double-1>", _tree_double_click)
    +    tree.bind("<Return>", _tree_enter)
    +    tree.bind("<KP_Enter>", _tree_enter)
    +    tree.bind("<space>", _tree_toggle)
    +    tree.bind("n", _tree_set_val(0))
    +    tree.bind("m", _tree_set_val(1))
    +    tree.bind("y", _tree_set_val(2))
    +
    +    return panedwindow, tree
    +
    +
    +def _create_kconfig_tree(parent):
    +    # Creates a Treeview for showing Kconfig nodes
    +
    +    frame = ttk.Frame(parent)
    +
    +    tree = ttk.Treeview(frame, selectmode="browse", height=20,
    +                        columns=("name",))
    +    tree.heading("#0", text="Option", anchor="w")
    +    tree.heading("name", text="Name", anchor="w")
    +
    +    tree.tag_configure("n-bool", image=_n_bool_img)
    +    tree.tag_configure("y-bool", image=_y_bool_img)
    +    tree.tag_configure("m-tri", image=_m_tri_img)
    +    tree.tag_configure("n-tri", image=_n_tri_img)
    +    tree.tag_configure("m-tri", image=_m_tri_img)
    +    tree.tag_configure("y-tri", image=_y_tri_img)
    +    tree.tag_configure("m-my", image=_m_my_img)
    +    tree.tag_configure("y-my", image=_y_my_img)
    +    tree.tag_configure("n-locked", image=_n_locked_img)
    +    tree.tag_configure("m-locked", image=_m_locked_img)
    +    tree.tag_configure("y-locked", image=_y_locked_img)
    +    tree.tag_configure("not-selected", image=_not_selected_img)
    +    tree.tag_configure("selected", image=_selected_img)
    +    tree.tag_configure("edit", image=_edit_img)
    +    tree.tag_configure("invisible", foreground="red")
    +
    +    tree.grid(column=0, row=0, sticky="nsew")
    +
    +    _add_vscrollbar(frame, tree)
    +
    +    frame.columnconfigure(0, weight=1)
    +    frame.rowconfigure(0, weight=1)
    +
    +    # Create items for all menu nodes. These can be detached/moved later.
    +    # Micro-optimize this a bit.
    +    insert = tree.insert
    +    id_ = id
    +    Symbol_ = Symbol
    +    for node in _kconf.node_iter():
    +        item = node.item
    +        insert("", "end", iid=id_(node),
    +               values=item.name if item.__class__ is Symbol_ else "")
    +
    +    return frame, tree
    +
    +
    +def _create_kconfig_desc(parent):
    +    # Creates a Text for showing the description of the selected Kconfig 
node
    +
    +    frame = ttk.Frame(parent)
    +
    +    desc = Text(frame, height=12, wrap="none", borderwidth=0,
    +                state="disabled")
    +    desc.grid(column=0, row=0, sticky="nsew")
    +
    +    # Work around not being to Ctrl-C/V text from a disabled Text widget, 
with a
    +    # tip found in 
https://stackoverflow.com/questions/3842155/is-there-a-way-to-make-the-tkinter-text-widget-read-only
    +    desc.bind("<1>", lambda _: desc.focus_set())
    +
    +    _add_vscrollbar(frame, desc)
    +
    +    frame.columnconfigure(0, weight=1)
    +    frame.rowconfigure(0, weight=1)
    +
    +    return frame, desc
    +
    +
    +def _add_vscrollbar(parent, widget):
    +    # Adds a vertical scrollbar to 'widget' that's only shown as needed
    +
    +    vscrollbar = ttk.Scrollbar(parent, orient="vertical",
    +                               command=widget.yview)
    +    vscrollbar.grid(column=1, row=0, sticky="ns")
    +
    +    def yscrollcommand(first, last):
    +        # Only show the scrollbar when needed. 'first' and 'last' are
    +        # strings.
    +        if float(first) <= 0.0 and float(last) >= 1.0:
    +            vscrollbar.grid_remove()
    +        else:
    +            vscrollbar.grid()
    +
    +        vscrollbar.set(first, last)
    +
    +    widget["yscrollcommand"] = yscrollcommand
    +
    +
    +def _create_status_bar():
    +    # Creates the status bar at the bottom of the main window
    +
    +    global _status_label
    +
    +    _status_label = ttk.Label(_root, anchor="e", padding="0 0 0.4c 0")
    +    _status_label.grid(column=0, row=3, sticky="ew")
    +
    +
    +def _set_status(s):
    +    # Sets the text in the status bar to 's'
    +
    +    _status_label["text"] = s
    +
    +
    +def _set_conf_changed(changed):
    +    # Updates the status re. whether there are unsaved changes
    +
    +    global _conf_changed
    +
    +    _conf_changed = changed
    +    if changed:
    +        _set_status("Modified")
    +
    +
    +def _update_tree():
    +    # Updates the Kconfig tree in the main window by first detaching all 
nodes
    +    # and then updating and reattaching them. The tree structure might have
    +    # changed.
    +
    +    # If a selected/focused item is detached and later reattached, it stays
    +    # selected/focused. That can give multiple selections even though
    +    # selectmode=browse. Save and later restore the selection and focus as 
a
    +    # workaround.
    +    old_selection = _tree.selection()
    +    old_focus = _tree.focus()
    +
    +    # Detach all tree items before re-stringing them. This is relatively 
fast,
    +    # luckily.
    +    _tree.detach(*_id_to_node.keys())
    +
    +    if _single_menu:
    +        _build_menu_tree()
    +    else:
    +        _build_full_tree(_kconf.top_node)
    +
    +    _tree.selection_set(old_selection)
    +    _tree.focus(old_focus)
    +
    +
    +def _build_full_tree(menu):
    +    # Updates the tree starting from menu.list, in full-tree mode. To speed
    +    # things up, only open menus are updated. The menu-at-a-time logic 
here is
    +    # to deal with invisible items that can show up outside show-all mode 
(see
    +    # _shown_full_nodes()).
    +
    +    for node in _shown_full_nodes(menu):
    +        _add_to_tree(node, _kconf.top_node)
    +
    +        # _shown_full_nodes() includes nodes from menus rooted at symbols, 
so
    +        # we only need to check "real" menus/choices here
    +        if node.list and not isinstance(node.item, Symbol):
    +            if _tree.item(id(node), "open"):
    +                _build_full_tree(node)
    +            else:
    +                # We're just probing here, so _shown_menu_nodes() will work
    +                # fine, and might be a bit faster
    +                shown = _shown_menu_nodes(node)
    +                if shown:
    +                    # Dummy element to make the open/closed toggle appear
    +                    _tree.move(id(shown[0]), id(shown[0].parent), "end")
    +
    +
    +def _shown_full_nodes(menu):
    +    # Returns the list of menu nodes shown in 'menu' (a menu node for a 
menu)
    +    # for full-tree mode. A tricky detail is that invisible items need to 
be
    +    # shown if they have visible children.
    +
    +    def rec(node):
    +        res = []
    +
    +        while node:
    +            if _visible(node) or _show_all:
    +                res.append(node)
    +                if node.list and isinstance(node.item, Symbol):
    +                    # Nodes from menu created from dependencies
    +                    res += rec(node.list)
    +
    +            elif node.list and isinstance(node.item, Symbol):
    +                # Show invisible symbols (defined with either 'config' and
    +                # 'menuconfig') if they have visible children. This can 
happen
    +                # for an m/y-valued symbol with an optional prompt
    +                # ('prompt "foo" is COND') that is currently disabled.
    +                shown_children = rec(node.list)
    +                if shown_children:
    +                    res.append(node)
    +                    res += shown_children
    +
    +            node = node.next
    +
    +        return res
    +
    +    return rec(menu.list)
    +
    +
    +def _build_menu_tree():
    +    # Updates the tree in single-menu mode. See _build_full_tree() as well.
    +
    +    for node in _shown_menu_nodes(_cur_menu):
    +        _add_to_tree(node, _cur_menu)
    +
    +
    +def _shown_menu_nodes(menu):
    +    # Used for single-menu mode. Similar to _shown_full_nodes(), but 
doesn't
    +    # include children of symbols defined with 'menuconfig'.
    +
    +    def rec(node):
    +        res = []
    +
    +        while node:
    +            if _visible(node) or _show_all:
    +                res.append(node)
    +                if node.list and not node.is_menuconfig:
    +                    res += rec(node.list)
    +
    +            elif node.list and isinstance(node.item, Symbol):
    +                shown_children = rec(node.list)
    +                if shown_children:
    +                    # Invisible item with visible children
    +                    res.append(node)
    +                    if not node.is_menuconfig:
    +                        res += shown_children
    +
    +            node = node.next
    +
    +        return res
    +
    +    return rec(menu.list)
    +
    +
    +def _visible(node):
    +    # Returns True if the node should appear in the menu (outside show-all
    +    # mode)
    +
    +    return node.prompt and expr_value(node.prompt[1]) and not \
    +        (node.item == MENU and not expr_value(node.visibility))
    +
    +
    +def _add_to_tree(node, top):
    +    # Adds 'node' to the tree, at the end of its menu. We rely on going 
through
    +    # the nodes linearly to get the correct order. 'top' holds the menu 
that
    +    # corresponds to the top-level menu, and can vary in single-menu mode.
    +
    +    parent = node.parent
    +    _tree.move(id(node), "" if parent is top else id(parent), "end")
    +    _tree.item(
    +        id(node),
    +        text=_node_str(node),
    +        # The _show_all test avoids showing invisible items in red outside
    +        # show-all mode, which could look confusing/broken. Invisible 
symbols
    +        # are shown outside show-all mode if an invisible symbol has 
visible
    +        # children in an implicit menu.
    +        tags=_img_tag(node) if _visible(node) or not _show_all else
    +            _img_tag(node) + " invisible")
    +
    +
    +def _node_str(node):
    +    # Returns the string shown to the right of the image (if any) for the 
node
    +
    +    if node.prompt:
    +        if node.item == COMMENT:
    +            s = "*** {} ***".format(node.prompt[0])
    +        else:
    +            s = node.prompt[0]
    +
    +        if isinstance(node.item, Symbol):
    +            sym = node.item
    +
    +            # Print "(NEW)" next to symbols without a user value (from 
e.g. a
    +            # .config), but skip it for choice symbols in choices in y 
mode,
    +            # and for symbols of UNKNOWN type (which generate a warning 
though)
    +            if sym.user_value is None and sym.type and not \
    +                (sym.choice and sym.choice.tri_value == 2):
    +
    +                s += " (NEW)"
    +
    +    elif isinstance(node.item, Symbol):
    +        # Symbol without prompt (can show up in show-all)
    +        s = "<{}>".format(node.item.name)
    +
    +    else:
    +        # Choice without prompt. Use standard_sc_expr_str() so that it 
shows up
    +        # as '<choice (name if any)>'.
    +        s = standard_sc_expr_str(node.item)
    +
    +
    +    if isinstance(node.item, Symbol):
    +        sym = node.item
    +        if sym.orig_type == STRING:
    +            s += ": " + sym.str_value
    +        elif sym.orig_type in (INT, HEX):
    +            s = "({}) {}".format(sym.str_value, s)
    +
    +    elif isinstance(node.item, Choice) and node.item.tri_value == 2:
    +        # Print the prompt of the selected symbol after the choice for
    +        # choices in y mode
    +        sym = node.item.selection
    +        if sym:
    +            for sym_node in sym.nodes:
    +                # Use the prompt used at this choice location, in case the
    +                # choice symbol is defined in multiple locations
    +                if sym_node.parent is node and sym_node.prompt:
    +                    s += " ({})".format(sym_node.prompt[0])
    +                    break
    +            else:
    +                # If the symbol isn't defined at this choice location, then
    +                # just use whatever prompt we can find for it
    +                for sym_node in sym.nodes:
    +                    if sym_node.prompt:
    +                        s += " ({})".format(sym_node.prompt[0])
    +                        break
    +
    +    # In single-menu mode, print "--->" next to nodes that have menus that 
can
    +    # potentially be entered. Print "----" if the menu is empty. We don't 
allow
    +    # those to be entered.
    +    if _single_menu and node.is_menuconfig:
    +        s += "  --->" if _shown_menu_nodes(node) else "  ----"
    +
    +    return s
    +
    +
    +def _img_tag(node):
    +    # Returns the tag for the image that should be shown next to 'node', 
or the
    +    # empty string if it shouldn't have an image
    +
    +    item = node.item
    +
    +    if item in (MENU, COMMENT) or not item.orig_type:
    +        return ""
    +
    +    if item.orig_type in (STRING, INT, HEX):
    +        return "edit"
    +
    +    # BOOL or TRISTATE
    +
    +    if _is_y_mode_choice_sym(item):
    +        # Choice symbol in y-mode choice
    +        return "selected" if item.choice.selection is item else 
"not-selected"
    +
    +    if len(item.assignable) <= 1:
    +        # Pinned to a single value
    +        return "" if isinstance(item, Choice) else item.str_value + 
"-locked"
    +
    +    if item.type == BOOL:
    +        return item.str_value + "-bool"
    +
    +    # item.type == TRISTATE
    +    if item.assignable == (1, 2):
    +        return item.str_value + "-my"
    +    return item.str_value + "-tri"
    +
    +
    +def _is_y_mode_choice_sym(item):
    +    # The choice mode is an upper bound on the visibility of choice 
symbols, so
    +    # we can check the choice symbols' own visibility to see if the choice 
is
    +    # in y mode
    +    return isinstance(item, Symbol) and item.choice and item.visibility == 
2
    +
    +
    +def _tree_click(event):
    +    # Click on the Kconfig Treeview
    +
    +    tree = event.widget
    +    if tree.identify_element(event.x, event.y) == "image":
    +        item = tree.identify_row(event.y)
    +        # Select the item before possibly popping up a dialog for
    +        # string/int/hex items, so that its help is visible
    +        _select(tree, item)
    +        _change_node(_id_to_node[item], tree.winfo_toplevel())
    +        return "break"
    +
    +
    +def _tree_double_click(event):
    +    # Double-click on the Kconfig treeview
    +
    +    # Do an extra check to avoid weirdness when double-clicking in the tree
    +    # heading area
    +    if not _in_heading(event):
    +        return _tree_enter(event)
    +
    +
    +def _in_heading(event):
    +    # Returns True if 'event' took place in the tree heading
    +
    +    tree = event.widget
    +    return hasattr(tree, "identify_region") and \
    +        tree.identify_region(event.x, event.y) in ("heading", "separator")
    +
    +
    +def _tree_enter(event):
    +    # Enter press or double-click within the Kconfig treeview. Prefer to
    +    # open/close/enter menus, but toggle the value if that's not possible.
    +
    +    tree = event.widget
    +    sel = tree.focus()
    +    if sel:
    +        node = _id_to_node[sel]
    +
    +        if tree.get_children(sel):
    +            _tree_toggle_open(sel)
    +        elif _single_menu_mode_menu(node, tree):
    +            _enter_menu_and_select_first(node)
    +        else:
    +            _change_node(node, tree.winfo_toplevel())
    +
    +        return "break"
    +
    +
    +def _tree_toggle(event):
    +    # Space press within the Kconfig treeview. Prefer to toggle the value, 
but
    +    # open/close/enter the menu if that's not possible.
    +
    +    tree = event.widget
    +    sel = tree.focus()
    +    if sel:
    +        node = _id_to_node[sel]
    +
    +        if _changeable(node):
    +            _change_node(node, tree.winfo_toplevel())
    +        elif _single_menu_mode_menu(node, tree):
    +            _enter_menu_and_select_first(node)
    +        elif tree.get_children(sel):
    +            _tree_toggle_open(sel)
    +
    +        return "break"
    +
    +
    +def _tree_left_key(_):
    +    # Left arrow key press within the Kconfig treeview
    +
    +    if _single_menu:
    +        # Leave the current menu in single-menu mode
    +        _leave_menu()
    +        return "break"
    +
    +    # Otherwise, default action
    +
    +
    +def _tree_right_key(_):
    +    # Right arrow key press within the Kconfig treeview
    +
    +    sel = _tree.focus()
    +    if sel:
    +        node = _id_to_node[sel]
    +        # If the node can be entered in single-menu mode, do it
    +        if _single_menu_mode_menu(node, _tree):
    +            _enter_menu_and_select_first(node)
    +            return "break"
    +
    +    # Otherwise, default action
    +
    +
    +def _single_menu_mode_menu(node, tree):
    +    # Returns True if single-menu mode is on and 'node' is an (interface)
    +    # menu that can be entered
    +
    +    return _single_menu and tree is _tree and node.is_menuconfig and \
    +           _shown_menu_nodes(node)
    +
    +
    +def _changeable(node):
    +    # Returns True if 'node' is a Symbol/Choice whose value can be changed
    +
    +    sc = node.item
    +
    +    if not isinstance(sc, (Symbol, Choice)):
    +        return False
    +
    +    # This will hit for invisible symbols, which appear in show-all mode 
and
    +    # when an invisible symbol has visible children (which can happen e.g. 
for
    +    # symbols with optional prompts)
    +    if not (node.prompt and expr_value(node.prompt[1])):
    +        return False
    +
    +    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
    +           or _is_y_mode_choice_sym(sc)
    +
    +
    +def _tree_toggle_open(item):
    +    # Opens/closes the Treeview item 'item'
    +
    +    if _tree.item(item, "open"):
    +        _tree.item(item, open=False)
    +    else:
    +        node = _id_to_node[item]
    +        if not isinstance(node.item, Symbol):
    +            # Can only get here in full-tree mode
    +            _build_full_tree(node)
    +        _tree.item(item, open=True)
    +
    +
    +def _tree_set_val(tri_val):
    +    def tree_set_val(event):
    +        # n/m/y press within the Kconfig treeview
    +
    +        # Sets the value of the currently selected item to 'tri_val', if 
that
    +        # value can be assigned
    +
    +        sel = event.widget.focus()
    +        if sel:
    +            sc = _id_to_node[sel].item
    +            if isinstance(sc, (Symbol, Choice)) and tri_val in 
sc.assignable:
    +                _set_val(sc, tri_val)
    +
    +    return tree_set_val
    +
    +
    +def _tree_open(_):
    +    # Lazily populates the Kconfig tree when menus are opened in full-tree 
mode
    +
    +    if _single_menu:
    +        # Work around https://core.tcl.tk/tk/tktview?name=368fa4561e
    +        # ("ttk::treeview open/closed indicators can be toggled while 
hidden").
    +        # Clicking on the hidden indicator will call _build_full_tree() in
    +        # single-menu mode otherwise.
    +        return
    +
    +    node = _id_to_node[_tree.focus()]
    +    # _shown_full_nodes() includes nodes from menus rooted at symbols, so 
we
    +    # only need to check "real" menus and choices here
    +    if not isinstance(node.item, Symbol):
    +        _build_full_tree(node)
    +
    +
    +def _update_menu_path(_):
    +    # Updates the displayed menu path when nodes are selected in the 
Kconfig
    +    # treeview
    +
    +    sel = _tree.selection()
    +    _menupath["text"] = _menu_path_info(_id_to_node[sel[0]]) if sel else ""
    +
    +
    +def _item_row(item):
    +    # Returns the row number 'item' appears on within the Kconfig treeview,
    +    # starting from the top of the tree. Used to preserve scrolling.
    +    #
    +    # ttkTreeview.c in the Tk sources defines a RowNumber() function that 
does
    +    # the same thing, but it's not exposed.
    +
    +    row = 0
    +
    +    while True:
    +        prev = _tree.prev(item)
    +        if prev:
    +            item = prev
    +            row += _n_rows(item)
    +        else:
    +            item = _tree.parent(item)
    +            if not item:
    +                return row
    +            row += 1
    +
    +
    +def _n_rows(item):
    +    # _item_row() helper. Returns the number of rows occupied by 'item' 
and #
    +    # its children.
    +
    +    rows = 1
    +
    +    if _tree.item(item, "open"):
    +        for child in _tree.get_children(item):
    +            rows += _n_rows(child)
    +
    +    return rows
    +
    +
    +def _attached(item):
    +    # Heuristic for checking if a Treeview item is attached. Doesn't seem 
to be
    +    # good APIs for this. Might fail for super-obscure cases with tiny 
trees,
    +    # but you'd just get a small scroll mess-up.
    +
    +    return bool(_tree.next(item) or _tree.prev(item) or _tree.parent(item))
    +
    +
    +def _change_node(node, parent):
    +    # Toggles/changes the value of 'node'. 'parent' is the parent window
    +    # (either the main window or the jump-to dialog), in case we need to 
pop up
    +    # a dialog.
    +
    +    if not _changeable(node):
    +        return
    +
    +    # sc = symbol/choice
    +    sc = node.item
    +
    +    if sc.type in (INT, HEX, STRING):
    +        s = _set_val_dialog(node, parent)
    +
    +        # Tkinter can return 'unicode' strings on Python 2, which 
Kconfiglib
    +        # can't deal with. UTF-8-encode the string to work around it.
    +        if _PY2 and isinstance(s, unicode):
    +            s = s.encode("utf-8", "ignore")
    +
    +        if s is not None:
    +            _set_val(sc, s)
    +
    +    elif len(sc.assignable) == 1:
    +        # Handles choice symbols for choices in y mode, which are a special
    +        # case: .assignable can be (2,) while .tri_value is 0.
    +        _set_val(sc, sc.assignable[0])
    +
    +    else:
    +        # Set the symbol to the value after the current value in
    +        # sc.assignable, with wrapping
    +        val_index = sc.assignable.index(sc.tri_value)
    +        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
    +
    +
    +def _set_val(sc, val):
    +    # Wrapper around Symbol/Choice.set_value() for updating the menu state 
and
    +    # _conf_changed
    +
    +    # Use the string representation of tristate values. This makes the 
format
    +    # consistent for all symbol types.
    +    if val in TRI_TO_STR:
    +        val = TRI_TO_STR[val]
    +
    +    if val != sc.str_value:
    +        sc.set_value(val)
    +        _set_conf_changed(True)
    +
    +        # Update the tree and try to preserve the scroll. Do a cheaper 
variant
    +        # than in the show-all case, that might mess up the scroll 
slightly in
    +        # rare cases, but is fast and flicker-free.
    +
    +        stayput = _loc_ref_item()  # Item to preserve scroll for
    +        old_row = _item_row(stayput)
    +
    +        _update_tree()
    +
    +        # If the reference item disappeared (can happen if the change was 
done
    +        # from the jump-to dialog), then avoid messing with the scroll and 
hope
    +        # for the best
    +        if _attached(stayput):
    +            _tree.yview_scroll(_item_row(stayput) - old_row, "units")
    +
    +        if _jump_to_tree:
    +            _update_jump_to_display()
    +
    +
    +def _set_val_dialog(node, parent):
    +    # Pops up a dialog for setting the value of the string/int/hex
    +    # symbol at node 'node'. 'parent' is the parent window.
    +
    +    def ok(_=None):
    +        # No 'nonlocal' in Python 2
    +        global _entry_res
    +
    +        s = entry.get()
    +        if sym.type == HEX and not s.startswith(("0x", "0X")):
    +            s = "0x" + s
    +
    +        if _check_valid(dialog, entry, sym, s):
    +            _entry_res = s
    +            dialog.destroy()
    +
    +    def cancel(_=None):
    +        global _entry_res
    +        _entry_res = None
    +        dialog.destroy()
    +
    +    sym = node.item
    +
    +    dialog = Toplevel(parent)
    +    dialog.title("Enter {} value".format(TYPE_TO_STR[sym.type]))
    +    dialog.resizable(False, False)
    +    dialog.transient(parent)
    +    dialog.protocol("WM_DELETE_WINDOW", cancel)
    +
    +    ttk.Label(dialog, text=node.prompt[0] + ":") \
    +        .grid(column=0, row=0, columnspan=2, sticky="w", padx=".3c",
    +              pady=".2c .05c")
    +
    +    entry = ttk.Entry(dialog, width=30)
    +    # Start with the previous value in the editbox, selected
    +    entry.insert(0, sym.str_value)
    +    entry.selection_range(0, "end")
    +    entry.grid(column=0, row=1, columnspan=2, sticky="ew", padx=".3c")
    +    entry.focus_set()
    +
    +    range_info = _range_info(sym)
    +    if range_info:
    +        ttk.Label(dialog, text=range_info) \
    +            .grid(column=0, row=2, columnspan=2, sticky="w", padx=".3c",
    +                  pady=".2c 0")
    +
    +    ttk.Button(dialog, text="OK", command=ok) \
    +        .grid(column=0, row=4 if range_info else 3, sticky="e", padx=".3c",
    +              pady=".4c")
    +
    +    ttk.Button(dialog, text="Cancel", command=cancel) \
    +        .grid(column=1, row=4 if range_info else 3, padx="0 .3c")
    +
    +    # Give all horizontal space to the grid cell with the OK button, so 
that
    +    # Cancel moves to the right
    +    dialog.columnconfigure(0, weight=1)
    +
    +    _center_on_root(dialog)
    +
    +    # Hack to scroll the entry so that the end of the text is shown, from
    +    # 
https://stackoverflow.com/questions/29334544/why-does-tkinters-entry-xview-moveto-fail.
    +    # Related Tk ticket: https://core.tcl.tk/tk/info/2513186fff
    +    def scroll_entry(_):
    +        _root.update_idletasks()
    +        entry.unbind("<Expose>")
    +        entry.xview_moveto(1)
    +    entry.bind("<Expose>", scroll_entry)
    +
    +    # The dialog must be visible before we can grab the input
    +    dialog.wait_visibility()
    +    dialog.grab_set()
    +
    +    dialog.bind("<Return>", ok)
    +    dialog.bind("<KP_Enter>", ok)
    +    dialog.bind("<Escape>", cancel)
    +
    +    # Wait for the user to be done with the dialog
    +    parent.wait_window(dialog)
    +
    +    # Regrab the input in the parent
    +    parent.grab_set()
    +
    +    return _entry_res
    +
    +
    +def _center_on_root(dialog):
    +    # Centers 'dialog' on the root window. It often ends up at some bad 
place
    +    # like the top-left corner of the screen otherwise. See the 
menuconfig()
    +    # function, which has similar logic.
    +
    +    dialog.withdraw()
    +    _root.update_idletasks()
    +
    +    dialog_width = dialog.winfo_reqwidth()
    +    dialog_height = dialog.winfo_reqheight()
    +
    +    screen_width = _root.winfo_screenwidth()
    +    screen_height = _root.winfo_screenheight()
    +
    +    x = _root.winfo_rootx() + (_root.winfo_width() - dialog_width)//2
    +    y = _root.winfo_rooty() + (_root.winfo_height() - dialog_height)//2
    +
    +    # Clamp so that no part of the dialog is outside the screen
    +    if x + dialog_width > screen_width:
    +        x = screen_width - dialog_width
    +    elif x < 0:
    +        x = 0
    +    if y + dialog_height > screen_height:
    +        y = screen_height - dialog_height
    +    elif y < 0:
    +        y = 0
    +
    +    dialog.geometry("+{}+{}".format(x, y))
    +
    +    dialog.deiconify()
    +
    +
    +def _check_valid(dialog, entry, sym, s):
    +    # Returns True if the string 's' is a well-formed value for 'sym'.
    +    # Otherwise, pops up an error and returns False.
    +
    +    if sym.type not in (INT, HEX):
    +        # Anything goes for non-int/hex symbols
    +        return True
    +
    +    base = 10 if sym.type == INT else 16
    +    try:
    +        int(s, base)
    +    except ValueError:
    +        messagebox.showerror(
    +            "Bad value",
    +            "'{}' is a malformed {} value".format(
    +                s, TYPE_TO_STR[sym.type]),
    +            parent=dialog)
    +        entry.focus_set()
    +        return False
    +
    +    for low_sym, high_sym, cond in sym.ranges:
    +        if expr_value(cond):
    +            low_s = low_sym.str_value
    +            high_s = high_sym.str_value
    +
    +            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
    +                messagebox.showerror(
    +                    "Value out of range",
    +                    "{} is outside the range {}-{}".format(s, low_s, 
high_s),
    +                    parent=dialog)
    +                entry.focus_set()
    +                return False
    +
    +            break
    +
    +    return True
    +
    +
    +def _range_info(sym):
    +    # Returns a string with information about the valid range for the 
symbol
    +    # 'sym', or None if 'sym' doesn't have a range
    +
    +    if sym.type in (INT, HEX):
    +        for low, high, cond in sym.ranges:
    +            if expr_value(cond):
    +                return "Range: {}-{}".format(low.str_value, high.str_value)
    +
    +    return None
    +
    +
    +def _save(_=None):
    +    # Tries to save the configuration
    +
    +    if _try_save(_kconf.write_config, _conf_filename, "configuration"):
    +        _set_conf_changed(False)
    +
    +    _tree.focus_set()
    +
    +
    +def _save_as():
    +    # Pops up a dialog for saving the configuration to a specific location
    +
    +    global _conf_filename
    +
    +    filename = _conf_filename
    +    while True:
    +        filename = filedialog.asksaveasfilename(
    +            title="Save configuration as",
    +            initialdir=os.path.dirname(filename),
    +            initialfile=os.path.basename(filename),
    +            parent=_root)
    +
    +        if not filename:
    +            break
    +
    +        if _try_save(_kconf.write_config, filename, "configuration"):
    +            _conf_filename = filename
    +            break
    +
    +    _tree.focus_set()
    +
    +
    +def _save_minimal():
    +    # Pops up a dialog for saving a minimal configuration (defconfig) to a
    +    # specific location
    +
    +    global _minconf_filename
    +
    +    filename = _minconf_filename
    +    while True:
    +        filename = filedialog.asksaveasfilename(
    +            title="Save minimal configuration as",
    +            initialdir=os.path.dirname(filename),
    +            initialfile=os.path.basename(filename),
    +            parent=_root)
    +
    +        if not filename:
    +            break
    +
    +        if _try_save(_kconf.write_min_config, filename,
    +                     "minimal configuration"):
    +
    +            _minconf_filename = filename
    +            break
    +
    +    _tree.focus_set()
    +
    +
    +def _open(_=None):
    +    # Pops up a dialog for loading a configuration
    +
    +    global _conf_filename
    +
    +    if _conf_changed and \
    +        not messagebox.askokcancel(
    +            "Unsaved changes",
    +            "You have unsaved changes. Load new configuration anyway?"):
    +
    +        return
    +
    +    filename = _conf_filename
    +    while True:
    +        filename = filedialog.askopenfilename(
    +            title="Open configuration",
    +            initialdir=os.path.dirname(filename),
    +            initialfile=os.path.basename(filename),
    +            parent=_root)
    +
    +        if not filename:
    +            break
    +
    +        if _try_load(filename):
    +            # Maybe something fancier could be done here later to try to
    +            # preserve the scroll
    +
    +            _conf_filename = filename
    +            _set_conf_changed(_needs_save())
    +
    +            if _single_menu and not _shown_menu_nodes(_cur_menu):
    +                # Turn on show-all if we're in single-menu mode and would 
end
    +                # up with an empty menu
    +                _show_all_var.set(True)
    +
    +            _update_tree()
    +
    +            break
    +
    +    _tree.focus_set()
    +
    +
    +def _toggle_showname(_):
    +    # Toggles show-name mode on/off
    +
    +    _show_name_var.set(not _show_name_var.get())
    +    _do_showname()
    +
    +
    +def _do_showname():
    +    # Updates the UI for the current show-name setting
    +
    +    # Columns do not automatically shrink/expand, so we have to update
    +    # column widths ourselves
    +
    +    tree_width = _tree.winfo_width()
    +
    +    if _show_name_var.get():
    +        _tree["displaycolumns"] = ("name",)
    +        _tree["show"] = "tree headings"
    +        name_width = tree_width//3
    +        _tree.column("#0", width=max(tree_width - name_width, 1))
    +        _tree.column("name", width=name_width)
    +    else:
    +        _tree["displaycolumns"] = ()
    +        _tree["show"] = "tree"
    +        _tree.column("#0", width=tree_width)
    +
    +    _tree.focus_set()
    +
    +
    +def _toggle_showall(_):
    +    # Toggles show-all mode on/off
    +
    +    _show_all_var.set(not _show_all)
    +    _do_showall()
    +
    +
    +def _do_showall():
    +    # Updates the UI for the current show-all setting
    +
    +    # Don't allow turning off show-all if we're in single-menu mode and the
    +    # current menu would become empty
    +    if _single_menu and not _shown_menu_nodes(_cur_menu):
    +        _show_all_var.set(True)
    +        return
    +
    +    # Save scroll information. old_scroll can end up negative here, if the
    +    # reference item isn't shown (only invisible items on the screen, and
    +    # show-all being turned off).
    +
    +    stayput = _vis_loc_ref_item()
    +    # Probe the middle of the first row, to play it safe. identify_row(0) 
seems
    +    # to return the row before the top row.
    +    old_scroll = _item_row(stayput) - \
    +        _item_row(_tree.identify_row(_treeview_rowheight//2))
    +
    +    _update_tree()
    +
    +    if _show_all:
    +        # Deep magic: Unless we call update_idletasks(), the scroll 
adjustment
    +        # below is restricted to the height of the old tree, instead of the
    +        # height of the new tree. Since the tree with show-all on is 
guaranteed
    +        # to be taller, and we want the maximum range, we only call it when
    +        # turning show-all on.
    +        #
    +        # Strictly speaking, something similar ought to be done when 
changing
    +        # symbol values, but it causes annoying flicker, and in 99% of 
cases
    +        # things work anyway there (with usually minor scroll mess-ups in 
the
    +        # 1% case).
    +        _root.update_idletasks()
    +
    +    # Restore scroll
    +    _tree.yview(_item_row(stayput) - old_scroll)
    +
    +    _tree.focus_set()
    +
    +
    +def _toggle_tree_mode(_):
    +    # Toggles single-menu mode on/off
    +
    +    _single_menu_var.set(not _single_menu)
    +    _do_tree_mode()
    +
    +
    +def _do_tree_mode():
    +    # Updates the UI for the current tree mode (full-tree or single-menu)
    +
    +    loc_ref_node = _id_to_node[_loc_ref_item()]
    +
    +    if not _single_menu:
    +        # _jump_to() -> _enter_menu() already updates the tree, but
    +        # _jump_to() -> load_parents() doesn't, because it isn't always 
needed.
    +        # We always need to update the tree here, e.g. to add/remove 
"--->".
    +        _update_tree()
    +
    +    _jump_to(loc_ref_node)
    +    _tree.focus_set()
    +
    +
    +def _enter_menu_and_select_first(menu):
    +    # Enters the menu 'menu' and selects the first item. Used in 
single-menu
    +    # mode.
    +
    +    _enter_menu(menu)
    +    _select(_tree, _tree.get_children()[0])
    +
    +
    +def _enter_menu(menu):
    +    # Enters the menu 'menu'. Used in single-menu mode.
    +
    +    global _cur_menu
    +
    +    _cur_menu = menu
    +    _update_tree()
    +
    +    _backbutton["state"] = "disabled" if menu is _kconf.top_node else 
"normal"
    +
    +
    +def _leave_menu():
    +    # Leaves the current menu. Used in single-menu mode.
    +
    +    global _cur_menu
    +
    +    if _cur_menu is not _kconf.top_node:
    +        old_menu = _cur_menu
    +
    +        _cur_menu = _parent_menu(_cur_menu)
    +        _update_tree()
    +
    +        _select(_tree, id(old_menu))
    +
    +        if _cur_menu is _kconf.top_node:
    +            _backbutton["state"] = "disabled"
    +
    +    _tree.focus_set()
    +
    +
    +def _select(tree, item):
    +    # Selects, focuses, and see()s 'item' in 'tree'
    +
    +    tree.selection_set(item)
    +    tree.focus(item)
    +    tree.see(item)
    +
    +
    +def _loc_ref_item():
    +    # Returns a Treeview item that can serve as a reference for the current
    +    # scroll location. We try to make this item stay on the same row on the
    +    # screen when updating the tree.
    +
    +    # If the selected item is visible, use that
    +    sel = _tree.selection()
    +    if sel and _tree.bbox(sel[0]):
    +        return sel[0]
    +
    +    # Otherwise, use the middle item on the screen. If it doesn't exist, 
the
    +    # tree is probably really small, so use the first item in the entire 
tree.
    +    return _tree.identify_row(_tree.winfo_height()//2) or \
    +        _tree.get_children()[0]
    +
    +
    +def _vis_loc_ref_item():
    +    # Like _loc_ref_item(), but finds a visible item around the reference 
item.
    +    # Used when changing show-all mode, where non-visible (red) items will
    +    # disappear.
    +
    +    item = _loc_ref_item()
    +
    +    vis_before = _vis_before(item)
    +    if vis_before and _tree.bbox(vis_before):
    +        return vis_before
    +
    +    vis_after = _vis_after(item)
    +    if vis_after and _tree.bbox(vis_after):
    +        return vis_after
    +
    +    return vis_before or vis_after
    +
    +
    +def _vis_before(item):
    +    # _vis_loc_ref_item() helper. Returns the first visible (not red) item,
    +    # searching backwards from 'item'.
    +
    +    while item:
    +        if not _tree.tag_has("invisible", item):
    +            return item
    +
    +        prev = _tree.prev(item)
    +        item = prev if prev else _tree.parent(item)
    +
    +    return None
    +
    +
    +def _vis_after(item):
    +    # _vis_loc_ref_item() helper. Returns the first visible (not red) item,
    +    # searching forwards from 'item'.
    +
    +    while item:
    +        if not _tree.tag_has("invisible", item):
    +            return item
    +
    +        next = _tree.next(item)
    +        if next:
    +            item = next
    +        else:
    +            item = _tree.parent(item)
    +            if not item:
    +                break
    +            item = _tree.next(item)
    +
    +    return None
    +
    +
    +def _on_quit(_=None):
    +    # Called when the user wants to exit
    +
    +    if not _conf_changed:
    +        _quit("No changes to save (for '{}')".format(_conf_filename))
    +        return
    +
    +    while True:
    +        ync = messagebox.askyesnocancel("Quit", "Save changes?")
    +        if ync is None:
    +            return
    +
    +        if not ync:
    +            _quit("Configuration ({}) was not 
saved".format(_conf_filename))
    +            return
    +
    +        if _try_save(_kconf.write_config, _conf_filename, "configuration"):
    +            # _try_save() already prints the "Configuration saved to ..."
    +            # message
    +            _quit()
    +            return
    +
    +
    +def _quit(msg=None):
    +    # Quits the application
    +
    +    # Do not call sys.exit() here, in case we're being run from a script
    +    _root.destroy()
    +    if msg:
    +        print(msg)
    +
    +
    +def _try_save(save_fn, filename, description):
    +    # Tries to save a configuration file. Pops up an error and returns 
False on
    +    # failure.
    +    #
    +    # save_fn:
    +    #   Function to call with 'filename' to save the file
    +    #
    +    # description:
    +    #   String describing the thing being saved
    +
    +    try:
    +        # save_fn() returns a message to print
    +        msg = save_fn(filename)
    +        _set_status(msg)
    +        print(msg)
    +        return True
    +    except EnvironmentError as e:
    +        messagebox.showerror(
    +            "Error saving " + description,
    +            "Error saving {} to '{}': {} (errno: {})"
    +            .format(description, e.filename, e.strerror,
    +                    errno.errorcode[e.errno]))
    +        return False
    +
    +
    +def _try_load(filename):
    +    # Tries to load a configuration file. Pops up an error and returns 
False on
    +    # failure.
    +    #
    +    # filename:
    +    #   Configuration file to load
    +
    +    try:
    +        msg = _kconf.load_config(filename)
    +        _set_status(msg)
    +        print(msg)
    +        return True
    +    except EnvironmentError as e:
    +        messagebox.showerror(
    +            "Error loading configuration",
    +            "Error loading '{}': {} (errno: {})"
    +            .format(filename, e.strerror, errno.errorcode[e.errno]))
    +        return False
    +
    +
    +def _jump_to_dialog(_=None):
    +    # Pops up a dialog for jumping directly to a particular node. Symbol 
values
    +    # can also be changed within the dialog.
    +    #
    +    # Note: There's nothing preventing this from doing an incremental 
search
    +    # like menuconfig.py does, but currently it's a bit jerky for large 
Kconfig
    +    # trees, at least when inputting the beginning of the search string. 
We'd
    +    # need to somehow only update the tree items that are shown in the 
Treeview
    +    # to fix it.
    +
    +    global _jump_to_tree
    +
    +    def search(_=None):
    +        _update_jump_to_matches(msglabel, entry.get())
    +
    +    def jump_to_selected(event=None):
    +        # Jumps to the selected node and closes the dialog
    +
    +        # Ignore double clicks on the image and in the heading area
    +        if event and (tree.identify_element(event.x, event.y) == "image" or
    +                      _in_heading(event)):
    +            return
    +
    +        sel = tree.selection()
    +        if not sel:
    +            return
    +
    +        node = _id_to_node[sel[0]]
    +
    +        if node not in _shown_menu_nodes(_parent_menu(node)):
    +            _show_all_var.set(True)
    +            if not _single_menu:
    +                # See comment in _do_tree_mode()
    +                _update_tree()
    +
    +        _jump_to(node)
    +
    +        dialog.destroy()
    +
    +    def tree_select(_):
    +        jumpto_button["state"] = "normal" if tree.selection() else 
"disabled"
    +
    +
    +    dialog = Toplevel(_root)
    +    dialog.geometry("+{}+{}".format(
    +        _root.winfo_rootx() + 50, _root.winfo_rooty() + 50))
    +    dialog.title("Jump to symbol/choice/menu/comment")
    +    dialog.minsize(128, 128)  # See _create_ui()
    +    dialog.transient(_root)
    +
    +    ttk.Label(dialog, text=_JUMP_TO_HELP) \
    +        .grid(column=0, row=0, columnspan=2, sticky="w", padx=".1c",
    +              pady=".1c")
    +
    +    entry = ttk.Entry(dialog)
    +    entry.grid(column=0, row=1, sticky="ew", padx=".1c", pady=".1c")
    +    entry.focus_set()
    +
    +    entry.bind("<Return>", search)
    +    entry.bind("<KP_Enter>", search)
    +
    +    ttk.Button(dialog, text="Search", command=search) \
    +        .grid(column=1, row=1, padx="0 .1c", pady="0 .1c")
    +
    +    msglabel = ttk.Label(dialog)
    +    msglabel.grid(column=0, row=2, sticky="w", pady="0 .1c")
    +
    +    panedwindow, tree = _create_kconfig_tree_and_desc(dialog)
    +    panedwindow.grid(column=0, row=3, columnspan=2, sticky="nsew")
    +
    +    # Clear tree
    +    tree.set_children("")
    +
    +    _jump_to_tree = tree
    +
    +    jumpto_button = ttk.Button(dialog, text="Jump to selected item",
    +                               state="disabled", command=jump_to_selected)
    +    jumpto_button.grid(column=0, row=4, columnspan=2, sticky="ns", 
pady=".1c")
    +
    +    dialog.columnconfigure(0, weight=1)
    +    # Only the pane with the Kconfig tree and description grows vertically
    +    dialog.rowconfigure(3, weight=1)
    +
    +    # See the menuconfig() function
    +    _root.update_idletasks()
    +    dialog.geometry(dialog.geometry())
    +
    +    # The dialog must be visible before we can grab the input
    +    dialog.wait_visibility()
    +    dialog.grab_set()
    +
    +    tree.bind("<Double-1>", jump_to_selected)
    +    tree.bind("<Return>", jump_to_selected)
    +    tree.bind("<KP_Enter>", jump_to_selected)
    +    # add=True to avoid overriding the description text update
    +    tree.bind("<<TreeviewSelect>>", tree_select, add=True)
    +
    +    dialog.bind("<Escape>", lambda _: dialog.destroy())
    +
    +    # Wait for the user to be done with the dialog
    +    _root.wait_window(dialog)
    +
    +    _jump_to_tree = None
    +
    +    _tree.focus_set()
    +
    +
    +def _update_jump_to_matches(msglabel, search_string):
    +    # Searches for nodes matching the search string and updates
    +    # _jump_to_matches. Puts a message in 'msglabel' if there are no 
matches,
    +    # or regex errors.
    +
    +    global _jump_to_matches
    +
    +    _jump_to_tree.selection_set(())
    +
    +    try:
    +        # We could use re.IGNORECASE here instead of lower(), but this is
    +        # faster for regexes like '.*debug$' (though the '.*' is redundant
    +        # there). Those probably have bad interactions with re.search(), 
which
    +        # matches anywhere in the string.
    +        regex_searches = [re.compile(regex).search
    +                          for regex in search_string.lower().split()]
    +    except re.error as e:
    +        msg = "Bad regular expression"
    +        # re.error.msg was added in Python 3.5
    +        if hasattr(e, "msg"):
    +            msg += ": " + e.msg
    +        msglabel["text"] = msg
    +        # Clear tree
    +        _jump_to_tree.set_children("")
    +        return
    +
    +    _jump_to_matches = []
    +    add_match = _jump_to_matches.append
    +
    +    for node in _sorted_sc_nodes():
    +        # Symbol/choice
    +        sc = node.item
    +
    +        for search in regex_searches:
    +            # Both the name and the prompt might be missing, since
    +            # we're searching both symbols and choices
    +
    +            # Does the regex match either the symbol name or the
    +            # prompt (if any)?
    +            if not (sc.name and search(sc.name.lower()) or
    +                    node.prompt and search(node.prompt[0].lower())):
    +
    +                # Give up on the first regex that doesn't match, to
    +                # speed things up a bit when multiple regexes are
    +                # entered
    +                break
    +
    +        else:
    +            add_match(node)
    +
    +    # Search menus and comments
    +
    +    for node in _sorted_menu_comment_nodes():
    +        for search in regex_searches:
    +            if not search(node.prompt[0].lower()):
    +                break
    +        else:
    +            add_match(node)
    +
    +    msglabel["text"] = "" if _jump_to_matches else "No matches"
    +
    +    _update_jump_to_display()
    +
    +    if _jump_to_matches:
    +        item = id(_jump_to_matches[0])
    +        _jump_to_tree.selection_set(item)
    +        _jump_to_tree.focus(item)
    +
    +
    +def _update_jump_to_display():
    +    # Updates the images and text for the items in _jump_to_matches, and 
sets
    +    # them as the items of _jump_to_tree
    +
    +    # Micro-optimize a bit
    +    item = _jump_to_tree.item
    +    id_ = id
    +    node_str = _node_str
    +    img_tag = _img_tag
    +    visible = _visible
    +    for node in _jump_to_matches:
    +        item(id_(node),
    +             text=node_str(node),
    +             tags=img_tag(node) if visible(node) else
    +                 img_tag(node) + " invisible")
    +
    +    _jump_to_tree.set_children("", *map(id, _jump_to_matches))
    +
    +
    +def _jump_to(node):
    +    # Jumps directly to 'node' and selects it
    +
    +    if _single_menu:
    +        _enter_menu(_parent_menu(node))
    +    else:
    +        _load_parents(node)
    +
    +    _select(_tree, id(node))
    +
    +
    +# Obscure Python: We never pass a value for cached_nodes, and it keeps 
pointing
    +# to the same list. This avoids a global.
    +def _sorted_sc_nodes(cached_nodes=[]):
    +    # Returns a sorted list of symbol and choice nodes to search. The 
symbol
    +    # nodes appear first, sorted by name, and then the choice nodes, 
sorted by
    +    # prompt and (secondarily) name.
    +
    +    if not cached_nodes:
    +        # Add symbol nodes
    +        for sym in sorted(_kconf.unique_defined_syms,
    +                          key=lambda sym: sym.name):
    +            # += is in-place for lists
    +            cached_nodes += sym.nodes
    +
    +        # Add choice nodes
    +
    +        choices = sorted(_kconf.unique_choices,
    +                         key=lambda choice: choice.name or "")
    +
    +        cached_nodes += sorted(
    +            [node
    +             for choice in choices
    +                 for node in choice.nodes],
    +            key=lambda node: node.prompt[0] if node.prompt else "")
    +
    +    return cached_nodes
    +
    +
    +def _sorted_menu_comment_nodes(cached_nodes=[]):
    +    # Returns a list of menu and comment nodes to search, sorted by prompt,
    +    # with the menus first
    +
    +    if not cached_nodes:
    +        def prompt_text(mc):
    +            return mc.prompt[0]
    +
    +        cached_nodes += sorted(_kconf.menus, key=prompt_text)
    +        cached_nodes += sorted(_kconf.comments, key=prompt_text)
    +
    +    return cached_nodes
    +
    +
    +def _load_parents(node):
    +    # Menus are lazily populated as they're opened in full-tree mode, but
    +    # jumping to an item needs its parent menus to be populated. This 
function
    +    # populates 'node's parents.
    +
    +    # Get all parents leading up to 'node', sorted with the root first
    +    parents = []
    +    cur = node.parent
    +    while cur is not _kconf.top_node:
    +        parents.append(cur)
    +        cur = cur.parent
    +    parents.reverse()
    +
    +    for i, parent in enumerate(parents):
    +        if not _tree.item(id(parent), "open"):
    +            # Found a closed menu. Populate it and all the remaining menus
    +            # leading up to 'node'.
    +            for parent in parents[i:]:
    +                # We only need to populate "real" menus/choices. Implicit 
menus
    +                # are populated when their parents menus are entered.
    +                if not isinstance(parent.item, Symbol):
    +                    _build_full_tree(parent)
    +            return
    +
    +
    +def _parent_menu(node):
    +    # Returns the menu node of the menu that contains 'node'. In addition 
to
    +    # proper 'menu's, this might also be a 'menuconfig' symbol or a 
'choice'.
    +    # "Menu" here means a menu in the interface.
    +
    +    menu = node.parent
    +    while not menu.is_menuconfig:
    +        menu = menu.parent
    +    return menu
    +
    +
    +def _trace_write(var, fn):
    +    # Makes fn() be called whenever the Tkinter Variable 'var' changes 
value
    +
    +    # trace_variable() is deprecated according to the docstring,
    +    # which recommends trace_add()
    +    if hasattr(var, "trace_add"):
    +        var.trace_add("write", fn)
    +    else:
    +        var.trace_variable("w", fn)
    +
    +
    +def _info_str(node):
    +    # Returns information about the menu node 'node' as a string.
    +    #
    +    # The helper functions are responsible for adding newlines. This allows
    +    # them to return "" if they don't want to add any output.
    +
    +    if isinstance(node.item, Symbol):
    +        sym = node.item
    +
    +        return (
    +            _name_info(sym) +
    +            _help_info(sym) +
    +            _direct_dep_info(sym) +
    +            _defaults_info(sym) +
    +            _select_imply_info(sym) +
    +            _kconfig_def_info(sym)
    +        )
    +
    +    if isinstance(node.item, Choice):
    +        choice = node.item
    +
    +        return (
    +            _name_info(choice) +
    +            _help_info(choice) +
    +            'Mode: {}\n\n'.format(choice.str_value) +
    +            _choice_syms_info(choice) +
    +            _direct_dep_info(choice) +
    +            _defaults_info(choice) +
    +            _kconfig_def_info(choice)
    +        )
    +
    +    # node.item in (MENU, COMMENT)
    +    return _kconfig_def_info(node)
    +
    +
    +def _name_info(sc):
    +    # Returns a string with the name of the symbol/choice. Choices are 
shown as
    +    # <choice (name if any)>.
    +
    +    return (sc.name if sc.name else standard_sc_expr_str(sc)) + "\n\n"
    +
    +
    +def _value_info(sym):
    +    # Returns a string showing 'sym's value
    +
    +    # Only put quotes around the value for string symbols
    +    return "Value: {}\n".format(
    +        '"{}"'.format(sym.str_value)
    +        if sym.orig_type == STRING
    +        else sym.str_value)
    +
    +
    +def _choice_syms_info(choice):
    +    # Returns a string listing the choice symbols in 'choice'. Adds
    +    # "(selected)" next to the selected one.
    +
    +    s = "Choice symbols:\n"
    +
    +    for sym in choice.syms:
    +        s += "  - " + sym.name
    +        if sym is choice.selection:
    +            s += " (selected)"
    +        s += "\n"
    +
    +    return s + "\n"
    +
    +
    +def _help_info(sc):
    +    # Returns a string with the help text(s) of 'sc' (Symbol or Choice).
    +    # Symbols and choices defined in multiple locations can have multiple 
help
    +    # texts.
    +
    +    s = ""
    +
    +    for node in sc.nodes:
    +        if node.help is not None:
    +            s += node.help + "\n\n"
    +
    +    return s
    +
    +
    +def _direct_dep_info(sc):
    +    # Returns a string describing the direct dependencies of 'sc' (Symbol 
or
    +    # Choice). The direct dependencies are the OR of the dependencies from 
each
    +    # definition location. The dependencies at each definition location 
come
    +    # from 'depends on' and dependencies inherited from parent items.
    +
    +    return "" if sc.direct_dep is _kconf.y else \
    +        'Direct dependencies (={}):\n{}\n' \
    +        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
    +                _split_expr_info(sc.direct_dep, 2))
    +
    +
    +def _defaults_info(sc):
    +    # Returns a string describing the defaults of 'sc' (Symbol or Choice)
    +
    +    if not sc.defaults:
    +        return ""
    +
    +    s = "Default"
    +    if len(sc.defaults) > 1:
    +        s += "s"
    +    s += ":\n"
    +
    +    for val, cond in sc.orig_defaults:
    +        s += "  - "
    +        if isinstance(sc, Symbol):
    +            s += _expr_str(val)
    +
    +            # Skip the tristate value hint if the expression is just a 
single
    +            # symbol. _expr_str() already shows its value as a string.
    +            #
    +            # This also avoids showing the tristate value for 
string/int/hex
    +            # defaults, which wouldn't make any sense.
    +            if isinstance(val, tuple):
    +                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
    +        else:
    +            # Don't print the value next to the symbol name for choice
    +            # defaults, as it looks a bit confusing
    +            s += val.name
    +        s += "\n"
    +
    +        if cond is not _kconf.y:
    +            s += "    Condition (={}):\n{}" \
    +                 .format(TRI_TO_STR[expr_value(cond)],
    +                         _split_expr_info(cond, 4))
    +
    +    return s + "\n"
    +
    +
    +def _split_expr_info(expr, indent):
    +    # Returns a string with 'expr' split into its top-level && or || 
operands,
    +    # with one operand per line, together with the operand's value. This is
    +    # usually enough to get something readable for long expressions. A 
fancier
    +    # recursive thingy would be possible too.
    +    #
    +    # indent:
    +    #   Number of leading spaces to add before the split expression.
    +
    +    if len(split_expr(expr, AND)) > 1:
    +        split_op = AND
    +        op_str = "&&"
    +    else:
    +        split_op = OR
    +        op_str = "||"
    +
    +    s = ""
    +    for i, term in enumerate(split_expr(expr, split_op)):
    +        s += "{}{} {}".format(indent*" ",
    +                              "  " if i == 0 else op_str,
    +                              _expr_str(term))
    +
    +        # Don't bother showing the value hint if the expression is just a
    +        # single symbol. _expr_str() already shows its value.
    +        if isinstance(term, tuple):
    +            s += "  (={})".format(TRI_TO_STR[expr_value(term)])
    +
    +        s += "\n"
    +
    +    return s
    +
    +
    +def _select_imply_info(sym):
    +    # Returns a string with information about which symbols 'select' or 
'imply'
    +    # 'sym'. The selecting/implying symbols are grouped according to which
    +    # value they select/imply 'sym' to (n/m/y).
    +
    +    def sis(expr, val, title):
    +        # sis = selects/implies
    +        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
    +        if not sis:
    +            return ""
    +
    +        res = title
    +        for si in sis:
    +            res += "  - {}\n".format(split_expr(si, AND)[0].name)
    +        return res + "\n"
    +
    +    s = ""
    +
    +    if sym.rev_dep is not _kconf.n:
    +        s += sis(sym.rev_dep, 2,
    +                 "Symbols currently y-selecting this symbol:\n")
    +        s += sis(sym.rev_dep, 1,
    +                 "Symbols currently m-selecting this symbol:\n")
    +        s += sis(sym.rev_dep, 0,
    +                 "Symbols currently n-selecting this symbol (no 
effect):\n")
    +
    +    if sym.weak_rev_dep is not _kconf.n:
    +        s += sis(sym.weak_rev_dep, 2,
    +                 "Symbols currently y-implying this symbol:\n")
    +        s += sis(sym.weak_rev_dep, 1,
    +                 "Symbols currently m-implying this symbol:\n")
    +        s += sis(sym.weak_rev_dep, 0,
    +                 "Symbols currently n-implying this symbol (no effect):\n")
    +
    +    return s
    +
    +
    +def _kconfig_def_info(item):
    +    # Returns a string with the definition of 'item' in Kconfig syntax,
    +    # together with the definition location(s) and their include and menu 
paths
    +
    +    nodes = [item] if isinstance(item, MenuNode) else item.nodes
    +
    +    s = "Kconfig definition{}, with parent deps. propagated to 'depends 
on'\n" \
    +        .format("s" if len(nodes) > 1 else "")
    +    s += (len(s) - 1)*"="
    +
    +    for node in nodes:
    +        s += "\n\n" \
    +             "At {}:{}\n" \
    +             "{}" \
    +             "Menu path: {}\n\n" \
    +             "{}" \
    +             .format(node.filename, node.linenr,
    +                     _include_path_info(node),
    +                     _menu_path_info(node),
    +                     node.custom_str(_name_and_val_str))
    +
    +    return s
    +
    +
    +def _include_path_info(node):
    +    if not node.include_path:
    +        # In the top-level Kconfig file
    +        return ""
    +
    +    return "Included via {}\n".format(
    +        " -> ".join("{}:{}".format(filename, linenr)
    +                    for filename, linenr in node.include_path))
    +
    +
    +def _menu_path_info(node):
    +    # Returns a string describing the menu path leading up to 'node'
    +
    +    path = ""
    +
    +    while node.parent is not _kconf.top_node:
    +        node = node.parent
    +
    +        # Promptless choices might appear among the parents. Use
    +        # standard_sc_expr_str() for them, so that they show up as
    +        # '<choice (name if any)>'.
    +        path = " -> " + (node.prompt[0] if node.prompt else
    +                         standard_sc_expr_str(node.item)) + path
    +
    +    return "(Top)" + path
    +
    +
    +def _name_and_val_str(sc):
    +    # Custom symbol/choice printer that shows symbol values after symbols
    +
    +    # Show the values of non-constant (non-quoted) symbols that don't look 
like
    +    # numbers. Things like 123 are actually symbol references, and only 
work as
    +    # expected due to undefined symbols getting their name as their value.
    +    # Showing the symbol value for those isn't helpful though.
    +    if isinstance(sc, Symbol) and not sc.is_constant and not 
_is_num(sc.name):
    +        if not sc.nodes:
    +            # Undefined symbol reference
    +            return "{}(undefined/n)".format(sc.name)
    +
    +        return '{}(={})'.format(sc.name, sc.str_value)
    +
    +    # For other items, use the standard format
    +    return standard_sc_expr_str(sc)
    +
    +
    +def _expr_str(expr):
    +    # Custom expression printer that shows symbol values
    +    return expr_str(expr, _name_and_val_str)
    +
    +
    +def _is_num(name):
    +    # Heuristic to see if a symbol name looks like a number, for nicer 
output
    +    # when printing expressions. Things like 16 are actually symbol names, 
only
    +    # they get their name as their value when the symbol is undefined.
    +
    +    try:
    +        int(name)
    +    except ValueError:
    +        if not name.startswith(("0x", "0X")):
    +            return False
    +
    +        try:
    +            int(name, 16)
    +        except ValueError:
    +            return False
    +
    +    return True
    +
    +
    +if __name__ == "__main__":
    +    _main()
    diff --git a/support/kconfiglib/kconfiglib.py 
b/support/kconfiglib/kconfiglib.py
    new file mode 100644
    index 0000000..410db28
    --- /dev/null
    +++ b/support/kconfiglib/kconfiglib.py
    @@ -0,0 +1,7018 @@
    +# Copyright (c) 2011-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Overview
    +========
    +
    +Kconfiglib is a Python 2/3 library for scripting and extracting information
    +from Kconfig 
(https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt)
    +configuration systems.
    +
    +See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer
    +overview.
    +
    +Since Kconfiglib 12.0.0, the library version is available in
    +kconfiglib.VERSION, which is a (<major>, <minor>, <patch>) tuple, e.g.
    +(12, 0, 0).
    +
    +
    +Using Kconfiglib on the Linux kernel with the Makefile targets
    +==============================================================
    +
    +For the Linux kernel, a handy interface is provided by the
    +scripts/kconfig/Makefile patch, which can be applied with either 'git am' 
or
    +the 'patch' utility:
    +
    +  $ wget -qO- 
https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | 
git am
    +  $ wget -qO- 
https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | 
patch -p1
    +
    +Warning: Not passing -p1 to patch will cause the wrong file to be patched.
    +
    +Please tell me if the patch does not apply. It should be trivial to apply
    +manually, as it's just a block of text that needs to be inserted near the 
other
    +*conf: targets in scripts/kconfig/Makefile.
    +
    +Look further down for a motivation for the Makefile patch and for 
instructions
    +on how you can use Kconfiglib without it.
    +
    +If you do not wish to install Kconfiglib via pip, the Makefile patch is 
set up
    +so that you can also just clone Kconfiglib into the kernel root:
    +
    +  $ git clone git://github.com/ulfalizer/Kconfiglib.git
    +  $ git am Kconfiglib/makefile.patch  (or 'patch -p1 < 
Kconfiglib/makefile.patch')
    +
    +Warning: The directory name Kconfiglib/ is significant in this case, 
because
    +it's added to PYTHONPATH by the new targets in makefile.patch.
    +
    +The targets added by the Makefile patch are described in the following
    +sections.
    +
    +
    +make kmenuconfig
    +----------------
    +
    +This target runs the curses menuconfig interface with Python 3. As of
    +Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, 
only
    +Python 3 was supported, so this was a backport).
    +
    +
    +make guiconfig
    +--------------
    +
    +This target runs the Tkinter menuconfig interface. Both Python 2 and 
Python 3
    +are supported. To change the Python interpreter used, pass
    +PYTHONCMD=<executable> to 'make'. The default is 'python'.
    +
    +
    +make [ARCH=<arch>] iscriptconfig
    +--------------------------------
    +
    +This target gives an interactive Python prompt where a Kconfig instance has
    +been preloaded and is available in 'kconf'. To change the Python 
interpreter
    +used, pass PYTHONCMD=<executable> to 'make'. The default is 'python'.
    +
    +To get a feel for the API, try evaluating and printing the symbols in
    +kconf.defined_syms, and explore the MenuNode menu tree starting at
    +kconf.top_node by following 'next' and 'list' pointers.
    +
    +The item contained in a menu node is found in MenuNode.item (note that 
this can
    +be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all
    +symbols and choices have a 'nodes' attribute containing their menu nodes
    +(usually only one). Printing a menu node will print its item, in Kconfig
    +format.
    +
    +If you want to look up a symbol by name, use the kconf.syms dictionary.
    +
    +
    +make scriptconfig SCRIPT=<script> [SCRIPT_ARG=<arg>]
    +----------------------------------------------------
    +
    +This target runs the Python script given by the SCRIPT parameter on the
    +configuration. sys.argv[1] holds the name of the top-level Kconfig file
    +(currently always "Kconfig" in practice), and sys.argv[2] holds the 
SCRIPT_ARG
    +argument, if given.
    +
    +See the examples/ subdirectory for example scripts.
    +
    +
    +make dumpvarsconfig
    +-------------------
    +
    +This target prints a list of all environment variables referenced from the
    +Kconfig files, together with their values. See the
    +Kconfiglib/examples/dumpvars.py script.
    +
    +Only environment variables that are referenced via the Kconfig preprocessor
    +$(FOO) syntax are included. The preprocessor was added in Linux 4.18.
    +
    +
    +Using Kconfiglib without the Makefile targets
    +=============================================
    +
    +The make targets are only needed to pick up environment variables exported 
from
    +the Kbuild makefiles and referenced inside Kconfig files, via e.g.
    +'source "arch/$(SRCARCH)/Kconfig" and commands run via '$(shell,...)'.
    +
    +These variables are referenced as of writing (Linux 4.18), together with 
sample
    +values:
    +
    +  srctree          (.)
    +  ARCH             (x86)
    +  SRCARCH          (x86)
    +  KERNELVERSION    (4.18.0)
    +  CC               (gcc)
    +  HOSTCC           (gcc)
    +  HOSTCXX          (g++)
    +  CC_VERSION_TEXT  (gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0)
    +
    +Older kernels only reference ARCH, SRCARCH, and KERNELVERSION.
    +
    +If your kernel is recent enough (4.18+), you can get a list of referenced
    +environment variables via 'make dumpvarsconfig' (see above). Note that this
    +command is added by the Makefile patch.
    +
    +To run Kconfiglib without the Makefile patch, set the environment variables
    +manually:
    +
    +  $ srctree=. ARCH=x86 SRCARCH=x86 KERNELVERSION=`make kernelversion` ... 
python(3)
    +  >>> import kconfiglib
    +  >>> kconf = kconfiglib.Kconfig()  # filename defaults to "Kconfig"
    +
    +Search the top-level Makefile for "Additional ARCH settings" to see other
    +possibilities for ARCH and SRCARCH.
    +
    +
    +Intro to symbol values
    +======================
    +
    +Kconfiglib has the same assignment semantics as the C implementation.
    +
    +Any symbol can be assigned a value by the user (via Kconfig.load_config() 
or
    +Symbol.set_value()), but this user value is only respected if the symbol is
    +visible, which corresponds to it (currently) being visible in the 
menuconfig
    +interface.
    +
    +For symbols with prompts, the visibility of the symbol is determined by the
    +condition on the prompt. Symbols without prompts are never visible, so 
setting
    +a user value on them is pointless. A warning will be printed by default if
    +Symbol.set_value() is called on a promptless symbol. Assignments to 
promptless
    +symbols are normal within a .config file, so no similar warning will be 
printed
    +by load_config().
    +
    +Dependencies from parents and 'if'/'depends on' are propagated to 
properties,
    +including prompts, so these two configurations are logically equivalent:
    +
    +(1)
    +
    +  menu "menu"
    +      depends on A
    +
    +  if B
    +
    +  config FOO
    +      tristate "foo" if D
    +      default y
    +      depends on C
    +
    +  endif
    +
    +  endmenu
    +
    +(2)
    +
    +  menu "menu"
    +      depends on A
    +
    +  config FOO
    +      tristate "foo" if A && B && C && D
    +      default y if A && B && C
    +
    +  endmenu
    +
    +In this example, A && B && C && D (the prompt condition) needs to be non-n 
for
    +FOO to be visible (assignable). If its value is m, the symbol can only be
    +assigned the value m: The visibility sets an upper bound on the value that 
can
    +be assigned by the user, and any higher user value will be truncated down.
    +
    +'default' properties are independent of the visibility, though a 'default' 
will
    +often get the same condition as the prompt due to dependency propagation.
    +'default' properties are used if the symbol is not visible or has no user
    +value.
    +
    +Symbols with no user value (or that have a user value but are not visible) 
and
    +no (active) 'default' default to n for bool/tristate symbols, and to the 
empty
    +string for other symbol types.
    +
    +'select' works similarly to symbol visibility, but sets a lower bound on 
the
    +value of the symbol. The lower bound is determined by the value of the
    +select*ing* symbol. 'select' does not respect visibility, so non-visible
    +symbols can be forced to a particular (minimum) value by a select as well.
    +
    +For non-bool/tristate symbols, it only matters whether the visibility is n 
or
    +non-n: m visibility acts the same as y visibility.
    +
    +Conditions on 'default' and 'select' work in mostly intuitive ways. If the
    +condition is n, the 'default' or 'select' is disabled. If it is m, the
    +'default' or 'select' value (the value of the selecting symbol) is 
truncated
    +down to m.
    +
    +When writing a configuration with Kconfig.write_config(), only symbols 
that are
    +visible, have an (active) default, or are selected will get written out 
(note
    +that this includes all symbols that would accept user values). Kconfiglib
    +matches the .config format produced by the C implementations down to the
    +character. This eases testing.
    +
    +For a visible bool/tristate symbol FOO with value n, this line is written 
to
    +.config:
    +
    +    # CONFIG_FOO is not set
    +
    +The point is to remember the user n selection (which might differ from the
    +default value the symbol would get), while at the same sticking to the rule
    +that undefined corresponds to n (.config uses Makefile format, making the 
line
    +above a comment). When the .config file is read back in, this line will be
    +treated the same as the following assignment:
    +
    +    CONFIG_FOO=n
    +
    +In Kconfiglib, the set of (currently) assignable values for a bool/tristate
    +symbol appear in Symbol.assignable. For other symbol types, just check if
    +sym.visibility is non-0 (non-n) to see whether the user value will have an
    +effect.
    +
    +
    +Intro to the menu tree
    +======================
    +
    +The menu structure, as seen in e.g. menuconfig, is represented by a tree of
    +MenuNode objects. The top node of the configuration corresponds to an 
implicit
    +top-level menu, the title of which is shown at the top in the standard
    +menuconfig interface. (The title is also available in 
Kconfig.mainmenu_text in
    +Kconfiglib.)
    +
    +The top node is found in Kconfig.top_node. From there, you can visit child 
menu
    +nodes by following the 'list' pointer, and any following menu nodes by
    +following the 'next' pointer. Usually, a non-None 'list' pointer indicates 
a
    +menu or Choice, but menu nodes for symbols can sometimes have a non-None 
'list'
    +pointer too due to submenus created implicitly from dependencies.
    +
    +MenuNode.item is either a Symbol or a Choice object, or one of the 
constants
    +MENU and COMMENT. The prompt of the menu node can be found in 
MenuNode.prompt,
    +which also holds the title for menus and comments. For Symbol and Choice,
    +MenuNode.help holds the help text (if any, otherwise None).
    +
    +Most symbols will only have a single menu node. A symbol defined in 
multiple
    +locations will have one menu node for each location. The list of menu 
nodes for
    +a Symbol or Choice can be found in the Symbol/Choice.nodes attribute.
    +
    +Note that prompts and help texts for symbols and choices are stored in 
their
    +menu node(s) rather than in the Symbol or Choice objects themselves. This 
makes
    +it possible to define a symbol in multiple locations with a different 
prompt or
    +help text in each location. To get the help text or prompt for a symbol 
with a
    +single menu node, do sym.nodes[0].help and sym.nodes[0].prompt, 
respectively.
    +The prompt is a (text, condition) tuple, where condition determines the
    +visibility (see 'Intro to expressions' below).
    +
    +This organization mirrors the C implementation. MenuNode is called
    +'struct menu' there, but I thought "menu" was a confusing name.
    +
    +It is possible to give a Choice a name and define it in multiple locations,
    +hence why Choice.nodes is also a list.
    +
    +As a convenience, the properties added at a particular definition location 
are
    +available on the MenuNode itself, in e.g. MenuNode.defaults. This is 
helpful
    +when generating documentation, so that symbols/choices defined in multiple
    +locations can be shown with the correct properties at each location.
    +
    +
    +Intro to expressions
    +====================
    +
    +Expressions can be evaluated with the expr_value() function and printed 
with
    +the expr_str() function (these are used internally as well). Evaluating an
    +expression always yields a tristate value, where n, m, and y are 
represented as
    +0, 1, and 2, respectively.
    +
    +The following table should help you figure out how expressions are 
represented.
    +A, B, C, ... are symbols (Symbol instances), NOT is the kconfiglib.NOT
    +constant, etc.
    +
    +Expression            Representation
    +----------            --------------
    +A                     A
    +"A"                   A (constant symbol)
    +!A                    (NOT, A)
    +A && B                (AND, A, B)
    +A && B && C           (AND, A, (AND, B, C))
    +A || B                (OR, A, B)
    +A || (B && C && D)    (OR, A, (AND, B, (AND, C, D)))
    +A = B                 (EQUAL, A, B)
    +A != "foo"            (UNEQUAL, A, foo (constant symbol))
    +A && B = C && D       (AND, A, (AND, (EQUAL, B, C), D))
    +n                     Kconfig.n (constant symbol)
    +m                     Kconfig.m (constant symbol)
    +y                     Kconfig.y (constant symbol)
    +"y"                   Kconfig.y (constant symbol)
    +
    +Strings like "foo" in 'default "foo"' or 'depends on SYM = "foo"' are
    +represented as constant symbols, so the only values that appear in 
expressions
    +are symbols***. This mirrors the C implementation.
    +
    +***For choice symbols, the parent Choice will appear in expressions as 
well,
    +but it's usually invisible as the value interfaces of Symbol and Choice are
    +identical. This mirrors the C implementation and makes different choice 
modes
    +"just work".
    +
    +Manual evaluation examples:
    +
    +  - The value of A && B is min(A.tri_value, B.tri_value)
    +
    +  - The value of A || B is max(A.tri_value, B.tri_value)
    +
    +  - The value of !A is 2 - A.tri_value
    +
    +  - The value of A = B is 2 (y) if A.str_value == B.str_value, and 0 (n)
    +    otherwise. Note that str_value is used here instead of tri_value.
    +
    +    For constant (as well as undefined) symbols, str_value matches the 
name of
    +    the symbol. This mirrors the C implementation and explains why
    +    'depends on SYM = "foo"' above works as expected.
    +
    +n/m/y are automatically converted to the corresponding constant symbols
    +"n"/"m"/"y" (Kconfig.n/m/y) during parsing.
    +
    +Kconfig.const_syms is a dictionary like Kconfig.syms but for constant 
symbols.
    +
    +If a condition is missing (e.g., <cond> when the 'if <cond>' is removed 
from
    +'default A if <cond>'), it is actually Kconfig.y. The standard __str__()
    +functions just avoid printing 'if y' conditions to give cleaner output.
    +
    +
    +Kconfig extensions
    +==================
    +
    +Kconfiglib includes a couple of Kconfig extensions:
    +
    +'source' with relative path
    +---------------------------
    +
    +The 'rsource' statement sources Kconfig files with a path relative to 
directory
    +of the Kconfig file containing the 'rsource' statement, instead of 
relative to
    +the project root.
    +
    +Consider following directory tree:
    +
    +  Project
    +  +--Kconfig
    +  |
    +  +--src
    +     +--Kconfig
    +     |
    +     +--SubSystem1
    +        +--Kconfig
    +        |
    +        +--ModuleA
    +           +--Kconfig
    +
    +In this example, assume that src/SubSystem1/Kconfig wants to source
    +src/SubSystem1/ModuleA/Kconfig.
    +
    +With 'source', this statement would be used:
    +
    +  source "src/SubSystem1/ModuleA/Kconfig"
    +
    +With 'rsource', this turns into
    +
    +  rsource "ModuleA/Kconfig"
    +
    +If an absolute path is given to 'rsource', it acts the same as 'source'.
    +
    +'rsource' can be used to create "position-independent" Kconfig trees that 
can
    +be moved around freely.
    +
    +
    +Globbing 'source'
    +-----------------
    +
    +'source' and 'rsource' accept glob patterns, sourcing all matching Kconfig
    +files. They require at least one matching file, raising a KconfigError
    +otherwise.
    +
    +For example, the following statement might source sub1/foofoofoo and
    +sub2/foobarfoo:
    +
    +  source "sub[12]/foo*foo"
    +
    +The glob patterns accepted are the same as for the standard glob.glob()
    +function.
    +
    +Two additional statements are provided for cases where it's acceptable for 
a
    +pattern to match no files: 'osource' and 'orsource' (the o is for 
"optional").
    +
    +For example, the following statements will be no-ops if neither "foo" nor 
any
    +files matching "bar*" exist:
    +
    +  osource "foo"
    +  osource "bar*"
    +
    +'orsource' does a relative optional source.
    +
    +'source' and 'osource' are analogous to 'include' and '-include' in Make.
    +
    +
    +Generalized def_* keywords
    +--------------------------
    +
    +def_int, def_hex, and def_string are available in addition to def_bool and
    +def_tristate, allowing int, hex, and string symbols to be given a type and 
a
    +default at the same time.
    +
    +
    +Extra optional warnings
    +-----------------------
    +
    +Some optional warnings can be controlled via environment variables:
    +
    +  - KCONFIG_WARN_UNDEF: If set to 'y', warnings will be generated for all
    +    references to undefined symbols within Kconfig files. The only gotcha 
is
    +    that all hex literals must be prefixed with "0x" or "0X", to make it
    +    possible to distinguish them from symbol references.
    +
    +    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with 
many
    +    shared Kconfig files, leading to some safe undefined symbol references.
    +    KCONFIG_WARN_UNDEF is useful in projects that only have a single 
Kconfig
    +    tree though.
    +
    +    KCONFIG_STRICT is an older alias for this environment variable, 
supported
    +    for backwards compatibility.
    +
    +  - KCONFIG_WARN_UNDEF_ASSIGN: If set to 'y', warnings will be generated 
for
    +    all assignments to undefined symbols within .config files. By default, 
no
    +    such warnings are generated.
    +
    +    This warning can also be enabled/disabled via the 
Kconfig.warn_assign_undef
    +    variable.
    +
    +
    +Preprocessor user functions defined in Python
    +---------------------------------------------
    +
    +Preprocessor functions can be defined in Python, which makes it simple to
    +integrate information from existing Python tools into Kconfig (e.g. to have
    +Kconfig symbols depend on hardware information stored in some other 
format).
    +
    +Putting a Python module named kconfigfunctions(.py) anywhere in sys.path 
will
    +cause it to be imported by Kconfiglib (in Kconfig.__init__()). Note that
    +sys.path can be customized via PYTHONPATH, and includes the directory of 
the
    +module being run by default, as well as installation directories.
    +
    +If the KCONFIG_FUNCTIONS environment variable is set, it gives a different
    +module name to use instead of 'kconfigfunctions'.
    +
    +The imported module is expected to define a global dictionary named 
'functions'
    +that maps function names to Python functions, as follows:
    +
    +  def my_fn(kconf, name, arg_1, arg_2, ...):
    +      # kconf:
    +      #   Kconfig instance
    +      #
    +      # name:
    +      #   Name of the user-defined function ("my-fn"). Think argv[0].
    +      #
    +      # arg_1, arg_2, ...:
    +      #   Arguments passed to the function from Kconfig (strings)
    +      #
    +      # Returns a string to be substituted as the result of calling the
    +      # function
    +      ...
    +
    +  def my_other_fn(kconf, name, arg_1, arg_2, ...):
    +      ...
    +
    +  functions = {
    +      "my-fn":       (my_fn,       <min.args>, <max.args>/None),
    +      "my-other-fn": (my_other_fn, <min.args>, <max.args>/None),
    +      ...
    +  }
    +
    +  ...
    +
    +<min.args> and <max.args> are the minimum and maximum number of arguments
    +expected by the function (excluding the implicit 'name' argument). If
    +<max.args> is None, there is no upper limit to the number of arguments. 
Passing
    +an invalid number of arguments will generate a KconfigError exception.
    +
    +Once defined, user functions can be called from Kconfig in the same way as
    +other preprocessor functions:
    +
    +    config FOO
    +        ...
    +        depends on $(my-fn,arg1,arg2)
    +
    +If my_fn() returns "n", this will result in
    +
    +    config FOO
    +        ...
    +        depends on n
    +
    +Warning
    +*******
    +
    +User-defined preprocessor functions are called as they're encountered at 
parse
    +time, before all Kconfig files have been processed, and before the menu 
tree
    +has been finalized. There are no guarantees that accessing Kconfig symbols 
or
    +the menu tree via the 'kconf' parameter will work, and it could potentially
    +lead to a crash. The 'kconf' parameter is provided for future extension 
(and
    +because the predefined functions take it anyway).
    +
    +Preferably, user-defined functions should be stateless.
    +
    +
    +Feedback
    +========
    +
    +Send bug reports, suggestions, and questions to ulfalizer a.t Google's 
email
    +service, or open a ticket on the GitHub page.
    +"""
    +import errno
    +import importlib
    +import os
    +import re
    +import sys
    +
    +# Get rid of some attribute lookups. These are obvious in context.
    +from glob import iglob
    +from os.path import dirname, exists, expandvars, islink, join, realpath
    +
    +
    +VERSION = (12, 13, 0)
    +
    +
    +# File layout:
    +#
    +# Public classes
    +# Public functions
    +# Internal functions
    +# Global constants
    +
    +# Line length: 79 columns
    +
    +
    +#
    +# Public classes
    +#
    +
    +
    +class Kconfig(object):
    +    """
    +    Represents a Kconfig configuration, e.g. for x86 or ARM. This is the 
set of
    +    symbols, choices, and menu nodes appearing in the configuration. 
Creating
    +    any number of Kconfig objects (including for different architectures) 
is
    +    safe. Kconfiglib doesn't keep any global state.
    +
    +    The following attributes are available. They should be treated as
    +    read-only, and some are implemented through @property magic.
    +
    +    syms:
    +      A dictionary with all symbols in the configuration, indexed by name. 
Also
    +      includes all symbols that are referenced in expressions but never
    +      defined, except for constant (quoted) symbols.
    +
    +      Undefined symbols can be recognized by Symbol.nodes being empty -- 
see
    +      the 'Intro to the menu tree' section in the module docstring.
    +
    +    const_syms:
    +      A dictionary like 'syms' for constant (quoted) symbols
    +
    +    named_choices:
    +      A dictionary like 'syms' for named choices (choice FOO)
    +
    +    defined_syms:
    +      A list with all defined symbols, in the same order as they appear in 
the
    +      Kconfig files. Symbols defined in multiple locations appear multiple
    +      times.
    +
    +      Note: You probably want to use 'unique_defined_syms' instead. This
    +      attribute is mostly maintained for backwards compatibility.
    +
    +    unique_defined_syms:
    +      A list like 'defined_syms', but with duplicates removed. Just the 
first
    +      instance is kept for symbols defined in multiple locations. Kconfig 
order
    +      is preserved otherwise.
    +
    +      Using this attribute instead of 'defined_syms' can save work, and
    +      automatically gives reasonable behavior when writing configuration 
output
    +      (symbols defined in multiple locations only generate output once, 
while
    +      still preserving Kconfig order for readability).
    +
    +    choices:
    +      A list with all choices, in the same order as they appear in the 
Kconfig
    +      files.
    +
    +      Note: You probably want to use 'unique_choices' instead. This 
attribute
    +      is mostly maintained for backwards compatibility.
    +
    +    unique_choices:
    +      Analogous to 'unique_defined_syms', for choices. Named choices can 
have
    +      multiple definition locations.
    +
    +    menus:
    +      A list with all menus, in the same order as they appear in the 
Kconfig
    +      files
    +
    +    comments:
    +      A list with all comments, in the same order as they appear in the 
Kconfig
    +      files
    +
    +    kconfig_filenames:
    +      A list with the filenames of all Kconfig files included in the
    +      configuration, relative to $srctree (or relative to the current 
directory
    +      if $srctree isn't set), except absolute paths (e.g.
    +      'source "/foo/Kconfig"') are kept as-is.
    +
    +      The files are listed in the order they are source'd, starting with 
the
    +      top-level Kconfig file. If a file is source'd multiple times, it will
    +      appear multiple times. Use set() to get unique filenames.
    +
    +      Note that Kconfig.sync_deps() already indirectly catches any file
    +      modifications that change configuration output.
    +
    +    env_vars:
    +      A set() with the names of all environment variables referenced in the
    +      Kconfig files.
    +
    +      Only environment variables referenced with the preprocessor $(FOO) 
syntax
    +      will be registered. The older $FOO syntax is only supported for 
backwards
    +      compatibility.
    +
    +      Also note that $(FOO) won't be registered unless the environment 
variable
    +      $FOO is actually set. If it isn't, $(FOO) is an expansion of an unset
    +      preprocessor variable (which gives the empty string).
    +
    +      Another gotcha is that environment variables referenced in the 
values of
    +      recursively expanded preprocessor variables (those defined with =) 
will
    +      only be registered if the variable is actually used (expanded) 
somewhere.
    +
    +      The note from the 'kconfig_filenames' documentation applies here too.
    +
    +    n/m/y:
    +      The predefined constant symbols n/m/y. Also available in const_syms.
    +
    +    modules:
    +      The Symbol instance for the modules symbol. Currently hardcoded to
    +      MODULES, which is backwards compatible. Kconfiglib will warn if
    +      'option modules' is set on some other symbol. Tell me if you need 
proper
    +      'option modules' support.
    +
    +      'modules' is never None. If the MODULES symbol is not explicitly 
defined,
    +      its tri_value will be 0 (n), as expected.
    +
    +      A simple way to enable modules is to do 'kconf.modules.set_value(2)'
    +      (provided the MODULES symbol is defined and visible). Modules are
    +      disabled by default in the kernel Kconfig files as of writing, though
    +      nearly all defconfig files enable them (with 'CONFIG_MODULES=y').
    +
    +    defconfig_list:
    +      The Symbol instance for the 'option defconfig_list' symbol, or None 
if no
    +      defconfig_list symbol exists. The defconfig filename derived from 
this
    +      symbol can be found in Kconfig.defconfig_filename.
    +
    +    defconfig_filename:
    +      The filename given by the defconfig_list symbol. This is taken from 
the
    +      first 'default' with a satisfied condition where the specified file
    +      exists (can be opened for reading). If a defconfig file 
foo/defconfig is
    +      not found and $srctree was set when the Kconfig was created,
    +      $srctree/foo/defconfig is looked up as well.
    +
    +      'defconfig_filename' is None if either no defconfig_list symbol 
exists,
    +      or if the defconfig_list symbol has no 'default' with a satisfied
    +      condition that specifies a file that exists.
    +
    +      Gotcha: scripts/kconfig/Makefile might pass --defconfig=<defconfig> 
to
    +      scripts/kconfig/conf when running e.g. 'make defconfig'. This option
    +      overrides the defconfig_list symbol, meaning defconfig_filename 
might not
    +      always match what 'make defconfig' would use.
    +
    +    top_node:
    +      The menu node (see the MenuNode class) of the implicit top-level 
menu.
    +      Acts as the root of the menu tree.
    +
    +    mainmenu_text:
    +      The prompt (title) of the top menu (top_node). Defaults to "Main 
menu".
    +      Can be changed with the 'mainmenu' statement (see 
kconfig-language.txt).
    +
    +    variables:
    +      A dictionary with all preprocessor variables, indexed by name. See 
the
    +      Variable class.
    +
    +    warn:
    +      Set this variable to True/False to enable/disable warnings. See
    +      Kconfig.__init__().
    +
    +      When 'warn' is False, the values of the other warning-related 
variables
    +      are ignored.
    +
    +      This variable as well as the other warn* variables can be read to 
check
    +      the current warning settings.
    +
    +    warn_to_stderr:
    +      Set this variable to True/False to enable/disable warnings on 
stderr. See
    +      Kconfig.__init__().
    +
    +    warn_assign_undef:
    +      Set this variable to True to generate warnings for assignments to
    +      undefined symbols in configuration files.
    +
    +      This variable is False by default unless the 
KCONFIG_WARN_UNDEF_ASSIGN
    +      environment variable was set to 'y' when the Kconfig instance was
    +      created.
    +
    +    warn_assign_override:
    +      Set this variable to True to generate warnings for multiple 
assignments
    +      to the same symbol in configuration files, where the assignments set
    +      different values (e.g. CONFIG_FOO=m followed by CONFIG_FOO=y, where 
the
    +      last value would get used).
    +
    +      This variable is True by default. Disabling it might be useful when
    +      merging configurations.
    +
    +    warn_assign_redun:
    +      Like warn_assign_override, but for multiple assignments setting a 
symbol
    +      to the same value.
    +
    +      This variable is True by default. Disabling it might be useful when
    +      merging configurations.
    +
    +    warnings:
    +      A list of strings containing all warnings that have been generated, 
for
    +      cases where more flexibility is needed.
    +
    +      See the 'warn_to_stderr' parameter to Kconfig.__init__() and the
    +      Kconfig.warn_to_stderr variable as well. Note that warnings still get
    +      added to Kconfig.warnings when 'warn_to_stderr' is True.
    +
    +      Just as for warnings printed to stderr, only warnings that are 
enabled
    +      will get added to Kconfig.warnings. See the various Kconfig.warn*
    +      variables.
    +
    +    missing_syms:
    +      A list with (name, value) tuples for all assignments to undefined 
symbols
    +      within the most recently loaded .config file(s). 'name' is the symbol
    +      name without the 'CONFIG_' prefix. 'value' is a string that gives the
    +      right-hand side of the assignment verbatim.
    +
    +      See Kconfig.load_config() as well.
    +
    +    srctree:
    +      The value of the $srctree environment variable when the 
configuration was
    +      loaded, or the empty string if $srctree wasn't set. This gives nice
    +      behavior with os.path.join(), which treats "" as the current 
directory,
    +      without adding "./".
    +
    +      Kconfig files are looked up relative to $srctree (unless absolute 
paths
    +      are used), and .config files are looked up relative to $srctree if 
they
    +      are not found in the current directory. This is used to support
    +      out-of-tree builds. The C tools use this environment variable in the 
same
    +      way.
    +
    +      Changing $srctree after creating the Kconfig instance has no effect. 
Only
    +      the value when the configuration is loaded matters. This avoids 
surprises
    +      if multiple configurations are loaded with different values for 
$srctree.
    +
    +    config_prefix:
    +      The value of the $CONFIG_ environment variable when the 
configuration was
    +      loaded. This is the prefix used (and expected) on symbol names in 
.config
    +      files and C headers. Defaults to "CONFIG_". Used in the same way in 
the C
    +      tools.
    +
    +      Like for srctree, only the value of $CONFIG_ when the configuration 
is
    +      loaded matters.
    +    """
    +    __slots__ = (
    +        "_encoding",
    +        "_functions",
    +        "_set_match",
    +        "_srctree_prefix",
    +        "_unset_match",
    +        "_warn_assign_no_prompt",
    +        "choices",
    +        "comments",
    +        "config_prefix",
    +        "const_syms",
    +        "defconfig_list",
    +        "defined_syms",
    +        "env_vars",
    +        "kconfig_filenames",
    +        "m",
    +        "menus",
    +        "missing_syms",
    +        "modules",
    +        "n",
    +        "named_choices",
    +        "srctree",
    +        "syms",
    +        "top_node",
    +        "unique_choices",
    +        "unique_defined_syms",
    +        "variables",
    +        "warn",
    +        "warn_assign_override",
    +        "warn_assign_redun",
    +        "warn_assign_undef",
    +        "warn_to_stderr",
    +        "warnings",
    +        "y",
    +
    +        # Parsing-related
    +        "_parsing_kconfigs",
    +        "_readline",
    +        "_filename",
    +        "_linenr",
    +        "_include_path",
    +        "_filestack",
    +        "_line",
    +        "_tokens",
    +        "_tokens_i",
    +        "_reuse_tokens",
    +    )
    +
    +    #
    +    # Public interface
    +    #
    +
    +    def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
    +                 encoding="utf-8"):
    +        """
    +        Creates a new Kconfig object by parsing Kconfig files.
    +        Note that Kconfig files are not the same as .config files (which 
store
    +        configuration symbol values).
    +
    +        See the module docstring for some environment variables that 
influence
    +        default warning settings (KCONFIG_WARN_UNDEF and
    +        KCONFIG_WARN_UNDEF_ASSIGN).
    +
    +        Raises KconfigError on syntax/semantic errors, and OSError or 
(possibly
    +        a subclass of) IOError on IO errors ('errno', 'strerror', and
    +        'filename' are available). Note that IOError is an alias for 
OSError on
    +        Python 3, so it's enough to catch OSError there. If you need 
Python 2/3
    +        compatibility, it's easiest to catch EnvironmentError, which is a
    +        common base class of OSError/IOError on Python 2 and an alias for
    +        OSError on Python 3.
    +
    +        filename (default: "Kconfig"):
    +          The Kconfig file to load. For the Linux kernel, you'll want 
"Kconfig"
    +          from the top-level directory, as environment variables will make 
sure
    +          the right Kconfig is included from there (arch/$SRCARCH/Kconfig 
as of
    +          writing).
    +
    +          If $srctree is set, 'filename' will be looked up relative to it.
    +          $srctree is also used to look up source'd files within Kconfig 
files.
    +          See the class documentation.
    +
    +          If you are using Kconfiglib via 'make scriptconfig', the 
filename of
    +          the base base Kconfig file will be in sys.argv[1]. It's currently
    +          always "Kconfig" in practice.
    +
    +        warn (default: True):
    +          True if warnings related to this configuration should be 
generated.
    +          This can be changed later by setting Kconfig.warn to True/False. 
It
    +          is provided as a constructor argument since warnings might be
    +          generated during parsing.
    +
    +          See the other Kconfig.warn_* variables as well, which enable or
    +          suppress certain warnings when warnings are enabled.
    +
    +          All generated warnings are added to the Kconfig.warnings list. 
See
    +          the class documentation.
    +
    +        warn_to_stderr (default: True):
    +          True if warnings should be printed to stderr in addition to being
    +          added to Kconfig.warnings.
    +
    +          This can be changed later by setting Kconfig.warn_to_stderr to
    +          True/False.
    +
    +        encoding (default: "utf-8"):
    +          The encoding to use when reading and writing files, and when 
decoding
    +          output from commands run via $(shell). If None, the encoding
    +          specified in the current locale will be used.
    +
    +          The "utf-8" default avoids exceptions on systems that are 
configured
    +          to use the C locale, which implies an ASCII encoding.
    +
    +          This parameter has no effect on Python 2, due to implementation
    +          issues (regular strings turning into Unicode strings, which are
    +          distinct in Python 2). Python 2 doesn't decode regular strings
    +          anyway.
    +
    +          Related PEP: https://www.python.org/dev/peps/pep-0538/
    +        """
    +        self._encoding = encoding
    +
    +        self.srctree = os.getenv("srctree", "")
    +        # A prefix we can reliably strip from glob() results to get a 
filename
    +        # relative to $srctree. relpath() can cause issues for symlinks,
    +        # because it assumes symlink/../foo is the same as foo/.
    +        self._srctree_prefix = realpath(self.srctree) + os.sep
    +
    +        self.warn = warn
    +        self.warn_to_stderr = warn_to_stderr
    +        self.warn_assign_undef = os.getenv("KCONFIG_WARN_UNDEF_ASSIGN") == 
"y"
    +        self.warn_assign_override = True
    +        self.warn_assign_redun = True
    +        self._warn_assign_no_prompt = True
    +
    +        self.warnings = []
    +
    +        self.config_prefix = os.getenv("CONFIG_", "CONFIG_")
    +        # Regular expressions for parsing .config files
    +        self._set_match = _re_match(self.config_prefix + r"([^=]+)=(.*)")
    +        self._unset_match = _re_match(r"# {}([^ ]+) is not set".format(
    +            self.config_prefix))
    +
    +        self.syms = {}
    +        self.const_syms = {}
    +        self.defined_syms = []
    +        self.missing_syms = []
    +        self.named_choices = {}
    +        self.choices = []
    +        self.menus = []
    +        self.comments = []
    +
    +        for nmy in "n", "m", "y":
    +            sym = Symbol()
    +            sym.kconfig = self
    +            sym.name = nmy
    +            sym.is_constant = True
    +            sym.orig_type = TRISTATE
    +            sym._cached_tri_val = STR_TO_TRI[nmy]
    +
    +            self.const_syms[nmy] = sym
    +
    +        self.n = self.const_syms["n"]
    +        self.m = self.const_syms["m"]
    +        self.y = self.const_syms["y"]
    +
    +        # Make n/m/y well-formed symbols
    +        for nmy in "n", "m", "y":
    +            sym = self.const_syms[nmy]
    +            sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
    +
    +        # Maps preprocessor variables names to Variable instances
    +        self.variables = {}
    +
    +        # Predefined preprocessor functions, with min/max number of 
arguments
    +        self._functions = {
    +            "info":       (_info_fn,       1, 1),
    +            "error-if":   (_error_if_fn,   2, 2),
    +            "filename":   (_filename_fn,   0, 0),
    +            "lineno":     (_lineno_fn,     0, 0),
    +            "shell":      (_shell_fn,      1, 1),
    +            "warning-if": (_warning_if_fn, 2, 2),
    +        }
    +
    +        # Add any user-defined preprocessor functions
    +        try:
    +            self._functions.update(
    +                importlib.import_module(
    +                    os.getenv("KCONFIG_FUNCTIONS", "kconfigfunctions")
    +                ).functions)
    +        except ImportError:
    +            pass
    +
    +        # This determines whether previously unseen symbols are registered.
    +        # They shouldn't be if we parse expressions after parsing, as part 
of
    +        # Kconfig.eval_string().
    +        self._parsing_kconfigs = True
    +
    +        self.modules = self._lookup_sym("MODULES")
    +        self.defconfig_list = None
    +
    +        self.top_node = MenuNode()
    +        self.top_node.kconfig = self
    +        self.top_node.item = MENU
    +        self.top_node.is_menuconfig = True
    +        self.top_node.visibility = self.y
    +        self.top_node.prompt = ("Main menu", self.y)
    +        self.top_node.parent = None
    +        self.top_node.dep = self.y
    +        self.top_node.filename = filename
    +        self.top_node.linenr = 1
    +        self.top_node.include_path = ()
    +
    +        # Parse the Kconfig files
    +
    +        # Not used internally. Provided as a convenience.
    +        self.kconfig_filenames = [filename]
    +        self.env_vars = set()
    +
    +        # Keeps track of the location in the parent Kconfig files. Kconfig
    +        # files usually source other Kconfig files. See _enter_file().
    +        self._filestack = []
    +        self._include_path = ()
    +
    +        # The current parsing location
    +        self._filename = filename
    +        self._linenr = 0
    +
    +        # Used to avoid retokenizing lines when we discover that they're 
not
    +        # part of the construct currently being parsed. This is kinda like 
an
    +        # unget operation.
    +        self._reuse_tokens = False
    +
    +        # Open the top-level Kconfig file. Store the readline() method 
directly
    +        # as a small optimization.
    +        self._readline = self._open(join(self.srctree, filename), 
"r").readline
    +
    +        try:
    +            # Parse the Kconfig files
    +            self._parse_block(None, self.top_node, self.top_node)
    +            self.top_node.list = self.top_node.next
    +            self.top_node.next = None
    +        except UnicodeDecodeError as e:
    +            _decoding_error(e, self._filename)
    +
    +        # Close the top-level Kconfig file. __self__ fetches the 'file' 
object
    +        # for the method.
    +        self._readline.__self__.close()
    +
    +        self._parsing_kconfigs = False
    +
    +        # Do various menu tree post-processing
    +        self._finalize_node(self.top_node, self.y)
    +
    +        self.unique_defined_syms = _ordered_unique(self.defined_syms)
    +        self.unique_choices = _ordered_unique(self.choices)
    +
    +        # Do sanity checks. Some of these depend on everything being 
finalized.
    +        self._check_sym_sanity()
    +        self._check_choice_sanity()
    +
    +        # KCONFIG_STRICT is an older alias for KCONFIG_WARN_UNDEF, 
supported
    +        # for backwards compatibility
    +        if os.getenv("KCONFIG_WARN_UNDEF") == "y" or \
    +           os.getenv("KCONFIG_STRICT") == "y":
    +
    +            self._check_undef_syms()
    +
    +        # Build Symbol._dependents for all symbols and choices
    +        self._build_dep()
    +
    +        # Check for dependency loops
    +        check_dep_loop_sym = _check_dep_loop_sym  # Micro-optimization
    +        for sym in self.unique_defined_syms:
    +            check_dep_loop_sym(sym, False)
    +
    +        # Add extra dependencies from choices to choice symbols that get
    +        # awkward during dependency loop detection
    +        self._add_choice_deps()
    +
    +    @property
    +    def mainmenu_text(self):
    +        """
    +        See the class documentation.
    +        """
    +        return self.top_node.prompt[0]
    +
    +    @property
    +    def defconfig_filename(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self.defconfig_list:
    +            for filename, cond in self.defconfig_list.defaults:
    +                if expr_value(cond):
    +                    try:
    +                        with self._open_config(filename.str_value) as f:
    +                            return f.name
    +                    except EnvironmentError:
    +                        continue
    +
    +        return None
    +
    +    def load_config(self, filename=None, replace=True, verbose=None):
    +        """
    +        Loads symbol values from a file in the .config format. Equivalent 
to
    +        calling Symbol.set_value() to set each of the values.
    +
    +        "# CONFIG_FOO is not set" within a .config file sets the user 
value of
    +        FOO to n. The C tools work the same way.
    +
    +        For each symbol, the Symbol.user_value attribute holds the value 
the
    +        symbol was assigned in the .config file (if any). The user value 
might
    +        differ from Symbol.str/tri_value if there are unsatisfied 
dependencies.
    +
    +        Calling this function also updates the Kconfig.missing_syms 
attribute
    +        with a list of all assignments to undefined symbols within the
    +        configuration file. Kconfig.missing_syms is cleared if 'replace' is
    +        True, and appended to otherwise. See the documentation for
    +        Kconfig.missing_syms as well.
    +
    +        See the Kconfig.__init__() docstring for raised exceptions
    +        (OSError/IOError). KconfigError is never raised here.
    +
    +        filename (default: None):
    +          Path to load configuration from (a string). Respects $srctree if 
set
    +          (see the class documentation).
    +
    +          If 'filename' is None (the default), the configuration file to 
load
    +          (if any) is calculated automatically, giving the behavior you'd
    +          usually want:
    +
    +            1. If the KCONFIG_CONFIG environment variable is set, it gives 
the
    +               path to the configuration file to load. Otherwise, 
".config" is
    +               used. See standard_config_filename().
    +
    +            2. If the path from (1.) doesn't exist, the configuration file
    +               given by kconf.defconfig_filename is loaded instead, which 
is
    +               derived from the 'option defconfig_list' symbol.
    +
    +            3. If (1.) and (2.) fail to find a configuration file to load, 
no
    +               configuration file is loaded, and symbols retain their 
current
    +               values (e.g., their default values). This is not an error.
    +
    +           See the return value as well.
    +
    +        replace (default: True):
    +          If True, all existing user values will be cleared before loading 
the
    +          .config. Pass False to merge configurations.
    +
    +        verbose (default: None):
    +          Limited backwards compatibility to prevent crashes. A warning is
    +          printed if anything but None is passed.
    +
    +          Prior to Kconfiglib 12.0.0, this option enabled printing of 
messages
    +          to stdout when 'filename' was None. A message is (always) 
returned
    +          now instead, which is more flexible.
    +
    +          Will probably be removed in some future version.
    +
    +        Returns a string with a message saying which file got loaded (or
    +        possibly that no file got loaded, when 'filename' is None). This is
    +        meant to reduce boilerplate in tools, which can do e.g.
    +        print(kconf.load_config()). The returned message distinguishes 
between
    +        loading (replace == True) and merging (replace == False).
    +        """
    +        if verbose is not None:
    +            _warn_verbose_deprecated("load_config")
    +
    +        msg = None
    +        if filename is None:
    +            filename = standard_config_filename()
    +            if not exists(filename) and \
    +               not exists(join(self.srctree, filename)):
    +                defconfig = self.defconfig_filename
    +                if defconfig is None:
    +                    return "Using default symbol values (no '{}')" \
    +                           .format(filename)
    +
    +                msg = " default configuration '{}' (no '{}')" \
    +                      .format(defconfig, filename)
    +                filename = defconfig
    +
    +        if not msg:
    +            msg = " configuration '{}'".format(filename)
    +
    +        # Disable the warning about assigning to symbols without prompts. 
This
    +        # is normal and expected within a .config file.
    +        self._warn_assign_no_prompt = False
    +
    +        # This stub only exists to make sure _warn_assign_no_prompt gets
    +        # reenabled
    +        try:
    +            self._load_config(filename, replace)
    +        except UnicodeDecodeError as e:
    +            _decoding_error(e, filename)
    +        finally:
    +            self._warn_assign_no_prompt = True
    +
    +        return ("Loaded" if replace else "Merged") + msg
    +
    +    def _load_config(self, filename, replace):
    +        with self._open_config(filename) as f:
    +            if replace:
    +                self.missing_syms = []
    +
    +                # If we're replacing the configuration, keep track of which
    +                # symbols and choices got set so that we can unset the rest
    +                # later. This avoids invalidating everything and is faster.
    +                # Another benefit is that invalidation must be rock solid 
for
    +                # it to work, making it a good test.
    +
    +                for sym in self.unique_defined_syms:
    +                    sym._was_set = False
    +
    +                for choice in self.unique_choices:
    +                    choice._was_set = False
    +
    +            # Small optimizations
    +            set_match = self._set_match
    +            unset_match = self._unset_match
    +            get_sym = self.syms.get
    +
    +            for linenr, line in enumerate(f, 1):
    +                # The C tools ignore trailing whitespace
    +                line = line.rstrip()
    +
    +                match = set_match(line)
    +                if match:
    +                    name, val = match.groups()
    +                    sym = get_sym(name)
    +                    if not sym or not sym.nodes:
    +                        self._undef_assign(name, val, filename, linenr)
    +                        continue
    +
    +                    if sym.orig_type in _BOOL_TRISTATE:
    +                        # The C implementation only checks the first 
character
    +                        # to the right of '=', for whatever reason
    +                        if not (sym.orig_type is BOOL
    +                                and val.startswith(("y", "n")) or
    +                                sym.orig_type is TRISTATE
    +                                and val.startswith(("y", "m", "n"))):
    +                            self._warn("'{}' is not a valid value for the 
{} "
    +                                       "symbol {}. Assignment ignored."
    +                                       .format(val, 
TYPE_TO_STR[sym.orig_type],
    +                                               _name_and_loc(sym)),
    +                                       filename, linenr)
    +                            continue
    +
    +                        val = val[0]
    +
    +                        if sym.choice and val != "n":
    +                            # During .config loading, we infer the mode of 
the
    +                            # choice from the kind of values that are 
assigned
    +                            # to the choice symbols
    +
    +                            prev_mode = sym.choice.user_value
    +                            if prev_mode is not None and \
    +                               TRI_TO_STR[prev_mode] != val:
    +
    +                                self._warn("both m and y assigned to 
symbols "
    +                                           "within the same choice",
    +                                           filename, linenr)
    +
    +                            # Set the choice's mode
    +                            sym.choice.set_value(val)
    +
    +                    elif sym.orig_type is STRING:
    +                        match = _conf_string_match(val)
    +                        if not match:
    +                            self._warn("malformed string literal in "
    +                                       "assignment to {}. Assignment 
ignored."
    +                                       .format(_name_and_loc(sym)),
    +                                       filename, linenr)
    +                            continue
    +
    +                        val = unescape(match.group(1))
    +
    +                else:
    +                    match = unset_match(line)
    +                    if not match:
    +                        # Print a warning for lines that match neither
    +                        # set_match() nor unset_match() and that are not 
blank
    +                        # lines or comments. 'line' has already been
    +                        # rstrip()'d, so blank lines show up as "" here.
    +                        if line and not line.lstrip().startswith("#"):
    +                            self._warn("ignoring malformed line '{}'"
    +                                       .format(line),
    +                                       filename, linenr)
    +
    +                        continue
    +
    +                    name = match.group(1)
    +                    sym = get_sym(name)
    +                    if not sym or not sym.nodes:
    +                        self._undef_assign(name, "n", filename, linenr)
    +                        continue
    +
    +                    if sym.orig_type not in _BOOL_TRISTATE:
    +                        continue
    +
    +                    val = "n"
    +
    +                # Done parsing the assignment. Set the value.
    +
    +                if sym._was_set:
    +                    self._assigned_twice(sym, val, filename, linenr)
    +
    +                sym.set_value(val)
    +
    +        if replace:
    +            # If we're replacing the configuration, unset the symbols that
    +            # didn't get set
    +
    +            for sym in self.unique_defined_syms:
    +                if not sym._was_set:
    +                    sym.unset_value()
    +
    +            for choice in self.unique_choices:
    +                if not choice._was_set:
    +                    choice.unset_value()
    +
    +    def _undef_assign(self, name, val, filename, linenr):
    +        # Called for assignments to undefined symbols during .config 
loading
    +
    +        self.missing_syms.append((name, val))
    +        if self.warn_assign_undef:
    +            self._warn(
    +                "attempt to assign the value '{}' to the undefined symbol 
{}"
    +                .format(val, name), filename, linenr)
    +
    +    def _assigned_twice(self, sym, new_val, filename, linenr):
    +        # Called when a symbol is assigned more than once in a .config file
    +
    +        # Use strings for bool/tristate user values in the warning
    +        if sym.orig_type in _BOOL_TRISTATE:
    +            user_val = TRI_TO_STR[sym.user_value]
    +        else:
    +            user_val = sym.user_value
    +
    +        msg = '{} set more than once. Old value "{}", new value 
"{}".'.format(
    +            _name_and_loc(sym), user_val, new_val)
    +
    +        if user_val == new_val:
    +            if self.warn_assign_redun:
    +                self._warn(msg, filename, linenr)
    +        elif self.warn_assign_override:
    +            self._warn(msg, filename, linenr)
    +
    +    def write_autoconf(self, filename,
    +                       header="/* Generated by Kconfiglib 
(https://github.com/ulfalizer/Kconfiglib) */\n"):
    +        r"""
    +        Writes out symbol values as a C header file, matching the format 
used
    +        by include/generated/autoconf.h in the kernel.
    +
    +        The ordering of the #defines matches the one generated by
    +        write_config(). The order in the C implementation depends on the 
hash
    +        table implementation as of writing, and so won't match.
    +
    +        If 'filename' exists and its contents is identical to what would 
get
    +        written out, it is left untouched. This avoids updating file 
metadata
    +        like the modification time and possibly triggering redundant work 
in
    +        build tools.
    +
    +        filename:
    +          Self-explanatory.
    +
    +        header (default: "/* Generated by Kconfiglib 
(https://github.com/ulfalizer/Kconfiglib) */\n"):
    +          Text that will be inserted verbatim at the beginning of the 
file. You
    +          would usually want it enclosed in '/* */' to make it a C comment,
    +          and include a final terminating newline.
    +        """
    +        self._write_if_changed(filename, self._autoconf_contents(header))
    +
    +    def _autoconf_contents(self, header):
    +        # write_autoconf() helper. Returns the contents to write as a 
string,
    +        # with 'header' at the beginning.
    +
    +        # "".join()ed later
    +        chunks = [header]
    +        add = chunks.append
    +
    +        for sym in self.unique_defined_syms:
    +            # _write_to_conf is determined when the value is calculated. 
This
    +            # is a hidden function call due to property magic.
    +            #
    +            # Note: In client code, you can check if sym.config_string is 
empty
    +            # instead, to avoid accessing the internal _write_to_conf 
variable
    +            # (though it's likely to keep working).
    +            val = sym.str_value
    +            if not sym._write_to_conf:
    +                continue
    +
    +            if sym.orig_type in _BOOL_TRISTATE:
    +                if val == "y":
    +                    add("#define {}{} 1\n"
    +                        .format(self.config_prefix, sym.name))
    +                elif val == "m":
    +                    add("#define {}{}_MODULE 1\n"
    +                        .format(self.config_prefix, sym.name))
    +
    +            elif sym.orig_type is STRING:
    +                add('#define {}{} "{}"\n'
    +                    .format(self.config_prefix, sym.name, escape(val)))
    +
    +            else:  # sym.orig_type in _INT_HEX:
    +                if sym.orig_type is HEX and \
    +                   not val.startswith(("0x", "0X")):
    +                    val = "0x" + val
    +
    +                add("#define {}{} {}\n"
    +                    .format(self.config_prefix, sym.name, val))
    +
    +        return "".join(chunks)
    +
    +    def write_config(self, filename=None,
    +                     header="# Generated by Kconfiglib 
(https://github.com/ulfalizer/Kconfiglib)\n",
    +                     save_old=True, verbose=None):
    +        r"""
    +        Writes out symbol values in the .config format. The format matches 
the
    +        C implementation, including ordering.
    +
    +        Symbols appear in the same order in generated .config files as 
they do
    +        in the Kconfig files. For symbols defined in multiple locations, a
    +        single assignment is written out corresponding to the first 
location
    +        where the symbol is defined.
    +
    +        See the 'Intro to symbol values' section in the module docstring to
    +        understand which symbols get written out.
    +
    +        If 'filename' exists and its contents is identical to what would 
get
    +        written out, it is left untouched. This avoids updating file 
metadata
    +        like the modification time and possibly triggering redundant work 
in
    +        build tools.
    +
    +        See the Kconfig.__init__() docstring for raised exceptions
    +        (OSError/IOError). KconfigError is never raised here.
    +
    +        filename (default: None):
    +          Filename to save configuration to (a string).
    +
    +          If None (the default), the filename in the environment variable
    +          KCONFIG_CONFIG is used if set, and ".config" otherwise. See
    +          standard_config_filename().
    +
    +        header (default: "# Generated by Kconfiglib 
(https://github.com/ulfalizer/Kconfiglib)\n"):
    +          Text that will be inserted verbatim at the beginning of the 
file. You
    +          would usually want each line to start with '#' to make it a 
comment,
    +          and include a final terminating newline.
    +
    +        save_old (default: True):
    +          If True and <filename> already exists, a copy of it will be 
saved to
    +          <filename>.old in the same directory before the new 
configuration is
    +          written.
    +
    +          Errors are silently ignored if <filename>.old cannot be written 
(e.g.
    +          due to being a directory, or <filename> being something like
    +          /dev/null).
    +
    +        verbose (default: None):
    +          Limited backwards compatibility to prevent crashes. A warning is
    +          printed if anything but None is passed.
    +
    +          Prior to Kconfiglib 12.0.0, this option enabled printing of 
messages
    +          to stdout when 'filename' was None. A message is (always) 
returned
    +          now instead, which is more flexible.
    +
    +          Will probably be removed in some future version.
    +
    +        Returns a string with a message saying which file got saved. This 
is
    +        meant to reduce boilerplate in tools, which can do e.g.
    +        print(kconf.write_config()).
    +        """
    +        if verbose is not None:
    +            _warn_verbose_deprecated("write_config")
    +
    +        if filename is None:
    +            filename = standard_config_filename()
    +
    +        contents = self._config_contents(header)
    +        if self._contents_eq(filename, contents):
    +            return "No change to '{}'".format(filename)
    +
    +        if save_old:
    +            _save_old(filename)
    +
    +        with self._open(filename, "w") as f:
    +            f.write(contents)
    +
    +        return "Configuration saved to '{}'".format(filename)
    +
    +    def _config_contents(self, header):
    +        # write_config() helper. Returns the contents to write as a string,
    +        # with 'header' at the beginning.
    +        #
    +        # More memory friendly would be to 'yield' the strings and
    +        # "".join(_config_contents()), but it was a bit slower on my 
system.
    +
    +        # node_iter() was used here before commit 3aea9f7 ("Add '# end of
    +        # <menu>' after menus in .config"). Those comments get tricky to
    +        # implement with it.
    +
    +        for sym in self.unique_defined_syms:
    +            sym._visited = False
    +
    +        # Did we just print an '# end of ...' comment?
    +        after_end_comment = False
    +
    +        # "".join()ed later
    +        chunks = [header]
    +        add = chunks.append
    +
    +        node = self.top_node
    +        while 1:
    +            # Jump to the next node with an iterative tree walk
    +            if node.list:
    +                node = node.list
    +            elif node.next:
    +                node = node.next
    +            else:
    +                while node.parent:
    +                    node = node.parent
    +
    +                    # Add a comment when leaving visible menus
    +                    if node.item is MENU and expr_value(node.dep) and \
    +                       expr_value(node.visibility) and \
    +                       node is not self.top_node:
    +                        add("# end of {}\n".format(node.prompt[0]))
    +                        after_end_comment = True
    +
    +                    if node.next:
    +                        node = node.next
    +                        break
    +                else:
    +                    # No more nodes
    +                    return "".join(chunks)
    +
    +            # Generate configuration output for the node
    +
    +            item = node.item
    +
    +            if item.__class__ is Symbol:
    +                if item._visited:
    +                    continue
    +                item._visited = True
    +
    +                conf_string = item.config_string
    +                if not conf_string:
    +                    continue
    +
    +                if after_end_comment:
    +                    # Add a blank line before the first symbol printed 
after an
    +                    # '# end of ...' comment
    +                    after_end_comment = False
    +                    add("\n")
    +                add(conf_string)
    +
    +            elif expr_value(node.dep) and \
    +                 ((item is MENU and expr_value(node.visibility)) or
    +                  item is COMMENT):
    +
    +                add("\n#\n# {}\n#\n".format(node.prompt[0]))
    +                after_end_comment = False
    +
    +    def write_min_config(self, filename,
    +                         header="# Generated by Kconfiglib 
(https://github.com/ulfalizer/Kconfiglib)\n"):
    +        """
    +        Writes out a "minimal" configuration file, omitting symbols whose 
value
    +        matches their default value. The format matches the one produced by
    +        'make savedefconfig'.
    +
    +        The resulting configuration file is incomplete, but a complete
    +        configuration can be derived from it by loading it. Minimal
    +        configuration files can serve as a more manageable configuration 
format
    +        compared to a "full" .config file, especially when configurations 
files
    +        are merged or edited by hand.
    +
    +        See the Kconfig.__init__() docstring for raised exceptions
    +        (OSError/IOError). KconfigError is never raised here.
    +
    +        filename:
    +          Self-explanatory.
    +
    +        header (default: "# Generated by Kconfiglib 
(https://github.com/ulfalizer/Kconfiglib)\n"):
    +          Text that will be inserted verbatim at the beginning of the 
file. You
    +          would usually want each line to start with '#' to make it a 
comment,
    +          and include a final terminating newline.
    +
    +        Returns a string with a message saying which file got saved. This 
is
    +        meant to reduce boilerplate in tools, which can do e.g.
    +        print(kconf.write_min_config()).
    +        """
    +        contents = self._min_config_contents(header)
    +        if self._contents_eq(filename, contents):
    +            return "No change to '{}'".format(filename)
    +
    +        with self._open(filename, "w") as f:
    +            f.write(contents)
    +
    +        return "Minimal configuration saved to '{}'".format(filename)
    +
    +    def _min_config_contents(self, header):
    +        # write_min_config() helper. Returns the contents to write as a 
string,
    +        # with 'header' at the beginning.
    +
    +        chunks = [header]
    +        add = chunks.append
    +
    +        for sym in self.unique_defined_syms:
    +            # Skip symbols that cannot be changed. Only check
    +            # non-choice symbols, as selects don't affect choice
    +            # symbols.
    +            if not sym.choice and \
    +               sym.visibility <= expr_value(sym.rev_dep):
    +                continue
    +
    +            # Skip symbols whose value matches their default
    +            if sym.str_value == sym._str_default():
    +                continue
    +
    +            # Skip symbols that would be selected by default in a
    +            # choice, unless the choice is optional or the symbol type
    +            # isn't bool (it might be possible to set the choice mode
    +            # to n or the symbol to m in those cases).
    +            if sym.choice and \
    +               not sym.choice.is_optional and \
    +               sym.choice._selection_from_defaults() is sym and \
    +               sym.orig_type is BOOL and \
    +               sym.tri_value == 2:
    +                continue
    +
    +            add(sym.config_string)
    +
    +        return "".join(chunks)
    +
    +    def sync_deps(self, path):
    +        """
    +        Creates or updates a directory structure that can be used to avoid
    +        doing a full rebuild whenever the configuration is changed, 
mirroring
    +        include/config/ in the kernel.
    +
    +        This function is intended to be called during each build, before
    +        compiling source files that depend on configuration symbols.
    +
    +        See the Kconfig.__init__() docstring for raised exceptions
    +        (OSError/IOError). KconfigError is never raised here.
    +
    +        path:
    +          Path to directory
    +
    +        sync_deps(path) does the following:
    +
    +          1. If the directory <path> does not exist, it is created.
    +
    +          2. If <path>/auto.conf exists, old symbol values are loaded from 
it,
    +             which are then compared against the current symbol values. If 
a
    +             symbol has changed value (would generate different output in
    +             autoconf.h compared to before), the change is signaled by
    +             touch'ing a file corresponding to the symbol.
    +
    +             The first time sync_deps() is run on a directory, 
<path>/auto.conf
    +             won't exist, and no old symbol values will be available. This
    +             logically has the same effect as updating the entire
    +             configuration.
    +
    +             The path to a symbol's file is calculated from the symbol's 
name
    +             by replacing all '_' with '/' and appending '.h'. For 
example, the
    +             symbol FOO_BAR_BAZ gets the file <path>/foo/bar/baz.h, and FOO
    +             gets the file <path>/foo.h.
    +
    +             This scheme matches the C tools. The point is to avoid having 
a
    +             single directory with a huge number of files, which the 
underlying
    +             filesystem might not handle well.
    +
    +          3. A new auto.conf with the current symbol values is written, to 
keep
    +             track of them for the next build.
    +
    +             If auto.conf exists and its contents is identical to what 
would
    +             get written out, it is left untouched. This avoids updating 
file
    +             metadata like the modification time and possibly triggering
    +             redundant work in build tools.
    +
    +
    +        The last piece of the puzzle is knowing what symbols each source 
file
    +        depends on. Knowing that, dependencies can be added from source 
files
    +        to the files corresponding to the symbols they depends on. The 
source
    +        file will then get recompiled (only) when the symbol value changes
    +        (provided sync_deps() is run first during each build).
    +
    +        The tool in the kernel that extracts symbol dependencies from 
source
    +        files is scripts/basic/fixdep.c. Missing symbol files also 
correspond
    +        to "not changed", which fixdep deals with by using the $(wildcard) 
Make
    +        function when adding symbol prerequisites to source files.
    +
    +        In case you need a different scheme for your project, the 
sync_deps()
    +        implementation can be used as a template.
    +        """
    +        if not exists(path):
    +            os.mkdir(path, 0o755)
    +
    +        # Load old values from auto.conf, if any
    +        self._load_old_vals(path)
    +
    +        for sym in self.unique_defined_syms:
    +            # _write_to_conf is determined when the value is calculated. 
This
    +            # is a hidden function call due to property magic.
    +            #
    +            # Note: In client code, you can check if sym.config_string is 
empty
    +            # instead, to avoid accessing the internal _write_to_conf 
variable
    +            # (though it's likely to keep working).
    +            val = sym.str_value
    +
    +            # n tristate values do not get written to auto.conf and 
autoconf.h,
    +            # making a missing symbol logically equivalent to n
    +
    +            if sym._write_to_conf:
    +                if sym._old_val is None and \
    +                   sym.orig_type in _BOOL_TRISTATE and \
    +                   val == "n":
    +                    # No old value (the symbol was missing or n), new 
value n.
    +                    # No change.
    +                    continue
    +
    +                if val == sym._old_val:
    +                    # New value matches old. No change.
    +                    continue
    +
    +            elif sym._old_val is None:
    +                # The symbol wouldn't appear in autoconf.h (because
    +                # _write_to_conf is false), and it wouldn't have appeared 
in
    +                # autoconf.h previously either (because it didn't appear in
    +                # auto.conf). No change.
    +                continue
    +
    +            # 'sym' has a new value. Flag it.
    +            _touch_dep_file(path, sym.name)
    +
    +        # Remember the current values as the "new old" values.
    +        #
    +        # This call could go anywhere after the call to _load_old_vals(), 
but
    +        # putting it last means _sync_deps() can be safely rerun if it 
fails
    +        # before this point.
    +        self._write_old_vals(path)
    +
    +    def _load_old_vals(self, path):
    +        # Loads old symbol values from auto.conf into a dedicated
    +        # Symbol._old_val field. Mirrors load_config().
    +        #
    +        # The extra field could be avoided with some trickery involving 
dumping
    +        # symbol values and restoring them later, but this is simpler and
    +        # faster. The C tools also use a dedicated field for this purpose.
    +
    +        for sym in self.unique_defined_syms:
    +            sym._old_val = None
    +
    +        try:
    +            auto_conf = self._open(join(path, "auto.conf"), "r")
    +        except EnvironmentError as e:
    +            if e.errno == errno.ENOENT:
    +                # No old values
    +                return
    +            raise
    +
    +        with auto_conf as f:
    +            for line in f:
    +                match = self._set_match(line)
    +                if not match:
    +                    # We only expect CONFIG_FOO=... (and possibly a header
    +                    # comment) in auto.conf
    +                    continue
    +
    +                name, val = match.groups()
    +                if name in self.syms:
    +                    sym = self.syms[name]
    +
    +                    if sym.orig_type is STRING:
    +                        match = _conf_string_match(val)
    +                        if not match:
    +                            continue
    +                        val = unescape(match.group(1))
    +
    +                    self.syms[name]._old_val = val
    +                else:
    +                    # Flag that the symbol no longer exists, in
    +                    # case something still depends on it
    +                    _touch_dep_file(path, name)
    +
    +    def _write_old_vals(self, path):
    +        # Helper for writing auto.conf. Basically just a simplified
    +        # write_config() that doesn't write any comments (including
    +        # '# CONFIG_FOO is not set' comments). The format matches the C
    +        # implementation, though the ordering is arbitrary there (depends 
on
    +        # the hash table implementation).
    +        #
    +        # A separate helper function is neater than complicating 
write_config()
    +        # by passing a flag to it, plus we only need to look at symbols 
here.
    +
    +        self._write_if_changed(
    +            os.path.join(path, "auto.conf"),
    +            self._old_vals_contents())
    +
    +    def _old_vals_contents(self):
    +        # _write_old_vals() helper. Returns the contents to write as a 
string.
    +
    +        # Temporary list instead of generator makes this a bit faster
    +        return "".join([
    +            sym.config_string for sym in self.unique_defined_syms
    +                if not (sym.orig_type in _BOOL_TRISTATE and not 
sym.tri_value)
    +        ])
    +
    +    def node_iter(self, unique_syms=False):
    +        """
    +        Returns a generator for iterating through all MenuNode's in the 
Kconfig
    +        tree. The iteration is done in Kconfig definition order (each node 
is
    +        visited before its children, and the children of a node are visited
    +        before the next node).
    +
    +        The Kconfig.top_node menu node is skipped. It contains an implicit 
menu
    +        that holds the top-level items.
    +
    +        As an example, the following code will produce a list equal to
    +        Kconfig.defined_syms:
    +
    +          defined_syms = [node.item for node in kconf.node_iter()
    +                          if isinstance(node.item, Symbol)]
    +
    +        unique_syms (default: False):
    +          If True, only the first MenuNode will be included for symbols 
defined
    +          in multiple locations.
    +
    +          Using kconf.node_iter(True) in the example above would give a 
list
    +          equal to unique_defined_syms.
    +        """
    +        if unique_syms:
    +            for sym in self.unique_defined_syms:
    +                sym._visited = False
    +
    +        node = self.top_node
    +        while 1:
    +            # Jump to the next node with an iterative tree walk
    +            if node.list:
    +                node = node.list
    +            elif node.next:
    +                node = node.next
    +            else:
    +                while node.parent:
    +                    node = node.parent
    +                    if node.next:
    +                        node = node.next
    +                        break
    +                else:
    +                    # No more nodes
    +                    return
    +
    +            if unique_syms and node.item.__class__ is Symbol:
    +                if node.item._visited:
    +                    continue
    +                node.item._visited = True
    +
    +            yield node
    +
    +    def eval_string(self, s):
    +        """
    +        Returns the tristate value of the expression 's', represented as 
0, 1,
    +        and 2 for n, m, and y, respectively. Raises KconfigError on syntax
    +        errors. Warns if undefined symbols are referenced.
    +
    +        As an example, if FOO and BAR are tristate symbols at least one of
    +        which has the value y, then eval_string("y && (FOO || BAR)") 
returns
    +        2 (y).
    +
    +        To get the string value of non-bool/tristate symbols, use
    +        Symbol.str_value. eval_string() always returns a tristate value, 
and
    +        all non-bool/tristate symbols have the tristate value 0 (n).
    +
    +        The expression parsing is consistent with how parsing works for
    +        conditional ('if ...') expressions in the configuration, and 
matches
    +        the C implementation. m is rewritten to 'm && MODULES', so
    +        eval_string("m") will return 0 (n) unless modules are enabled.
    +        """
    +        # The parser is optimized to be fast when parsing Kconfig files 
(where
    +        # an expression can never appear at the beginning of a line). We 
have
    +        # to monkey-patch things a bit here to reuse it.
    +
    +        self._filename = None
    +
    +        self._tokens = self._tokenize("if " + s)
    +        # Strip "if " to avoid giving confusing error messages
    +        self._line = s
    +        self._tokens_i = 1  # Skip the 'if' token
    +
    +        return expr_value(self._expect_expr_and_eol())
    +
    +    def unset_values(self):
    +        """
    +        Removes any user values from all symbols, as if 
Kconfig.load_config()
    +        or Symbol.set_value() had never been called.
    +        """
    +        self._warn_assign_no_prompt = False
    +        try:
    +            # set_value() already rejects undefined symbols, and they don't
    +            # need to be invalidated (because their value never changes), 
so we
    +            # can just iterate over defined symbols
    +            for sym in self.unique_defined_syms:
    +                sym.unset_value()
    +
    +            for choice in self.unique_choices:
    +                choice.unset_value()
    +        finally:
    +            self._warn_assign_no_prompt = True
    +
    +    def enable_warnings(self):
    +        """
    +        Do 'Kconfig.warn = True' instead. Maintained for backwards
    +        compatibility.
    +        """
    +        self.warn = True
    +
    +    def disable_warnings(self):
    +        """
    +        Do 'Kconfig.warn = False' instead. Maintained for backwards
    +        compatibility.
    +        """
    +        self.warn = False
    +
    +    def enable_stderr_warnings(self):
    +        """
    +        Do 'Kconfig.warn_to_stderr = True' instead. Maintained for 
backwards
    +        compatibility.
    +        """
    +        self.warn_to_stderr = True
    +
    +    def disable_stderr_warnings(self):
    +        """
    +        Do 'Kconfig.warn_to_stderr = False' instead. Maintained for 
backwards
    +        compatibility.
    +        """
    +        self.warn_to_stderr = False
    +
    +    def enable_undef_warnings(self):
    +        """
    +        Do 'Kconfig.warn_assign_undef = True' instead. Maintained for 
backwards
    +        compatibility.
    +        """
    +        self.warn_assign_undef = True
    +
    +    def disable_undef_warnings(self):
    +        """
    +        Do 'Kconfig.warn_assign_undef = False' instead. Maintained for
    +        backwards compatibility.
    +        """
    +        self.warn_assign_undef = False
    +
    +    def enable_override_warnings(self):
    +        """
    +        Do 'Kconfig.warn_assign_override = True' instead. Maintained for
    +        backwards compatibility.
    +        """
    +        self.warn_assign_override = True
    +
    +    def disable_override_warnings(self):
    +        """
    +        Do 'Kconfig.warn_assign_override = False' instead. Maintained for
    +        backwards compatibility.
    +        """
    +        self.warn_assign_override = False
    +
    +    def enable_redun_warnings(self):
    +        """
    +        Do 'Kconfig.warn_assign_redun = True' instead. Maintained for 
backwards
    +        compatibility.
    +        """
    +        self.warn_assign_redun = True
    +
    +    def disable_redun_warnings(self):
    +        """
    +        Do 'Kconfig.warn_assign_redun = False' instead. Maintained for
    +        backwards compatibility.
    +        """
    +        self.warn_assign_redun = False
    +
    +    def __repr__(self):
    +        """
    +        Returns a string with information about the Kconfig object when it 
is
    +        evaluated on e.g. the interactive Python prompt.
    +        """
    +        def status(flag):
    +            return "enabled" if flag else "disabled"
    +
    +        return "<{}>".format(", ".join((
    +            "configuration with {} symbols".format(len(self.syms)),
    +            'main menu prompt "{}"'.format(self.mainmenu_text),
    +            "srctree is current directory" if not self.srctree else
    +                'srctree "{}"'.format(self.srctree),
    +            'config symbol prefix "{}"'.format(self.config_prefix),
    +            "warnings " + status(self.warn),
    +            "printing of warnings to stderr " + 
status(self.warn_to_stderr),
    +            "undef. symbol assignment warnings " +
    +                status(self.warn_assign_undef),
    +            "overriding symbol assignment warnings " +
    +                status(self.warn_assign_override),
    +            "redundant symbol assignment warnings " +
    +                status(self.warn_assign_redun)
    +        )))
    +
    +    #
    +    # Private methods
    +    #
    +
    +
    +    #
    +    # File reading
    +    #
    +
    +    def _open_config(self, filename):
    +        # Opens a .config file. First tries to open 'filename', then
    +        # '$srctree/filename' if $srctree was set when the configuration 
was
    +        # loaded.
    +
    +        try:
    +            return self._open(filename, "r")
    +        except EnvironmentError as e:
    +            # This will try opening the same file twice if $srctree is 
unset,
    +            # but it's not a big deal
    +            try:
    +                return self._open(join(self.srctree, filename), "r")
    +            except EnvironmentError as e2:
    +                # This is needed for Python 3, because e2 is deleted after
    +                # the try block:
    +                #
    +                # 
https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
    +                e = e2
    +
    +            raise _KconfigIOError(
    +                e, "Could not open '{}' ({}: {}). Check that the $srctree "
    +                   "environment variable ({}) is set correctly."
    +                   .format(filename, errno.errorcode[e.errno], e.strerror,
    +                           "set to '{}'".format(self.srctree) if 
self.srctree
    +                               else "unset or blank"))
    +
    +    def _enter_file(self, filename):
    +        # Jumps to the beginning of a sourced Kconfig file, saving the 
previous
    +        # position and file object.
    +        #
    +        # filename:
    +        #   Absolute path to file
    +
    +        # Path relative to $srctree, stored in e.g. self._filename
    +        # (which makes it indirectly show up in MenuNode.filename). Equals
    +        # 'filename' for absolute paths passed to 'source'.
    +        if filename.startswith(self._srctree_prefix):
    +            # Relative path (or a redundant absolute path to within 
$srctree,
    +            # but it's probably fine to reduce those too)
    +            rel_filename = filename[len(self._srctree_prefix):]
    +        else:
    +            # Absolute path
    +            rel_filename = filename
    +
    +        self.kconfig_filenames.append(rel_filename)
    +
    +        # The parent Kconfig files are represented as a list of
    +        # (<include path>, <Python 'file' object for Kconfig file>) tuples.
    +        #
    +        # <include path> is immutable and holds a *tuple* of
    +        # (<filename>, <linenr>) tuples, giving the locations of the 
'source'
    +        # statements in the parent Kconfig files. The current include path 
is
    +        # also available in Kconfig._include_path.
    +        #
    +        # The point of this redundant setup is to allow 
Kconfig._include_path
    +        # to be assigned directly to MenuNode.include_path without having 
to
    +        # copy it, sharing it wherever possible.
    +
    +        # Save include path and 'file' object (via its 'readline' function)
    +        # before entering the file
    +        self._filestack.append((self._include_path, self._readline))
    +
    +        # _include_path is a tuple, so this rebinds the variable instead of
    +        # doing in-place modification
    +        self._include_path += ((self._filename, self._linenr),)
    +
    +        # Check for recursive 'source'
    +        for name, _ in self._include_path:
    +            if name == rel_filename:
    +                raise KconfigError(
    +                    "\n{}:{}: recursive 'source' of '{}' detected. Check 
that "
    +                    "environment variables are set correctly.\n"
    +                    "Include path:\n{}"
    +                    .format(self._filename, self._linenr, rel_filename,
    +                            "\n".join("{}:{}".format(name, linenr)
    +                                      for name, linenr in 
self._include_path)))
    +
    +        try:
    +            self._readline = self._open(filename, "r").readline
    +        except EnvironmentError as e:
    +            # We already know that the file exists
    +            raise _KconfigIOError(
    +                e, "{}:{}: Could not open '{}' (in '{}') ({}: {})"
    +                   .format(self._filename, self._linenr, filename,
    +                           self._line.strip(),
    +                           errno.errorcode[e.errno], e.strerror))
    +
    +        self._filename = rel_filename
    +        self._linenr = 0
    +
    +    def _leave_file(self):
    +        # Returns from a Kconfig file to the file that sourced it. See
    +        # _enter_file().
    +
    +        # Restore location from parent Kconfig file
    +        self._filename, self._linenr = self._include_path[-1]
    +        # Restore include path and 'file' object
    +        self._readline.__self__.close()  # __self__ fetches the 'file' 
object
    +        self._include_path, self._readline = self._filestack.pop()
    +
    +    def _next_line(self):
    +        # Fetches and tokenizes the next line from the current Kconfig 
file.
    +        # Returns False at EOF and True otherwise.
    +
    +        # We might already have tokens from parsing a line and discovering 
that
    +        # it's part of a different construct
    +        if self._reuse_tokens:
    +            self._reuse_tokens = False
    +            # self._tokens_i is known to be 1 here, because 
_parse_properties()
    +            # leaves it like that when it can't recognize a line (or parses
    +            # a help text)
    +            return True
    +
    +        # readline() returns '' over and over at EOF, which we rely on for 
help
    +        # texts at the end of files (see _line_after_help())
    +        line = self._readline()
    +        if not line:
    +            return False
    +        self._linenr += 1
    +
    +        # Handle line joining
    +        while line.endswith("\\\n"):
    +            line = line[:-2] + self._readline()
    +            self._linenr += 1
    +
    +        self._tokens = self._tokenize(line)
    +        # Initialize to 1 instead of 0 to factor out code from 
_parse_block()
    +        # and _parse_properties(). They immediately fetch self._tokens[0].
    +        self._tokens_i = 1
    +
    +        return True
    +
    +    def _line_after_help(self, line):
    +        # Tokenizes a line after a help text. This case is special in that 
the
    +        # line has already been fetched (to discover that it isn't part of 
the
    +        # help text).
    +        #
    +        # An earlier version used a _saved_line variable instead that was
    +        # checked in _next_line(). This special-casing gets rid of it and 
makes
    +        # _reuse_tokens alone sufficient to handle unget.
    +
    +        # Handle line joining
    +        while line.endswith("\\\n"):
    +            line = line[:-2] + self._readline()
    +            self._linenr += 1
    +
    +        self._tokens = self._tokenize(line)
    +        self._reuse_tokens = True
    +
    +    def _write_if_changed(self, filename, contents):
    +        # Writes 'contents' into 'filename', but only if it differs from 
the
    +        # current contents of the file.
    +        #
    +        # Another variant would be write a temporary file on the same
    +        # filesystem, compare the files, and rename() the temporary file 
if it
    +        # differs, but it breaks stuff like write_config("/dev/null"), 
which is
    +        # used out there to force evaluation-related warnings to be 
generated.
    +        # This simple version is pretty failsafe and portable.
    +
    +        if not self._contents_eq(filename, contents):
    +            with self._open(filename, "w") as f:
    +                f.write(contents)
    +
    +    def _contents_eq(self, filename, contents):
    +        # Returns True if the contents of 'filename' is 'contents' (a 
string),
    +        # and False otherwise (including if 'filename' can't be 
opened/read)
    +
    +        try:
    +            with self._open(filename, "r") as f:
    +                # Robust re. things like encoding and line endings (mmap()
    +                # trickery isn't)
    +                return f.read(len(contents) + 1) == contents
    +        except EnvironmentError:
    +            # If the error here would prevent writing the file as well, 
we'll
    +            # notice it later
    +            return False
    +
    +    #
    +    # Tokenization
    +    #
    +
    +    def _lookup_sym(self, name):
    +        # Fetches the symbol 'name' from the symbol table, creating and
    +        # registering it if it does not exist. If '_parsing_kconfigs' is 
False,
    +        # it means we're in eval_string(), and new symbols won't be 
registered.
    +
    +        if name in self.syms:
    +            return self.syms[name]
    +
    +        sym = Symbol()
    +        sym.kconfig = self
    +        sym.name = name
    +        sym.is_constant = False
    +        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
    +
    +        if self._parsing_kconfigs:
    +            self.syms[name] = sym
    +        else:
    +            self._warn("no symbol {} in configuration".format(name))
    +
    +        return sym
    +
    +    def _lookup_const_sym(self, name):
    +        # Like _lookup_sym(), for constant (quoted) symbols
    +
    +        if name in self.const_syms:
    +            return self.const_syms[name]
    +
    +        sym = Symbol()
    +        sym.kconfig = self
    +        sym.name = name
    +        sym.is_constant = True
    +        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
    +
    +        if self._parsing_kconfigs:
    +            self.const_syms[name] = sym
    +
    +        return sym
    +
    +    def _tokenize(self, s):
    +        # Parses 's', returning a None-terminated list of tokens. 
Registers any
    +        # new symbols encountered with _lookup(_const)_sym().
    +        #
    +        # Tries to be reasonably speedy by processing chunks of text via
    +        # regexes and string operations where possible. This is the biggest
    +        # hotspot during parsing.
    +        #
    +        # It might be possible to rewrite this to 'yield' tokens instead,
    +        # working across multiple lines. Lookback and compatibility with 
old
    +        # janky versions of the C tools complicate things though.
    +
    +        self._line = s  # Used for error reporting
    +
    +        # Initial token on the line
    +        match = _command_match(s)
    +        if not match:
    +            if s.isspace() or s.lstrip().startswith("#"):
    +                return (None,)
    +            self._parse_error("unknown token at start of line")
    +
    +        # Tricky implementation detail: While parsing a token, 'token' 
refers
    +        # to the previous token. See _STRING_LEX for why this is needed.
    +        token = _get_keyword(match.group(1))
    +        if not token:
    +            # Backwards compatibility with old versions of the C tools, 
which
    +            # (accidentally) accepted stuff like "--help--" and "-help---".
    +            # This was fixed in the C tools by commit c2264564 ("kconfig: 
warn
    +            # of unhandled characters in Kconfig commands"), committed in 
July
    +            # 2015, but it seems people still run Kconfiglib on older 
kernels.
    +            if s.strip(" \t\n-") == "help":
    +                return (_T_HELP, None)
    +
    +            # If the first token is not a keyword (and not a weird help 
token),
    +            # we have a preprocessor variable assignment (or a bare macro 
on a
    +            # line)
    +            self._parse_assignment(s)
    +            return (None,)
    +
    +        tokens = [token]
    +        # The current index in the string being tokenized
    +        i = match.end()
    +
    +        # Main tokenization loop (for tokens past the first one)
    +        while i < len(s):
    +            # Test for an identifier/keyword first. This is the most common
    +            # case.
    +            match = _id_keyword_match(s, i)
    +            if match:
    +                # We have an identifier or keyword
    +
    +                # Check what it is. lookup_sym() will take care of 
allocating
    +                # new symbols for us the first time we see them. Note that
    +                # 'token' still refers to the previous token.
    +
    +                name = match.group(1)
    +                keyword = _get_keyword(name)
    +                if keyword:
    +                    # It's a keyword
    +                    token = keyword
    +                    # Jump past it
    +                    i = match.end()
    +
    +                elif token not in _STRING_LEX:
    +                    # It's a non-const symbol, except we translate n, m, 
and y
    +                    # into the corresponding constant symbols, like the C
    +                    # implementation
    +
    +                    if "$" in name:
    +                        # Macro expansion within symbol name
    +                        name, s, i = self._expand_name(s, i)
    +                    else:
    +                        i = match.end()
    +
    +                    token = self.const_syms[name] if name in STR_TO_TRI 
else \
    +                        self._lookup_sym(name)
    +
    +                else:
    +                    # It's a case of missing quotes. For example, the
    +                    # following is accepted:
    +                    #
    +                    #   menu unquoted_title
    +                    #
    +                    #   config A
    +                    #       tristate unquoted_prompt
    +                    #
    +                    #   endmenu
    +                    #
    +                    # Named choices ('choice FOO') also end up here.
    +
    +                    if token is not _T_CHOICE:
    +                        self._warn("style: quotes recommended around '{}' 
in '{}'"
    +                                   .format(name, self._line.strip()),
    +                                   self._filename, self._linenr)
    +
    +                    token = name
    +                    i = match.end()
    +
    +            else:
    +                # Neither a keyword nor a non-const symbol
    +
    +                # We always strip whitespace after tokens, so it is safe to
    +                # assume that s[i] is the start of a token here.
    +                c = s[i]
    +
    +                if c in "\"'":
    +                    if "$" not in s and "\\" not in s:
    +                        # Fast path for lines without $ and \. Find the
    +                        # matching quote.
    +                        end_i = s.find(c, i + 1) + 1
    +                        if not end_i:
    +                            self._parse_error("unterminated string")
    +                        val = s[i + 1:end_i - 1]
    +                        i = end_i
    +                    else:
    +                        # Slow path
    +                        s, end_i = self._expand_str(s, i)
    +
    +                        # os.path.expandvars() and the $UNAME_RELEASE 
replace()
    +                        # is a backwards compatibility hack, which should 
be
    +                        # reasonably safe as expandvars() leaves 
references to
    +                        # undefined env. vars. as is.
    +                        #
    +                        # The preprocessor functionality changed how
    +                        # environment variables are referenced, to $(FOO).
    +                        val = expandvars(s[i + 1:end_i - 1]
    +                                         .replace("$UNAME_RELEASE",
    +                                                  _UNAME_RELEASE))
    +
    +                        i = end_i
    +
    +                    # This is the only place where we don't survive with a
    +                    # single token of lookback: 'option env="FOO"' does not
    +                    # refer to a constant symbol named "FOO".
    +                    token = \
    +                        val if token in _STRING_LEX or tokens[0] is 
_T_OPTION \
    +                        else self._lookup_const_sym(val)
    +
    +                elif s.startswith("&&", i):
    +                    token = _T_AND
    +                    i += 2
    +
    +                elif s.startswith("||", i):
    +                    token = _T_OR
    +                    i += 2
    +
    +                elif c == "=":
    +                    token = _T_EQUAL
    +                    i += 1
    +
    +                elif s.startswith("!=", i):
    +                    token = _T_UNEQUAL
    +                    i += 2
    +
    +                elif c == "!":
    +                    token = _T_NOT
    +                    i += 1
    +
    +                elif c == "(":
    +                    token = _T_OPEN_PAREN
    +                    i += 1
    +
    +                elif c == ")":
    +                    token = _T_CLOSE_PAREN
    +                    i += 1
    +
    +                elif c == "#":
    +                    break
    +
    +
    +                # Very rare
    +
    +                elif s.startswith("<=", i):
    +                    token = _T_LESS_EQUAL
    +                    i += 2
    +
    +                elif c == "<":
    +                    token = _T_LESS
    +                    i += 1
    +
    +                elif s.startswith(">=", i):
    +                    token = _T_GREATER_EQUAL
    +                    i += 2
    +
    +                elif c == ">":
    +                    token = _T_GREATER
    +                    i += 1
    +
    +
    +                else:
    +                    self._parse_error("unknown tokens in line")
    +
    +
    +                # Skip trailing whitespace
    +                while i < len(s) and s[i].isspace():
    +                    i += 1
    +
    +
    +            # Add the token
    +            tokens.append(token)
    +
    +        # None-terminating the token list makes token fetching 
simpler/faster
    +        tokens.append(None)
    +
    +        return tokens
    +
    +    # Helpers for syntax checking and token fetching. See the
    +    # 'Intro to expressions' section for what a constant symbol is.
    +    #
    +    # More of these could be added, but the single-use cases are inlined 
as an
    +    # optimization.
    +
    +    def _expect_sym(self):
    +        token = self._tokens[self._tokens_i]
    +        self._tokens_i += 1
    +
    +        if token.__class__ is not Symbol:
    +            self._parse_error("expected symbol")
    +
    +        return token
    +
    +    def _expect_nonconst_sym(self):
    +        # Used for 'select' and 'imply' only. We know the token indices.
    +
    +        token = self._tokens[1]
    +        self._tokens_i = 2
    +
    +        if token.__class__ is not Symbol or token.is_constant:
    +            self._parse_error("expected nonconstant symbol")
    +
    +        return token
    +
    +    def _expect_str_and_eol(self):
    +        token = self._tokens[self._tokens_i]
    +        self._tokens_i += 1
    +
    +        if token.__class__ is not str:
    +            self._parse_error("expected string")
    +
    +        if self._tokens[self._tokens_i] is not None:
    +            self._trailing_tokens_error()
    +
    +        return token
    +
    +    def _expect_expr_and_eol(self):
    +        expr = self._parse_expr(True)
    +
    +        if self._tokens[self._tokens_i] is not None:
    +            self._trailing_tokens_error()
    +
    +        return expr
    +
    +    def _check_token(self, token):
    +        # If the next token is 'token', removes it and returns True
    +
    +        if self._tokens[self._tokens_i] is token:
    +            self._tokens_i += 1
    +            return True
    +        return False
    +
    +    #
    +    # Preprocessor logic
    +    #
    +
    +    def _parse_assignment(self, s):
    +        # Parses a preprocessor variable assignment, registering the 
variable
    +        # if it doesn't already exist. Also takes care of bare macros on 
lines
    +        # (which are allowed, and can be useful for their side effects).
    +
    +        # Expand any macros in the left-hand side of the assignment (the
    +        # variable name)
    +        s = s.lstrip()
    +        i = 0
    +        while 1:
    +            i = _assignment_lhs_fragment_match(s, i).end()
    +            if s.startswith("$(", i):
    +                s, i = self._expand_macro(s, i, ())
    +            else:
    +                break
    +
    +        if s.isspace():
    +            # We also accept a bare macro on a line (e.g.
    +            # $(warning-if,$(foo),ops)), provided it expands to a blank 
string
    +            return
    +
    +        # Assigned variable
    +        name = s[:i]
    +
    +
    +        # Extract assignment operator (=, :=, or +=) and value
    +        rhs_match = _assignment_rhs_match(s, i)
    +        if not rhs_match:
    +            self._parse_error("syntax error")
    +
    +        op, val = rhs_match.groups()
    +
    +
    +        if name in self.variables:
    +            # Already seen variable
    +            var = self.variables[name]
    +        else:
    +            # New variable
    +            var = Variable()
    +            var.kconfig = self
    +            var.name = name
    +            var._n_expansions = 0
    +            self.variables[name] = var
    +
    +            # += acts like = on undefined variables (defines a recursive
    +            # variable)
    +            if op == "+=":
    +                op = "="
    +
    +        if op == "=":
    +            var.is_recursive = True
    +            var.value = val
    +        elif op == ":=":
    +            var.is_recursive = False
    +            var.value = self._expand_whole(val, ())
    +        else:  # op == "+="
    +            # += does immediate expansion if the variable was last set
    +            # with :=
    +            var.value += " " + (val if var.is_recursive else
    +                                self._expand_whole(val, ()))
    +
    +    def _expand_whole(self, s, args):
    +        # Expands preprocessor macros in all of 's'. Used whenever we don't
    +        # have to worry about delimiters. See _expand_macro() re. the 
'args'
    +        # parameter.
    +        #
    +        # Returns the expanded string.
    +
    +        i = 0
    +        while 1:
    +            i = s.find("$(", i)
    +            if i == -1:
    +                break
    +            s, i = self._expand_macro(s, i, args)
    +        return s
    +
    +    def _expand_name(self, s, i):
    +        # Expands a symbol name starting at index 'i' in 's'.
    +        #
    +        # Returns the expanded name, the expanded 's' (including the part
    +        # before the name), and the index of the first character in the 
next
    +        # token after the name.
    +
    +        s, end_i = self._expand_name_iter(s, i)
    +        name = s[i:end_i]
    +        # isspace() is False for empty strings
    +        if not name.strip():
    +            # Avoid creating a Kconfig symbol with a blank name. It's 
almost
    +            # guaranteed to be an error.
    +            self._parse_error("macro expanded to blank string")
    +
    +        # Skip trailing whitespace
    +        while end_i < len(s) and s[end_i].isspace():
    +            end_i += 1
    +
    +        return name, s, end_i
    +
    +    def _expand_name_iter(self, s, i):
    +        # Expands a symbol name starting at index 'i' in 's'.
    +        #
    +        # Returns the expanded 's' (including the part before the name) 
and the
    +        # index of the first character after the expanded name in 's'.
    +
    +        while 1:
    +            match = _name_special_search(s, i)
    +
    +            if match.group() == "$(":
    +                s, i = self._expand_macro(s, match.start(), ())
    +            else:
    +                return (s, match.start())
    +
    +    def _expand_str(self, s, i):
    +        # Expands a quoted string starting at index 'i' in 's'. Handles 
both
    +        # backslash escapes and macro expansion.
    +        #
    +        # Returns the expanded 's' (including the part before the string) 
and
    +        # the index of the first character after the expanded string in 
's'.
    +
    +        quote = s[i]
    +        i += 1  # Skip over initial "/'
    +        while 1:
    +            match = _string_special_search(s, i)
    +            if not match:
    +                self._parse_error("unterminated string")
    +
    +
    +            if match.group() == quote:
    +                # Found the end of the string
    +                return (s, match.end())
    +
    +            elif match.group() == "\\":
    +                # Replace '\x' with 'x'. 'i' ends up pointing to the 
character
    +                # after 'x', which allows macros to be canceled with 
'\$(foo)'.
    +                i = match.end()
    +                s = s[:match.start()] + s[i:]
    +
    +            elif match.group() == "$(":
    +                # A macro call within the string
    +                s, i = self._expand_macro(s, match.start(), ())
    +
    +            else:
    +                # A ' quote within " quotes or vice versa
    +                i += 1
    +
    +    def _expand_macro(self, s, i, args):
    +        # Expands a macro starting at index 'i' in 's'. If this macro 
resulted
    +        # from the expansion of another macro, 'args' holds the arguments
    +        # passed to that macro.
    +        #
    +        # Returns the expanded 's' (including the part before the macro) 
and
    +        # the index of the first character after the expanded macro in 's'.
    +
    +        start = i
    +        i += 2  # Skip over "$("
    +
    +        # Start of current macro argument
    +        arg_start = i
    +
    +        # Arguments of this macro call
    +        new_args = []
    +
    +        while 1:
    +            match = _macro_special_search(s, i)
    +            if not match:
    +                self._parse_error("missing end parenthesis in macro 
expansion")
    +
    +
    +            if match.group() == ")":
    +                # Found the end of the macro
    +
    +                new_args.append(s[arg_start:match.start()])
    +
    +                prefix = s[:start]
    +
    +                # $(1) is replaced by the first argument to the function, 
etc.,
    +                # provided at least that many arguments were passed
    +
    +                try:
    +                    # Does the macro look like an integer, with a 
corresponding
    +                    # argument? If so, expand it to the value of the 
argument.
    +                    prefix += args[int(new_args[0])]
    +                except (ValueError, IndexError):
    +                    # Regular variables are just functions without 
arguments,
    +                    # and also go through the function value path
    +                    prefix += self._fn_val(new_args)
    +
    +                return (prefix + s[match.end():],
    +                        len(prefix))
    +
    +            elif match.group() == ",":
    +                # Found the end of a macro argument
    +                new_args.append(s[arg_start:match.start()])
    +                arg_start = i = match.end()
    +
    +            else:  # match.group() == "$("
    +                # A nested macro call within the macro
    +                s, i = self._expand_macro(s, match.start(), args)
    +
    +    def _fn_val(self, args):
    +        # Returns the result of calling the function args[0] with the 
arguments
    +        # args[1..len(args)-1]. Plain variables are treated as functions
    +        # without arguments.
    +
    +        fn = args[0]
    +
    +        if fn in self.variables:
    +            var = self.variables[fn]
    +
    +            if len(args) == 1:
    +                # Plain variable
    +                if var._n_expansions:
    +                    self._parse_error("Preprocessor variable {} 
recursively "
    +                                      "references itself".format(var.name))
    +            elif var._n_expansions > 100:
    +                # Allow functions to call themselves, but guess that 
functions
    +                # that are overly recursive are stuck
    +                self._parse_error("Preprocessor function {} seems stuck "
    +                                  "in infinite recursion".format(var.name))
    +
    +            var._n_expansions += 1
    +            res = self._expand_whole(self.variables[fn].value, args)
    +            var._n_expansions -= 1
    +            return res
    +
    +        if fn in self._functions:
    +            # Built-in or user-defined function
    +
    +            py_fn, min_arg, max_arg = self._functions[fn]
    +
    +            if len(args) - 1 < min_arg or \
    +               (max_arg is not None and len(args) - 1 > max_arg):
    +
    +                if min_arg == max_arg:
    +                    expected_args = min_arg
    +                elif max_arg is None:
    +                    expected_args = "{} or more".format(min_arg)
    +                else:
    +                    expected_args = "{}-{}".format(min_arg, max_arg)
    +
    +                raise KconfigError("{}:{}: bad number of arguments in call 
"
    +                                   "to {}, expected {}, got {}"
    +                                   .format(self._filename, self._linenr, 
fn,
    +                                           expected_args, len(args) - 1))
    +
    +            return py_fn(self, *args)
    +
    +        # Environment variables are tried last
    +        if fn in os.environ:
    +            self.env_vars.add(fn)
    +            return os.environ[fn]
    +
    +        return ""
    +
    +    #
    +    # Parsing
    +    #
    +
    +    def _make_and(self, e1, e2):
    +        # Constructs an AND (&&) expression. Performs trivial 
simplification.
    +
    +        if e1 is self.y:
    +            return e2
    +
    +        if e2 is self.y:
    +            return e1
    +
    +        if e1 is self.n or e2 is self.n:
    +            return self.n
    +
    +        return (AND, e1, e2)
    +
    +    def _make_or(self, e1, e2):
    +        # Constructs an OR (||) expression. Performs trivial 
simplification.
    +
    +        if e1 is self.n:
    +            return e2
    +
    +        if e2 is self.n:
    +            return e1
    +
    +        if e1 is self.y or e2 is self.y:
    +            return self.y
    +
    +        return (OR, e1, e2)
    +
    +    def _parse_block(self, end_token, parent, prev):
    +        # Parses a block, which is the contents of either a file or an if,
    +        # menu, or choice statement.
    +        #
    +        # end_token:
    +        #   The token that ends the block, e.g. _T_ENDIF ("endif") for ifs.
    +        #   None for files.
    +        #
    +        # parent:
    +        #   The parent menu node, corresponding to a menu, Choice, or 'if'.
    +        #   'if's are flattened after parsing.
    +        #
    +        # prev:
    +        #   The previous menu node. New nodes will be added after this one 
(by
    +        #   modifying their 'next' pointer).
    +        #
    +        #   'prev' is reused to parse a list of child menu nodes (for a 
menu or
    +        #   Choice): After parsing the children, the 'next' pointer is 
assigned
    +        #   to the 'list' pointer to "tilt up" the children above the node.
    +        #
    +        # Returns the final menu node in the block (or 'prev' if the block 
is
    +        # empty). This allows chaining.
    +
    +        while self._next_line():
    +            t0 = self._tokens[0]
    +
    +            if t0 is _T_CONFIG or t0 is _T_MENUCONFIG:
    +                # The tokenizer allocates Symbol objects for us
    +                sym = self._tokens[1]
    +
    +                if sym.__class__ is not Symbol or sym.is_constant:
    +                    self._parse_error("missing or bad symbol name")
    +
    +                if self._tokens[2] is not None:
    +                    self._trailing_tokens_error()
    +
    +                self.defined_syms.append(sym)
    +
    +                node = MenuNode()
    +                node.kconfig = self
    +                node.item = sym
    +                node.is_menuconfig = (t0 is _T_MENUCONFIG)
    +                node.prompt = node.help = node.list = None
    +                node.parent = parent
    +                node.filename = self._filename
    +                node.linenr = self._linenr
    +                node.include_path = self._include_path
    +
    +                sym.nodes.append(node)
    +
    +                self._parse_properties(node)
    +
    +                if node.is_menuconfig and not node.prompt:
    +                    self._warn("the menuconfig symbol {} has no prompt"
    +                               .format(_name_and_loc(sym)))
    +
    +                # Equivalent to
    +                #
    +                #   prev.next = node
    +                #   prev = node
    +                #
    +                # due to tricky Python semantics. The order matters.
    +                prev.next = prev = node
    +
    +            elif t0 is None:
    +                # Blank line
    +                continue
    +
    +            elif t0 in _SOURCE_TOKENS:
    +                pattern = self._expect_str_and_eol()
    +
    +                if t0 in _REL_SOURCE_TOKENS:
    +                    # Relative source
    +                    pattern = join(dirname(self._filename), pattern)
    +
    +                # - glob() doesn't support globbing relative to a 
directory, so
    +                #   we need to prepend $srctree to 'pattern'. Use join()
    +                #   instead of '+' so that an absolute path in 'pattern' is
    +                #   preserved.
    +                #
    +                # - Sort the glob results to ensure a consistent ordering 
of
    +                #   Kconfig symbols, which indirectly ensures a consistent
    +                #   ordering in e.g. .config files
    +                filenames = sorted(iglob(join(self._srctree_prefix, 
pattern)))
    +
    +                if not filenames and t0 in _OBL_SOURCE_TOKENS:
    +                    raise KconfigError(
    +                        "{}:{}: '{}' not found (in '{}'). Check that "
    +                        "environment variables are set correctly (e.g. "
    +                        "$srctree, which is {}). Also note that unset "
    +                        "environment variables expand to the empty string."
    +                        .format(self._filename, self._linenr, pattern,
    +                                self._line.strip(),
    +                                "set to '{}'".format(self.srctree)
    +                                    if self.srctree else "unset or blank"))
    +
    +                for filename in filenames:
    +                    self._enter_file(filename)
    +                    prev = self._parse_block(None, parent, prev)
    +                    self._leave_file()
    +
    +            elif t0 is end_token:
    +                # Reached the end of the block. Terminate the final node 
and
    +                # return it.
    +
    +                if self._tokens[1] is not None:
    +                    self._trailing_tokens_error()
    +
    +                prev.next = None
    +                return prev
    +
    +            elif t0 is _T_IF:
    +                node = MenuNode()
    +                node.item = node.prompt = None
    +                node.parent = parent
    +                node.dep = self._expect_expr_and_eol()
    +
    +                self._parse_block(_T_ENDIF, node, node)
    +                node.list = node.next
    +
    +                prev.next = prev = node
    +
    +            elif t0 is _T_MENU:
    +                node = MenuNode()
    +                node.kconfig = self
    +                node.item = t0  # _T_MENU == MENU
    +                node.is_menuconfig = True
    +                node.prompt = (self._expect_str_and_eol(), self.y)
    +                node.visibility = self.y
    +                node.parent = parent
    +                node.filename = self._filename
    +                node.linenr = self._linenr
    +                node.include_path = self._include_path
    +
    +                self.menus.append(node)
    +
    +                self._parse_properties(node)
    +                self._parse_block(_T_ENDMENU, node, node)
    +                node.list = node.next
    +
    +                prev.next = prev = node
    +
    +            elif t0 is _T_COMMENT:
    +                node = MenuNode()
    +                node.kconfig = self
    +                node.item = t0  # _T_COMMENT == COMMENT
    +                node.is_menuconfig = False
    +                node.prompt = (self._expect_str_and_eol(), self.y)
    +                node.list = None
    +                node.parent = parent
    +                node.filename = self._filename
    +                node.linenr = self._linenr
    +                node.include_path = self._include_path
    +
    +                self.comments.append(node)
    +
    +                self._parse_properties(node)
    +
    +                prev.next = prev = node
    +
    +            elif t0 is _T_CHOICE:
    +                if self._tokens[1] is None:
    +                    choice = Choice()
    +                    choice.direct_dep = self.n
    +                else:
    +                    # Named choice
    +                    name = self._expect_str_and_eol()
    +                    choice = self.named_choices.get(name)
    +                    if not choice:
    +                        choice = Choice()
    +                        choice.name = name
    +                        choice.direct_dep = self.n
    +                        self.named_choices[name] = choice
    +
    +                self.choices.append(choice)
    +
    +                node = MenuNode()
    +                node.kconfig = choice.kconfig = self
    +                node.item = choice
    +                node.is_menuconfig = True
    +                node.prompt = node.help = None
    +                node.parent = parent
    +                node.filename = self._filename
    +                node.linenr = self._linenr
    +                node.include_path = self._include_path
    +
    +                choice.nodes.append(node)
    +
    +                self._parse_properties(node)
    +                self._parse_block(_T_ENDCHOICE, node, node)
    +                node.list = node.next
    +
    +                prev.next = prev = node
    +
    +            elif t0 is _T_MAINMENU:
    +                self.top_node.prompt = (self._expect_str_and_eol(), self.y)
    +
    +            else:
    +                # A valid endchoice/endif/endmenu is caught by the 
'end_token'
    +                # check above
    +                self._parse_error(
    +                    "no corresponding 'choice'" if t0 is _T_ENDCHOICE else
    +                    "no corresponding 'if'"     if t0 is _T_ENDIF else
    +                    "no corresponding 'menu'"   if t0 is _T_ENDMENU else
    +                    "unrecognized construct")
    +
    +        # End of file reached. Terminate the final node and return it.
    +
    +        if end_token:
    +            raise KconfigError(
    +                "expected '{}' at end of '{}'"
    +                .format("endchoice" if end_token is _T_ENDCHOICE else
    +                        "endif"     if end_token is _T_ENDIF else
    +                        "endmenu",
    +                        self._filename))
    +
    +        prev.next = None
    +        return prev
    +
    +    def _parse_cond(self):
    +        # Parses an optional 'if <expr>' construct and returns the parsed
    +        # <expr>, or self.y if the next token is not _T_IF
    +
    +        expr = self._parse_expr(True) if self._check_token(_T_IF) else 
self.y
    +
    +        if self._tokens[self._tokens_i] is not None:
    +            self._trailing_tokens_error()
    +
    +        return expr
    +
    +    def _parse_properties(self, node):
    +        # Parses and adds properties to the MenuNode 'node' (type, 
'prompt',
    +        # 'default's, etc.) Properties are later copied up to symbols and
    +        # choices in a separate pass after parsing, in e.g.
    +        # _add_props_to_sym().
    +        #
    +        # An older version of this code added properties directly to 
symbols
    +        # and choices instead of to their menu nodes (and handled 
dependency
    +        # propagation simultaneously), but that loses information on where 
a
    +        # property is added when a symbol or choice is defined in multiple
    +        # locations. Some Kconfig configuration systems rely heavily on 
such
    +        # symbols, and better docs can be generated by keeping track of 
where
    +        # properties are added.
    +        #
    +        # node:
    +        #   The menu node we're parsing properties on
    +
    +        # Dependencies from 'depends on'. Will get propagated to the 
properties
    +        # below.
    +        node.dep = self.y
    +
    +        while self._next_line():
    +            t0 = self._tokens[0]
    +
    +            if t0 in _TYPE_TOKENS:
    +                # Relies on '_T_BOOL is BOOL', etc., to save a conversion
    +                self._set_type(node, t0)
    +                if self._tokens[1] is not None:
    +                    self._parse_prompt(node)
    +
    +            elif t0 is _T_DEPENDS:
    +                if not self._check_token(_T_ON):
    +                    self._parse_error("expected 'on' after 'depends'")
    +
    +                node.dep = self._make_and(node.dep,
    +                                          self._expect_expr_and_eol())
    +
    +            elif t0 is _T_HELP:
    +                self._parse_help(node)
    +
    +            elif t0 is _T_SELECT:
    +                if node.item.__class__ is not Symbol:
    +                    self._parse_error("only symbols can select")
    +
    +                node.selects.append((self._expect_nonconst_sym(),
    +                                     self._parse_cond()))
    +
    +            elif t0 is None:
    +                # Blank line
    +                continue
    +
    +            elif t0 is _T_DEFAULT:
    +                node.defaults.append((self._parse_expr(False),
    +                                      self._parse_cond()))
    +
    +            elif t0 in _DEF_TOKEN_TO_TYPE:
    +                self._set_type(node, _DEF_TOKEN_TO_TYPE[t0])
    +                node.defaults.append((self._parse_expr(False),
    +                                      self._parse_cond()))
    +
    +            elif t0 is _T_PROMPT:
    +                self._parse_prompt(node)
    +
    +            elif t0 is _T_RANGE:
    +                node.ranges.append((self._expect_sym(), self._expect_sym(),
    +                                    self._parse_cond()))
    +
    +            elif t0 is _T_IMPLY:
    +                if node.item.__class__ is not Symbol:
    +                    self._parse_error("only symbols can imply")
    +
    +                node.implies.append((self._expect_nonconst_sym(),
    +                                     self._parse_cond()))
    +
    +            elif t0 is _T_VISIBLE:
    +                if not self._check_token(_T_IF):
    +                    self._parse_error("expected 'if' after 'visible'")
    +
    +                node.visibility = self._make_and(node.visibility,
    +                                                 
self._expect_expr_and_eol())
    +
    +            elif t0 is _T_OPTION:
    +                if self._check_token(_T_ENV):
    +                    if not self._check_token(_T_EQUAL):
    +                        self._parse_error("expected '=' after 'env'")
    +
    +                    env_var = self._expect_str_and_eol()
    +                    node.item.env_var = env_var
    +
    +                    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)
    +
    +                    if env_var != node.item.name:
    +                        self._warn("Kconfiglib expands environment 
variables "
    +                                   "in strings directly, meaning you do 
not "
    +                                   "need 'option env=...' \"bounce\" 
symbols. "
    +                                   "For compatibility with the C tools, "
    +                                   "rename {} to {} (so that the symbol 
name "
    +                                   "matches the environment variable 
name)."
    +                                   .format(node.item.name, env_var),
    +                                   self._filename, self._linenr)
    +
    +                elif self._check_token(_T_DEFCONFIG_LIST):
    +                    if not self.defconfig_list:
    +                        self.defconfig_list = node.item
    +                    else:
    +                        self._warn("'option defconfig_list' set on 
multiple "
    +                                   "symbols ({0} and {1}). Only {0} will 
be "
    +                                   "used.".format(self.defconfig_list.name,
    +                                                  node.item.name),
    +                                   self._filename, self._linenr)
    +
    +                elif self._check_token(_T_MODULES):
    +                    # To reduce warning spam, only warn if 'option 
modules' is
    +                    # set on some symbol that isn't MODULES, which should 
be
    +                    # safe. I haven't run into any projects that make use
    +                    # modules besides the kernel yet, and there it's 
likely to
    +                    # keep being called "MODULES".
    +                    if node.item is not self.modules:
    +                        self._warn("the 'modules' option is not supported. 
"
    +                                   "Let me know if this is a problem for 
you, "
    +                                   "as it wouldn't be that hard to 
implement. "
    +                                   "Note that modules are supported -- "
    +                                   "Kconfiglib just assumes the symbol 
name "
    +                                   "MODULES, like older versions of the C "
    +                                   "implementation did when 'option 
modules' "
    +                                   "wasn't used.",
    +                                   self._filename, self._linenr)
    +
    +                elif self._check_token(_T_ALLNOCONFIG_Y):
    +                    if node.item.__class__ is not Symbol:
    +                        self._parse_error("the 'allnoconfig_y' option is 
only "
    +                                          "valid for symbols")
    +
    +                    node.item.is_allnoconfig_y = True
    +
    +                else:
    +                    self._parse_error("unrecognized option")
    +
    +            elif t0 is _T_OPTIONAL:
    +                if node.item.__class__ is not Choice:
    +                    self._parse_error('"optional" is only valid for 
choices')
    +
    +                node.item.is_optional = True
    +
    +            else:
    +                # Reuse the tokens for the non-property line later
    +                self._reuse_tokens = True
    +                return
    +
    +    def _set_type(self, node, new_type):
    +        # UNKNOWN is falsy
    +        if node.item.orig_type and node.item.orig_type is not 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(_name_and_loc(node.item) +
    +                       " defined with multiple prompts in single location")
    +
    +        prompt = self._tokens[1]
    +        self._tokens_i = 2
    +
    +        if prompt.__class__ is not str:
    +            self._parse_error("expected prompt string")
    +
    +        if prompt != prompt.strip():
    +            self._warn(_name_and_loc(node.item) +
    +                       " has leading or trailing whitespace in its prompt")
    +
    +            # 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):
    +        if node.help is not None:
    +            self._warn(_name_and_loc(node.item) + " defined with more than 
"
    +                       "one help text -- only the last one will be used")
    +
    +        # Micro-optimization. This code is pretty hot.
    +        readline = self._readline
    +
    +        # Find first non-blank (not all-space) line and get its
    +        # indentation
    +
    +        while 1:
    +            line = readline()
    +            self._linenr += 1
    +            if not line:
    +                self._empty_help(node, line)
    +                return
    +            if not line.isspace():
    +                break
    +
    +        len_ = len  # Micro-optimization
    +
    +        # Use a separate 'expline' variable here and below to avoid 
stomping on
    +        # any tabs people might've put deliberately into the first line 
after
    +        # the help text
    +        expline = line.expandtabs()
    +        indent = len_(expline) - len_(expline.lstrip())
    +        if not indent:
    +            self._empty_help(node, line)
    +            return
    +
    +        # The help text goes on till the first non-blank line with less 
indent
    +        # than the first line
    +
    +        # Add the first line
    +        lines = [expline[indent:]]
    +        add_line = lines.append  # Micro-optimization
    +
    +        while 1:
    +            line = readline()
    +            if line.isspace():
    +                # No need to preserve the exact whitespace in these
    +                add_line("\n")
    +            elif not line:
    +                # End of file
    +                break
    +            else:
    +                expline = line.expandtabs()
    +                if len_(expline) - len_(expline.lstrip()) < indent:
    +                    break
    +                add_line(expline[indent:])
    +
    +        self._linenr += len_(lines)
    +        node.help = "".join(lines).rstrip()
    +        if line:
    +            self._line_after_help(line)
    +
    +    def _empty_help(self, node, line):
    +        self._warn(_name_and_loc(node.item) +
    +                   " has 'help' but empty help text")
    +        node.help = ""
    +        if line:
    +            self._line_after_help(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
    +        # format.
    +        #
    +        # transform_m:
    +        #   True if m should be rewritten to m && MODULES. See the
    +        #   Kconfig.eval_string() documentation.
    +
    +        # Grammar:
    +        #
    +        #   expr:     and_expr ['||' expr]
    +        #   and_expr: factor ['&&' and_expr]
    +        #   factor:   <symbol> ['='/'!='/'<'/... <symbol>]
    +        #             '!' factor
    +        #             '(' expr ')'
    +        #
    +        # It helps to think of the 'expr: and_expr' case as a 
single-operand OR
    +        # (no ||), and of the 'and_expr: factor' case as a single-operand 
AND
    +        # (no &&). Parsing code is always a bit tricky.
    +
    +        # Mind dump: parse_factor() and two nested loops for OR and AND 
would
    +        # work as well. The straightforward implementation there gives a
    +        # (op, (op, (op, A, B), C), D) parse for A op B op C op D. 
Representing
    +        # expressions as (op, [list of operands]) instead goes nicely with 
that
    +        # version, but is wasteful for short expressions and complicates
    +        # expression evaluation and other code that works on expressions 
(more
    +        # complicated code likely offsets any performance gain from less
    +        # recursion too). If we also try to optimize the list 
representation by
    +        # merging lists when possible (e.g. when ANDing two AND 
expressions),
    +        # we end up allocating a ton of lists instead of reusing 
expressions,
    +        # which is bad.
    +
    +        and_expr = self._parse_and_expr(transform_m)
    +
    +        # Return 'and_expr' directly if we have a "single-operand" OR.
    +        # Otherwise, parse the expression on the right and make an OR node.
    +        # This turns A || B || C || D into (OR, A, (OR, B, (OR, C, D))).
    +        return and_expr if not self._check_token(_T_OR) else \
    +            (OR, and_expr, self._parse_expr(transform_m))
    +
    +    def _parse_and_expr(self, transform_m):
    +        factor = self._parse_factor(transform_m)
    +
    +        # Return 'factor' directly if we have a "single-operand" AND.
    +        # Otherwise, parse the right operand and make an AND node. This 
turns
    +        # A && B && C && D into (AND, A, (AND, B, (AND, C, D))).
    +        return factor if not self._check_token(_T_AND) else \
    +            (AND, factor, self._parse_and_expr(transform_m))
    +
    +    def _parse_factor(self, transform_m):
    +        token = self._tokens[self._tokens_i]
    +        self._tokens_i += 1
    +
    +        if token.__class__ is Symbol:
    +            # Plain symbol or relation
    +
    +            if self._tokens[self._tokens_i] not in _RELATIONS:
    +                # Plain symbol
    +
    +                # For conditional expressions ('depends on <expr>',
    +                # '... if <expr>', etc.), m is rewritten to m && MODULES.
    +                if transform_m and token is self.m:
    +                    return (AND, self.m, self.modules)
    +
    +                return token
    +
    +            # Relation
    +            #
    +            # _T_EQUAL, _T_UNEQUAL, etc., deliberately have the same 
values as
    +            # EQUAL, UNEQUAL, etc., so we can just use the token directly
    +            self._tokens_i += 1
    +            return (self._tokens[self._tokens_i - 1], token,
    +                    self._expect_sym())
    +
    +        if token is _T_NOT:
    +            # token == _T_NOT == NOT
    +            return (token, self._parse_factor(transform_m))
    +
    +        if token is _T_OPEN_PAREN:
    +            expr_parse = self._parse_expr(transform_m)
    +            if self._check_token(_T_CLOSE_PAREN):
    +                return expr_parse
    +
    +        self._parse_error("malformed expression")
    +
    +    #
    +    # Caching and invalidation
    +    #
    +
    +    def _build_dep(self):
    +        # Populates the Symbol/Choice._dependents sets, which contain all 
other
    +        # items (symbols and choices) that immediately depend on the item 
in
    +        # the sense that changing the value of the item might affect the 
value
    +        # of the dependent items. This is used for caching/invalidation.
    +        #
    +        # The calculated sets might be larger than necessary as we don't 
do any
    +        # complex analysis of the expressions.
    +
    +        make_depend_on = _make_depend_on  # Micro-optimization
    +
    +        # Only calculate _dependents for defined symbols. Constant and
    +        # undefined symbols could theoretically be selected/implied, but it
    +        # wouldn't change their value, so it's not a true dependency.
    +        for sym in self.unique_defined_syms:
    +            # Symbols depend on the following:
    +
    +            # The prompt conditions
    +            for node in sym.nodes:
    +                if node.prompt:
    +                    make_depend_on(sym, node.prompt[1])
    +
    +            # The default values and their conditions
    +            for value, cond in sym.defaults:
    +                make_depend_on(sym, value)
    +                make_depend_on(sym, cond)
    +
    +            # The reverse and weak reverse dependencies
    +            make_depend_on(sym, sym.rev_dep)
    +            make_depend_on(sym, sym.weak_rev_dep)
    +
    +            # The ranges along with their conditions
    +            for low, high, cond in sym.ranges:
    +                make_depend_on(sym, low)
    +                make_depend_on(sym, high)
    +                make_depend_on(sym, cond)
    +
    +            # The direct dependencies. This is usually redundant, as the 
direct
    +            # dependencies get propagated to properties, but it's needed 
to get
    +            # invalidation solid for 'imply', which only checks the direct
    +            # dependencies (even if there are no properties to propagate it
    +            # to).
    +            make_depend_on(sym, sym.direct_dep)
    +
    +            # In addition to the above, choice symbols depend on the choice
    +            # they're in, but that's handled automatically since the 
Choice is
    +            # propagated to the conditions of the properties before
    +            # _build_dep() runs.
    +
    +        for choice in self.unique_choices:
    +            # Choices depend on the following:
    +
    +            # The prompt conditions
    +            for node in choice.nodes:
    +                if node.prompt:
    +                    make_depend_on(choice, node.prompt[1])
    +
    +            # The default symbol conditions
    +            for _, cond in choice.defaults:
    +                make_depend_on(choice, cond)
    +
    +    def _add_choice_deps(self):
    +        # Choices also depend on the choice symbols themselves, because the
    +        # y-mode selection of the choice might change if a choice symbol's
    +        # visibility changes.
    +        #
    +        # We add these dependencies separately after dependency loop 
detection.
    +        # The invalidation algorithm can handle the resulting
    +        # <choice symbol> <-> <choice> dependency loops, but they make loop
    +        # detection awkward.
    +
    +        for choice in self.unique_choices:
    +            for sym in choice.syms:
    +                sym._dependents.add(choice)
    +
    +    def _invalidate_all(self):
    +        # Undefined symbols never change value and don't need to be
    +        # invalidated, so we can just iterate over defined symbols.
    +        # Invalidating constant symbols would break things horribly.
    +        for sym in self.unique_defined_syms:
    +            sym._invalidate()
    +
    +        for choice in self.unique_choices:
    +            choice._invalidate()
    +
    +    #
    +    # Post-parsing menu tree processing, including dependency propagation 
and
    +    # implicit submenu creation
    +    #
    +
    +    def _finalize_node(self, node, visible_if):
    +        # Finalizes a menu node and its children:
    +        #
    +        #  - Copies properties from menu nodes up to their contained
    +        #    symbols/choices
    +        #
    +        #  - Propagates dependencies from parent to child nodes
    +        #
    +        #  - Creates implicit menus (see kconfig-language.txt)
    +        #
    +        #  - Removes 'if' nodes
    +        #
    +        #  - Sets 'choice' types and registers choice symbols
    +        #
    +        # menu_finalize() in the C implementation is similar.
    +        #
    +        # node:
    +        #   The menu node to finalize. This node and its children will have
    +        #   been finalized when the function returns, and any implicit 
menus
    +        #   will have been created.
    +        #
    +        # visible_if:
    +        #   Dependencies from 'visible if' on parent menus. These are 
added to
    +        #   the prompts of symbols and choices.
    +
    +        if node.item.__class__ is Symbol:
    +            # Copy defaults, ranges, selects, and implies to the Symbol
    +            self._add_props_to_sym(node)
    +
    +            # Find any items that should go in an implicit menu rooted at 
the
    +            # symbol
    +            cur = node
    +            while cur.next and _auto_menu_dep(node, cur.next):
    +                # This makes implicit submenu creation work recursively, 
with
    +                # implicit menus inside implicit menus
    +                self._finalize_node(cur.next, visible_if)
    +                cur = cur.next
    +                cur.parent = node
    +
    +            if cur is not node:
    +                # Found symbols that should go in an implicit submenu. Tilt
    +                # them up above us.
    +                node.list = node.next
    +                node.next = cur.next
    +                cur.next = None
    +
    +        elif node.list:
    +            # The menu node is a choice, menu, or if. Finalize each child 
node.
    +
    +            if node.item is MENU:
    +                visible_if = self._make_and(visible_if, node.visibility)
    +
    +            # Propagate the menu node's dependencies to each child menu 
node.
    +            #
    +            # This needs to go before the recursive _finalize_node() call 
so
    +            # that implicit submenu creation can look ahead at 
dependencies.
    +            self._propagate_deps(node, visible_if)
    +
    +            # Finalize the children
    +            cur = node.list
    +            while cur:
    +                self._finalize_node(cur, visible_if)
    +                cur = cur.next
    +
    +        if node.list:
    +            # node's children have been individually finalized. Do final 
steps
    +            # to finalize this "level" in the menu tree.
    +            _flatten(node.list)
    +            _remove_ifs(node)
    +
    +        # Empty choices (node.list None) are possible, so this needs to go
    +        # outside
    +        if node.item.__class__ is Choice:
    +            # Add the node's non-node-specific properties to the choice, 
like
    +            # _add_props_to_sym() does
    +            choice = node.item
    +            choice.direct_dep = self._make_or(choice.direct_dep, node.dep)
    +            choice.defaults += node.defaults
    +
    +            _finalize_choice(node)
    +
    +    def _propagate_deps(self, node, visible_if):
    +        # Propagates 'node's dependencies to its child menu nodes
    +
    +        # If the parent node holds a Choice, we use the Choice itself as 
the
    +        # parent dependency. This makes sense as the value (mode) of the 
choice
    +        # limits the visibility of the contained choice symbols. The C
    +        # implementation works the same way.
    +        #
    +        # Due to the similar interface, Choice works as a drop-in 
replacement
    +        # for Symbol here.
    +        basedep = node.item if node.item.__class__ is Choice else node.dep
    +
    +        cur = node.list
    +        while cur:
    +            dep = cur.dep = self._make_and(cur.dep, basedep)
    +
    +            if cur.item.__class__ in _SYMBOL_CHOICE:
    +                # Propagate 'visible if' and dependencies to the prompt
    +                if cur.prompt:
    +                    cur.prompt = (cur.prompt[0],
    +                                  self._make_and(
    +                                      cur.prompt[1],
    +                                      self._make_and(visible_if, dep)))
    +
    +                # Propagate dependencies to defaults
    +                if cur.defaults:
    +                    cur.defaults = [(default, self._make_and(cond, dep))
    +                                    for default, cond in cur.defaults]
    +
    +                # Propagate dependencies to ranges
    +                if cur.ranges:
    +                    cur.ranges = [(low, high, self._make_and(cond, dep))
    +                                  for low, high, cond in cur.ranges]
    +
    +                # Propagate dependencies to selects
    +                if cur.selects:
    +                    cur.selects = [(target, self._make_and(cond, dep))
    +                                   for target, cond in cur.selects]
    +
    +                # Propagate dependencies to implies
    +                if cur.implies:
    +                    cur.implies = [(target, self._make_and(cond, dep))
    +                                   for target, cond in cur.implies]
    +
    +            elif cur.prompt:  # Not a symbol/choice
    +                # Propagate dependencies to the prompt. 'visible if' is 
only
    +                # propagated to symbols/choices.
    +                cur.prompt = (cur.prompt[0],
    +                              self._make_and(cur.prompt[1], dep))
    +
    +            cur = cur.next
    +
    +    def _add_props_to_sym(self, node):
    +        # Copies properties from the menu node 'node' up to its contained
    +        # symbol, and adds (weak) reverse dependencies to selected/implied
    +        # symbols.
    +        #
    +        # This can't be rolled into _propagate_deps(), because that 
function
    +        # traverses the menu tree roughly breadth-first, meaning 
properties on
    +        # symbols defined in multiple locations could end up in the wrong
    +        # order.
    +
    +        sym = node.item
    +
    +        # See the Symbol class docstring
    +        sym.direct_dep = self._make_or(sym.direct_dep, node.dep)
    +
    +        sym.defaults += node.defaults
    +        sym.ranges += node.ranges
    +        sym.selects += node.selects
    +        sym.implies += node.implies
    +
    +        # Modify the reverse dependencies of the selected symbol
    +        for target, cond in node.selects:
    +            target.rev_dep = self._make_or(
    +                target.rev_dep,
    +                self._make_and(sym, cond))
    +
    +        # Modify the weak reverse dependencies of the implied
    +        # symbol
    +        for target, cond in node.implies:
    +            target.weak_rev_dep = self._make_or(
    +                target.weak_rev_dep,
    +                self._make_and(sym, cond))
    +
    +    #
    +    # Misc.
    +    #
    +
    +    def _check_sym_sanity(self):
    +        # Checks various symbol properties that are handiest to check after
    +        # parsing. Only generates errors and warnings.
    +
    +        def num_ok(sym, type_):
    +            # Returns True if the (possibly constant) symbol 'sym' is 
valid as a value
    +            # for a symbol of type type_ (INT or HEX)
    +
    +            # 'not sym.nodes' implies a constant or undefined symbol, e.g. 
a plain
    +            # "123"
    +            if not sym.nodes:
    +                return _is_base_n(sym.name, _TYPE_TO_BASE[type_])
    +
    +            return sym.orig_type is type_
    +
    +        for sym in self.unique_defined_syms:
    +            if sym.orig_type in _BOOL_TRISTATE:
    +                # A helper function could be factored out here, but keep it
    +                # speedy/straightforward
    +
    +                for target_sym, _ in sym.selects:
    +                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
    +                        self._warn("{} selects the {} symbol {}, which is 
not "
    +                                   "bool or tristate"
    +                                   .format(_name_and_loc(sym),
    +                                           
TYPE_TO_STR[target_sym.orig_type],
    +                                           _name_and_loc(target_sym)))
    +
    +                for target_sym, _ in sym.implies:
    +                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
    +                        self._warn("{} implies the {} symbol {}, which is 
not "
    +                                   "bool or tristate"
    +                                   .format(_name_and_loc(sym),
    +                                           
TYPE_TO_STR[target_sym.orig_type],
    +                                           _name_and_loc(target_sym)))
    +
    +            elif sym.orig_type:  # STRING/INT/HEX
    +                for default, _ in sym.defaults:
    +                    if default.__class__ is not Symbol:
    +                        raise KconfigError(
    +                            "the {} symbol {} has a malformed default {} 
-- expected "
    +                            "a single symbol"
    +                            .format(TYPE_TO_STR[sym.orig_type], 
_name_and_loc(sym),
    +                                    expr_str(default)))
    +
    +                    if sym.orig_type is STRING:
    +                        if not default.is_constant and not default.nodes 
and \
    +                           not default.name.isupper():
    +                            # 'default foo' on a string symbol could be 
either a symbol
    +                            # reference or someone leaving out the quotes. 
Guess that
    +                            # the quotes were left out if 'foo' isn't 
all-uppercase
    +                            # (and no symbol named 'foo' exists).
    +                            self._warn("style: quotes recommended around "
    +                                       "default value for string symbol "
    +                                       + _name_and_loc(sym))
    +
    +                    elif not num_ok(default, sym.orig_type):  # INT/HEX
    +                        self._warn("the {0} symbol {1} has a non-{0} 
default {2}"
    +                                   .format(TYPE_TO_STR[sym.orig_type],
    +                                           _name_and_loc(sym),
    +                                           _name_and_loc(default)))
    +
    +                if sym.selects or sym.implies:
    +                    self._warn("the {} symbol {} has selects or implies"
    +                               .format(TYPE_TO_STR[sym.orig_type],
    +                                       _name_and_loc(sym)))
    +
    +            else:  # UNKNOWN
    +                self._warn("{} defined without a type"
    +                           .format(_name_and_loc(sym)))
    +
    +
    +            if sym.ranges:
    +                if sym.orig_type not in _INT_HEX:
    +                    self._warn(
    +                        "the {} symbol {} has ranges, but is not int or 
hex"
    +                        .format(TYPE_TO_STR[sym.orig_type],
    +                                _name_and_loc(sym)))
    +                else:
    +                    for low, high, _ in sym.ranges:
    +                        if not num_ok(low, sym.orig_type) or \
    +                           not num_ok(high, sym.orig_type):
    +
    +                            self._warn("the {0} symbol {1} has a non-{0} "
    +                                       "range [{2}, {3}]"
    +                                       .format(TYPE_TO_STR[sym.orig_type],
    +                                               _name_and_loc(sym),
    +                                               _name_and_loc(low),
    +                                               _name_and_loc(high)))
    +
    +    def _check_choice_sanity(self):
    +        # Checks various choice properties that are handiest to check after
    +        # parsing. Only generates errors and warnings.
    +
    +        def warn_select_imply(sym, expr, expr_type):
    +            msg = "the choice symbol {} is {} by the following symbols, 
but " \
    +                  "select/imply has no effect on choice symbols" \
    +                  .format(_name_and_loc(sym), expr_type)
    +
    +            # si = select/imply
    +            for si in split_expr(expr, OR):
    +                msg += "\n - " + _name_and_loc(split_expr(si, AND)[0])
    +
    +            self._warn(msg)
    +
    +        for choice in self.unique_choices:
    +            if choice.orig_type not in _BOOL_TRISTATE:
    +                self._warn("{} defined with type {}"
    +                           .format(_name_and_loc(choice),
    +                                   TYPE_TO_STR[choice.orig_type]))
    +
    +            for node in choice.nodes:
    +                if node.prompt:
    +                    break
    +            else:
    +                self._warn(_name_and_loc(choice) + " defined without a 
prompt")
    +
    +            for default, _ in choice.defaults:
    +                if default.__class__ is not Symbol:
    +                    raise KconfigError(
    +                        "{} has a malformed default {}"
    +                        .format(_name_and_loc(choice), expr_str(default)))
    +
    +                if default.choice is not choice:
    +                    self._warn("the default selection {} of {} is not "
    +                               "contained in the choice"
    +                               .format(_name_and_loc(default),
    +                                       _name_and_loc(choice)))
    +
    +            for sym in choice.syms:
    +                if sym.defaults:
    +                    self._warn("default on the choice symbol {} will have "
    +                               "no effect, as defaults do not affect 
choice "
    +                               "symbols".format(_name_and_loc(sym)))
    +
    +                if sym.rev_dep is not sym.kconfig.n:
    +                    warn_select_imply(sym, sym.rev_dep, "selected")
    +
    +                if sym.weak_rev_dep is not sym.kconfig.n:
    +                    warn_select_imply(sym, sym.weak_rev_dep, "implied")
    +
    +                for node in sym.nodes:
    +                    if node.parent.item is choice:
    +                        if not node.prompt:
    +                            self._warn("the choice symbol {} has no prompt"
    +                                       .format(_name_and_loc(sym)))
    +
    +                    elif node.prompt:
    +                        self._warn("the choice symbol {} is defined with a 
"
    +                                   "prompt outside the choice"
    +                                   .format(_name_and_loc(sym)))
    +
    +    def _parse_error(self, msg):
    +        raise KconfigError("{}couldn't parse '{}': {}".format(
    +            "" if self._filename is None else
    +                "{}:{}: ".format(self._filename, self._linenr),
    +            self._line.strip(), msg))
    +
    +    def _trailing_tokens_error(self):
    +        self._parse_error("extra tokens at end of line")
    +
    +    def _open(self, filename, mode):
    +        # open() wrapper:
    +        #
    +        # - Enable universal newlines mode on Python 2 to ease
    +        #   interoperability between Linux and Windows. It's already the
    +        #   default on Python 3.
    +        #
    +        #   The "U" flag would currently work for both Python 2 and 3, but 
it's
    +        #   deprecated on Python 3, so play it future-safe.
    +        #
    +        #   io.open() defaults to universal newlines on Python 2 (and is an
    +        #   alias for open() on Python 3), but it returns 'unicode' 
strings and
    +        #   slows things down:
    +        #
    +        #     Parsing x86 Kconfigs on Python 2
    +        #
    +        #     with open(..., "rU"):
    +        #
    +        #       real  0m0.930s
    +        #       user  0m0.905s
    +        #       sys   0m0.025s
    +        #
    +        #     with io.open():
    +        #
    +        #       real  0m1.069s
    +        #       user  0m1.040s
    +        #       sys   0m0.029s
    +        #
    +        #   There's no appreciable performance difference between "r" and
    +        #   "rU" for parsing performance on Python 2.
    +        #
    +        # - For Python 3, force the encoding. Forcing the encoding on 
Python 2
    +        #   turns strings into Unicode strings, which gets messy. Python 2
    +        #   doesn't decode regular strings anyway.
    +        return open(filename, "rU" if mode == "r" else mode) if _IS_PY2 
else \
    +               open(filename, mode, encoding=self._encoding)
    +
    +    def _check_undef_syms(self):
    +        # Prints warnings for all references to undefined symbols within 
the
    +        # Kconfig files
    +
    +        def is_num(s):
    +            # Returns True if the string 's' looks like a number.
    +            #
    +            # Internally, all operands in Kconfig are symbols, only 
undefined symbols
    +            # (which numbers usually are) get their name as their value.
    +            #
    +            # Only hex numbers that start with 0x/0X are classified as 
numbers.
    +            # Otherwise, symbols whose names happen to contain only the 
letters A-F
    +            # would trigger false positives.
    +
    +            try:
    +                int(s)
    +            except ValueError:
    +                if not s.startswith(("0x", "0X")):
    +                    return False
    +
    +                try:
    +                    int(s, 16)
    +                except ValueError:
    +                    return False
    +
    +            return True
    +
    +        for sym in (self.syms.viewvalues if _IS_PY2 else 
self.syms.values)():
    +            # - sym.nodes empty means the symbol is undefined (has no
    +            #   definition locations)
    +            #
    +            # - Due to Kconfig internals, numbers show up as undefined 
Kconfig
    +            #   symbols, but shouldn't be flagged
    +            #
    +            # - The MODULES symbol always exists
    +            if not sym.nodes and not is_num(sym.name) and \
    +               sym.name != "MODULES":
    +
    +                msg = "undefined symbol {}:".format(sym.name)
    +                for node in self.node_iter():
    +                    if sym in node.referenced:
    +                        msg += "\n\n- Referenced at {}:{}:\n\n{}" \
    +                               .format(node.filename, node.linenr, node)
    +                self._warn(msg)
    +
    +    def _warn(self, msg, filename=None, linenr=None):
    +        # For printing general warnings
    +
    +        if not self.warn:
    +            return
    +
    +        msg = "warning: " + msg
    +        if filename is not None:
    +            msg = "{}:{}: {}".format(filename, linenr, msg)
    +
    +        self.warnings.append(msg)
    +        if self.warn_to_stderr:
    +            sys.stderr.write(msg + "\n")
    +
    +
    +class Symbol(object):
    +    """
    +    Represents a configuration symbol:
    +
    +      (menu)config FOO
    +          ...
    +
    +    The following attributes are available. They should be viewed as 
read-only,
    +    and some are implemented through @property magic (but are still 
efficient
    +    to access due to internal caching).
    +
    +    Note: Prompts, help texts, and locations are stored in the Symbol's
    +    MenuNode(s) rather than in the Symbol itself. Check the MenuNode class 
and
    +    the Symbol.nodes attribute. This organization matches the C tools.
    +
    +    name:
    +      The name of the symbol, e.g. "FOO" for 'config FOO'.
    +
    +    type:
    +      The type of the symbol. One of BOOL, TRISTATE, STRING, INT, HEX, 
UNKNOWN.
    +      UNKNOWN is for undefined symbols, (non-special) constant symbols, and
    +      symbols defined without a type.
    +
    +      When running without modules (MODULES having the value n), TRISTATE
    +      symbols magically change type to BOOL. This also happens for symbols
    +      within choices in "y" mode. This matches the C tools, and makes 
sense for
    +      menuconfig-like functionality.
    +
    +    orig_type:
    +      The type as given in the Kconfig file, without any magic applied. 
Used
    +      when printing the symbol.
    +
    +    str_value:
    +      The value of the symbol as a string. Gives the value for 
string/int/hex
    +      symbols. For bool/tristate symbols, gives "n", "m", or "y".
    +
    +      This is the symbol value that's used in relational expressions
    +      (A = B, A != B, etc.)
    +
    +      Gotcha: For int/hex symbols, the exact format of the value must 
often be
    +      preserved (e.g., when writing a .config file), hence why you can't 
get it
    +      directly as an int. Do int(int_sym.str_value) or
    +      int(hex_sym.str_value, 16) to get the integer value.
    +
    +    tri_value:
    +      The tristate value of the symbol as an integer. One of 0, 1, 2,
    +      representing n, m, y. Always 0 (n) for non-bool/tristate symbols.
    +
    +      This is the symbol value that's used outside of relation expressions
    +      (A, !A, A && B, A || B).
    +
    +    assignable:
    +      A tuple containing the tristate user values that can currently be
    +      assigned to the symbol (that would be respected), ordered from 
lowest (0,
    +      representing n) to highest (2, representing y). This corresponds to 
the
    +      selections available in the menuconfig interface. The set of 
assignable
    +      values is calculated from the symbol's visibility and 
selects/implies.
    +
    +      Returns the empty set for non-bool/tristate symbols and for symbols 
with
    +      visibility n. The other possible values are (0, 2), (0, 1, 2), (1, 
2),
    +      (1,), and (2,). A (1,) or (2,) result means the symbol is visible but
    +      "locked" to m or y through a select, perhaps in combination with the
    +      visibility. menuconfig represents this as -M- and -*-, respectively.
    +
    +      For string/hex/int symbols, check if Symbol.visibility is non-0 
(non-n)
    +      instead to determine if the value can be changed.
    +
    +      Some handy 'assignable' idioms:
    +
    +        # Is 'sym' an assignable (visible) bool/tristate symbol?
    +        if sym.assignable:
    +            # What's the highest value it can be assigned? [-1] in Python
    +            # gives the last element.
    +            sym_high = sym.assignable[-1]
    +
    +            # The lowest?
    +            sym_low = sym.assignable[0]
    +
    +            # Can the symbol be set to at least m?
    +            if sym.assignable[-1] >= 1:
    +                ...
    +
    +        # Can the symbol be set to m?
    +        if 1 in sym.assignable:
    +            ...
    +
    +    visibility:
    +      The visibility of the symbol. One of 0, 1, 2, representing n, m, y. 
See
    +      the module documentation for an overview of symbol values and 
visibility.
    +
    +    user_value:
    +      The user value of the symbol. None if no user value has been assigned
    +      (via Kconfig.load_config() or Symbol.set_value()).
    +
    +      Holds 0, 1, or 2 for bool/tristate symbols, and a string for the 
other
    +      symbol types.
    +
    +      WARNING: Do not assign directly to this. It will break things. Use
    +      Symbol.set_value().
    +
    +    config_string:
    +      The .config assignment string that would get written out for the 
symbol
    +      by Kconfig.write_config(). Returns the empty string if no .config
    +      assignment would get written out.
    +
    +      In general, visible symbols, symbols with (active) defaults, and 
selected
    +      symbols get written out. This includes all non-n-valued bool/tristate
    +      symbols, and all visible string/int/hex symbols.
    +
    +      Symbols with the (no longer needed) 'option env=...' option generate 
no
    +      configuration output, and neither does the special
    +      'option defconfig_list' symbol.
    +
    +      Tip: This field is useful when generating custom configuration 
output,
    +      even for non-.config-like formats. To write just the symbols that 
would
    +      get written out to .config files, do this:
    +
    +        if sym.config_string:
    +            *Write symbol, e.g. by looking sym.str_value*
    +
    +      This is a superset of the symbols written out by write_autoconf().
    +      That function skips all n-valued symbols.
    +
    +      There usually won't be any great harm in just writing all symbols 
either,
    +      though you might get some special symbols and possibly some 
"redundant"
    +      n-valued symbol entries in there.
    +
    +    nodes:
    +      A list of MenuNodes for this symbol. Will contain a single MenuNode 
for
    +      most symbols. Undefined and constant symbols have an empty nodes 
list.
    +      Symbols defined in multiple locations get one node for each location.
    +
    +    choice:
    +      Holds the parent Choice for choice symbols, and None for non-choice
    +      symbols. Doubles as a flag for whether a symbol is a choice symbol.
    +
    +    defaults:
    +      List of (default, cond) tuples for the symbol's 'default' 
properties. For
    +      example, 'default A && B if C || D' is represented as
    +      ((AND, A, B), (OR, C, D)). If no condition was given, 'cond' is
    +      self.kconfig.y.
    +
    +      Note that 'depends on' and parent dependencies are propagated to
    +      'default' conditions.
    +
    +    selects:
    +      List of (symbol, cond) tuples for the symbol's 'select' properties. 
For
    +      example, 'select A if B && C' is represented as (A, (AND, B, C)). If 
no
    +      condition was given, 'cond' is self.kconfig.y.
    +
    +      Note that 'depends on' and parent dependencies are propagated to 
'select'
    +      conditions.
    +
    +    implies:
    +      Like 'selects', for imply.
    +
    +    ranges:
    +      List of (low, high, cond) tuples for the symbol's 'range' 
properties. For
    +      example, 'range 1 2 if A' is represented as (1, 2, A). If there is no
    +      condition, 'cond' is self.kconfig.y.
    +
    +      Note that 'depends on' and parent dependencies are propagated to 
'range'
    +      conditions.
    +
    +      Gotcha: 1 and 2 above will be represented as (undefined) Symbols 
rather
    +      than plain integers. Undefined symbols get their name as their string
    +      value, so this works out. The C tools work the same way.
    +
    +    orig_defaults:
    +    orig_selects:
    +    orig_implies:
    +    orig_ranges:
    +      See the corresponding attributes on the MenuNode class.
    +
    +    rev_dep:
    +      Reverse dependency expression from other symbols selecting this 
symbol.
    +      Multiple selections get ORed together. A condition on a select is 
ANDed
    +      with the selecting symbol.
    +
    +      For example, if A has 'select FOO' and B has 'select FOO if C', then
    +      FOO's rev_dep will be (OR, A, (AND, B, C)).
    +
    +    weak_rev_dep:
    +      Like rev_dep, for imply.
    +
    +    direct_dep:
    +      The direct ('depends on') dependencies for the symbol, or 
self.kconfig.y
    +      if there are no direct dependencies.
    +
    +      This attribute includes any dependencies from surrounding menus and 
ifs.
    +      Those get propagated to the direct dependencies, and the resulting 
direct
    +      dependencies in turn get propagated to the conditions of all 
properties.
    +
    +      If the symbol is defined in multiple locations, the dependencies 
from the
    +      different locations get ORed together.
    +
    +    referenced:
    +      A set() with all symbols and choices referenced in the properties and
    +      property conditions of the symbol.
    +
    +      Also includes dependencies from surrounding menus and ifs, because 
those
    +      get propagated to the symbol (see the 'Intro to symbol values' 
section in
    +      the module docstring).
    +
    +      Choices appear in the dependencies of choice symbols.
    +
    +      For the following definitions, only B and not C appears in A's
    +      'referenced'. To get transitive references, you'll have to 
recursively
    +      expand 'references' until no new items appear.
    +
    +        config A
    +                bool
    +                depends on B
    +
    +        config B
    +                bool
    +                depends on C
    +
    +        config C
    +                bool
    +
    +      See the Symbol.direct_dep attribute if you're only interested in the
    +      direct dependencies of the symbol (its 'depends on'). You can 
extract the
    +      symbols in it with the global expr_items() function.
    +
    +    env_var:
    +      If the Symbol has an 'option env="FOO"' option, this contains the 
name
    +      ("FOO") of the environment variable. None for symbols without no
    +      'option env'.
    +
    +      'option env="FOO"' acts like a 'default' property whose value is the
    +      value of $FOO.
    +
    +      Symbols with 'option env' are never written out to .config files, 
even if
    +      they are visible. env_var corresponds to a flag called SYMBOL_AUTO 
in the
    +      C implementation.
    +
    +    is_allnoconfig_y:
    +      True if the symbol has 'option allnoconfig_y' set on it. This has no
    +      effect internally (except when printing symbols), but can be checked 
by
    +      scripts.
    +
    +    is_constant:
    +      True if the symbol is a constant (quoted) symbol.
    +
    +    kconfig:
    +      The Kconfig instance this symbol is from.
    +    """
    +    __slots__ = (
    +        "_cached_assignable",
    +        "_cached_str_val",
    +        "_cached_tri_val",
    +        "_cached_vis",
    +        "_dependents",
    +        "_old_val",
    +        "_visited",
    +        "_was_set",
    +        "_write_to_conf",
    +        "choice",
    +        "defaults",
    +        "direct_dep",
    +        "env_var",
    +        "implies",
    +        "is_allnoconfig_y",
    +        "is_constant",
    +        "kconfig",
    +        "name",
    +        "nodes",
    +        "orig_type",
    +        "ranges",
    +        "rev_dep",
    +        "selects",
    +        "user_value",
    +        "weak_rev_dep",
    +    )
    +
    +    #
    +    # Public interface
    +    #
    +
    +    @property
    +    def type(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self.orig_type is TRISTATE and \
    +           (self.choice and self.choice.tri_value == 2 or
    +            not self.kconfig.modules.tri_value):
    +
    +            return BOOL
    +
    +        return self.orig_type
    +
    +    @property
    +    def str_value(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self._cached_str_val is not None:
    +            return self._cached_str_val
    +
    +        if self.orig_type in _BOOL_TRISTATE:
    +            # Also calculates the visibility, so invalidation safe
    +            self._cached_str_val = TRI_TO_STR[self.tri_value]
    +            return self._cached_str_val
    +
    +        # As a quirk of Kconfig, undefined symbols get their name as their
    +        # string value. This is why things like "FOO = bar" work for 
seeing if
    +        # FOO has the value "bar".
    +        if not self.orig_type:  # UNKNOWN
    +            self._cached_str_val = self.name
    +            return self.name
    +
    +        val = ""
    +        # Warning: See Symbol._rec_invalidate(), and note that this is a 
hidden
    +        # function call (property magic)
    +        vis = self.visibility
    +
    +        self._write_to_conf = (vis != 0)
    +
    +        if self.orig_type in _INT_HEX:
    +            # The C implementation checks the user value against the range 
in a
    +            # separate code path (post-processing after loading a .config).
    +            # Checking all values here instead makes more sense for us. It
    +            # requires that we check for a range first.
    +
    +            base = _TYPE_TO_BASE[self.orig_type]
    +
    +            # Check if a range is in effect
    +            for low_expr, high_expr, cond in self.ranges:
    +                if expr_value(cond):
    +                    has_active_range = True
    +
    +                    # The zeros are from the C implementation running 
strtoll()
    +                    # on empty strings
    +                    low = int(low_expr.str_value, base) if \
    +                      _is_base_n(low_expr.str_value, base) else 0
    +                    high = int(high_expr.str_value, base) if \
    +                      _is_base_n(high_expr.str_value, base) else 0
    +
    +                    break
    +            else:
    +                has_active_range = False
    +
    +            # Defaults are used if the symbol is invisible, lacks a user 
value,
    +            # or has an out-of-range user value
    +            use_defaults = True
    +
    +            if vis and self.user_value:
    +                user_val = int(self.user_value, base)
    +                if has_active_range and not low <= user_val <= high:
    +                    num2str = str if base == 10 else hex
    +                    self.kconfig._warn(
    +                        "user value {} on the {} symbol {} ignored due to "
    +                        "being outside the active range ([{}, {}]) -- 
falling "
    +                        "back on defaults"
    +                        .format(num2str(user_val), 
TYPE_TO_STR[self.orig_type],
    +                                _name_and_loc(self),
    +                                num2str(low), num2str(high)))
    +                else:
    +                    # If the user value is well-formed and satisfies range
    +                    # contraints, it is stored in exactly the same form as
    +                    # specified in the assignment (with or without "0x", 
etc.)
    +                    val = self.user_value
    +                    use_defaults = False
    +
    +            if use_defaults:
    +                # No user value or invalid user value. Look at defaults.
    +
    +                # Used to implement the warning below
    +                has_default = False
    +
    +                for sym, cond in self.defaults:
    +                    if expr_value(cond):
    +                        has_default = self._write_to_conf = True
    +
    +                        val = sym.str_value
    +
    +                        if _is_base_n(val, base):
    +                            val_num = int(val, base)
    +                        else:
    +                            val_num = 0  # strtoll() on empty string
    +
    +                        break
    +                else:
    +                    val_num = 0  # strtoll() on empty string
    +
    +                # This clamping procedure runs even if there's no default
    +                if has_active_range:
    +                    clamp = None
    +                    if val_num < low:
    +                        clamp = low
    +                    elif val_num > high:
    +                        clamp = high
    +
    +                    if clamp is not None:
    +                        # The value is rewritten to a standard form if it 
is
    +                        # clamped
    +                        val = str(clamp) \
    +                              if self.orig_type is INT else \
    +                              hex(clamp)
    +
    +                        if has_default:
    +                            num2str = str if base == 10 else hex
    +                            self.kconfig._warn(
    +                                "default value {} on {} clamped to {} due 
to "
    +                                "being outside the active range ([{}, {}])"
    +                                .format(val_num, _name_and_loc(self),
    +                                        num2str(clamp), num2str(low),
    +                                        num2str(high)))
    +
    +        elif self.orig_type is STRING:
    +            if vis and self.user_value is not None:
    +                # If the symbol is visible and has a user value, use that
    +                val = self.user_value
    +            else:
    +                # Otherwise, look at defaults
    +                for sym, cond in self.defaults:
    +                    if expr_value(cond):
    +                        val = sym.str_value
    +                        self._write_to_conf = True
    +                        break
    +
    +        # env_var corresponds to SYMBOL_AUTO in the C implementation, and 
is
    +        # also set on the defconfig_list symbol there. Test for the
    +        # defconfig_list symbol explicitly instead here, to avoid a 
nonsensical
    +        # env_var setting and the defconfig_list symbol being printed
    +        # incorrectly. This code is pretty cold anyway.
    +        if self.env_var is not None or self is self.kconfig.defconfig_list:
    +            self._write_to_conf = False
    +
    +        self._cached_str_val = val
    +        return val
    +
    +    @property
    +    def tri_value(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self._cached_tri_val is not None:
    +            return self._cached_tri_val
    +
    +        if self.orig_type not in _BOOL_TRISTATE:
    +            if self.orig_type:  # != UNKNOWN
    +                # Would take some work to give the location here
    +                self.kconfig._warn(
    +                    "The {} symbol {} is being evaluated in a logical 
context "
    +                    "somewhere. It will always evaluate to n."
    +                    .format(TYPE_TO_STR[self.orig_type], 
_name_and_loc(self)))
    +
    +            self._cached_tri_val = 0
    +            return 0
    +
    +        # Warning: See Symbol._rec_invalidate(), and note that this is a 
hidden
    +        # function call (property magic)
    +        vis = self.visibility
    +        self._write_to_conf = (vis != 0)
    +
    +        val = 0
    +
    +        if not self.choice:
    +            # Non-choice symbol
    +
    +            if vis and self.user_value is not None:
    +                # If the symbol is visible and has a user value, use that
    +                val = min(self.user_value, vis)
    +
    +            else:
    +                # Otherwise, look at defaults and weak reverse dependencies
    +                # (implies)
    +
    +                for default, cond in self.defaults:
    +                    dep_val = expr_value(cond)
    +                    if dep_val:
    +                        val = min(expr_value(default), dep_val)
    +                        if val:
    +                            self._write_to_conf = True
    +                        break
    +
    +                # Weak reverse dependencies are only considered if our
    +                # direct dependencies are met
    +                dep_val = expr_value(self.weak_rev_dep)
    +                if dep_val and expr_value(self.direct_dep):
    +                    val = max(dep_val, val)
    +                    self._write_to_conf = True
    +
    +            # Reverse (select-related) dependencies take precedence
    +            dep_val = expr_value(self.rev_dep)
    +            if dep_val:
    +                if expr_value(self.direct_dep) < dep_val:
    +                    self._warn_select_unsatisfied_deps()
    +
    +                val = max(dep_val, val)
    +                self._write_to_conf = True
    +
    +            # m is promoted to y for (1) bool symbols and (2) symbols with 
a
    +            # weak_rev_dep (from imply) of y
    +            if val == 1 and \
    +               (self.type is BOOL or expr_value(self.weak_rev_dep) == 2):
    +                val = 2
    +
    +        elif vis == 2:
    +            # Visible choice symbol in y-mode choice. The choice mode 
limits
    +            # the visibility of choice symbols, so it's sufficient to just
    +            # check the visibility of the choice symbols themselves.
    +            val = 2 if self.choice.selection is self else 0
    +
    +        elif vis and self.user_value:
    +            # Visible choice symbol in m-mode choice, with set non-0 user 
value
    +            val = 1
    +
    +        self._cached_tri_val = val
    +        return val
    +
    +    @property
    +    def assignable(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self._cached_assignable is None:
    +            self._cached_assignable = self._assignable()
    +        return self._cached_assignable
    +
    +    @property
    +    def visibility(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self._cached_vis is None:
    +            self._cached_vis = _visibility(self)
    +        return self._cached_vis
    +
    +    @property
    +    def config_string(self):
    +        """
    +        See the class documentation.
    +        """
    +        # _write_to_conf is determined when the value is calculated. This 
is a
    +        # hidden function call due to property magic.
    +        val = self.str_value
    +        if not self._write_to_conf:
    +            return ""
    +
    +        if self.orig_type in _BOOL_TRISTATE:
    +            return "{}{}={}\n" \
    +                   .format(self.kconfig.config_prefix, self.name, val) \
    +                   if val != "n" else \
    +                   "# {}{} is not set\n" \
    +                   .format(self.kconfig.config_prefix, self.name)
    +
    +        if self.orig_type in _INT_HEX:
    +            return "{}{}={}\n" \
    +                   .format(self.kconfig.config_prefix, self.name, val)
    +
    +        # sym.orig_type is STRING
    +        return '{}{}="{}"\n' \
    +               .format(self.kconfig.config_prefix, self.name, escape(val))
    +
    +    def set_value(self, value):
    +        """
    +        Sets the user value of the symbol.
    +
    +        Equal in effect to assigning the value to the symbol within a 
.config
    +        file. For bool and tristate symbols, use the 'assignable' 
attribute to
    +        check which values can currently be assigned. Setting values 
outside
    +        'assignable' will cause Symbol.user_value to differ from
    +        Symbol.str/tri_value (be truncated down or up).
    +
    +        Setting a choice symbol to 2 (y) sets Choice.user_selection to the
    +        choice symbol in addition to setting Symbol.user_value.
    +        Choice.user_selection is considered when the choice is in y mode 
(the
    +        "normal" mode).
    +
    +        Other symbols that depend (possibly indirectly) on this symbol are
    +        automatically recalculated to reflect the assigned value.
    +
    +        value:
    +          The user value to give to the symbol. For bool and tristate 
symbols,
    +          n/m/y can be specified either as 0/1/2 (the usual format for 
tristate
    +          values in Kconfiglib) or as one of the strings "n"/"m"/"y". For 
other
    +          symbol types, pass a string.
    +
    +          Note that the value for an int/hex symbol is passed as a string, 
e.g.
    +          "123" or "0x0123". The format of this string is preserved in the
    +          output.
    +
    +          Values that are invalid for the type (such as "foo" or 1 (m) for 
a
    +          BOOL or "0x123" for an INT) are ignored and won't be stored in
    +          Symbol.user_value. Kconfiglib will print a warning by default for
    +          invalid assignments, and set_value() will return False.
    +
    +        Returns True if the value is valid for the type of the symbol, and
    +        False otherwise. This only looks at the form of the value. For 
BOOL and
    +        TRISTATE symbols, check the Symbol.assignable attribute to see what
    +        values are currently in range and would actually be reflected in 
the
    +        value of the symbol. For other symbol types, check whether the
    +        visibility is non-n.
    +        """
    +        if self.orig_type in _BOOL_TRISTATE and value in STR_TO_TRI:
    +            value = STR_TO_TRI[value]
    +
    +        # If the new user value matches the old, nothing changes, and we 
can
    +        # avoid invalidating cached values.
    +        #
    +        # This optimization is skipped for choice symbols: Setting a choice
    +        # symbol's user value to y might change the state of the choice, 
so it
    +        # wouldn't be safe (symbol user values always match the values set 
in a
    +        # .config file or via set_value(), and are never implicitly 
updated).
    +        if value == self.user_value and not self.choice:
    +            self._was_set = True
    +            return True
    +
    +        # Check if the value is valid for our type
    +        if not (self.orig_type is BOOL     and value in (2, 0)     or
    +                self.orig_type is TRISTATE and value in TRI_TO_STR or
    +                value.__class__ is str and
    +                (self.orig_type is STRING                        or
    +                 self.orig_type is INT and _is_base_n(value, 10) or
    +                 self.orig_type is HEX and _is_base_n(value, 16)
    +                                       and int(value, 16) >= 0)):
    +
    +            # Display tristate values as n, m, y in the warning
    +            self.kconfig._warn(
    +                "the value {} is invalid for {}, which has type {} -- "
    +                "assignment ignored"
    +                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
    +                            "'{}'".format(value),
    +                        _name_and_loc(self), TYPE_TO_STR[self.orig_type]))
    +
    +            return False
    +
    +        self.user_value = value
    +        self._was_set = True
    +
    +        if self.choice and value == 2:
    +            # Setting a choice symbol to y makes it the user selection of 
the
    +            # choice. Like for symbol user values, the user selection is 
not
    +            # guaranteed to match the actual selection of the choice, as
    +            # dependencies come into play.
    +            self.choice.user_selection = self
    +            self.choice._was_set = True
    +            self.choice._rec_invalidate()
    +        else:
    +            self._rec_invalidate_if_has_prompt()
    +
    +        return True
    +
    +    def unset_value(self):
    +        """
    +        Removes any user value from the symbol, as if the symbol had never
    +        gotten a user value via Kconfig.load_config() or 
Symbol.set_value().
    +        """
    +        if self.user_value is not None:
    +            self.user_value = None
    +            self._rec_invalidate_if_has_prompt()
    +
    +    @property
    +    def referenced(self):
    +        """
    +        See the class documentation.
    +        """
    +        return {item for node in self.nodes for item in node.referenced}
    +
    +    @property
    +    def orig_defaults(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [d for node in self.nodes for d in node.orig_defaults]
    +
    +    @property
    +    def orig_selects(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [s for node in self.nodes for s in node.orig_selects]
    +
    +    @property
    +    def orig_implies(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [i for node in self.nodes for i in node.orig_implies]
    +
    +    @property
    +    def orig_ranges(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [r for node in self.nodes for r in node.orig_ranges]
    +
    +    def __repr__(self):
    +        """
    +        Returns a string with information about the symbol (including its 
name,
    +        value, visibility, and location(s)) when it is evaluated on e.g. 
the
    +        interactive Python prompt.
    +        """
    +        fields = ["symbol " + self.name, TYPE_TO_STR[self.type]]
    +        add = fields.append
    +
    +        for node in self.nodes:
    +            if node.prompt:
    +                add('"{}"'.format(node.prompt[0]))
    +
    +        # Only add quotes for non-bool/tristate symbols
    +        add("value " + (self.str_value if self.orig_type in _BOOL_TRISTATE
    +                        else '"{}"'.format(self.str_value)))
    +
    +        if not self.is_constant:
    +            # These aren't helpful to show for constant symbols
    +
    +            if self.user_value is not None:
    +                # Only add quotes for non-bool/tristate symbols
    +                add("user value " + (TRI_TO_STR[self.user_value]
    +                                     if self.orig_type in _BOOL_TRISTATE
    +                                     else '"{}"'.format(self.user_value)))
    +
    +            add("visibility " + TRI_TO_STR[self.visibility])
    +
    +            if self.choice:
    +                add("choice symbol")
    +
    +            if self.is_allnoconfig_y:
    +                add("allnoconfig_y")
    +
    +            if self is self.kconfig.defconfig_list:
    +                add("is the defconfig_list symbol")
    +
    +            if self.env_var is not None:
    +                add("from environment variable " + self.env_var)
    +
    +            if self is self.kconfig.modules:
    +                add("is the modules symbol")
    +
    +            add("direct deps " + TRI_TO_STR[expr_value(self.direct_dep)])
    +
    +        if self.nodes:
    +            for node in self.nodes:
    +                add("{}:{}".format(node.filename, node.linenr))
    +        else:
    +            add("constant" if self.is_constant else "undefined")
    +
    +        return "<{}>".format(", ".join(fields))
    +
    +    def __str__(self):
    +        """
    +        Returns a string representation of the symbol when it is printed.
    +        Matches the Kconfig format, with any parent dependencies 
propagated to
    +        the 'depends on' condition.
    +
    +        The string is constructed by joining the strings returned by
    +        MenuNode.__str__() for each of the symbol's menu nodes, so symbols
    +        defined in multiple locations will return a string with all
    +        definitions.
    +
    +        The returned string does not end in a newline. An empty string is
    +        returned for undefined and constant symbols.
    +        """
    +        return self.custom_str(standard_sc_expr_str)
    +
    +    def custom_str(self, sc_expr_str_fn):
    +        """
    +        Works like Symbol.__str__(), but allows a custom format to be used 
for
    +        all symbol/choice references. See expr_str().
    +        """
    +        return "\n\n".join(node.custom_str(sc_expr_str_fn)
    +                           for node in self.nodes)
    +
    +    #
    +    # Private methods
    +    #
    +
    +    def __init__(self):
    +        """
    +        Symbol constructor -- not intended to be called directly by 
Kconfiglib
    +        clients.
    +        """
    +        # These attributes are always set on the instance from outside and
    +        # don't need defaults:
    +        #   kconfig
    +        #   direct_dep
    +        #   is_constant
    +        #   name
    +        #   rev_dep
    +        #   weak_rev_dep
    +
    +        # - UNKNOWN == 0
    +        # - _visited is used during tree iteration and dep. loop detection
    +        self.orig_type = self._visited = 0
    +
    +        self.nodes = []
    +
    +        self.defaults = []
    +        self.selects = []
    +        self.implies = []
    +        self.ranges = []
    +
    +        self.user_value = \
    +        self.choice = \
    +        self.env_var = \
    +        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
    +        self._cached_assignable = None
    +
    +        # _write_to_conf is calculated along with the value. If True, the
    +        # Symbol gets a .config entry.
    +
    +        self.is_allnoconfig_y = \
    +        self._was_set = \
    +        self._write_to_conf = False
    +
    +        # See Kconfig._build_dep()
    +        self._dependents = set()
    +
    +    def _assignable(self):
    +        # Worker function for the 'assignable' attribute
    +
    +        if self.orig_type not in _BOOL_TRISTATE:
    +            return ()
    +
    +        # Warning: See Symbol._rec_invalidate(), and note that this is a 
hidden
    +        # function call (property magic)
    +        vis = self.visibility
    +        if not vis:
    +            return ()
    +
    +        rev_dep_val = expr_value(self.rev_dep)
    +
    +        if vis == 2:
    +            if self.choice:
    +                return (2,)
    +
    +            if not rev_dep_val:
    +                if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
    +                    return (0, 2)
    +                return (0, 1, 2)
    +
    +            if rev_dep_val == 2:
    +                return (2,)
    +
    +            # rev_dep_val == 1
    +
    +            if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
    +                return (2,)
    +            return (1, 2)
    +
    +        # vis == 1
    +
    +        # Must be a tristate here, because bool m visibility gets promoted 
to y
    +
    +        if not rev_dep_val:
    +            return (0, 1) if expr_value(self.weak_rev_dep) != 2 else (0, 2)
    +
    +        if rev_dep_val == 2:
    +            return (2,)
    +
    +        # vis == rev_dep_val == 1
    +
    +        return (1,)
    +
    +    def _invalidate(self):
    +        # Marks the symbol as needing to be recalculated
    +
    +        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
    +        self._cached_assignable = None
    +
    +    def _rec_invalidate(self):
    +        # Invalidates the symbol and all items that (possibly) depend on it
    +
    +        if self is self.kconfig.modules:
    +            # Invalidating MODULES has wide-ranging effects
    +            self.kconfig._invalidate_all()
    +        else:
    +            self._invalidate()
    +
    +            for item in self._dependents:
    +                # _cached_vis doubles as a flag that tells us whether 
'item'
    +                # has cached values, because it's calculated as a side 
effect
    +                # of calculating all other (non-constant) cached values.
    +                #
    +                # If item._cached_vis is None, it means there can't be 
cached
    +                # values on other items that depend on 'item', because if 
there
    +                # were, some value on 'item' would have been calculated and
    +                # item._cached_vis set as a side effect. It's therefore 
safe to
    +                # stop the invalidation at symbols with _cached_vis None.
    +                #
    +                # This approach massively speeds up scripts that set a lot 
of
    +                # values, vs simply invalidating all possibly dependent 
symbols
    +                # (even when you already have a list of all the dependent
    +                # symbols, because some symbols get huge dependency trees).
    +                #
    +                # This gracefully handles dependency loops too, which is 
nice
    +                # for choices, where the choice depends on the choice 
symbols
    +                # and vice versa.
    +                if item._cached_vis is not None:
    +                    item._rec_invalidate()
    +
    +    def _rec_invalidate_if_has_prompt(self):
    +        # Invalidates the symbol and its dependent symbols, but only if the
    +        # symbol has a prompt. User values never have an effect on 
promptless
    +        # symbols, so we skip invalidation for them as an optimization.
    +        #
    +        # This also prevents constant (quoted) symbols from being 
invalidated
    +        # if set_value() is called on them, which would make them lose 
their
    +        # value and break things.
    +        #
    +        # Prints a warning if the symbol has no prompt. In some contexts 
(e.g.
    +        # when loading a .config files) assignments to promptless symbols 
are
    +        # normal and expected, so the warning can be disabled.
    +
    +        for node in self.nodes:
    +            if node.prompt:
    +                self._rec_invalidate()
    +                return
    +
    +        if self.kconfig._warn_assign_no_prompt:
    +            self.kconfig._warn(_name_and_loc(self) + " has no prompt, 
meaning "
    +                               "user values have no effect on it")
    +
    +    def _str_default(self):
    +        # write_min_config() helper function. Returns the value the symbol
    +        # would get from defaults if it didn't have a user value. Uses 
exactly
    +        # the same algorithm as the C implementation (though a bit cleaned 
up),
    +        # for compatibility.
    +
    +        if self.orig_type in _BOOL_TRISTATE:
    +            val = 0
    +
    +            # Defaults, selects, and implies do not affect choice symbols
    +            if not self.choice:
    +                for default, cond in self.defaults:
    +                    cond_val = expr_value(cond)
    +                    if cond_val:
    +                        val = min(expr_value(default), cond_val)
    +                        break
    +
    +                val = max(expr_value(self.rev_dep),
    +                          expr_value(self.weak_rev_dep),
    +                          val)
    +
    +                # Transpose mod to yes if type is bool (possibly due to 
modules
    +                # being disabled)
    +                if val == 1 and self.type is BOOL:
    +                    val = 2
    +
    +            return TRI_TO_STR[val]
    +
    +        if self.orig_type:  # STRING/INT/HEX
    +            for default, cond in self.defaults:
    +                if expr_value(cond):
    +                    return default.str_value
    +
    +        return ""
    +
    +    def _warn_select_unsatisfied_deps(self):
    +        # Helper for printing an informative warning when a symbol with
    +        # unsatisfied direct dependencies (dependencies from 'depends on', 
ifs,
    +        # and menus) is selected by some other symbol. Also warn if a 
symbol
    +        # whose direct dependencies evaluate to m is selected to y.
    +
    +        msg = "{} has direct dependencies {} with value {}, but is " \
    +              "currently being {}-selected by the following symbols:" \
    +              .format(_name_and_loc(self), expr_str(self.direct_dep),
    +                      TRI_TO_STR[expr_value(self.direct_dep)],
    +                      TRI_TO_STR[expr_value(self.rev_dep)])
    +
    +        # The reverse dependencies from each select are ORed together
    +        for select in split_expr(self.rev_dep, OR):
    +            if expr_value(select) <= expr_value(self.direct_dep):
    +                # Only include selects that exceed the direct dependencies
    +                continue
    +
    +            # - 'select A if B' turns into A && B
    +            # - 'select A' just turns into A
    +            #
    +            # In both cases, we can split on AND and pick the first operand
    +            selecting_sym = split_expr(select, AND)[0]
    +
    +            msg += "\n - {}, with value {}, direct dependencies {} " \
    +                   "(value: {})" \
    +                   .format(_name_and_loc(selecting_sym),
    +                           selecting_sym.str_value,
    +                           expr_str(selecting_sym.direct_dep),
    +                           
TRI_TO_STR[expr_value(selecting_sym.direct_dep)])
    +
    +            if select.__class__ is tuple:
    +                msg += ", and select condition {} (value: {})" \
    +                       .format(expr_str(select[2]),
    +                               TRI_TO_STR[expr_value(select[2])])
    +
    +        self.kconfig._warn(msg)
    +
    +
    +class Choice(object):
    +    """
    +    Represents a choice statement:
    +
    +      choice
    +          ...
    +      endchoice
    +
    +    The following attributes are available on Choice instances. They 
should be
    +    treated as read-only, and some are implemented through @property magic 
(but
    +    are still efficient to access due to internal caching).
    +
    +    Note: Prompts, help texts, and locations are stored in the Choice's
    +    MenuNode(s) rather than in the Choice itself. Check the MenuNode class 
and
    +    the Choice.nodes attribute. This organization matches the C tools.
    +
    +    name:
    +      The name of the choice, e.g. "FOO" for 'choice FOO', or None if the
    +      Choice has no name.
    +
    +    type:
    +      The type of the choice. One of BOOL, TRISTATE, UNKNOWN. UNKNOWN is 
for
    +      choices defined without a type where none of the contained symbols 
have a
    +      type either (otherwise the choice inherits the type of the first 
symbol
    +      defined with a type).
    +
    +      When running without modules (CONFIG_MODULES=n), TRISTATE choices
    +      magically change type to BOOL. This matches the C tools, and makes 
sense
    +      for menuconfig-like functionality.
    +
    +    orig_type:
    +      The type as given in the Kconfig file, without any magic applied. 
Used
    +      when printing the choice.
    +
    +    tri_value:
    +      The tristate value (mode) of the choice. A choice can be in one of 
three
    +      modes:
    +
    +        0 (n) - The choice is disabled and no symbols can be selected. For
    +                visible choices, this mode is only possible for choices 
with
    +                the 'optional' flag set (see kconfig-language.txt).
    +
    +        1 (m) - Any number of choice symbols can be set to m, the rest will
    +                be n.
    +
    +        2 (y) - One symbol will be y, the rest n.
    +
    +      Only tristate choices can be in m mode. The visibility of the choice 
is
    +      an upper bound on the mode, and the mode in turn is an upper bound 
on the
    +      visibility of the choice symbols.
    +
    +      To change the mode, use Choice.set_value().
    +
    +      Implementation note:
    +        The C tools internally represent choices as a type of symbol, with
    +        special-casing in many code paths. This is why there is a lot of
    +        similarity to Symbol. The value (mode) of a choice is really just a
    +        normal symbol value, and an implicit reverse dependency forces its
    +        lower bound to m for visible non-optional choices (the reverse
    +        dependency is 'm && <visibility>').
    +
    +        Symbols within choices get the choice propagated as a dependency to
    +        their properties. This turns the mode of the choice into an upper 
bound
    +        on e.g. the visibility of choice symbols, and explains the gotcha
    +        related to printing choice symbols mentioned in the module 
docstring.
    +
    +        Kconfiglib uses a separate Choice class only because it makes the 
code
    +        and interface less confusing (especially in a user-facing 
interface).
    +        Corresponding attributes have the same name in the Symbol and 
Choice
    +        classes, for consistency and compatibility.
    +
    +    assignable:
    +      See the symbol class documentation. Gives the assignable values 
(modes).
    +
    +    visibility:
    +      See the Symbol class documentation. Acts on the value (mode).
    +
    +    selection:
    +      The Symbol instance of the currently selected symbol. None if the 
Choice
    +      is not in y mode or has no selected symbol (due to unsatisfied
    +      dependencies on choice symbols).
    +
    +      WARNING: Do not assign directly to this. It will break things. Call
    +      sym.set_value(2) on the choice symbol you want to select instead.
    +
    +    user_value:
    +      The value (mode) selected by the user through Choice.set_value(). 
Either
    +      0, 1, or 2, or None if the user hasn't selected a mode. See
    +      Symbol.user_value.
    +
    +      WARNING: Do not assign directly to this. It will break things. Use
    +      Choice.set_value() instead.
    +
    +    user_selection:
    +      The symbol selected by the user (by setting it to y). Ignored if the
    +      choice is not in y mode, but still remembered so that the choice 
"snaps
    +      back" to the user selection if the mode is changed back to y. This 
might
    +      differ from 'selection' due to unsatisfied dependencies.
    +
    +      WARNING: Do not assign directly to this. It will break things. Call
    +      sym.set_value(2) on the choice symbol to be selected instead.
    +
    +    syms:
    +      List of symbols contained in the choice.
    +
    +      Obscure gotcha: If a symbol depends on the previous symbol within a
    +      choice so that an implicit menu is created, it won't be a choice 
symbol,
    +      and won't be included in 'syms'.
    +
    +    nodes:
    +      A list of MenuNodes for this choice. In practice, the list will 
probably
    +      always contain a single MenuNode, but it is possible to give a 
choice a
    +      name and define it in multiple locations.
    +
    +    defaults:
    +      List of (symbol, cond) tuples for the choice's 'defaults' 
properties. For
    +      example, 'default A if B && C' is represented as (A, (AND, B, C)). If
    +      there is no condition, 'cond' is self.kconfig.y.
    +
    +      Note that 'depends on' and parent dependencies are propagated to
    +      'default' conditions.
    +
    +    orig_defaults:
    +      See the corresponding attribute on the MenuNode class.
    +
    +    direct_dep:
    +      See Symbol.direct_dep.
    +
    +    referenced:
    +      A set() with all symbols referenced in the properties and property
    +      conditions of the choice.
    +
    +      Also includes dependencies from surrounding menus and ifs, because 
those
    +      get propagated to the choice (see the 'Intro to symbol values' 
section in
    +      the module docstring).
    +
    +    is_optional:
    +      True if the choice has the 'optional' flag set on it and can be in
    +      n mode.
    +
    +    kconfig:
    +      The Kconfig instance this choice is from.
    +    """
    +    __slots__ = (
    +        "_cached_assignable",
    +        "_cached_selection",
    +        "_cached_vis",
    +        "_dependents",
    +        "_visited",
    +        "_was_set",
    +        "defaults",
    +        "direct_dep",
    +        "is_constant",
    +        "is_optional",
    +        "kconfig",
    +        "name",
    +        "nodes",
    +        "orig_type",
    +        "syms",
    +        "user_selection",
    +        "user_value",
    +    )
    +
    +    #
    +    # Public interface
    +    #
    +
    +    @property
    +    def type(self):
    +        """
    +        Returns the type of the choice. See Symbol.type.
    +        """
    +        if self.orig_type is TRISTATE and not 
self.kconfig.modules.tri_value:
    +            return BOOL
    +        return self.orig_type
    +
    +    @property
    +    def str_value(self):
    +        """
    +        See the class documentation.
    +        """
    +        return TRI_TO_STR[self.tri_value]
    +
    +    @property
    +    def tri_value(self):
    +        """
    +        See the class documentation.
    +        """
    +        # This emulates a reverse dependency of 'm && visibility' for
    +        # non-optional choices, which is how the C implementation does it
    +
    +        val = 0 if self.is_optional else 1
    +
    +        if self.user_value is not None:
    +            val = max(val, self.user_value)
    +
    +        # Warning: See Symbol._rec_invalidate(), and note that this is a 
hidden
    +        # function call (property magic)
    +        val = min(val, self.visibility)
    +
    +        # Promote m to y for boolean choices
    +        return 2 if val == 1 and self.type is BOOL else val
    +
    +    @property
    +    def assignable(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self._cached_assignable is None:
    +            self._cached_assignable = self._assignable()
    +        return self._cached_assignable
    +
    +    @property
    +    def visibility(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self._cached_vis is None:
    +            self._cached_vis = _visibility(self)
    +        return self._cached_vis
    +
    +    @property
    +    def selection(self):
    +        """
    +        See the class documentation.
    +        """
    +        if self._cached_selection is _NO_CACHED_SELECTION:
    +            self._cached_selection = self._selection()
    +        return self._cached_selection
    +
    +    def set_value(self, value):
    +        """
    +        Sets the user value (mode) of the choice. Like for 
Symbol.set_value(),
    +        the visibility might truncate the value. Choices without the 
'optional'
    +        attribute (is_optional) can never be in n mode, but 0/"n" is still
    +        accepted since it's not a malformed value (though it will have no
    +        effect).
    +
    +        Returns True if the value is valid for the type of the choice, and
    +        False otherwise. This only looks at the form of the value. Check 
the
    +        Choice.assignable attribute to see what values are currently in 
range
    +        and would actually be reflected in the mode of the choice.
    +        """
    +        if value in STR_TO_TRI:
    +            value = STR_TO_TRI[value]
    +
    +        if value == self.user_value:
    +            # We know the value must be valid if it was successfully set
    +            # previously
    +            self._was_set = True
    +            return True
    +
    +        if not (self.orig_type is BOOL     and value in (2, 0) or
    +                self.orig_type is TRISTATE and value in TRI_TO_STR):
    +
    +            # Display tristate values as n, m, y in the warning
    +            self.kconfig._warn(
    +                "the value {} is invalid for {}, which has type {} -- "
    +                "assignment ignored"
    +                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
    +                            "'{}'".format(value),
    +                        _name_and_loc(self), TYPE_TO_STR[self.orig_type]))
    +
    +            return False
    +
    +        self.user_value = value
    +        self._was_set = True
    +        self._rec_invalidate()
    +
    +        return True
    +
    +    def unset_value(self):
    +        """
    +        Resets the user value (mode) and user selection of the Choice, as 
if
    +        the user had never touched the mode or any of the choice symbols.
    +        """
    +        if self.user_value is not None or self.user_selection:
    +            self.user_value = self.user_selection = None
    +            self._rec_invalidate()
    +
    +    @property
    +    def referenced(self):
    +        """
    +        See the class documentation.
    +        """
    +        return {item for node in self.nodes for item in node.referenced}
    +
    +    @property
    +    def orig_defaults(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [d for node in self.nodes for d in node.orig_defaults]
    +
    +    def __repr__(self):
    +        """
    +        Returns a string with information about the choice when it is 
evaluated
    +        on e.g. the interactive Python prompt.
    +        """
    +        fields = ["choice " + self.name if self.name else "choice",
    +                  TYPE_TO_STR[self.type]]
    +        add = fields.append
    +
    +        for node in self.nodes:
    +            if node.prompt:
    +                add('"{}"'.format(node.prompt[0]))
    +
    +        add("mode " + self.str_value)
    +
    +        if self.user_value is not None:
    +            add('user mode {}'.format(TRI_TO_STR[self.user_value]))
    +
    +        if self.selection:
    +            add("{} selected".format(self.selection.name))
    +
    +        if self.user_selection:
    +            user_sel_str = "{} selected by user" \
    +                           .format(self.user_selection.name)
    +
    +            if self.selection is not self.user_selection:
    +                user_sel_str += " (overridden)"
    +
    +            add(user_sel_str)
    +
    +        add("visibility " + TRI_TO_STR[self.visibility])
    +
    +        if self.is_optional:
    +            add("optional")
    +
    +        for node in self.nodes:
    +            add("{}:{}".format(node.filename, node.linenr))
    +
    +        return "<{}>".format(", ".join(fields))
    +
    +    def __str__(self):
    +        """
    +        Returns a string representation of the choice when it is printed.
    +        Matches the Kconfig format (though without the contained choice
    +        symbols), with any parent dependencies propagated to the 'depends 
on'
    +        condition.
    +
    +        The returned string does not end in a newline.
    +
    +        See Symbol.__str__() as well.
    +        """
    +        return self.custom_str(standard_sc_expr_str)
    +
    +    def custom_str(self, sc_expr_str_fn):
    +        """
    +        Works like Choice.__str__(), but allows a custom format to be used 
for
    +        all symbol/choice references. See expr_str().
    +        """
    +        return "\n\n".join(node.custom_str(sc_expr_str_fn)
    +                           for node in self.nodes)
    +
    +    #
    +    # Private methods
    +    #
    +
    +    def __init__(self):
    +        """
    +        Choice constructor -- not intended to be called directly by 
Kconfiglib
    +        clients.
    +        """
    +        # These attributes are always set on the instance from outside and
    +        # don't need defaults:
    +        #   direct_dep
    +        #   kconfig
    +
    +        # - UNKNOWN == 0
    +        # - _visited is used during dep. loop detection
    +        self.orig_type = self._visited = 0
    +
    +        self.nodes = []
    +
    +        self.syms = []
    +        self.defaults = []
    +
    +        self.name = \
    +        self.user_value = self.user_selection = \
    +        self._cached_vis = self._cached_assignable = None
    +
    +        self._cached_selection = _NO_CACHED_SELECTION
    +
    +        # is_constant is checked by _make_depend_on(). Just set it to avoid
    +        # having to special-case choices.
    +        self.is_constant = self.is_optional = False
    +
    +        # See Kconfig._build_dep()
    +        self._dependents = set()
    +
    +    def _assignable(self):
    +        # Worker function for the 'assignable' attribute
    +
    +        # Warning: See Symbol._rec_invalidate(), and note that this is a 
hidden
    +        # function call (property magic)
    +        vis = self.visibility
    +
    +        if not vis:
    +            return ()
    +
    +        if vis == 2:
    +            if not self.is_optional:
    +                return (2,) if self.type is BOOL else (1, 2)
    +            return (0, 2) if self.type is BOOL else (0, 1, 2)
    +
    +        # vis == 1
    +
    +        return (0, 1) if self.is_optional else (1,)
    +
    +    def _selection(self):
    +        # Worker function for the 'selection' attribute
    +
    +        # Warning: See Symbol._rec_invalidate(), and note that this is a 
hidden
    +        # function call (property magic)
    +        if self.tri_value != 2:
    +            # Not in y mode, so no selection
    +            return None
    +
    +        # Use the user selection if it's visible
    +        if self.user_selection and self.user_selection.visibility:
    +            return self.user_selection
    +
    +        # Otherwise, check if we have a default
    +        return self._selection_from_defaults()
    +
    +    def _selection_from_defaults(self):
    +        # Check if we have a default
    +        for sym, cond in self.defaults:
    +            # The default symbol must be visible too
    +            if expr_value(cond) and sym.visibility:
    +                return sym
    +
    +        # Otherwise, pick the first visible symbol, if any
    +        for sym in self.syms:
    +            if sym.visibility:
    +                return sym
    +
    +        # Couldn't find a selection
    +        return None
    +
    +    def _invalidate(self):
    +        self._cached_vis = self._cached_assignable = None
    +        self._cached_selection = _NO_CACHED_SELECTION
    +
    +    def _rec_invalidate(self):
    +        # See Symbol._rec_invalidate()
    +
    +        self._invalidate()
    +
    +        for item in self._dependents:
    +            if item._cached_vis is not None:
    +                item._rec_invalidate()
    +
    +
    +class MenuNode(object):
    +    """
    +    Represents a menu node in the configuration. This corresponds to an 
entry
    +    in e.g. the 'make menuconfig' interface, though non-visible choices, 
menus,
    +    and comments also get menu nodes. If a symbol or choice is defined in
    +    multiple locations, it gets one menu node for each location.
    +
    +    The top-level menu node, corresponding to the implicit top-level menu, 
is
    +    available in Kconfig.top_node.
    +
    +    The menu nodes for a Symbol or Choice can be found in the
    +    Symbol/Choice.nodes attribute. Menus and comments are represented as 
plain
    +    menu nodes, with their text stored in the prompt attribute (prompt[0]).
    +    This mirrors the C implementation.
    +
    +    The following attributes are available on MenuNode instances. They 
should
    +    be viewed as read-only.
    +
    +    item:
    +      Either a Symbol, a Choice, or one of the constants MENU and COMMENT.
    +      Menus and comments are represented as plain menu nodes. Ifs are 
collapsed
    +      (matching the C implementation) and do not appear in the final menu 
tree.
    +
    +    next:
    +      The following menu node. None if there is no following node.
    +
    +    list:
    +      The first child menu node. None if there are no children.
    +
    +      Choices and menus naturally have children, but Symbols can also have
    +      children because of menus created automatically from dependencies 
(see
    +      kconfig-language.txt).
    +
    +    parent:
    +      The parent menu node. None if there is no parent.
    +
    +    prompt:
    +      A (string, cond) tuple with the prompt for the menu node and its
    +      conditional expression (which is self.kconfig.y if there is no
    +      condition). None if there is no prompt.
    +
    +      For symbols and choices, the prompt is stored in the MenuNode rather 
than
    +      the Symbol or Choice instance. For menus and comments, the prompt 
holds
    +      the text.
    +
    +    defaults:
    +      The 'default' properties for this particular menu node. See
    +      symbol.defaults.
    +
    +      When evaluating defaults, you should use Symbol/Choice.defaults 
instead,
    +      as it include properties from all menu nodes (a symbol/choice can 
have
    +      multiple definition locations/menu nodes). MenuNode.defaults is 
meant for
    +      documentation generation.
    +
    +    selects:
    +      Like MenuNode.defaults, for selects.
    +
    +    implies:
    +      Like MenuNode.defaults, for implies.
    +
    +    ranges:
    +      Like MenuNode.defaults, for ranges.
    +
    +    orig_prompt:
    +    orig_defaults:
    +    orig_selects:
    +    orig_implies:
    +    orig_ranges:
    +      These work the like the corresponding attributes without orig_*, but 
omit
    +      any dependencies propagated from 'depends on' and surrounding 'if's 
(the
    +      direct dependencies, stored in MenuNode.dep).
    +
    +      One use for this is generating less cluttered documentation, by only
    +      showing the direct dependencies in one place.
    +
    +    help:
    +      The help text for the menu node for Symbols and Choices. None if 
there is
    +      no help text. Always stored in the node rather than the Symbol or 
Choice.
    +      It is possible to have a separate help text at each location if a 
symbol
    +      is defined in multiple locations.
    +
    +      Trailing whitespace (including a final newline) is stripped from the 
help
    +      text. This was not the case before Kconfiglib 10.21.0, where the 
format
    +      was undocumented.
    +
    +    dep:
    +      The direct ('depends on') dependencies for the menu node, or
    +      self.kconfig.y if there are no direct dependencies.
    +
    +      This attribute includes any dependencies from surrounding menus and 
ifs.
    +      Those get propagated to the direct dependencies, and the resulting 
direct
    +      dependencies in turn get propagated to the conditions of all 
properties.
    +
    +      If a symbol or choice is defined in multiple locations, only the
    +      properties defined at a particular location get the corresponding
    +      MenuNode.dep dependencies propagated to them.
    +
    +    visibility:
    +      The 'visible if' dependencies for the menu node (which must 
represent a
    +      menu), or self.kconfig.y if there are no 'visible if' dependencies.
    +      'visible if' dependencies are recursively propagated to the prompts 
of
    +      symbols and choices within the menu.
    +
    +    referenced:
    +      A set() with all symbols and choices referenced in the properties and
    +      property conditions of the menu node.
    +
    +      Also includes dependencies inherited from surrounding menus and ifs.
    +      Choices appear in the dependencies of choice symbols.
    +
    +    is_menuconfig:
    +      Set to True if the children of the menu node should be displayed in a
    +      separate menu. This is the case for the following items:
    +
    +        - Menus (node.item == MENU)
    +
    +        - Choices
    +
    +        - Symbols defined with the 'menuconfig' keyword. The children come 
from
    +          implicitly created submenus, and should be displayed in a 
separate
    +          menu rather than being indented.
    +
    +      'is_menuconfig' is just a hint on how to display the menu node. It's
    +      ignored internally by Kconfiglib, except when printing symbols.
    +
    +    filename/linenr:
    +      The location where the menu node appears. The filename is relative to
    +      $srctree (or to the current directory if $srctree isn't set), except
    +      absolute paths are used for paths outside $srctree.
    +
    +    include_path:
    +      A tuple of (filename, linenr) tuples, giving the locations of the
    +      'source' statements via which the Kconfig file containing this menu 
node
    +      was included. The first element is the location of the 'source' 
statement
    +      in the top-level Kconfig file passed to Kconfig.__init__(), etc.
    +
    +      Note that the Kconfig file of the menu node itself isn't included. 
Check
    +      'filename' and 'linenr' for that.
    +
    +    kconfig:
    +      The Kconfig instance the menu node is from.
    +    """
    +    __slots__ = (
    +        "dep",
    +        "filename",
    +        "help",
    +        "include_path",
    +        "is_menuconfig",
    +        "item",
    +        "kconfig",
    +        "linenr",
    +        "list",
    +        "next",
    +        "parent",
    +        "prompt",
    +        "visibility",
    +
    +        # Properties
    +        "defaults",
    +        "selects",
    +        "implies",
    +        "ranges",
    +    )
    +
    +    def __init__(self):
    +        # Properties defined on this particular menu node. A local 
'depends on'
    +        # only applies to these, in case a symbol is defined in multiple
    +        # locations.
    +        self.defaults = []
    +        self.selects = []
    +        self.implies = []
    +        self.ranges = []
    +
    +    @property
    +    def orig_prompt(self):
    +        """
    +        See the class documentation.
    +        """
    +        if not self.prompt:
    +            return None
    +        return (self.prompt[0], self._strip_dep(self.prompt[1]))
    +
    +    @property
    +    def orig_defaults(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [(default, self._strip_dep(cond))
    +                for default, cond in self.defaults]
    +
    +    @property
    +    def orig_selects(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [(select, self._strip_dep(cond))
    +                for select, cond in self.selects]
    +
    +    @property
    +    def orig_implies(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [(imply, self._strip_dep(cond))
    +                for imply, cond in self.implies]
    +
    +    @property
    +    def orig_ranges(self):
    +        """
    +        See the class documentation.
    +        """
    +        return [(low, high, self._strip_dep(cond))
    +                for low, high, cond in self.ranges]
    +
    +    @property
    +    def referenced(self):
    +        """
    +        See the class documentation.
    +        """
    +        # self.dep is included to catch dependencies from a lone 'depends 
on'
    +        # when there are no properties to propagate it to
    +        res = expr_items(self.dep)
    +
    +        if self.prompt:
    +            res |= expr_items(self.prompt[1])
    +
    +        if self.item is MENU:
    +            res |= expr_items(self.visibility)
    +
    +        for value, cond in self.defaults:
    +            res |= expr_items(value)
    +            res |= expr_items(cond)
    +
    +        for value, cond in self.selects:
    +            res.add(value)
    +            res |= expr_items(cond)
    +
    +        for value, cond in self.implies:
    +            res.add(value)
    +            res |= expr_items(cond)
    +
    +        for low, high, cond in self.ranges:
    +            res.add(low)
    +            res.add(high)
    +            res |= expr_items(cond)
    +
    +        return res
    +
    +    def __repr__(self):
    +        """
    +        Returns a string with information about the menu node when it is
    +        evaluated on e.g. the interactive Python prompt.
    +        """
    +        fields = []
    +        add = fields.append
    +
    +        if self.item.__class__ is Symbol:
    +            add("menu node for symbol " + self.item.name)
    +
    +        elif self.item.__class__ is Choice:
    +            s = "menu node for choice"
    +            if self.item.name is not None:
    +                s += " " + self.item.name
    +            add(s)
    +
    +        elif self.item is MENU:
    +            add("menu node for menu")
    +
    +        else:  # self.item is COMMENT
    +            add("menu node for comment")
    +
    +        if self.prompt:
    +            add('prompt "{}" (visibility {})'.format(
    +                self.prompt[0], TRI_TO_STR[expr_value(self.prompt[1])]))
    +
    +        if self.item.__class__ is Symbol and self.is_menuconfig:
    +            add("is menuconfig")
    +
    +        add("deps " + TRI_TO_STR[expr_value(self.dep)])
    +
    +        if self.item is MENU:
    +            add("'visible if' deps " + 
TRI_TO_STR[expr_value(self.visibility)])
    +
    +        if self.item.__class__ in _SYMBOL_CHOICE and self.help is not None:
    +            add("has help")
    +
    +        if self.list:
    +            add("has child")
    +
    +        if self.next:
    +            add("has next")
    +
    +        add("{}:{}".format(self.filename, self.linenr))
    +
    +        return "<{}>".format(", ".join(fields))
    +
    +    def __str__(self):
    +        """
    +        Returns a string representation of the menu node. Matches the 
Kconfig
    +        format, with any parent dependencies propagated to the 'depends on'
    +        condition.
    +
    +        The output could (almost) be fed back into a Kconfig parser to 
redefine
    +        the object associated with the menu node. See the module 
documentation
    +        for a gotcha related to choice symbols.
    +
    +        For symbols and choices with multiple menu nodes (multiple 
definition
    +        locations), properties that aren't associated with a particular 
menu
    +        node are shown on all menu nodes ('option env=...', 'optional' for
    +        choices, etc.).
    +
    +        The returned string does not end in a newline.
    +        """
    +        return self.custom_str(standard_sc_expr_str)
    +
    +    def custom_str(self, sc_expr_str_fn):
    +        """
    +        Works like MenuNode.__str__(), but allows a custom format to be 
used
    +        for all symbol/choice references. See expr_str().
    +        """
    +        return self._menu_comment_node_str(sc_expr_str_fn) \
    +               if self.item in _MENU_COMMENT else \
    +               self._sym_choice_node_str(sc_expr_str_fn)
    +
    +    def _menu_comment_node_str(self, sc_expr_str_fn):
    +        s = '{} "{}"'.format("menu" if self.item is MENU else "comment",
    +                             self.prompt[0])
    +
    +        if self.dep is not self.kconfig.y:
    +            s += "\n\tdepends on {}".format(expr_str(self.dep, 
sc_expr_str_fn))
    +
    +        if self.item is MENU and self.visibility is not self.kconfig.y:
    +            s += "\n\tvisible if {}".format(expr_str(self.visibility,
    +                                                     sc_expr_str_fn))
    +
    +        return s
    +
    +    def _sym_choice_node_str(self, sc_expr_str_fn):
    +        def indent_add(s):
    +            lines.append("\t" + s)
    +
    +        def indent_add_cond(s, cond):
    +            if cond is not self.kconfig.y:
    +                s += " if " + expr_str(cond, sc_expr_str_fn)
    +            indent_add(s)
    +
    +        sc = self.item
    +
    +        if sc.__class__ is Symbol:
    +            lines = [("menuconfig " if self.is_menuconfig else "config ")
    +                     + sc.name]
    +        else:
    +            lines = ["choice " + sc.name if sc.name else "choice"]
    +
    +        if sc.orig_type and not self.prompt:  # sc.orig_type != UNKNOWN
    +            # If there's a prompt, we'll use the '<type> "prompt"' 
shorthand
    +            # instead
    +            indent_add(TYPE_TO_STR[sc.orig_type])
    +
    +        if self.prompt:
    +            if sc.orig_type:
    +                prefix = TYPE_TO_STR[sc.orig_type]
    +            else:
    +                # Symbol defined without a type (which generates a warning)
    +                prefix = "prompt"
    +
    +            indent_add_cond(prefix + ' 
"{}"'.format(escape(self.prompt[0])),
    +                            self.orig_prompt[1])
    +
    +        if sc.__class__ is Symbol:
    +            if sc.is_allnoconfig_y:
    +                indent_add("option allnoconfig_y")
    +
    +            if sc is sc.kconfig.defconfig_list:
    +                indent_add("option defconfig_list")
    +
    +            if sc.env_var is not None:
    +                indent_add('option env="{}"'.format(sc.env_var))
    +
    +            if sc is sc.kconfig.modules:
    +                indent_add("option modules")
    +
    +            for low, high, cond in self.orig_ranges:
    +                indent_add_cond(
    +                    "range {} {}".format(sc_expr_str_fn(low),
    +                                         sc_expr_str_fn(high)),
    +                    cond)
    +
    +        for default, cond in self.orig_defaults:
    +            indent_add_cond("default " + expr_str(default, sc_expr_str_fn),
    +                            cond)
    +
    +        if sc.__class__ is Choice and sc.is_optional:
    +            indent_add("optional")
    +
    +        if sc.__class__ is Symbol:
    +            for select, cond in self.orig_selects:
    +                indent_add_cond("select " + sc_expr_str_fn(select), cond)
    +
    +            for imply, cond in self.orig_implies:
    +                indent_add_cond("imply " + sc_expr_str_fn(imply), cond)
    +
    +        if self.dep is not sc.kconfig.y:
    +            indent_add("depends on " + expr_str(self.dep, sc_expr_str_fn))
    +
    +        if self.help is not None:
    +            indent_add("help")
    +            for line in self.help.splitlines():
    +                indent_add("  " + line)
    +
    +        return "\n".join(lines)
    +
    +    def _strip_dep(self, expr):
    +        # Helper function for removing MenuNode.dep from 'expr'. Uses two
    +        # pieces of internal knowledge: (1) Expressions are reused rather 
than
    +        # copied, and (2) the direct dependencies always appear at the end.
    +
    +        # ... if dep -> ... if y
    +        if self.dep is expr:
    +            return self.kconfig.y
    +
    +        # (AND, X, dep) -> X
    +        if expr.__class__ is tuple and expr[0] is AND and expr[2] is 
self.dep:
    +            return expr[1]
    +
    +        return expr
    +
    +
    +class Variable(object):
    +    """
    +    Represents a preprocessor variable/function.
    +
    +    The following attributes are available:
    +
    +    name:
    +      The name of the variable.
    +
    +    value:
    +      The unexpanded value of the variable.
    +
    +    expanded_value:
    +      The expanded value of the variable. For simple variables (those 
defined
    +      with :=), this will equal 'value'. Accessing this property will 
raise a
    +      KconfigError if the expansion seems to be stuck in a loop.
    +
    +      Accessing this field is the same as calling expanded_value_w_args() 
with
    +      no arguments. I hadn't considered function arguments when adding it. 
It
    +      is retained for backwards compatibility though.
    +
    +    is_recursive:
    +      True if the variable is recursive (defined with =).
    +    """
    +    __slots__ = (
    +        "_n_expansions",
    +        "is_recursive",
    +        "kconfig",
    +        "name",
    +        "value",
    +    )
    +
    +    @property
    +    def expanded_value(self):
    +        """
    +        See the class documentation.
    +        """
    +        return self.expanded_value_w_args()
    +
    +    def expanded_value_w_args(self, *args):
    +        """
    +        Returns the expanded value of the variable/function. Any arguments
    +        passed will be substituted for $(1), $(2), etc.
    +
    +        Raises a KconfigError if the expansion seems to be stuck in a loop.
    +        """
    +        return self.kconfig._fn_val((self.name,) + args)
    +
    +    def __repr__(self):
    +        return "<variable {}, {}, value '{}'>" \
    +               .format(self.name,
    +                       "recursive" if self.is_recursive else "immediate",
    +                       self.value)
    +
    +
    +class KconfigError(Exception):
    +    """
    +    Exception raised for Kconfig-related errors.
    +
    +    KconfigError and KconfigSyntaxError are the same class. The
    +    KconfigSyntaxError alias is only maintained for backwards 
compatibility.
    +    """
    +
    +KconfigSyntaxError = KconfigError  # Backwards compatibility
    +
    +
    +class InternalError(Exception):
    +    "Never raised. Kept around for backwards compatibility."
    +
    +
    +# Workaround:
    +#
    +# If 'errno' and 'strerror' are set on IOError, then __str__() always 
returns
    +# "[Errno <errno>] <strerror>", ignoring any custom message passed to the
    +# constructor. By defining our own subclass, we can use a custom message 
while
    +# also providing 'errno', 'strerror', and 'filename' to scripts.
    +class _KconfigIOError(IOError):
    +    def __init__(self, ioerror, msg):
    +        self.msg = msg
    +        super(_KconfigIOError, self).__init__(
    +            ioerror.errno, ioerror.strerror, ioerror.filename)
    +
    +    def __str__(self):
    +        return self.msg
    +
    +
    +#
    +# Public functions
    +#
    +
    +
    +def expr_value(expr):
    +    """
    +    Evaluates the expression 'expr' to a tristate value. Returns 0 (n), 1 
(m),
    +    or 2 (y).
    +
    +    'expr' must be an already-parsed expression from a Symbol, Choice, or
    +    MenuNode property. To evaluate an expression represented as a string, 
use
    +    Kconfig.eval_string().
    +
    +    Passing subexpressions of expressions to this function works as 
expected.
    +    """
    +    if expr.__class__ is not tuple:
    +        return expr.tri_value
    +
    +    if expr[0] is AND:
    +        v1 = expr_value(expr[1])
    +        # Short-circuit the n case as an optimization (~5% faster
    +        # allnoconfig.py and allyesconfig.py, as of writing)
    +        return 0 if not v1 else min(v1, expr_value(expr[2]))
    +
    +    if expr[0] is OR:
    +        v1 = expr_value(expr[1])
    +        # Short-circuit the y case as an optimization
    +        return 2 if v1 == 2 else max(v1, expr_value(expr[2]))
    +
    +    if expr[0] is NOT:
    +        return 2 - expr_value(expr[1])
    +
    +    # Relation
    +    #
    +    # Implements <, <=, >, >= comparisons as well. These were added to
    +    # kconfig in 31847b67 (kconfig: allow use of relations other than
    +    # (in)equality).
    +
    +    rel, v1, v2 = expr
    +
    +    # If both operands are strings...
    +    if v1.orig_type is STRING and v2.orig_type is STRING:
    +        # ...then compare them lexicographically
    +        comp = _strcmp(v1.str_value, v2.str_value)
    +    else:
    +        # Otherwise, try to compare them as numbers
    +        try:
    +            comp = _sym_to_num(v1) - _sym_to_num(v2)
    +        except ValueError:
    +            # Fall back on a lexicographic comparison if the operands don't
    +            # parse as numbers
    +            comp = _strcmp(v1.str_value, v2.str_value)
    +
    +    return 2*(comp == 0 if rel is EQUAL else
    +              comp != 0 if rel is UNEQUAL else
    +              comp <  0 if rel is LESS else
    +              comp <= 0 if rel is LESS_EQUAL else
    +              comp >  0 if rel is GREATER else
    +              comp >= 0)
    +
    +
    +def standard_sc_expr_str(sc):
    +    """
    +    Standard symbol/choice printing function. Uses plain Kconfig syntax, 
and
    +    displays choices as <choice> (or <choice NAME>, for named choices).
    +
    +    See expr_str().
    +    """
    +    if sc.__class__ is Symbol:
    +        if sc.is_constant and sc.name not in STR_TO_TRI:
    +            return '"{}"'.format(escape(sc.name))
    +        return sc.name
    +
    +    return "<choice {}>".format(sc.name) if sc.name else "<choice>"
    +
    +
    +def expr_str(expr, sc_expr_str_fn=standard_sc_expr_str):
    +    """
    +    Returns the string representation of the expression 'expr', as in a 
Kconfig
    +    file.
    +
    +    Passing subexpressions of expressions to this function works as 
expected.
    +
    +    sc_expr_str_fn (default: standard_sc_expr_str):
    +      This function is called for every symbol/choice (hence "sc") 
appearing in
    +      the expression, with the symbol/choice as the argument. It is 
expected to
    +      return a string to be used for the symbol/choice.
    +
    +      This can be used e.g. to turn symbols/choices into links when 
generating
    +      documentation, or for printing the value of each symbol/choice after 
it.
    +
    +      Note that quoted values are represented as constants symbols
    +      (Symbol.is_constant == True).
    +    """
    +    if expr.__class__ is not tuple:
    +        return sc_expr_str_fn(expr)
    +
    +    if expr[0] is AND:
    +        return "{} && {}".format(_parenthesize(expr[1], OR, 
sc_expr_str_fn),
    +                                 _parenthesize(expr[2], OR, 
sc_expr_str_fn))
    +
    +    if expr[0] is OR:
    +        # This turns A && B || C && D into "(A && B) || (C && D)", which is
    +        # redundant, but more readable
    +        return "{} || {}".format(_parenthesize(expr[1], AND, 
sc_expr_str_fn),
    +                                 _parenthesize(expr[2], AND, 
sc_expr_str_fn))
    +
    +    if expr[0] is NOT:
    +        if expr[1].__class__ is tuple:
    +            return "!({})".format(expr_str(expr[1], sc_expr_str_fn))
    +        return "!" + sc_expr_str_fn(expr[1])  # Symbol
    +
    +    # Relation
    +    #
    +    # Relation operands are always symbols (quoted strings are constant
    +    # symbols)
    +    return "{} {} {}".format(sc_expr_str_fn(expr[1]), REL_TO_STR[expr[0]],
    +                             sc_expr_str_fn(expr[2]))
    +
    +
    +def expr_items(expr):
    +    """
    +    Returns a set() of all items (symbols and choices) that appear in the
    +    expression 'expr'.
    +
    +    Passing subexpressions of expressions to this function works as 
expected.
    +    """
    +    res = set()
    +
    +    def rec(subexpr):
    +        if subexpr.__class__ is tuple:
    +            # AND, OR, NOT, or relation
    +
    +            rec(subexpr[1])
    +
    +            # NOTs only have a single operand
    +            if subexpr[0] is not NOT:
    +                rec(subexpr[2])
    +
    +        else:
    +            # Symbol or choice
    +            res.add(subexpr)
    +
    +    rec(expr)
    +    return res
    +
    +
    +def split_expr(expr, op):
    +    """
    +    Returns a list containing the top-level AND or OR operands in the
    +    expression 'expr', in the same (left-to-right) order as they appear in
    +    the expression.
    +
    +    This can be handy e.g. for splitting (weak) reverse dependencies
    +    from 'select' and 'imply' into individual selects/implies.
    +
    +    op:
    +      Either AND to get AND operands, or OR to get OR operands.
    +
    +      (Having this as an operand might be more future-safe than having two
    +      hardcoded functions.)
    +
    +
    +    Pseudo-code examples:
    +
    +      split_expr( A                    , OR  )  ->  [A]
    +      split_expr( A && B               , OR  )  ->  [A && B]
    +      split_expr( A || B               , OR  )  ->  [A, B]
    +      split_expr( A || B               , AND )  ->  [A || B]
    +      split_expr( A || B || (C && D)   , OR  )  ->  [A, B, C && D]
    +
    +      # Second || is not at the top level
    +      split_expr( A || (B && (C || D)) , OR )  ->  [A, B && (C || D)]
    +
    +      # Parentheses don't matter as long as we stay at the top level (don't
    +      # encounter any non-'op' nodes)
    +      split_expr( (A || B) || C        , OR )  ->  [A, B, C]
    +      split_expr( A || (B || C)        , OR )  ->  [A, B, C]
    +    """
    +    res = []
    +
    +    def rec(subexpr):
    +        if subexpr.__class__ is tuple and subexpr[0] is op:
    +            rec(subexpr[1])
    +            rec(subexpr[2])
    +        else:
    +            res.append(subexpr)
    +
    +    rec(expr)
    +    return res
    +
    +
    +def escape(s):
    +    r"""
    +    Escapes the string 's' in the same fashion as is done for display in
    +    Kconfig format and when writing strings to a .config file. " and \ are
    +    replaced by \" and \\, respectively.
    +    """
    +    # \ must be escaped before " to avoid double escaping
    +    return s.replace("\\", r"\\").replace('"', r'\"')
    +
    +
    +def unescape(s):
    +    r"""
    +    Unescapes the string 's'. \ followed by any character is replaced with 
just
    +    that character. Used internally when reading .config files.
    +    """
    +    return _unescape_sub(r"\1", s)
    +
    +# unescape() helper
    +_unescape_sub = re.compile(r"\\(.)").sub
    +
    +
    +def standard_kconfig():
    +    """
    +    Helper for tools. Loads the top-level Kconfig specified as the first
    +    command-line argument, or "Kconfig" if there are no command-line 
arguments.
    +    Returns the Kconfig instance.
    +
    +    Exits with sys.exit() (which raises a SystemExit exception) and prints 
a
    +    usage note to stderr if more than one command-line argument is passed.
    +    """
    +    if len(sys.argv) > 2:
    +        sys.exit("usage: {} [Kconfig]".format(sys.argv[0]))
    +
    +    # Only show backtraces for unexpected exceptions
    +    try:
    +        return Kconfig("Kconfig" if len(sys.argv) < 2 else sys.argv[1])
    +    except (EnvironmentError, KconfigError) as e:
    +        # Some long exception messages have extra newlines for better
    +        # formatting when reported as an unhandled exception. Strip them 
here.
    +        sys.exit(str(e).strip())
    +
    +
    +def standard_config_filename():
    +    """
    +    Helper for tools. Returns the value of KCONFIG_CONFIG (which specifies 
the
    +    .config file to load/save) if it is set, and ".config" otherwise.
    +
    +    Calling load_config() with filename=None might give the behavior you 
want,
    +    without having to use this function.
    +    """
    +    return os.getenv("KCONFIG_CONFIG", ".config")
    +
    +
    +def load_allconfig(kconf, filename):
    +    """
    +    Helper for all*config. Loads (merges) the configuration file specified 
by
    +    KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in the
    +    Linux kernel.
    +
    +    Disables warnings for duplicated assignments within configuration 
files for
    +    the duration of the call (kconf.warn_assign_override/warn_assign_redun 
= False),
    +    and restores the previous warning settings at the end. The
    +    KCONFIG_ALLCONFIG configuration file is expected to override symbols.
    +
    +    Exits with sys.exit() (which raises a SystemExit exception) and prints 
an
    +    error to stderr if KCONFIG_ALLCONFIG is set but the configuration file
    +    can't be opened.
    +
    +    kconf:
    +      Kconfig instance to load the configuration in.
    +
    +    filename:
    +      Command-specific configuration filename - "allyes.config",
    +      "allno.config", etc.
    +    """
    +    allconfig = os.getenv("KCONFIG_ALLCONFIG")
    +    if allconfig is None:
    +        return
    +
    +    def std_msg(e):
    +        # "Upcasts" a _KconfigIOError to an IOError, removing the custom
    +        # __str__() message. The standard message is better here.
    +        #
    +        # This might also convert an OSError to an IOError in obscure 
cases,
    +        # but it's probably not a big deal. The distinction is shaky (see
    +        # PEP-3151).
    +        return IOError(e.errno, e.strerror, e.filename)
    +
    +    old_warn_assign_override = kconf.warn_assign_override
    +    old_warn_assign_redun = kconf.warn_assign_redun
    +    kconf.warn_assign_override = kconf.warn_assign_redun = False
    +
    +    if allconfig in ("", "1"):
    +        try:
    +            print(kconf.load_config(filename, False))
    +        except EnvironmentError as e1:
    +            try:
    +                print(kconf.load_config("all.config", False))
    +            except EnvironmentError as e2:
    +                sys.exit("error: KCONFIG_ALLCONFIG is set, but neither {} "
    +                         "nor all.config could be opened: {}, {}"
    +                         .format(filename, std_msg(e1), std_msg(e2)))
    +    else:
    +        try:
    +            print(kconf.load_config(allconfig, False))
    +        except EnvironmentError as e:
    +            sys.exit("error: KCONFIG_ALLCONFIG is set to '{}', which "
    +                     "could not be opened: {}"
    +                     .format(allconfig, std_msg(e)))
    +
    +    kconf.warn_assign_override = old_warn_assign_override
    +    kconf.warn_assign_redun = old_warn_assign_redun
    +
    +
    +#
    +# Internal functions
    +#
    +
    +
    +def _visibility(sc):
    +    # Symbols and Choices have a "visibility" that acts as an upper bound 
on
    +    # the values a user can set for them, corresponding to the visibility 
in
    +    # e.g. 'make menuconfig'. This function calculates the visibility for 
the
    +    # Symbol or Choice 'sc' -- the logic is nearly identical.
    +
    +    vis = 0
    +
    +    for node in sc.nodes:
    +        if node.prompt:
    +            vis = max(vis, expr_value(node.prompt[1]))
    +
    +    if sc.__class__ is Symbol and sc.choice:
    +        if sc.choice.orig_type is TRISTATE and \
    +           sc.orig_type is not TRISTATE and sc.choice.tri_value != 2:
    +            # Non-tristate choice symbols are only visible in y mode
    +            return 0
    +
    +        if sc.orig_type is TRISTATE and vis == 1 and sc.choice.tri_value 
== 2:
    +            # Choice symbols with m visibility are not visible in y mode
    +            return 0
    +
    +    # Promote m to y if we're dealing with a non-tristate (possibly due to
    +    # modules being disabled)
    +    if vis == 1 and sc.type is not TRISTATE:
    +        return 2
    +
    +    return vis
    +
    +
    +def _make_depend_on(sc, expr):
    +    # Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 
'expr'.
    +    # Constant symbols in 'expr' are skipped as they can never change value
    +    # anyway.
    +
    +    if expr.__class__ is tuple:
    +        # AND, OR, NOT, or relation
    +
    +        _make_depend_on(sc, expr[1])
    +
    +        # NOTs only have a single operand
    +        if expr[0] is not NOT:
    +            _make_depend_on(sc, expr[2])
    +
    +    elif not expr.is_constant:
    +        # Non-constant symbol, or choice
    +        expr._dependents.add(sc)
    +
    +
    +def _parenthesize(expr, type_, sc_expr_str_fn):
    +    # expr_str() helper. Adds parentheses around expressions of type 
'type_'.
    +
    +    if expr.__class__ is tuple and expr[0] is type_:
    +        return "({})".format(expr_str(expr, sc_expr_str_fn))
    +    return expr_str(expr, sc_expr_str_fn)
    +
    +
    +def _ordered_unique(lst):
    +    # Returns 'lst' with any duplicates removed, preserving order. This 
hacky
    +    # version seems to be a common idiom. It relies on short-circuit 
evaluation
    +    # and set.add() returning None, which is falsy.
    +
    +    seen = set()
    +    seen_add = seen.add
    +    return [x for x in lst if x not in seen and not seen_add(x)]
    +
    +
    +def _is_base_n(s, n):
    +    try:
    +        int(s, n)
    +        return True
    +    except ValueError:
    +        return False
    +
    +
    +def _strcmp(s1, s2):
    +    # strcmp()-alike that returns -1, 0, or 1
    +
    +    return (s1 > s2) - (s1 < s2)
    +
    +
    +def _sym_to_num(sym):
    +    # expr_value() helper for converting a symbol to a number. Raises
    +    # ValueError for symbols that can't be converted.
    +
    +    # For BOOL and TRISTATE, n/m/y count as 0/1/2. This mirrors 9059a3493ef
    +    # ("kconfig: fix relational operators for bool and tristate symbols") 
in
    +    # the C implementation.
    +    return sym.tri_value if sym.orig_type in _BOOL_TRISTATE else \
    +           int(sym.str_value, _TYPE_TO_BASE[sym.orig_type])
    +
    +
    +def _touch_dep_file(path, sym_name):
    +    # If sym_name is MY_SYM_NAME, touches my/sym/name.h. See the 
sync_deps()
    +    # docstring.
    +
    +    sym_path = path + os.sep + sym_name.lower().replace("_", os.sep) + ".h"
    +    sym_path_dir = dirname(sym_path)
    +    if not exists(sym_path_dir):
    +        os.makedirs(sym_path_dir, 0o755)
    +
    +    # A kind of truncating touch, mirroring the C tools
    +    os.close(os.open(
    +        sym_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644))
    +
    +
    +def _save_old(path):
    +    # See write_config()
    +
    +    def copy(src, dst):
    +        # Import as needed, to save some startup time
    +        import shutil
    +        shutil.copyfile(src, dst)
    +
    +    if islink(path):
    +        # Preserve symlinks
    +        copy_fn = copy
    +    elif hasattr(os, "replace"):
    +        # Python 3 (3.3+) only. Best choice when available, because it
    +        # removes <filename>.old on both *nix and Windows.
    +        copy_fn = os.replace
    +    elif os.name == "posix":
    +        # Removes <filename>.old on POSIX systems
    +        copy_fn = os.rename
    +    else:
    +        # Fall back on copying
    +        copy_fn = copy
    +
    +    try:
    +        copy_fn(path, path + ".old")
    +    except Exception:
    +        # Ignore errors from 'path' missing as well as other errors.
    +        # <filename>.old file is usually more of a nice-to-have, and not 
worth
    +        # erroring out over e.g. if <filename>.old happens to be a 
directory or
    +        # <filename> is something like /dev/null.
    +        pass
    +
    +
    +def _name_and_loc(sc):
    +    # Helper for giving the symbol/choice name and location(s) in e.g. 
warnings
    +
    +    # Reuse the expression format. That way choices show up as
    +    # '<choice (name, if any)>'
    +    name = standard_sc_expr_str(sc)
    +
    +    if not sc.nodes:
    +        return name + " (undefined)"
    +
    +    return "{} (defined at {})".format(
    +        name,
    +        ", ".join("{}:{}".format(node.filename, node.linenr)
    +                  for node in sc.nodes))
    +
    +
    +# Menu manipulation
    +
    +
    +def _expr_depends_on(expr, sym):
    +    # Reimplementation of expr_depends_symbol() from mconf.c. Used to 
determine
    +    # if a submenu should be implicitly created. This also influences which
    +    # items inside choice statements are considered choice items.
    +
    +    if expr.__class__ is not tuple:
    +        return expr is sym
    +
    +    if expr[0] in _EQUAL_UNEQUAL:
    +        # Check for one of the following:
    +        # sym = m/y, m/y = sym, sym != n, n != sym
    +
    +        left, right = expr[1:]
    +
    +        if right is sym:
    +            left, right = right, left
    +        elif left is not sym:
    +            return False
    +
    +        return (expr[0] is EQUAL and right is sym.kconfig.m or
    +                                     right is sym.kconfig.y) or \
    +               (expr[0] is UNEQUAL and right is sym.kconfig.n)
    +
    +    return expr[0] is AND and \
    +           (_expr_depends_on(expr[1], sym) or
    +            _expr_depends_on(expr[2], sym))
    +
    +
    +def _auto_menu_dep(node1, node2):
    +    # Returns True if node2 has an "automatic menu dependency" on node1. If
    +    # node2 has a prompt, we check its condition. Otherwise, we look 
directly
    +    # at node2.dep.
    +
    +    return _expr_depends_on(node2.prompt[1] if node2.prompt else node2.dep,
    +                            node1.item)
    +
    +
    +def _flatten(node):
    +    # "Flattens" menu nodes without prompts (e.g. 'if' nodes and 
non-visible
    +    # symbols with children from automatic menu creation) so that their
    +    # children appear after them instead. This gives a clean menu structure
    +    # with no unexpected "jumps" in the indentation.
    +    #
    +    # Do not flatten promptless choices (which can appear "legitimately" 
if a
    +    # named choice is defined in multiple locations to add on symbols). It
    +    # looks confusing, and the menuconfig already shows all choice symbols 
if
    +    # you enter the choice at some location with a prompt.
    +
    +    while node:
    +        if node.list and not node.prompt and \
    +           node.item.__class__ is not Choice:
    +
    +            last_node = node.list
    +            while 1:
    +                last_node.parent = node.parent
    +                if not last_node.next:
    +                    break
    +                last_node = last_node.next
    +
    +            last_node.next = node.next
    +            node.next = node.list
    +            node.list = None
    +
    +        node = node.next
    +
    +
    +def _remove_ifs(node):
    +    # Removes 'if' nodes (which can be recognized by MenuNode.item being 
None),
    +    # which are assumed to already have been flattened. The C 
implementation
    +    # doesn't bother to do this, but we expose the menu tree directly, and 
it
    +    # makes it nicer to work with.
    +
    +    cur = node.list
    +    while cur and not cur.item:
    +        cur = cur.next
    +
    +    node.list = cur
    +
    +    while cur:
    +        next = cur.next
    +        while next and not next.item:
    +            next = next.next
    +
    +        # Equivalent to
    +        #
    +        #   cur.next = next
    +        #   cur = next
    +        #
    +        # due to tricky Python semantics. The order matters.
    +        cur.next = cur = next
    +
    +
    +def _finalize_choice(node):
    +    # Finalizes a choice, marking each symbol whose menu node has the 
choice as
    +    # the parent as a choice symbol, and automatically determining types 
if not
    +    # specified.
    +
    +    choice = node.item
    +
    +    cur = node.list
    +    while cur:
    +        if cur.item.__class__ is Symbol:
    +            cur.item.choice = choice
    +            choice.syms.append(cur.item)
    +        cur = cur.next
    +
    +    # If no type is specified for the choice, its type is that of
    +    # the first choice item with a specified type
    +    if not choice.orig_type:
    +        for item in choice.syms:
    +            if item.orig_type:
    +                choice.orig_type = item.orig_type
    +                break
    +
    +    # Each choice item of UNKNOWN type gets the type of the choice
    +    for sym in choice.syms:
    +        if not sym.orig_type:
    +            sym.orig_type = choice.orig_type
    +
    +
    +def _check_dep_loop_sym(sym, ignore_choice):
    +    # Detects dependency loops using depth-first search on the dependency 
graph
    +    # (which is calculated earlier in Kconfig._build_dep()).
    +    #
    +    # Algorithm:
    +    #
    +    #  1. Symbols/choices start out with _visited = 0, meaning unvisited.
    +    #
    +    #  2. When a symbol/choice is first visited, _visited is set to 1, 
meaning
    +    #     "visited, potentially part of a dependency loop". The recursive
    +    #     search then continues from the symbol/choice.
    +    #
    +    #  3. If we run into a symbol/choice X with _visited already set to 1,
    +    #     there's a dependency loop. The loop is found on the call stack by
    +    #     recording symbols while returning ("on the way back") until X is 
seen
    +    #     again.
    +    #
    +    #  4. Once a symbol/choice and all its dependencies (or dependents in 
this
    +    #     case) have been checked recursively without detecting any loops, 
its
    +    #     _visited is set to 2, meaning "visited, not part of a dependency
    +    #     loop".
    +    #
    +    #     This saves work if we run into the symbol/choice again in later 
calls
    +    #     to _check_dep_loop_sym(). We just return immediately.
    +    #
    +    # Choices complicate things, as every choice symbol depends on every 
other
    +    # choice symbol in a sense. When a choice is "entered" via a choice 
symbol
    +    # X, we visit all choice symbols from the choice except X, and prevent
    +    # immediately revisiting the choice with a flag (ignore_choice).
    +    #
    +    # Maybe there's a better way to handle this (different flags or the
    +    # like...)
    +
    +    if not sym._visited:
    +        # sym._visited == 0, unvisited
    +
    +        sym._visited = 1
    +
    +        for dep in sym._dependents:
    +            # Choices show up in Symbol._dependents when the choice has the
    +            # symbol in a 'prompt' or 'default' condition (e.g.
    +            # 'default ... if SYM').
    +            #
    +            # Since we aren't entering the choice via a choice symbol, all
    +            # choice symbols need to be checked, hence the None.
    +            loop = _check_dep_loop_choice(dep, None) \
    +                   if dep.__class__ is Choice \
    +                   else _check_dep_loop_sym(dep, False)
    +
    +            if loop:
    +                # Dependency loop found
    +                return _found_dep_loop(loop, sym)
    +
    +        if sym.choice and not ignore_choice:
    +            loop = _check_dep_loop_choice(sym.choice, sym)
    +            if loop:
    +                # Dependency loop found
    +                return _found_dep_loop(loop, sym)
    +
    +        # The symbol is not part of a dependency loop
    +        sym._visited = 2
    +
    +        # No dependency loop found
    +        return None
    +
    +    if sym._visited == 2:
    +        # The symbol was checked earlier and is already known to not be 
part of
    +        # a dependency loop
    +        return None
    +
    +    # sym._visited == 1, found a dependency loop. Return the symbol as the
    +    # first element in it.
    +    return (sym,)
    +
    +
    +def _check_dep_loop_choice(choice, skip):
    +    if not choice._visited:
    +        # choice._visited == 0, unvisited
    +
    +        choice._visited = 1
    +
    +        # Check for loops involving choice symbols. If we came here via a
    +        # choice symbol, skip that one, as we'd get a false positive
    +        # '<sym FOO> -> <choice> -> <sym FOO>' loop otherwise.
    +        for sym in choice.syms:
    +            if sym is not skip:
    +                # Prevent the choice from being immediately re-entered via 
the
    +                # "is a choice symbol" path by passing True
    +                loop = _check_dep_loop_sym(sym, True)
    +                if loop:
    +                    # Dependency loop found
    +                    return _found_dep_loop(loop, choice)
    +
    +        # The choice is not part of a dependency loop
    +        choice._visited = 2
    +
    +        # No dependency loop found
    +        return None
    +
    +    if choice._visited == 2:
    +        # The choice was checked earlier and is already known to not be 
part of
    +        # a dependency loop
    +        return None
    +
    +    # choice._visited == 1, found a dependency loop. Return the choice as 
the
    +    # first element in it.
    +    return (choice,)
    +
    +
    +def _found_dep_loop(loop, cur):
    +    # Called "on the way back" when we know we have a loop
    +
    +    # Is the symbol/choice 'cur' where the loop started?
    +    if cur is not loop[0]:
    +        # Nope, it's just a part of the loop
    +        return loop + (cur,)
    +
    +    # Yep, we have the entire loop. Throw an exception that shows it.
    +
    +    msg = "\nDependency loop\n" \
    +            "===============\n\n"
    +
    +    for item in loop:
    +        if item is not loop[0]:
    +            msg += "...depends on "
    +            if item.__class__ is Symbol and item.choice:
    +                msg += "the choice symbol "
    +
    +        msg += "{}, with definition...\n\n{}\n\n" \
    +               .format(_name_and_loc(item), item)
    +
    +        # Small wart: Since we reuse the already calculated
    +        # Symbol/Choice._dependents sets for recursive dependency 
detection, we
    +        # lose information on whether a dependency came from a 
'select'/'imply'
    +        # condition or e.g. a 'depends on'.
    +        #
    +        # This might cause selecting symbols to "disappear". For example,
    +        # a symbol B having 'select A if C' gives a direct dependency from 
A to
    +        # C, since it corresponds to a reverse dependency of B && C.
    +        #
    +        # Always print reverse dependencies for symbols that have them to 
make
    +        # sure information isn't lost. I wonder if there's some neat way to
    +        # improve this.
    +
    +        if item.__class__ is Symbol:
    +            if item.rev_dep is not item.kconfig.n:
    +                msg += "(select-related dependencies: {})\n\n" \
    +                       .format(expr_str(item.rev_dep))
    +
    +            if item.weak_rev_dep is not item.kconfig.n:
    +                msg += "(imply-related dependencies: {})\n\n" \
    +                       .format(expr_str(item.rev_dep))
    +
    +    msg += "...depends again on {}".format(_name_and_loc(loop[0]))
    +
    +    raise KconfigError(msg)
    +
    +
    +def _decoding_error(e, filename, macro_linenr=None):
    +    # Gives the filename and context for UnicodeDecodeError's, which are a 
pain
    +    # to debug otherwise. 'e' is the UnicodeDecodeError object.
    +    #
    +    # If the decoding error is for the output of a $(shell,...) command,
    +    # macro_linenr holds the line number where it was run (the exact line
    +    # number isn't available for decoding errors in files).
    +
    +    raise KconfigError(
    +        "\n"
    +        "Malformed {} in {}\n"
    +        "Context: {}\n"
    +        "Problematic data: {}\n"
    +        "Reason: {}".format(
    +            e.encoding,
    +            "'{}'".format(filename) if macro_linenr is None else
    +                "output from macro at {}:{}".format(filename, 
macro_linenr),
    +            e.object[max(e.start - 40, 0):e.end + 40],
    +            e.object[e.start:e.end],
    +            e.reason))
    +
    +
    +def _warn_verbose_deprecated(fn_name):
    +    sys.stderr.write(
    +        "Deprecation warning: {0}()'s 'verbose' argument has no effect. 
Since "
    +        "Kconfiglib 12.0.0, the message is returned from {0}() instead, "
    +        "and is always generated. Do e.g. print(kconf.{0}()) if you want 
to "
    +        "want to show a message like \"Loaded configuration '.config'\" on 
"
    +        "stdout. The old API required ugly hacks to reuse messages in "
    +        "configuration interfaces.\n".format(fn_name))
    +
    +
    +# Predefined preprocessor functions
    +
    +
    +def _filename_fn(kconf, _):
    +    return kconf._filename
    +
    +
    +def _lineno_fn(kconf, _):
    +    return str(kconf._linenr)
    +
    +
    +def _info_fn(kconf, _, msg):
    +    print("{}:{}: {}".format(kconf._filename, kconf._linenr, msg))
    +
    +    return ""
    +
    +
    +def _warning_if_fn(kconf, _, cond, msg):
    +    if cond == "y":
    +        kconf._warn(msg, kconf._filename, kconf._linenr)
    +
    +    return ""
    +
    +
    +def _error_if_fn(kconf, _, cond, msg):
    +    if cond == "y":
    +        raise KconfigError("{}:{}: {}".format(
    +            kconf._filename, kconf._linenr, msg))
    +
    +    return ""
    +
    +
    +def _shell_fn(kconf, _, command):
    +    # Only import as needed, to save some startup time
    +    import subprocess
    +
    +    stdout, stderr = subprocess.Popen(
    +        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    +    ).communicate()
    +
    +    if not _IS_PY2:
    +        try:
    +            stdout = stdout.decode(kconf._encoding)
    +            stderr = stderr.decode(kconf._encoding)
    +        except UnicodeDecodeError as e:
    +            _decoding_error(e, kconf._filename, kconf._linenr)
    +
    +    if stderr:
    +        kconf._warn("'{}' wrote to stderr: {}".format(
    +                        command, "\n".join(stderr.splitlines())),
    +                    kconf._filename, kconf._linenr)
    +
    +    # Universal newlines with splitlines() (to prevent e.g. stray \r's in
    +    # command output on Windows), trailing newline removal, and
    +    # newline-to-space conversion.
    +    #
    +    # On Python 3 versions before 3.6, it's not possible to specify the
    +    # encoding when passing universal_newlines=True to Popen() (the 
'encoding'
    +    # parameter was added in 3.6), so we do this manual version instead.
    +    return "\n".join(stdout.splitlines()).rstrip("\n").replace("\n", " ")
    +
    +#
    +# Global constants
    +#
    +
    +TRI_TO_STR = {
    +    0: "n",
    +    1: "m",
    +    2: "y",
    +}
    +
    +STR_TO_TRI = {
    +    "n": 0,
    +    "m": 1,
    +    "y": 2,
    +}
    +
    +# Constant representing that there's no cached choice selection. This is
    +# distinct from a cached None (no selection). Any object that's not None 
or a
    +# Symbol will do. We test this with 'is'.
    +_NO_CACHED_SELECTION = 0
    +
    +# Are we running on Python 2?
    +_IS_PY2 = sys.version_info[0] < 3
    +
    +try:
    +    _UNAME_RELEASE = os.uname()[2]
    +except AttributeError:
    +    # Only import as needed, to save some startup time
    +    import platform
    +    _UNAME_RELEASE = platform.uname()[2]
    +
    +# The token and type constants below are safe to test with 'is', which is 
a bit
    +# faster (~30% faster on my machine, and a few % faster for total parsing
    +# time), even without assuming Python's small integer optimization (which
    +# caches small integer objects). The constants end up pointing to unique
    +# integer objects, and since we consistently refer to them via the names 
below,
    +# we always get the same object.
    +#
    +# Client code should use == though.
    +
    +# Tokens, with values 1, 2, ... . Avoiding 0 simplifies some checks by 
making
    +# all tokens except empty strings truthy.
    +(
    +    _T_ALLNOCONFIG_Y,
    +    _T_AND,
    +    _T_BOOL,
    +    _T_CHOICE,
    +    _T_CLOSE_PAREN,
    +    _T_COMMENT,
    +    _T_CONFIG,
    +    _T_DEFAULT,
    +    _T_DEFCONFIG_LIST,
    +    _T_DEF_BOOL,
    +    _T_DEF_HEX,
    +    _T_DEF_INT,
    +    _T_DEF_STRING,
    +    _T_DEF_TRISTATE,
    +    _T_DEPENDS,
    +    _T_ENDCHOICE,
    +    _T_ENDIF,
    +    _T_ENDMENU,
    +    _T_ENV,
    +    _T_EQUAL,
    +    _T_GREATER,
    +    _T_GREATER_EQUAL,
    +    _T_HELP,
    +    _T_HEX,
    +    _T_IF,
    +    _T_IMPLY,
    +    _T_INT,
    +    _T_LESS,
    +    _T_LESS_EQUAL,
    +    _T_MAINMENU,
    +    _T_MENU,
    +    _T_MENUCONFIG,
    +    _T_MODULES,
    +    _T_NOT,
    +    _T_ON,
    +    _T_OPEN_PAREN,
    +    _T_OPTION,
    +    _T_OPTIONAL,
    +    _T_OR,
    +    _T_ORSOURCE,
    +    _T_OSOURCE,
    +    _T_PROMPT,
    +    _T_RANGE,
    +    _T_RSOURCE,
    +    _T_SELECT,
    +    _T_SOURCE,
    +    _T_STRING,
    +    _T_TRISTATE,
    +    _T_UNEQUAL,
    +    _T_VISIBLE,
    +) = range(1, 51)
    +
    +# Keyword to token map, with the get() method assigned directly as a small
    +# optimization
    +_get_keyword = {
    +    "---help---":     _T_HELP,
    +    "allnoconfig_y":  _T_ALLNOCONFIG_Y,
    +    "bool":           _T_BOOL,
    +    "boolean":        _T_BOOL,
    +    "choice":         _T_CHOICE,
    +    "comment":        _T_COMMENT,
    +    "config":         _T_CONFIG,
    +    "def_bool":       _T_DEF_BOOL,
    +    "def_hex":        _T_DEF_HEX,
    +    "def_int":        _T_DEF_INT,
    +    "def_string":     _T_DEF_STRING,
    +    "def_tristate":   _T_DEF_TRISTATE,
    +    "default":        _T_DEFAULT,
    +    "defconfig_list": _T_DEFCONFIG_LIST,
    +    "depends":        _T_DEPENDS,
    +    "endchoice":      _T_ENDCHOICE,
    +    "endif":          _T_ENDIF,
    +    "endmenu":        _T_ENDMENU,
    +    "env":            _T_ENV,
    +    "grsource":       _T_ORSOURCE,  # Backwards compatibility
    +    "gsource":        _T_OSOURCE,   # Backwards compatibility
    +    "help":           _T_HELP,
    +    "hex":            _T_HEX,
    +    "if":             _T_IF,
    +    "imply":          _T_IMPLY,
    +    "int":            _T_INT,
    +    "mainmenu":       _T_MAINMENU,
    +    "menu":           _T_MENU,
    +    "menuconfig":     _T_MENUCONFIG,
    +    "modules":        _T_MODULES,
    +    "on":             _T_ON,
    +    "option":         _T_OPTION,
    +    "optional":       _T_OPTIONAL,
    +    "orsource":       _T_ORSOURCE,
    +    "osource":        _T_OSOURCE,
    +    "prompt":         _T_PROMPT,
    +    "range":          _T_RANGE,
    +    "rsource":        _T_RSOURCE,
    +    "select":         _T_SELECT,
    +    "source":         _T_SOURCE,
    +    "string":         _T_STRING,
    +    "tristate":       _T_TRISTATE,
    +    "visible":        _T_VISIBLE,
    +}.get
    +
    +# The constants below match the value of the corresponding tokens to 
remove the
    +# need for conversion
    +
    +# Node types
    +MENU    = _T_MENU
    +COMMENT = _T_COMMENT
    +
    +# Expression types
    +AND           = _T_AND
    +OR            = _T_OR
    +NOT           = _T_NOT
    +EQUAL         = _T_EQUAL
    +UNEQUAL       = _T_UNEQUAL
    +LESS          = _T_LESS
    +LESS_EQUAL    = _T_LESS_EQUAL
    +GREATER       = _T_GREATER
    +GREATER_EQUAL = _T_GREATER_EQUAL
    +
    +REL_TO_STR = {
    +    EQUAL:         "=",
    +    UNEQUAL:       "!=",
    +    LESS:          "<",
    +    LESS_EQUAL:    "<=",
    +    GREATER:       ">",
    +    GREATER_EQUAL: ">=",
    +}
    +
    +# Symbol/choice types. UNKNOWN is 0 (falsy) to simplify some checks.
    +# Client code shouldn't rely on it though, as it was non-zero in
    +# older versions.
    +UNKNOWN  = 0
    +BOOL     = _T_BOOL
    +TRISTATE = _T_TRISTATE
    +STRING   = _T_STRING
    +INT      = _T_INT
    +HEX      = _T_HEX
    +
    +TYPE_TO_STR = {
    +    UNKNOWN:  "unknown",
    +    BOOL:     "bool",
    +    TRISTATE: "tristate",
    +    STRING:   "string",
    +    INT:      "int",
    +    HEX:      "hex",
    +}
    +
    +# Used in comparisons. 0 means the base is inferred from the format of the
    +# string.
    +_TYPE_TO_BASE = {
    +    HEX:      16,
    +    INT:      10,
    +    STRING:   0,
    +    UNKNOWN:  0,
    +}
    +
    +# def_bool -> BOOL, etc.
    +_DEF_TOKEN_TO_TYPE = {
    +    _T_DEF_BOOL:     BOOL,
    +    _T_DEF_HEX:      HEX,
    +    _T_DEF_INT:      INT,
    +    _T_DEF_STRING:   STRING,
    +    _T_DEF_TRISTATE: TRISTATE,
    +}
    +
    +# Tokens after which strings are expected. This is used to tell strings 
from
    +# constant symbol references during tokenization, both of which are 
enclosed in
    +# quotes.
    +#
    +# Identifier-like lexemes ("missing quotes") are also treated as strings 
after
    +# these tokens. _T_CHOICE is included to avoid symbols being registered for
    +# named choices.
    +_STRING_LEX = frozenset({
    +    _T_BOOL,
    +    _T_CHOICE,
    +    _T_COMMENT,
    +    _T_HEX,
    +    _T_INT,
    +    _T_MAINMENU,
    +    _T_MENU,
    +    _T_ORSOURCE,
    +    _T_OSOURCE,
    +    _T_PROMPT,
    +    _T_RSOURCE,
    +    _T_SOURCE,
    +    _T_STRING,
    +    _T_TRISTATE,
    +})
    +
    +# Various sets for quick membership tests. Gives a single global lookup and
    +# avoids creating temporary dicts/tuples.
    +
    +_TYPE_TOKENS = frozenset({
    +    _T_BOOL,
    +    _T_TRISTATE,
    +    _T_INT,
    +    _T_HEX,
    +    _T_STRING,
    +})
    +
    +_SOURCE_TOKENS = frozenset({
    +    _T_SOURCE,
    +    _T_RSOURCE,
    +    _T_OSOURCE,
    +    _T_ORSOURCE,
    +})
    +
    +_REL_SOURCE_TOKENS = frozenset({
    +    _T_RSOURCE,
    +    _T_ORSOURCE,
    +})
    +
    +# Obligatory (non-optional) sources
    +_OBL_SOURCE_TOKENS = frozenset({
    +    _T_SOURCE,
    +    _T_RSOURCE,
    +})
    +
    +_BOOL_TRISTATE = frozenset({
    +    BOOL,
    +    TRISTATE,
    +})
    +
    +_BOOL_TRISTATE_UNKNOWN = frozenset({
    +    BOOL,
    +    TRISTATE,
    +    UNKNOWN,
    +})
    +
    +_INT_HEX = frozenset({
    +    INT,
    +    HEX,
    +})
    +
    +_SYMBOL_CHOICE = frozenset({
    +    Symbol,
    +    Choice,
    +})
    +
    +_MENU_COMMENT = frozenset({
    +    MENU,
    +    COMMENT,
    +})
    +
    +_EQUAL_UNEQUAL = frozenset({
    +    EQUAL,
    +    UNEQUAL,
    +})
    +
    +_RELATIONS = frozenset({
    +    EQUAL,
    +    UNEQUAL,
    +    LESS,
    +    LESS_EQUAL,
    +    GREATER,
    +    GREATER_EQUAL,
    +})
    +
    +# Helper functions for getting compiled regular expressions, with the 
needed
    +# matching function returned directly as a small optimization.
    +#
    +# Use ASCII regex matching on Python 3. It's already the default on Python 
2.
    +
    +
    +def _re_match(regex):
    +    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).match
    +
    +
    +def _re_search(regex):
    +    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).search
    +
    +
    +# Various regular expressions used during parsing
    +
    +# The initial token on a line. Also eats leading and trailing whitespace, 
so
    +# that we can jump straight to the next token (or to the end of the line if
    +# there is only one token).
    +#
    +# This regex will also fail to match for empty lines and comment lines.
    +#
    +# '$' is included to detect preprocessor variable assignments with macro
    +# expansions in the left-hand side.
    +_command_match = _re_match(r"\s*([A-Za-z0-9_$-]+)\s*")
    +
    +# An identifier/keyword after the first token. Also eats trailing 
whitespace.
    +# '$' is included to detect identifiers containing macro expansions.
    +_id_keyword_match = _re_match(r"([A-Za-z0-9_$/.-]+)\s*")
    +
    +# A fragment in the left-hand side of a preprocessor variable assignment. 
These
    +# are the portions between macro expansions ($(foo)). Macros are supported 
in
    +# the LHS (variable name).
    +_assignment_lhs_fragment_match = _re_match("[A-Za-z0-9_-]*")
    +
    +# The assignment operator and value (right-hand side) in a preprocessor
    +# variable assignment
    +_assignment_rhs_match = _re_match(r"\s*(=|:=|\+=)\s*(.*)")
    +
    +# Special characters/strings while expanding a macro (')', ',', and '$(')
    +_macro_special_search = _re_search(r"\)|,|\$\(")
    +
    +# Special characters/strings while expanding a string (quotes, '\', and 
'$(')
    +_string_special_search = _re_search(r'"|\'|\\|\$\(')
    +
    +# Special characters/strings while expanding a symbol name. Also includes
    +# end-of-line, in case the macro is the last thing on the line.
    +_name_special_search = _re_search(r'[^A-Za-z0-9_$/.-]|\$\(|$')
    +
    +# A valid right-hand side for an assignment to a string symbol in a .config
    +# file, including escaped characters. Extracts the contents.
    +_conf_string_match = _re_match(r'"((?:[^\\"]|\\.)*)"')
    diff --git a/support/kconfiglib/listnewconfig.py 
b/support/kconfiglib/listnewconfig.py
    new file mode 100755
    index 0000000..59de141
    --- /dev/null
    +++ b/support/kconfiglib/listnewconfig.py
    @@ -0,0 +1,47 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +List all user-modifiable symbols that are not given a value in the 
configuration
    +file. Usually, these are new symbols that have been added to the Kconfig 
files.
    +
    +The default configuration filename is '.config'. A different filename can 
be
    +passed in the KCONFIG_CONFIG environment variable.
    +"""
    +import sys
    +
    +from kconfiglib import standard_kconfig, BOOL, TRISTATE, INT, HEX, STRING, 
\
    +                       TRI_TO_STR
    +
    +
    +def main():
    +    kconf = standard_kconfig()
    +    # Make it possible to filter this message out
    +    sys.stderr.write(kconf.load_config() + "\n")
    +
    +    for sym in kconf.unique_defined_syms:
    +        # Only show symbols that can be toggled. Choice symbols are a 
special
    +        # case in that sym.assignable will be (2,) (length 1) for visible
    +        # symbols in choices in y mode, but they can still be toggled by
    +        # selecting some other symbol.
    +        if sym.user_value is None and \
    +           (len(sym.assignable) > 1 or
    +            (sym.visibility and (sym.orig_type in (INT, HEX, STRING) or
    +                                 sym.choice))):
    +
    +            # Don't reuse the 'config_string' format for bool/tristate 
symbols,
    +            # to show n-valued symbols as 'CONFIG_FOO=n' instead of
    +            # '# CONFIG_FOO is not set'. This matches the C tools.
    +            if sym.orig_type in (BOOL, TRISTATE):
    +                s = "{}{}={}\n".format(kconf.config_prefix, sym.name,
    +                                       TRI_TO_STR[sym.tri_value])
    +            else:
    +                s = sym.config_string
    +
    +            sys.stdout.write(s)
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/makefile.patch 
b/support/kconfiglib/makefile.patch
    new file mode 100644
    index 0000000..a617ebd
    --- /dev/null
    +++ b/support/kconfiglib/makefile.patch
    @@ -0,0 +1,48 @@
    +From 93daf46f309b0c8f86149ef58c4906387d054c22 Mon Sep 17 00:00:00 2001
    +From: Ulf Magnusson <ulfalizer@xxxxxxxxx>
    +Date: Tue, 9 Jun 2015 13:01:34 +0200
    +Subject: [PATCH] Kconfiglib scripts/kconfig/Makefile patch
    +
    +---
    + scripts/kconfig/Makefile | 29 +++++++++++++++++++++++++++++
    + 1 file changed, 29 insertions(+)
    +
    +diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
    +index 3f327e21f60e..8b7dd1292005 100644
    +--- a/scripts/kconfig/Makefile
    ++++ b/scripts/kconfig/Makefile
    +@@ -27,2 +27,31 @@ gconfig: $(obj)/gconf
    + 
    ++PHONY += scriptconfig iscriptconfig kmenuconfig guiconfig dumpvarsconfig
    ++
    ++PYTHONCMD ?= python
    ++kpython := PYTHONPATH=$(srctree)/Kconfiglib:$$PYTHONPATH $(PYTHONCMD)
    ++
    ++ifneq ($(filter scriptconfig,$(MAKECMDGOALS)),)
    ++ifndef SCRIPT
    ++$(error Use "make scriptconfig SCRIPT=<path to script> 
[SCRIPT_ARG=<argument>]")
    ++endif
    ++endif
    ++
    ++scriptconfig:
    ++  $(Q)$(kpython) $(SCRIPT) $(Kconfig) $(if $(SCRIPT_ARG),"$(SCRIPT_ARG)")
    ++
    ++iscriptconfig:
    ++  $(Q)$(kpython) -i -c \
    ++    "import kconfiglib; \
    ++     kconf = kconfiglib.Kconfig('$(Kconfig)'); \
    ++     print('A Kconfig instance \'kconf\' for the architecture $(ARCH) has 
been created.')"
    ++
    ++kmenuconfig:
    ++  $(Q)$(kpython) $(srctree)/Kconfiglib/menuconfig.py $(Kconfig)
    ++
    ++guiconfig:
    ++  $(Q)$(kpython) $(srctree)/Kconfiglib/guiconfig.py $(Kconfig)
    ++
    ++dumpvarsconfig:
    ++  $(Q)$(kpython) $(srctree)/Kconfiglib/examples/dumpvars.py $(Kconfig)
    ++
    + menuconfig: $(obj)/mconf
    +-- 
    +2.20.1
    +
    diff --git a/support/kconfiglib/menuconfig.py 
b/support/kconfiglib/menuconfig.py
    new file mode 100755
    index 0000000..4815c7f
    --- /dev/null
    +++ b/support/kconfiglib/menuconfig.py
    @@ -0,0 +1,3264 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Nordic Semiconductor ASA and Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Overview
    +========
    +
    +A curses-based Python 2/3 menuconfig implementation. The interface should 
feel
    +familiar to people used to mconf ('make menuconfig').
    +
    +Supports the same keys as mconf, and also supports a set of keybindings
    +inspired by Vi:
    +
    +  J/K     : Down/Up
    +  L       : Enter menu/Toggle item
    +  H       : Leave menu
    +  Ctrl-D/U: Page Down/Page Up
    +  G/End   : Jump to end of list
    +  g/Home  : Jump to beginning of list
    +
    +[Space] toggles values if possible, and enters menus otherwise. [Enter] 
works
    +the other way around.
    +
    +The mconf feature where pressing a key jumps to a menu entry with that
    +character in it in the current menu isn't supported. A jump-to feature for
    +jumping directly to any symbol (including invisible symbols), choice, menu 
or
    +comment (as in a Kconfig 'comment "Foo"') is available instead.
    +
    +A few different modes are available:
    +
    +  F: Toggle show-help mode, which shows the help text of the currently 
selected
    +  item in the window at the bottom of the menu display. This is handy when
    +  browsing through options.
    +
    +  C: Toggle show-name mode, which shows the symbol name before each symbol 
menu
    +  entry
    +
    +  A: Toggle show-all mode, which shows all items, including currently 
invisible
    +  items and items that lack a prompt. Invisible items are drawn in a 
different
    +  style to make them stand out.
    +
    +
    +Running
    +=======
    +
    +menuconfig.py can be run either as a standalone executable or by calling 
the
    +menuconfig() function with an existing Kconfig instance. The second option 
is a
    +bit inflexible in that it will still load and save .config, etc.
    +
    +When run in standalone mode, the top-level Kconfig file to load can be 
passed
    +as a command-line argument. With no argument, it defaults to "Kconfig".
    +
    +The KCONFIG_CONFIG environment variable specifies the .config file to load 
(if
    +it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
    +
    +When overwriting a configuration file, the old version is saved to
    +<filename>.old (e.g. .config.old).
    +
    +$srctree is supported through Kconfiglib.
    +
    +
    +Color schemes
    +=============
    +
    +It is possible to customize the color scheme by setting the 
MENUCONFIG_STYLE
    +environment variable. For example, setting it to 'aquatic' will enable an
    +alternative, less yellow, more 'make menuconfig'-like color scheme, 
contributed
    +by Mitja Horvat (pinkfluid).
    +
    +This is the current list of built-in styles:
    +    - default       classic Kconfiglib theme with a yellow accent
    +    - monochrome    colorless theme (uses only bold and standout) 
attributes,
    +                    this style is used if the terminal doesn't support 
colors
    +    - aquatic       blue tinted style loosely resembling the lxdialog theme
    +
    +It is possible to customize the current style by changing colors of UI
    +elements on the screen. This is the list of elements that can be stylized:
    +
    +    - path          Top row in the main display, with the menu path
    +    - separator     Separator lines between windows. Also used for the top 
line
    +                    in the symbol information display.
    +    - list          List of items, e.g. the main display
    +    - selection     Style for the selected item
    +    - inv-list      Like list, but for invisible items. Used in show-all 
mode.
    +    - inv-selection Like selection, but for invisible items. Used in 
show-all
    +                    mode.
    +    - help          Help text windows at the bottom of various fullscreen
    +                    dialogs
    +    - show-help     Window showing the help text in show-help mode
    +    - frame         Frame around dialog boxes
    +    - body          Body of dialog boxes
    +    - edit          Edit box in pop-up dialogs
    +    - jump-edit     Edit box in jump-to dialog
    +    - text          Symbol information text
    +
    +The color definition is a comma separated list of attributes:
    +
    +    - fg:COLOR      Set the foreground/background colors. COLOR can be one 
of
    +      * or *        the basic 16 colors (black, red, green, yellow, blue,
    +    - bg:COLOR      magenta, cyan, white and brighter versions, for 
example,
    +                    brightred). On terminals that support more than 8 
colors,
    +                    you can also directly put in a color number, e.g. 
fg:123
    +                    (hexadecimal and octal constants are accepted as well).
    +                    Colors outside the range -1..curses.COLORS-1 (which is
    +                    terminal-dependent) are ignored (with a warning). The 
COLOR
    +                    can be also specified using a RGB value in the HTML
    +                    notation, for example #RRGGBB. If the terminal supports
    +                    color changing, the color is rendered accurately.
    +                    Otherwise, the visually nearest color is used.
    +
    +                    If the background or foreground color of an element is 
not
    +                    specified, it defaults to -1, representing the default
    +                    terminal foreground or background color.
    +
    +                    Note: On some terminals a bright version of the color
    +                    implies bold.
    +    - bold          Use bold text
    +    - underline     Use underline text
    +    - standout      Standout text attribute (reverse color)
    +
    +More often than not, some UI elements share the same color definition. In 
such
    +cases the right value may specify an UI element from which the color 
definition
    +will be copied. For example, "separator=help" will apply the current color
    +definition for "help" to "separator".
    +
    +A keyword without the '=' is assumed to be a style template. The template 
name
    +is looked up in the built-in styles list and the style definition is 
expanded
    +in-place. With this, built-in styles can be used as basis for new styles.
    +
    +For example, take the aquatic theme and give it a red selection bar:
    +
    +MENUCONFIG_STYLE="aquatic selection=fg:white,bg:red"
    +
    +If there's an error in the style definition or if a missing style is 
assigned
    +to, the assignment will be ignored, along with a warning being printed on
    +stderr.
    +
    +The 'default' theme is always implicitly parsed first (or the 'monochrome'
    +theme if the terminal lacks colors), so the following two settings have the
    +same effect:
    +
    +    MENUCONFIG_STYLE="selection=fg:white,bg:red"
    +    MENUCONFIG_STYLE="default selection=fg:white,bg:red"
    +
    +
    +Other features
    +==============
    +
    +  - Seamless terminal resizing
    +
    +  - No dependencies on *nix, as the 'curses' module is in the Python 
standard
    +    library
    +
    +  - Unicode text entry
    +
    +  - Improved information screen compared to mconf:
    +
    +      * Expressions are split up by their top-level &&/|| operands to 
improve
    +        readability
    +
    +      * Undefined symbols in expressions are pointed out
    +
    +      * Menus and comments have information displays
    +
    +      * Kconfig definitions are printed
    +
    +      * The include path is shown, listing the locations of the 'source'
    +        statements that included the Kconfig file of the symbol (or other
    +        item)
    +
    +
    +Limitations
    +===========
    +
    +Doesn't work out of the box on Windows, but can be made to work with 'pip
    +install windows-curses'. See the
    +https://github.com/zephyrproject-rtos/windows-curses repository.
    +
    +'pip install kconfiglib' on Windows automatically installs windows-curses
    +to make the menuconfig usable.
    +"""
    +from __future__ import print_function
    +
    +import curses
    +import errno
    +import locale
    +import os
    +import re
    +import sys
    +import textwrap
    +
    +from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
    +                       BOOL, TRISTATE, STRING, INT, HEX, \
    +                       AND, OR, \
    +                       expr_str, expr_value, split_expr, \
    +                       standard_sc_expr_str, \
    +                       TRI_TO_STR, TYPE_TO_STR, \
    +                       standard_kconfig, standard_config_filename
    +
    +
    +#
    +# Configuration variables
    +#
    +
    +# If True, try to change LC_CTYPE to a UTF-8 locale if it is set to the C
    +# locale (which implies ASCII). This fixes curses Unicode I/O issues on 
systems
    +# with bad defaults. ncurses configures itself from the locale settings.
    +#
    +# Related PEP: https://www.python.org/dev/peps/pep-0538/
    +_CHANGE_C_LC_CTYPE_TO_UTF8 = True
    +
    +# How many steps an implicit submenu will be indented. Implicit submenus 
are
    +# created when an item depends on the symbol before it. Note that symbols
    +# defined with 'menuconfig' create a separate menu instead of indenting.
    +_SUBMENU_INDENT = 4
    +
    +# Number of steps for Page Up/Down to jump
    +_PG_JUMP = 6
    +
    +# Height of the help window in show-help mode
    +_SHOW_HELP_HEIGHT = 8
    +
    +# How far the cursor needs to be from the edge of the window before it 
starts
    +# to scroll. Used for the main menu display, the information display, the
    +# search display, and for text boxes.
    +_SCROLL_OFFSET = 5
    +
    +# Minimum width of dialogs that ask for text input
    +_INPUT_DIALOG_MIN_WIDTH = 30
    +
    +# Number of arrows pointing up/down to draw when a window is scrolled
    +_N_SCROLL_ARROWS = 14
    +
    +# Lines of help text shown at the bottom of the "main" display
    +_MAIN_HELP_LINES = """
    +[Space/Enter] Toggle/enter  [ESC] Leave menu           [S] Save
    +[O] Load                    [?] Symbol info            [/] Jump to symbol
    +[F] Toggle show-help mode   [C] Toggle show-name mode  [A] Toggle show-all 
mode
    +[Q] Quit (prompts for save) [D] Save minimal config (advanced)
    +"""[1:-1].split("\n")
    +
    +# Lines of help text shown at the bottom of the information dialog
    +_INFO_HELP_LINES = """
    +[ESC/q] Return to menu      [/] Jump to symbol
    +"""[1:-1].split("\n")
    +
    +# Lines of help text shown at the bottom of the search dialog
    +_JUMP_TO_HELP_LINES = """
    +Type text to narrow the search. Regexes are supported (via Python's 're'
    +module). The up/down cursor keys step in the list. [Enter] jumps to the
    +selected symbol. [ESC] aborts the search. Type multiple space-separated
    +strings/regexes to find entries that match all of them. Type Ctrl-F to
    +view the help of the selected item without leaving the dialog.
    +"""[1:-1].split("\n")
    +
    +#
    +# Styling
    +#
    +
    +_STYLES = {
    +    "default": """
    +    path=fg:black,bg:white,bold
    +    separator=fg:black,bg:yellow,bold
    +    list=fg:black,bg:white
    +    selection=fg:white,bg:blue,bold
    +    inv-list=fg:red,bg:white
    +    inv-selection=fg:red,bg:blue
    +    help=path
    +    show-help=list
    +    frame=fg:black,bg:yellow,bold
    +    body=fg:white,bg:black
    +    edit=fg:white,bg:blue
    +    jump-edit=edit
    +    text=list
    +    """,
    +
    +    # This style is forced on terminals that do no support colors
    +    "monochrome": """
    +    path=bold
    +    separator=bold,standout
    +    list=
    +    selection=bold,standout
    +    inv-list=bold
    +    inv-selection=bold,standout
    +    help=bold
    +    show-help=
    +    frame=bold,standout
    +    body=
    +    edit=standout
    +    jump-edit=
    +    text=
    +    """,
    +
    +    # Blue tinted style loosely resembling lxdialog
    +    "aquatic": """
    +    path=fg:cyan,bg:blue,bold
    +    separator=fg:white,bg:cyan,bold
    +    help=path
    +    frame=fg:white,bg:cyan,bold
    +    body=fg:brightwhite,bg:blue
    +    edit=fg:black,bg:white
    +    """
    +}
    +
    +# Standard colors definition
    +_STYLE_STD_COLORS = {
    +    # Basic colors
    +    "black":         curses.COLOR_BLACK,
    +    "red":           curses.COLOR_RED,
    +    "green":         curses.COLOR_GREEN,
    +    "yellow":        curses.COLOR_YELLOW,
    +    "blue":          curses.COLOR_BLUE,
    +    "magenta":       curses.COLOR_MAGENTA,
    +    "cyan":          curses.COLOR_CYAN,
    +    "white":         curses.COLOR_WHITE,
    +
    +    # Bright versions
    +    "brightblack":   curses.COLOR_BLACK + 8,
    +    "brightred":     curses.COLOR_RED + 8,
    +    "brightgreen":   curses.COLOR_GREEN + 8,
    +    "brightyellow":  curses.COLOR_YELLOW + 8,
    +    "brightblue":    curses.COLOR_BLUE + 8,
    +    "brightmagenta": curses.COLOR_MAGENTA + 8,
    +    "brightcyan":    curses.COLOR_CYAN + 8,
    +    "brightwhite":   curses.COLOR_WHITE + 8,
    +
    +    # Aliases
    +    "purple":        curses.COLOR_MAGENTA,
    +    "brightpurple":  curses.COLOR_MAGENTA + 8,
    +}
    +
    +
    +def _rgb_to_6cube(rgb):
    +    # Converts an 888 RGB color to a 3-tuple (nice in that it's hashable)
    +    # representing the closest xterm 256-color 6x6x6 color cube color.
    +    #
    +    # The xterm 256-color extension uses a RGB color palette with 
components in
    +    # the range 0-5 (a 6x6x6 cube). The catch is that the mapping is 
nonlinear.
    +    # Index 0 in the 6x6x6 cube is mapped to 0, index 1 to 95, then 135, 
175,
    +    # etc., in increments of 40. See the links below:
    +    #
    +    #   https://commons.wikimedia.org/wiki/File:Xterm_256color_chart.svg
    +    #   https://github.com/tmux/tmux/blob/master/colour.c
    +
    +    # 48 is the middle ground between 0 and 95.
    +    return tuple(0 if x < 48 else int(round(max(1, (x - 55)/40))) for x in 
rgb)
    +
    +
    +def _6cube_to_rgb(r6g6b6):
    +    # Returns the 888 RGB color for a 666 xterm color cube index
    +
    +    return tuple(0 if x == 0 else 40*x + 55 for x in r6g6b6)
    +
    +
    +def _rgb_to_gray(rgb):
    +    # Converts an 888 RGB color to the index of an xterm 256-color 
grayscale
    +    # color with approx. the same perceived brightness
    +
    +    # Calculate the luminance (gray intensity) of the color. See
    +    #   
https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
    +    # and
    +    #   https://www.w3.org/TR/AERT/#color-contrast
    +    luma = 0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]
    +
    +    # Closest index in the grayscale palette, which starts at RGB 0x080808,
    +    # with stepping 0x0A0A0A
    +    index = int(round((luma - 8)/10))
    +
    +    # Clamp the index to 0-23, corresponding to 232-255
    +    return max(0, min(index, 23))
    +
    +
    +def _gray_to_rgb(index):
    +    # Convert a grayscale index to its closet single RGB component
    +
    +    return 3*(10*index + 8,)  # Returns a 3-tuple
    +
    +
    +# Obscure Python: We never pass a value for rgb2index, and it keeps 
pointing to
    +# the same dict. This avoids a global.
    +def _alloc_rgb(rgb, rgb2index={}):
    +    # Initialize a new entry in the xterm palette to the given RGB color,
    +    # returning its index. If the color has already been initialized, the 
index
    +    # of the existing entry is returned.
    +    #
    +    # ncurses is palette-based, so we need to overwrite palette entries to 
make
    +    # new colors.
    +    #
    +    # The colors from 0 to 15 are user-defined, and there's no way to query
    +    # their RGB values, so we better leave them untouched. Also leave any
    +    # hypothetical colors above 255 untouched (though we're unlikely to
    +    # allocate that many colors anyway).
    +
    +    if rgb in rgb2index:
    +        return rgb2index[rgb]
    +
    +    # Many terminals allow the user to customize the first 16 colors. Avoid
    +    # changing their values.
    +    color_index = 16 + len(rgb2index)
    +    if color_index >= 256:
    +        _warn("Unable to allocate new RGB color ", rgb, ". Too many colors 
"
    +              "allocated.")
    +        return 0
    +
    +    # Map each RGB component from the range 0-255 to the range 0-1000, 
which is
    +    # what curses uses
    +    curses.init_color(color_index, *(int(round(1000*x/255)) for x in rgb))
    +    rgb2index[rgb] = color_index
    +
    +    return color_index
    +
    +
    +def _color_from_num(num):
    +    # Returns the index of a color that looks like color 'num' in the xterm
    +    # 256-color palette (but that might not be 'num', if we're redefining
    +    # colors)
    +
    +    # - _alloc_rgb() won't touch the first 16 colors or any (hypothetical)
    +    #   colors above 255, so we can always return them as-is
    +    #
    +    # - If the terminal doesn't support changing color definitions, or if
    +    #   curses.COLORS < 256, _alloc_rgb() won't touch any color, and all 
colors
    +    #   can be returned as-is
    +    if num < 16 or num > 255 or not curses.can_change_color() or \
    +       curses.COLORS < 256:
    +        return num
    +
    +    # _alloc_rgb() might redefine colors, so emulate the xterm 256-color
    +    # palette by allocating new colors instead of returning color numbers
    +    # directly
    +
    +    if num < 232:
    +        num -= 16
    +        return _alloc_rgb(_6cube_to_rgb(((num//36)%6, (num//6)%6, num%6)))
    +
    +    return _alloc_rgb(_gray_to_rgb(num - 232))
    +
    +
    +def _color_from_rgb(rgb):
    +    # Returns the index of a color matching the 888 RGB color 'rgb'. The
    +    # returned color might be an ~exact match or an approximation, 
depending on
    +    # terminal capabilities.
    +
    +    # Calculates the Euclidean distance between two RGB colors
    +    def dist(r1, r2): return sum((x - y)**2 for x, y in zip(r1, r2))
    +
    +    if curses.COLORS >= 256:
    +        # Assume we're dealing with xterm's 256-color extension
    +
    +        if curses.can_change_color():
    +            # Best case -- the terminal supports changing palette entries 
via
    +            # curses.init_color(). Initialize an unused palette entry and
    +            # return it.
    +            return _alloc_rgb(rgb)
    +
    +        # Second best case -- pick between the xterm 256-color extension 
colors
    +
    +        # Closest 6-cube "color" color
    +        c6 = _rgb_to_6cube(rgb)
    +        # Closest gray color
    +        gray = _rgb_to_gray(rgb)
    +
    +        if dist(rgb, _6cube_to_rgb(c6)) < dist(rgb, _gray_to_rgb(gray)):
    +            # Use the "color" color from the 6x6x6 color palette. 
Calculate the
    +            # color number from the 6-cube index triplet.
    +            return 16 + 36*c6[0] + 6*c6[1] + c6[2]
    +
    +        # Use the color from the gray palette
    +        return 232 + gray
    +
    +    # Terminal not in xterm 256-color mode. This is probably the best we 
can
    +    # do, or is it? Submit patches. :)
    +    min_dist = float('inf')
    +    best = -1
    +    for color in range(curses.COLORS):
    +        # ncurses uses the range 0..1000. Scale that down to 0..255.
    +        d = dist(rgb, tuple(int(round(255*c/1000))
    +                            for c in curses.color_content(color)))
    +        if d < min_dist:
    +            min_dist = d
    +            best = color
    +
    +    return best
    +
    +
    +def _parse_style(style_str, parsing_default):
    +    # Parses a string with '<element>=<style>' assignments. Anything not
    +    # containing '=' is assumed to be a reference to a built-in style, 
which is
    +    # treated as if all the assignments from the style were inserted at 
that
    +    # point in the string.
    +    #
    +    # The parsing_default flag is set to True when we're implicitly 
parsing the
    +    # 'default'/'monochrome' style, to prevent warnings.
    +
    +    for sline in style_str.split():
    +        # Words without a "=" character represents a style template
    +        if "=" in sline:
    +            key, data = sline.split("=", 1)
    +
    +            # The 'default' style template is assumed to define all keys. 
We
    +            # run _style_to_curses() for non-existing keys as well, so 
that we
    +            # print warnings for errors to the right of '=' for those too.
    +            if key not in _style and not parsing_default:
    +                _warn("Ignoring non-existent style", key)
    +
    +            # If data is a reference to another key, copy its style
    +            if data in _style:
    +                _style[key] = _style[data]
    +            else:
    +                _style[key] = _style_to_curses(data)
    +
    +        elif sline in _STYLES:
    +            # Recursively parse style template. Ignore styles that don't 
exist,
    +            # for backwards/forwards compatibility.
    +            _parse_style(_STYLES[sline], parsing_default)
    +
    +        else:
    +            _warn("Ignoring non-existent style template", sline)
    +
    +# Dictionary mapping element types to the curses attributes used to display
    +# them
    +_style = {}
    +
    +
    +def _style_to_curses(style_def):
    +    # Parses a style definition string (<element>=<style>), returning
    +    # a (fg_color, bg_color, attributes) tuple.
    +
    +    def parse_color(color_def):
    +        color_def = color_def.split(":", 1)[1]
    +
    +        if color_def in _STYLE_STD_COLORS:
    +            return _color_from_num(_STYLE_STD_COLORS[color_def])
    +
    +        # HTML format, #RRGGBB
    +        if re.match("#[A-Fa-f0-9]{6}", color_def):
    +            return _color_from_rgb((
    +                int(color_def[1:3], 16),
    +                int(color_def[3:5], 16),
    +                int(color_def[5:7], 16)))
    +
    +        try:
    +            color_num = _color_from_num(int(color_def, 0))
    +        except ValueError:
    +            _warn("Ignoring color ", color_def, "that's neither predefined 
"
    +                  "nor a number")
    +
    +            return -1
    +
    +        if not -1 <= color_num < curses.COLORS:
    +            _warn("Ignoring color {}, which is outside the range "
    +                  "-1..curses.COLORS-1 (-1..{})"
    +                  .format(color_def, curses.COLORS - 1))
    +
    +            return -1
    +
    +        return color_num
    +
    +    fg_color = -1
    +    bg_color = -1
    +    attrs = 0
    +
    +    if style_def:
    +        for field in style_def.split(","):
    +            if field.startswith("fg:"):
    +                fg_color = parse_color(field)
    +            elif field.startswith("bg:"):
    +                bg_color = parse_color(field)
    +            elif field == "bold":
    +                # A_BOLD tends to produce faint and hard-to-read text on 
the
    +                # Windows console, especially with the old color scheme, 
before
    +                # the introduction of
    +                # 
https://blogs.msdn.microsoft.com/commandline/2017/08/02/updating-the-windows-console-colors/
    +                attrs |= curses.A_NORMAL if _IS_WINDOWS else curses.A_BOLD
    +            elif field == "standout":
    +                attrs |= curses.A_STANDOUT
    +            elif field == "underline":
    +                attrs |= curses.A_UNDERLINE
    +            else:
    +                _warn("Ignoring unknown style attribute", field)
    +
    +    return _style_attr(fg_color, bg_color, attrs)
    +
    +
    +def _init_styles():
    +    if curses.has_colors():
    +        curses.use_default_colors()
    +
    +    # Use the 'monochrome' style template as the base on terminals without
    +    # color
    +    _parse_style("default" if curses.has_colors() else "monochrome", True)
    +
    +    # Add any user-defined style from the environment
    +    if "MENUCONFIG_STYLE" in os.environ:
    +        _parse_style(os.environ["MENUCONFIG_STYLE"], False)
    +
    +
    +# color_attribs holds the color pairs we've already created, indexed by a
    +# (<foreground color>, <background color>) tuple.
    +#
    +# Obscure Python: We never pass a value for color_attribs, and it keeps
    +# pointing to the same dict. This avoids a global.
    +def _style_attr(fg_color, bg_color, attribs, color_attribs={}):
    +    # Returns an attribute with the specified foreground and background 
color
    +    # and the attributes in 'attribs'. Reuses color pairs already created 
if
    +    # possible, and creates a new color pair otherwise.
    +    #
    +    # Returns 'attribs' if colors aren't supported.
    +
    +    if not curses.has_colors():
    +        return attribs
    +
    +    if (fg_color, bg_color) not in color_attribs:
    +        # Create new color pair. Color pair number 0 is hardcoded and 
cannot be
    +        # changed, hence the +1s.
    +        curses.init_pair(len(color_attribs) + 1, fg_color, bg_color)
    +        color_attribs[(fg_color, bg_color)] = \
    +            curses.color_pair(len(color_attribs) + 1)
    +
    +    return color_attribs[(fg_color, bg_color)] | attribs
    +
    +
    +#
    +# Main application
    +#
    +
    +
    +def _main():
    +    menuconfig(standard_kconfig())
    +
    +
    +def menuconfig(kconf):
    +    """
    +    Launches the configuration interface, returning after the user exits.
    +
    +    kconf:
    +      Kconfig instance to be configured
    +    """
    +    global _kconf
    +    global _conf_filename
    +    global _conf_changed
    +    global _minconf_filename
    +    global _show_all
    +
    +    _kconf = kconf
    +
    +    # Filename to save configuration to
    +    _conf_filename = standard_config_filename()
    +
    +    # Load existing configuration and set _conf_changed True if it is 
outdated
    +    _conf_changed = _load_config()
    +
    +    # Filename to save minimal configuration to
    +    _minconf_filename = "defconfig"
    +
    +    # Any visible items in the top menu?
    +    _show_all = False
    +    if not _shown_nodes(kconf.top_node):
    +        # Nothing visible. Start in show-all mode and try again.
    +        _show_all = True
    +        if not _shown_nodes(kconf.top_node):
    +            # Give up. The implementation relies on always having a 
selected
    +            # node.
    +            print("Empty configuration -- nothing to configure.\n"
    +                  "Check that environment variables are set properly.")
    +            return
    +
    +    # Disable warnings. They get mangled in curses mode, and we deal with
    +    # errors ourselves.
    +    kconf.warn = False
    +
    +    # Make curses use the locale settings specified in the environment
    +    locale.setlocale(locale.LC_ALL, "")
    +
    +    # Try to fix Unicode issues on systems with bad defaults
    +    if _CHANGE_C_LC_CTYPE_TO_UTF8:
    +        _change_c_lc_ctype_to_utf8()
    +
    +    # Get rid of the delay between pressing ESC and jumping to the parent 
menu,
    +    # unless the user has set ESCDELAY (see ncurses(3)). This makes the UI 
much
    +    # smoother to work with.
    +    #
    +    # Note: This is strictly pretty iffy, since escape codes for e.g. 
cursor
    +    # keys start with ESC, but I've never seen it cause problems in 
practice
    +    # (probably because it's unlikely that the escape code for a key would 
get
    +    # split up across read()s, at least with a terminal emulator). Please
    +    # report if you run into issues. Some suitable small default value 
could be
    +    # used here instead in that case. Maybe it's silly to not put in the
    +    # smallest imperceptible delay here already, though I don't like 
guessing.
    +    #
    +    # (From a quick glance at the ncurses source code, ESCDELAY might only 
be
    +    # relevant for mouse events there, so maybe escapes are assumed to 
arrive
    +    # in one piece already...)
    +    os.environ.setdefault("ESCDELAY", "0")
    +
    +    # Enter curses mode. _menuconfig() returns a string to print on exit, 
after
    +    # curses has been de-initialized.
    +    print(curses.wrapper(_menuconfig))
    +
    +
    +def _load_config():
    +    # Loads any existing .config file. See the Kconfig.load_config() 
docstring.
    +    #
    +    # Returns True if .config is missing or outdated. We always prompt for
    +    # saving the configuration in that case.
    +
    +    print(_kconf.load_config())
    +    if not os.path.exists(_conf_filename):
    +        # No .config
    +        return True
    +
    +    return _needs_save()
    +
    +
    +def _needs_save():
    +    # Returns True if a just-loaded .config file is outdated (would get
    +    # modified when saving)
    +
    +    if _kconf.missing_syms:
    +        # Assignments to undefined symbols in the .config
    +        return True
    +
    +    for sym in _kconf.unique_defined_syms:
    +        if sym.user_value is None:
    +            if sym.config_string:
    +                # Unwritten symbol
    +                return True
    +        elif sym.orig_type in (BOOL, TRISTATE):
    +            if sym.tri_value != sym.user_value:
    +                # Written bool/tristate symbol, new value
    +                return True
    +        elif sym.str_value != sym.user_value:
    +            # Written string/int/hex symbol, new value
    +            return True
    +
    +    # No need to prompt for save
    +    return False
    +
    +
    +# Global variables used below:
    +#
    +#   _stdscr:
    +#     stdscr from curses
    +#
    +#   _cur_menu:
    +#     Menu node of the menu (or menuconfig symbol, or choice) currently 
being
    +#     shown
    +#
    +#   _shown:
    +#     List of items in _cur_menu that are shown (ignoring scrolling). In
    +#     show-all mode, this list contains all items in _cur_menu. Otherwise, 
it
    +#     contains just the visible items.
    +#
    +#   _sel_node_i:
    +#     Index in _shown of the currently selected node
    +#
    +#   _menu_scroll:
    +#     Index in _shown of the top row of the main display
    +#
    +#   _parent_screen_rows:
    +#     List/stack of the row numbers that the selections in the parent menus
    +#     appeared on. This is used to prevent the scrolling from jumping 
around
    +#     when going in and out of menus.
    +#
    +#   _show_help/_show_name/_show_all:
    +#     If True, the corresponding mode is on. See the module docstring.
    +#
    +#   _conf_filename:
    +#     File to save the configuration to
    +#
    +#   _minconf_filename:
    +#     File to save minimal configurations to
    +#
    +#   _conf_changed:
    +#     True if the configuration has been changed. If False, we don't bother
    +#     showing the save-and-quit dialog.
    +#
    +#     We reset this to False whenever the configuration is saved explicitly
    +#     from the save dialog.
    +
    +
    +def _menuconfig(stdscr):
    +    # Logic for the main display, with the list of symbols, etc.
    +
    +    global _stdscr
    +    global _conf_filename
    +    global _conf_changed
    +    global _minconf_filename
    +    global _show_help
    +    global _show_name
    +
    +    _stdscr = stdscr
    +
    +    _init()
    +
    +    while True:
    +        _draw_main()
    +        curses.doupdate()
    +
    +
    +        c = _getch_compat(_menu_win)
    +
    +        if c == curses.KEY_RESIZE:
    +            _resize_main()
    +
    +        elif c in (curses.KEY_DOWN, "j", "J"):
    +            _select_next_menu_entry()
    +
    +        elif c in (curses.KEY_UP, "k", "K"):
    +            _select_prev_menu_entry()
    +
    +        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
    +            # Keep it simple. This way we get sane behavior for small 
windows,
    +            # etc., for free.
    +            for _ in range(_PG_JUMP):
    +                _select_next_menu_entry()
    +
    +        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
    +            for _ in range(_PG_JUMP):
    +                _select_prev_menu_entry()
    +
    +        elif c in (curses.KEY_END, "G"):
    +            _select_last_menu_entry()
    +
    +        elif c in (curses.KEY_HOME, "g"):
    +            _select_first_menu_entry()
    +
    +        elif c == " ":
    +            # Toggle the node if possible
    +            sel_node = _shown[_sel_node_i]
    +            if not _change_node(sel_node):
    +                _enter_menu(sel_node)
    +
    +        elif c in (curses.KEY_RIGHT, "\n", "l", "L"):
    +            # Enter the node if possible
    +            sel_node = _shown[_sel_node_i]
    +            if not _enter_menu(sel_node):
    +                _change_node(sel_node)
    +
    +        elif c in ("n", "N"):
    +            _set_sel_node_tri_val(0)
    +
    +        elif c in ("m", "M"):
    +            _set_sel_node_tri_val(1)
    +
    +        elif c in ("y", "Y"):
    +            _set_sel_node_tri_val(2)
    +
    +        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
    +                   "\x1B", "h", "H"):  # \x1B = ESC
    +
    +            if c == "\x1B" and _cur_menu is _kconf.top_node:
    +                res = _quit_dialog()
    +                if res:
    +                    return res
    +            else:
    +                _leave_menu()
    +
    +        elif c in ("o", "O"):
    +            _load_dialog()
    +
    +        elif c in ("s", "S"):
    +            filename = _save_dialog(_kconf.write_config, _conf_filename,
    +                                    "configuration")
    +            if filename:
    +                _conf_filename = filename
    +                _conf_changed = False
    +
    +        elif c in ("d", "D"):
    +            filename = _save_dialog(_kconf.write_min_config, 
_minconf_filename,
    +                                    "minimal configuration")
    +            if filename:
    +                _minconf_filename = filename
    +
    +        elif c == "/":
    +            _jump_to_dialog()
    +            # The terminal might have been resized while the fullscreen 
jump-to
    +            # dialog was open
    +            _resize_main()
    +
    +        elif c == "?":
    +            _info_dialog(_shown[_sel_node_i], False)
    +            # The terminal might have been resized while the fullscreen 
info
    +            # dialog was open
    +            _resize_main()
    +
    +        elif c in ("f", "F"):
    +            _show_help = not _show_help
    +            _set_style(_help_win, "show-help" if _show_help else "help")
    +            _resize_main()
    +
    +        elif c in ("c", "C"):
    +            _show_name = not _show_name
    +
    +        elif c in ("a", "A"):
    +            _toggle_show_all()
    +
    +        elif c in ("q", "Q"):
    +            res = _quit_dialog()
    +            if res:
    +                return res
    +
    +
    +def _quit_dialog():
    +    if not _conf_changed:
    +        return "No changes to save (for '{}')".format(_conf_filename)
    +
    +    while True:
    +        c = _key_dialog(
    +            "Quit",
    +            " Save configuration?\n"
    +            "\n"
    +            "(Y)es  (N)o  (C)ancel",
    +            "ync")
    +
    +        if c is None or c == "c":
    +            return None
    +
    +        if c == "y":
    +            # Returns a message to print
    +            msg = _try_save(_kconf.write_config, _conf_filename, 
"configuration")
    +            if msg:
    +                return msg
    +
    +        elif c == "n":
    +            return "Configuration ({}) was not 
saved".format(_conf_filename)
    +
    +
    +def _init():
    +    # Initializes the main display with the list of symbols, etc. Also does
    +    # misc. global initialization that needs to happen after initializing
    +    # curses.
    +
    +    global _ERASE_CHAR
    +
    +    global _path_win
    +    global _top_sep_win
    +    global _menu_win
    +    global _bot_sep_win
    +    global _help_win
    +
    +    global _parent_screen_rows
    +    global _cur_menu
    +    global _shown
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    global _show_help
    +    global _show_name
    +
    +    # Looking for this in addition to KEY_BACKSPACE (which is unreliable) 
makes
    +    # backspace work with TERM=vt100. That makes it likely to work in sane
    +    # environments.
    +    _ERASE_CHAR = curses.erasechar()
    +    if sys.version_info[0] >= 3:
    +        # erasechar() returns a one-byte bytes object on Python 3. This 
sets
    +        # _ERASE_CHAR to a blank string if it can't be decoded, which 
should be
    +        # harmless.
    +        _ERASE_CHAR = _ERASE_CHAR.decode("utf-8", "ignore")
    +
    +    _init_styles()
    +
    +    # Hide the cursor
    +    _safe_curs_set(0)
    +
    +    # Initialize windows
    +
    +    # Top row, with menu path
    +    _path_win = _styled_win("path")
    +
    +    # Separator below menu path, with title and arrows pointing up
    +    _top_sep_win = _styled_win("separator")
    +
    +    # List of menu entries with symbols, etc.
    +    _menu_win = _styled_win("list")
    +    _menu_win.keypad(True)
    +
    +    # Row below menu list, with arrows pointing down
    +    _bot_sep_win = _styled_win("separator")
    +
    +    # Help window with keys at the bottom. Shows help texts in show-help 
mode.
    +    _help_win = _styled_win("help")
    +
    +    # The rows we'd like the nodes in the parent menus to appear on. This
    +    # prevents the scroll from jumping around when going in and out of 
menus.
    +    _parent_screen_rows = []
    +
    +    # Initial state
    +
    +    _cur_menu = _kconf.top_node
    +    _shown = _shown_nodes(_cur_menu)
    +    _sel_node_i = _menu_scroll = 0
    +
    +    _show_help = _show_name = False
    +
    +    # Give windows their initial size
    +    _resize_main()
    +
    +
    +def _resize_main():
    +    # Resizes the main display, with the list of symbols, etc., to fill the
    +    # terminal
    +
    +    global _menu_scroll
    +
    +    screen_height, screen_width = _stdscr.getmaxyx()
    +
    +    _path_win.resize(1, screen_width)
    +    _top_sep_win.resize(1, screen_width)
    +    _bot_sep_win.resize(1, screen_width)
    +
    +    help_win_height = _SHOW_HELP_HEIGHT if _show_help else \
    +        len(_MAIN_HELP_LINES)
    +
    +    menu_win_height = screen_height - help_win_height - 3
    +
    +    if menu_win_height >= 1:
    +        _menu_win.resize(menu_win_height, screen_width)
    +        _help_win.resize(help_win_height, screen_width)
    +
    +        _top_sep_win.mvwin(1, 0)
    +        _menu_win.mvwin(2, 0)
    +        _bot_sep_win.mvwin(2 + menu_win_height, 0)
    +        _help_win.mvwin(2 + menu_win_height + 1, 0)
    +    else:
    +        # Degenerate case. Give up on nice rendering and just prevent 
errors.
    +
    +        menu_win_height = 1
    +
    +        _menu_win.resize(1, screen_width)
    +        _help_win.resize(1, screen_width)
    +
    +        for win in _top_sep_win, _menu_win, _bot_sep_win, _help_win:
    +            win.mvwin(0, 0)
    +
    +    # Adjust the scroll so that the selected node is still within the 
window,
    +    # if needed
    +    if _sel_node_i - _menu_scroll >= menu_win_height:
    +        _menu_scroll = _sel_node_i - menu_win_height + 1
    +
    +
    +def _height(win):
    +    # Returns the height of 'win'
    +
    +    return win.getmaxyx()[0]
    +
    +
    +def _width(win):
    +    # Returns the width of 'win'
    +
    +    return win.getmaxyx()[1]
    +
    +
    +def _enter_menu(menu):
    +    # Makes 'menu' the currently displayed menu. In addition to actual 
'menu's,
    +    # "menu" here includes choices and symbols defined with the 
'menuconfig'
    +    # keyword.
    +    #
    +    # Returns False if 'menu' can't be entered.
    +
    +    global _cur_menu
    +    global _shown
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    if not menu.is_menuconfig:
    +        # Not a menu
    +        return False
    +
    +    shown_sub = _shown_nodes(menu)
    +    # Never enter empty menus. We depend on having a current node.
    +    if not shown_sub:
    +        return False
    +
    +    # Remember where the current node appears on the screen, so we can try
    +    # to get it to appear in the same place when we leave the menu
    +    _parent_screen_rows.append(_sel_node_i - _menu_scroll)
    +
    +    # Jump into menu
    +    _cur_menu = menu
    +    _shown = shown_sub
    +    _sel_node_i = _menu_scroll = 0
    +
    +    if isinstance(menu.item, Choice):
    +        _select_selected_choice_sym()
    +
    +    return True
    +
    +
    +def _select_selected_choice_sym():
    +    # Puts the cursor on the currently selected (y-valued) choice symbol, 
if
    +    # any. Does nothing if if the choice has no selection (is not 
visible/in y
    +    # mode).
    +
    +    global _sel_node_i
    +
    +    choice = _cur_menu.item
    +    if choice.selection:
    +        # Search through all menu nodes to handle choice symbols being 
defined
    +        # in multiple locations
    +        for node in choice.selection.nodes:
    +            if node in _shown:
    +                _sel_node_i = _shown.index(node)
    +                _center_vertically()
    +                return
    +
    +
    +def _jump_to(node):
    +    # Jumps directly to the menu node 'node'
    +
    +    global _cur_menu
    +    global _shown
    +    global _sel_node_i
    +    global _menu_scroll
    +    global _show_all
    +    global _parent_screen_rows
    +
    +    # Clear remembered menu locations. We might not even have been in the
    +    # parent menus before.
    +    _parent_screen_rows = []
    +
    +    old_show_all = _show_all
    +    jump_into = (isinstance(node.item, Choice) or node.item == MENU) and \
    +                node.list
    +
    +    # If we're jumping to a non-empty choice or menu, jump to the first 
entry
    +    # in it instead of jumping to its menu node
    +    if jump_into:
    +        _cur_menu = node
    +        node = node.list
    +    else:
    +        _cur_menu = _parent_menu(node)
    +
    +    _shown = _shown_nodes(_cur_menu)
    +    if node not in _shown:
    +        # The node wouldn't be shown. Turn on show-all to show it.
    +        _show_all = True
    +        _shown = _shown_nodes(_cur_menu)
    +
    +    _sel_node_i = _shown.index(node)
    +
    +    if jump_into and not old_show_all and _show_all:
    +        # If we're jumping into a choice or menu and were forced to turn on
    +        # show-all because the first entry wasn't visible, try turning it 
off.
    +        # That will land us at the first visible node if there are visible
    +        # nodes, and is a no-op otherwise.
    +        _toggle_show_all()
    +
    +    _center_vertically()
    +
    +    # If we're jumping to a non-empty choice, jump to the selected symbol, 
if
    +    # any
    +    if jump_into and isinstance(_cur_menu.item, Choice):
    +        _select_selected_choice_sym()
    +
    +
    +def _leave_menu():
    +    # Jumps to the parent menu of the current menu. Does nothing if we're 
in
    +    # the top menu.
    +
    +    global _cur_menu
    +    global _shown
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    if _cur_menu is _kconf.top_node:
    +        return
    +
    +    # Jump to parent menu
    +    parent = _parent_menu(_cur_menu)
    +    _shown = _shown_nodes(parent)
    +    _sel_node_i = _shown.index(_cur_menu)
    +    _cur_menu = parent
    +
    +    # Try to make the menu entry appear on the same row on the screen as 
it did
    +    # before we entered the menu.
    +
    +    if _parent_screen_rows:
    +        # The terminal might have shrunk since we were last in the parent 
menu
    +        screen_row = min(_parent_screen_rows.pop(), _height(_menu_win) - 1)
    +        _menu_scroll = max(_sel_node_i - screen_row, 0)
    +    else:
    +        # No saved parent menu locations, meaning we jumped directly to 
some
    +        # node earlier
    +        _center_vertically()
    +
    +
    +def _select_next_menu_entry():
    +    # Selects the menu entry after the current one, adjusting the scroll if
    +    # necessary. Does nothing if we're already at the last menu entry.
    +
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    if _sel_node_i < len(_shown) - 1:
    +        # Jump to the next node
    +        _sel_node_i += 1
    +
    +        # If the new node is sufficiently close to the edge of the menu 
window
    +        # (as determined by _SCROLL_OFFSET), increase the scroll by one. 
This
    +        # gives nice and non-jumpy behavior even when
    +        # _SCROLL_OFFSET >= _height(_menu_win).
    +        if _sel_node_i >= _menu_scroll + _height(_menu_win) - 
_SCROLL_OFFSET \
    +           and _menu_scroll < _max_scroll(_shown, _menu_win):
    +
    +            _menu_scroll += 1
    +
    +
    +def _select_prev_menu_entry():
    +    # Selects the menu entry before the current one, adjusting the scroll 
if
    +    # necessary. Does nothing if we're already at the first menu entry.
    +
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    if _sel_node_i > 0:
    +        # Jump to the previous node
    +        _sel_node_i -= 1
    +
    +        # See _select_next_menu_entry()
    +        if _sel_node_i < _menu_scroll + _SCROLL_OFFSET:
    +            _menu_scroll = max(_menu_scroll - 1, 0)
    +
    +
    +def _select_last_menu_entry():
    +    # Selects the last menu entry in the current menu
    +
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    _sel_node_i = len(_shown) - 1
    +    _menu_scroll = _max_scroll(_shown, _menu_win)
    +
    +
    +def _select_first_menu_entry():
    +    # Selects the first menu entry in the current menu
    +
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    _sel_node_i = _menu_scroll = 0
    +
    +
    +def _toggle_show_all():
    +    # Toggles show-all mode on/off. If turning it off would give no visible
    +    # items in the current menu, it is left on.
    +
    +    global _show_all
    +    global _shown
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    # Row on the screen the cursor is on. Preferably we want the same row 
to
    +    # stay highlighted.
    +    old_row = _sel_node_i - _menu_scroll
    +
    +    _show_all = not _show_all
    +    # List of new nodes to be shown after toggling _show_all
    +    new_shown = _shown_nodes(_cur_menu)
    +
    +    # Find a good node to select. The selected node might disappear if 
show-all
    +    # mode is turned off.
    +
    +    # Select the previously selected node itself if it is still visible. If
    +    # there are visible nodes before it, select the closest one.
    +    for node in _shown[_sel_node_i::-1]:
    +        if node in new_shown:
    +            _sel_node_i = new_shown.index(node)
    +            break
    +    else:
    +        # No visible nodes before the previously selected node. Select the
    +        # closest visible node after it instead.
    +        for node in _shown[_sel_node_i + 1:]:
    +            if node in new_shown:
    +                _sel_node_i = new_shown.index(node)
    +                break
    +        else:
    +            # No visible nodes at all, meaning show-all was turned off 
inside
    +            # an invisible menu. Don't allow that, as the implementation 
relies
    +            # on always having a selected node.
    +            _show_all = True
    +            return
    +
    +    _shown = new_shown
    +
    +    # Try to make the cursor stay on the same row in the menu window. This
    +    # might be impossible if too many nodes have disappeared above the 
node.
    +    _menu_scroll = max(_sel_node_i - old_row, 0)
    +
    +
    +def _center_vertically():
    +    # Centers the selected node vertically, if possible
    +
    +    global _menu_scroll
    +
    +    _menu_scroll = min(max(_sel_node_i - _height(_menu_win)//2, 0),
    +                       _max_scroll(_shown, _menu_win))
    +
    +
    +def _draw_main():
    +    # Draws the "main" display, with the list of symbols, the header, and 
the
    +    # footer.
    +    #
    +    # This could be optimized to only update the windows that have actually
    +    # changed, but keep it simple for now and let curses sort it out.
    +
    +    term_width = _width(_stdscr)
    +
    +
    +    #
    +    # Update the separator row below the menu path
    +    #
    +
    +    _top_sep_win.erase()
    +
    +    # Draw arrows pointing up if the symbol window is scrolled down. Draw 
them
    +    # before drawing the title, so the title ends up on top for small 
windows.
    +    if _menu_scroll > 0:
    +        _safe_hline(_top_sep_win, 0, 4, curses.ACS_UARROW, 
_N_SCROLL_ARROWS)
    +
    +    # Add the 'mainmenu' text as the title, centered at the top
    +    _safe_addstr(_top_sep_win,
    +                 0, max((term_width - len(_kconf.mainmenu_text))//2, 0),
    +                 _kconf.mainmenu_text)
    +
    +    _top_sep_win.noutrefresh()
    +
    +    # Note: The menu path at the top is deliberately updated last. See 
below.
    +
    +    #
    +    # Update the symbol window
    +    #
    +
    +    _menu_win.erase()
    +
    +    # Draw the _shown nodes starting from index _menu_scroll up to either 
as
    +    # many as fit in the window, or to the end of _shown
    +    for i in range(_menu_scroll,
    +                   min(_menu_scroll + _height(_menu_win), len(_shown))):
    +
    +        node = _shown[i]
    +
    +        # The 'not _show_all' test avoids showing invisible items in red
    +        # outside show-all mode, which could look confusing/broken. 
Invisible
    +        # symbols show up outside show-all mode if an invisible symbol has
    +        # visible children in an implicit (indented) menu.
    +        if _visible(node) or not _show_all:
    +            style = _style["selection" if i == _sel_node_i else "list"]
    +        else:
    +            style = _style["inv-selection" if i == _sel_node_i else 
"inv-list"]
    +
    +        _safe_addstr(_menu_win, i - _menu_scroll, 0, _node_str(node), 
style)
    +
    +    _menu_win.noutrefresh()
    +
    +
    +    #
    +    # Update the bottom separator window
    +    #
    +
    +    _bot_sep_win.erase()
    +
    +    # Draw arrows pointing down if the symbol window is scrolled up
    +    if _menu_scroll < _max_scroll(_shown, _menu_win):
    +        _safe_hline(_bot_sep_win, 0, 4, curses.ACS_DARROW, 
_N_SCROLL_ARROWS)
    +
    +    # Indicate when show-name/show-help/show-all mode is enabled
    +    enabled_modes = []
    +    if _show_help:
    +        enabled_modes.append("show-help (toggle with [F])")
    +    if _show_name:
    +        enabled_modes.append("show-name")
    +    if _show_all:
    +        enabled_modes.append("show-all")
    +    if enabled_modes:
    +        s = " and ".join(enabled_modes) + " mode enabled"
    +        _safe_addstr(_bot_sep_win, 0, max(term_width - len(s) - 2, 0), s)
    +
    +    _bot_sep_win.noutrefresh()
    +
    +
    +    #
    +    # Update the help window, which shows either key bindings or help texts
    +    #
    +
    +    _help_win.erase()
    +
    +    if _show_help:
    +        node = _shown[_sel_node_i]
    +        if isinstance(node.item, (Symbol, Choice)) and node.help:
    +            help_lines = textwrap.wrap(node.help, _width(_help_win))
    +            for i in range(min(_height(_help_win), len(help_lines))):
    +                _safe_addstr(_help_win, i, 0, help_lines[i])
    +        else:
    +            _safe_addstr(_help_win, 0, 0, "(no help)")
    +    else:
    +        for i, line in enumerate(_MAIN_HELP_LINES):
    +            _safe_addstr(_help_win, i, 0, line)
    +
    +    _help_win.noutrefresh()
    +
    +
    +    #
    +    # Update the top row with the menu path.
    +    #
    +    # Doing this last leaves the cursor on the top row, which avoids some 
minor
    +    # annoying jumpiness in gnome-terminal when reducing the height of the
    +    # terminal. It seems to happen whenever the row with the cursor on it
    +    # disappears.
    +    #
    +
    +    _path_win.erase()
    +
    +    # Draw the menu path ("(Top) -> Menu -> Submenu -> ...")
    +
    +    menu_prompts = []
    +
    +    menu = _cur_menu
    +    while menu is not _kconf.top_node:
    +        # Promptless choices can be entered in show-all mode. Use
    +        # standard_sc_expr_str() for them, so they show up as
    +        # '<choice (name if any)>'.
    +        menu_prompts.append(menu.prompt[0] if menu.prompt else
    +                            standard_sc_expr_str(menu.item))
    +        menu = menu.parent
    +    menu_prompts.append("(Top)")
    +    menu_prompts.reverse()
    +
    +    # Hack: We can't put ACS_RARROW directly in the string. Temporarily
    +    # represent it with NULL.
    +    menu_path_str = " \0 ".join(menu_prompts)
    +
    +    # Scroll the menu path to the right if needed to make the current 
menu's
    +    # title visible
    +    if len(menu_path_str) > term_width:
    +        menu_path_str = menu_path_str[len(menu_path_str) - term_width:]
    +
    +    # Print the path with the arrows reinserted
    +    split_path = menu_path_str.split("\0")
    +    _safe_addstr(_path_win, split_path[0])
    +    for s in split_path[1:]:
    +        _safe_addch(_path_win, curses.ACS_RARROW)
    +        _safe_addstr(_path_win, s)
    +
    +    _path_win.noutrefresh()
    +
    +
    +def _parent_menu(node):
    +    # Returns the menu node of the menu that contains 'node'. In addition 
to
    +    # proper 'menu's, this might also be a 'menuconfig' symbol or a 
'choice'.
    +    # "Menu" here means a menu in the interface.
    +
    +    menu = node.parent
    +    while not menu.is_menuconfig:
    +        menu = menu.parent
    +    return menu
    +
    +
    +def _shown_nodes(menu):
    +    # Returns the list of menu nodes from 'menu' (see _parent_menu()) that
    +    # would be shown when entering it
    +
    +    def rec(node):
    +        res = []
    +
    +        while node:
    +            if _visible(node) or _show_all:
    +                res.append(node)
    +                if node.list and not node.is_menuconfig:
    +                    # Nodes from implicit menu created from dependencies. 
Will
    +                    # be shown indented. Note that is_menuconfig is True 
for
    +                    # menus and choices as well as 'menuconfig' symbols.
    +                    res += rec(node.list)
    +
    +            elif node.list and isinstance(node.item, Symbol):
    +                # Show invisible symbols if they have visible children. 
This
    +                # can happen for an m/y-valued symbol with an optional 
prompt
    +                # ('prompt "foo" is COND') that is currently disabled. Note
    +                # that it applies to both 'config' and 'menuconfig' 
symbols.
    +                shown_children = rec(node.list)
    +                if shown_children:
    +                    res.append(node)
    +                    if not node.is_menuconfig:
    +                        res += shown_children
    +
    +            node = node.next
    +
    +        return res
    +
    +    if isinstance(menu.item, Choice):
    +        # For named choices defined in multiple locations, entering the 
choice
    +        # at a particular menu node would normally only show the choice 
symbols
    +        # defined there (because that's what the MenuNode tree looks like).
    +        #
    +        # That might look confusing, and makes extending choices by 
defining
    +        # them in multiple locations less useful. Instead, gather all the 
child
    +        # menu nodes for all the choices whenever a choice is entered. That
    +        # makes all choice symbols visible at all locations.
    +        #
    +        # Choices can contain non-symbol items (people do all sorts of 
weird
    +        # stuff with them), hence the generality here. We really need to
    +        # preserve the menu tree at each choice location.
    +        #
    +        # Note: Named choices are pretty broken in the C tools, and this is
    +        # super obscure, so you probably won't find much that relies on 
this.
    +        # This whole 'if' could be deleted if you don't care about defining
    +        # choices in multiple locations to add symbols (which will still 
work,
    +        # just with things being displayed in a way that might be 
unexpected).
    +
    +        # Do some additional work to avoid listing choice symbols twice if 
all
    +        # or part of the choice is copied in multiple locations (e.g. by
    +        # including some Kconfig file multiple times). We give the prompts 
at
    +        # the current location precedence.
    +        seen_syms = {node.item for node in rec(menu.list)
    +                     if isinstance(node.item, Symbol)}
    +        res = []
    +        for choice_node in menu.item.nodes:
    +            for node in rec(choice_node.list):
    +                # 'choice_node is menu' checks if we're dealing with the
    +                # current location
    +                if node.item not in seen_syms or choice_node is menu:
    +                    res.append(node)
    +                    if isinstance(node.item, Symbol):
    +                        seen_syms.add(node.item)
    +        return res
    +
    +    return rec(menu.list)
    +
    +
    +def _visible(node):
    +    # Returns True if the node should appear in the menu (outside show-all
    +    # mode)
    +
    +    return node.prompt and expr_value(node.prompt[1]) and not \
    +        (node.item == MENU and not expr_value(node.visibility))
    +
    +
    +def _change_node(node):
    +    # Changes the value of the menu node 'node' if it is a symbol. Bools 
and
    +    # tristates are toggled, while other symbol types pop up a text entry
    +    # dialog.
    +    #
    +    # Returns False if the value of 'node' can't be changed.
    +
    +    if not _changeable(node):
    +        return False
    +
    +    # sc = symbol/choice
    +    sc = node.item
    +
    +    if sc.orig_type in (INT, HEX, STRING):
    +        s = sc.str_value
    +
    +        while True:
    +            s = _input_dialog(
    +                "{} ({})".format(node.prompt[0], 
TYPE_TO_STR[sc.orig_type]),
    +                s, _range_info(sc))
    +
    +            if s is None:
    +                break
    +
    +            if sc.orig_type in (INT, HEX):
    +                s = s.strip()
    +
    +                # 'make menuconfig' does this too. Hex values not starting 
with
    +                # '0x' are accepted when loading .config files though.
    +                if sc.orig_type == HEX and not s.startswith(("0x", "0X")):
    +                    s = "0x" + s
    +
    +            if _check_valid(sc, s):
    +                _set_val(sc, s)
    +                break
    +
    +    elif len(sc.assignable) == 1:
    +        # Handles choice symbols for choices in y mode, which are a special
    +        # case: .assignable can be (2,) while .tri_value is 0.
    +        _set_val(sc, sc.assignable[0])
    +
    +    else:
    +        # Set the symbol to the value after the current value in
    +        # sc.assignable, with wrapping
    +        val_index = sc.assignable.index(sc.tri_value)
    +        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
    +
    +
    +    if _is_y_mode_choice_sym(sc) and not node.list:
    +        # Immediately jump to the parent menu after making a choice 
selection,
    +        # like 'make menuconfig' does, except if the menu node has children
    +        # (which can happen if a symbol 'depends on' a choice symbol that
    +        # immediately precedes it).
    +        _leave_menu()
    +
    +
    +    return True
    +
    +
    +def _changeable(node):
    +    # Returns True if the value if 'node' can be changed
    +
    +    sc = node.item
    +
    +    if not isinstance(sc, (Symbol, Choice)):
    +        return False
    +
    +    # This will hit for invisible symbols, which appear in show-all mode 
and
    +    # when an invisible symbol has visible children (which can happen e.g. 
for
    +    # symbols with optional prompts)
    +    if not (node.prompt and expr_value(node.prompt[1])):
    +        return False
    +
    +    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
    +        or _is_y_mode_choice_sym(sc)
    +
    +
    +def _set_sel_node_tri_val(tri_val):
    +    # Sets the value of the currently selected menu entry to 'tri_val', if 
that
    +    # value can be assigned
    +
    +    sc = _shown[_sel_node_i].item
    +    if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable:
    +        _set_val(sc, tri_val)
    +
    +
    +def _set_val(sc, val):
    +    # Wrapper around Symbol/Choice.set_value() for updating the menu state 
and
    +    # _conf_changed
    +
    +    global _conf_changed
    +
    +    # Use the string representation of tristate values. This makes the 
format
    +    # consistent for all symbol types.
    +    if val in TRI_TO_STR:
    +        val = TRI_TO_STR[val]
    +
    +    if val != sc.str_value:
    +        sc.set_value(val)
    +        _conf_changed = True
    +
    +        # Changing the value of the symbol might have changed what items 
in the
    +        # current menu are visible. Recalculate the state.
    +        _update_menu()
    +
    +
    +def _update_menu():
    +    # Updates the current menu after the value of a symbol or choice has 
been
    +    # changed. Changing a value might change which items in the menu are
    +    # visible.
    +    #
    +    # If possible, preserves the location of the cursor on the screen when
    +    # items are added/removed above the selected item.
    +
    +    global _shown
    +    global _sel_node_i
    +    global _menu_scroll
    +
    +    # Row on the screen the cursor was on
    +    old_row = _sel_node_i - _menu_scroll
    +
    +    sel_node = _shown[_sel_node_i]
    +
    +    # New visible nodes
    +    _shown = _shown_nodes(_cur_menu)
    +
    +    # New index of selected node
    +    _sel_node_i = _shown.index(sel_node)
    +
    +    # Try to make the cursor stay on the same row in the menu window. This
    +    # might be impossible if too many nodes have disappeared above the 
node.
    +    _menu_scroll = max(_sel_node_i - old_row, 0)
    +
    +
    +def _input_dialog(title, initial_text, info_text=None):
    +    # Pops up a dialog that prompts the user for a string
    +    #
    +    # title:
    +    #   Title to display at the top of the dialog window's border
    +    #
    +    # initial_text:
    +    #   Initial text to prefill the input field with
    +    #
    +    # info_text:
    +    #   String to show next to the input field. If None, just the input 
field
    +    #   is shown.
    +
    +    win = _styled_win("body")
    +    win.keypad(True)
    +
    +    info_lines = info_text.split("\n") if info_text else []
    +
    +    # Give the input dialog its initial size
    +    _resize_input_dialog(win, title, info_lines)
    +
    +    _safe_curs_set(2)
    +
    +    # Input field text
    +    s = initial_text
    +
    +    # Cursor position
    +    i = len(initial_text)
    +
    +    def edit_width():
    +        return _width(win) - 4
    +
    +    # Horizontal scroll offset
    +    hscroll = max(i - edit_width() + 1, 0)
    +
    +    while True:
    +        # Draw the "main" display with the menu, etc., so that resizing 
still
    +        # works properly. This is like a stack of windows, only hardcoded 
for
    +        # now.
    +        _draw_main()
    +        _draw_input_dialog(win, title, info_lines, s, i, hscroll)
    +        curses.doupdate()
    +
    +
    +        c = _getch_compat(win)
    +
    +        if c == curses.KEY_RESIZE:
    +            # Resize the main display too. The dialog floats above it.
    +            _resize_main()
    +            _resize_input_dialog(win, title, info_lines)
    +
    +        elif c == "\n":
    +            _safe_curs_set(0)
    +            return s
    +
    +        elif c == "\x1B":  # \x1B = ESC
    +            _safe_curs_set(0)
    +            return None
    +
    +        else:
    +            s, i, hscroll = _edit_text(c, s, i, hscroll, edit_width())
    +
    +
    +def _resize_input_dialog(win, title, info_lines):
    +    # Resizes the input dialog to a size appropriate for the terminal size
    +
    +    screen_height, screen_width = _stdscr.getmaxyx()
    +
    +    win_height = 5
    +    if info_lines:
    +        win_height += len(info_lines) + 1
    +    win_height = min(win_height, screen_height)
    +
    +    win_width = max(_INPUT_DIALOG_MIN_WIDTH,
    +                    len(title) + 4,
    +                    *(len(line) + 4 for line in info_lines))
    +    win_width = min(win_width, screen_width)
    +
    +    win.resize(win_height, win_width)
    +    win.mvwin((screen_height - win_height)//2,
    +              (screen_width - win_width)//2)
    +
    +
    +def _draw_input_dialog(win, title, info_lines, s, i, hscroll):
    +    edit_width = _width(win) - 4
    +
    +    win.erase()
    +
    +    # Note: Perhaps having a separate window for the input field would be 
nicer
    +    visible_s = s[hscroll:hscroll + edit_width]
    +    _safe_addstr(win, 2, 2, visible_s + " "*(edit_width - len(visible_s)),
    +                 _style["edit"])
    +
    +    for linenr, line in enumerate(info_lines):
    +        _safe_addstr(win, 4 + linenr, 2, line)
    +
    +    # Draw the frame last so that it overwrites the body text for small 
windows
    +    _draw_frame(win, title)
    +
    +    _safe_move(win, 2, 2 + i - hscroll)
    +
    +    win.noutrefresh()
    +
    +
    +def _load_dialog():
    +    # Dialog for loading a new configuration
    +
    +    global _conf_changed
    +    global _conf_filename
    +    global _show_all
    +
    +    if _conf_changed:
    +        c = _key_dialog(
    +            "Load",
    +            "You have unsaved changes. Load new\n"
    +            "configuration anyway?\n"
    +            "\n"
    +            "         (O)K  (C)ancel",
    +            "oc")
    +
    +        if c is None or c == "c":
    +            return
    +
    +    filename = _conf_filename
    +    while True:
    +        filename = _input_dialog("File to load", filename, 
_load_save_info())
    +        if filename is None:
    +            return
    +
    +        filename = os.path.expanduser(filename)
    +
    +        if _try_load(filename):
    +            _conf_filename = filename
    +            _conf_changed = _needs_save()
    +
    +            # Turn on show-all mode if the selected node is not visible 
after
    +            # loading the new configuration. _shown still holds the old 
state.
    +            if _shown[_sel_node_i] not in _shown_nodes(_cur_menu):
    +                _show_all = True
    +
    +            _update_menu()
    +
    +            # The message dialog indirectly updates the menu display, so 
_msg()
    +            # must be called after the new state has been initialized
    +            _msg("Success", "Loaded " + filename)
    +            return
    +
    +
    +def _try_load(filename):
    +    # Tries to load a configuration file. Pops up an error and returns 
False on
    +    # failure.
    +    #
    +    # filename:
    +    #   Configuration file to load
    +
    +    try:
    +        _kconf.load_config(filename)
    +        return True
    +    except EnvironmentError as e:
    +        _error("Error loading '{}'\n\n{} (errno: {})"
    +               .format(filename, e.strerror, errno.errorcode[e.errno]))
    +        return False
    +
    +
    +def _save_dialog(save_fn, default_filename, description):
    +    # Dialog for saving the current configuration
    +    #
    +    # save_fn:
    +    #   Function to call with 'filename' to save the file
    +    #
    +    # default_filename:
    +    #   Prefilled filename in the input field
    +    #
    +    # description:
    +    #   String describing the thing being saved
    +    #
    +    # Return value:
    +    #   The path to the saved file, or None if no file was saved
    +
    +    filename = default_filename
    +    while True:
    +        filename = _input_dialog("Filename to save {} 
to".format(description),
    +                                 filename, _load_save_info())
    +        if filename is None:
    +            return None
    +
    +        filename = os.path.expanduser(filename)
    +
    +        msg = _try_save(save_fn, filename, description)
    +        if msg:
    +            _msg("Success", msg)
    +            return filename
    +
    +
    +def _try_save(save_fn, filename, description):
    +    # Tries to save a configuration file. Returns a message to print on
    +    # success.
    +    #
    +    # save_fn:
    +    #   Function to call with 'filename' to save the file
    +    #
    +    # description:
    +    #   String describing the thing being saved
    +    #
    +    # Return value:
    +    #   A message to print on success, and None on failure
    +
    +    try:
    +        # save_fn() returns a message to print
    +        return save_fn(filename)
    +    except EnvironmentError as e:
    +        _error("Error saving {} to '{}'\n\n{} (errno: {})"
    +               .format(description, e.filename, e.strerror,
    +                       errno.errorcode[e.errno]))
    +        return None
    +
    +
    +def _key_dialog(title, text, keys):
    +    # Pops up a dialog that can be closed by pressing a key
    +    #
    +    # title:
    +    #   Title to display at the top of the dialog window's border
    +    #
    +    # text:
    +    #   Text to show in the dialog
    +    #
    +    # keys:
    +    #   List of keys that will close the dialog. Other keys (besides ESC) 
are
    +    #   ignored. The caller is responsible for providing a hint about which
    +    #   keys can be pressed in 'text'.
    +    #
    +    # Return value:
    +    #   The key that was pressed to close the dialog. Uppercase characters 
are
    +    #   converted to lowercase. ESC will always close the dialog, and 
returns
    +    #   None.
    +
    +    win = _styled_win("body")
    +    win.keypad(True)
    +
    +    _resize_key_dialog(win, text)
    +
    +    while True:
    +        # See _input_dialog()
    +        _draw_main()
    +        _draw_key_dialog(win, title, text)
    +        curses.doupdate()
    +
    +
    +        c = _getch_compat(win)
    +
    +        if c == curses.KEY_RESIZE:
    +            # Resize the main display too. The dialog floats above it.
    +            _resize_main()
    +            _resize_key_dialog(win, text)
    +
    +        elif c == "\x1B":  # \x1B = ESC
    +            return None
    +
    +        elif isinstance(c, str):
    +            c = c.lower()
    +            if c in keys:
    +                return c
    +
    +
    +def _resize_key_dialog(win, text):
    +    # Resizes the key dialog to a size appropriate for the terminal size
    +
    +    screen_height, screen_width = _stdscr.getmaxyx()
    +
    +    lines = text.split("\n")
    +
    +    win_height = min(len(lines) + 4, screen_height)
    +    win_width = min(max(len(line) for line in lines) + 4, screen_width)
    +
    +    win.resize(win_height, win_width)
    +    win.mvwin((screen_height - win_height)//2,
    +              (screen_width - win_width)//2)
    +
    +
    +def _draw_key_dialog(win, title, text):
    +    win.erase()
    +
    +    for i, line in enumerate(text.split("\n")):
    +        _safe_addstr(win, 2 + i, 2, line)
    +
    +    # Draw the frame last so that it overwrites the body text for small 
windows
    +    _draw_frame(win, title)
    +
    +    win.noutrefresh()
    +
    +
    +def _draw_frame(win, title):
    +    # Draw a frame around the inner edges of 'win', with 'title' at the top
    +
    +    win_height, win_width = win.getmaxyx()
    +
    +    win.attron(_style["frame"])
    +
    +    # Draw top/bottom edge
    +    _safe_hline(win,              0, 0, " ", win_width)
    +    _safe_hline(win, win_height - 1, 0, " ", win_width)
    +
    +    # Draw left/right edge
    +    _safe_vline(win, 0,             0, " ", win_height)
    +    _safe_vline(win, 0, win_width - 1, " ", win_height)
    +
    +    # Draw title
    +    _safe_addstr(win, 0, max((win_width - len(title))//2, 0), title)
    +
    +    win.attroff(_style["frame"])
    +
    +
    +def _jump_to_dialog():
    +    # Implements the jump-to dialog, where symbols can be looked up via
    +    # incremental search and jumped to.
    +    #
    +    # Returns True if the user jumped to a symbol, and False if the dialog 
was
    +    # canceled.
    +
    +    s = ""  # Search text
    +    prev_s = None  # Previous search text
    +    s_i = 0  # Search text cursor position
    +    hscroll = 0  # Horizontal scroll offset
    +
    +    sel_node_i = 0  # Index of selected row
    +    scroll = 0  # Index in 'matches' of the top row of the list
    +
    +    # Edit box at the top
    +    edit_box = _styled_win("jump-edit")
    +    edit_box.keypad(True)
    +
    +    # List of matches
    +    matches_win = _styled_win("list")
    +
    +    # Bottom separator, with arrows pointing down
    +    bot_sep_win = _styled_win("separator")
    +
    +    # Help window with instructions at the bottom
    +    help_win = _styled_win("help")
    +
    +    # Give windows their initial size
    +    _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
    +                           sel_node_i, scroll)
    +
    +    _safe_curs_set(2)
    +
    +    # Logic duplication with _select_{next,prev}_menu_entry(), except we 
do a
    +    # functional variant that returns the new (sel_node_i, scroll) values 
to
    +    # avoid 'nonlocal'. TODO: Can this be factored out in some nice way?
    +
    +    def select_next_match():
    +        if sel_node_i == len(matches) - 1:
    +            return sel_node_i, scroll
    +
    +        if sel_node_i + 1 >= scroll + _height(matches_win) - 
_SCROLL_OFFSET \
    +           and scroll < _max_scroll(matches, matches_win):
    +
    +            return sel_node_i + 1, scroll + 1
    +
    +        return sel_node_i + 1, scroll
    +
    +    def select_prev_match():
    +        if sel_node_i == 0:
    +            return sel_node_i, scroll
    +
    +        if sel_node_i - 1 < scroll + _SCROLL_OFFSET:
    +            return sel_node_i - 1, max(scroll - 1, 0)
    +
    +        return sel_node_i - 1, scroll
    +
    +    while True:
    +        if s != prev_s:
    +            # The search text changed. Find new matching nodes.
    +
    +            prev_s = s
    +
    +            try:
    +                # We could use re.IGNORECASE here instead of lower(), but 
this
    +                # is noticeably less jerky while inputting regexes like
    +                # '.*debug$' (though the '.*' is redundant there). Those
    +                # probably have bad interactions with re.search(), which
    +                # matches anywhere in the string.
    +                #
    +                # It's not horrible either way. Just a bit smoother.
    +                regex_searches = [re.compile(regex).search
    +                                  for regex in s.lower().split()]
    +
    +                # No exception thrown, so the regexes are okay
    +                bad_re = None
    +
    +                # List of matching nodes
    +                matches = []
    +                add_match = matches.append
    +
    +                # Search symbols and choices
    +
    +                for node in _sorted_sc_nodes():
    +                    # Symbol/choice
    +                    sc = node.item
    +
    +                    for search in regex_searches:
    +                        # Both the name and the prompt might be missing, 
since
    +                        # we're searching both symbols and choices
    +
    +                        # Does the regex match either the symbol name or 
the
    +                        # prompt (if any)?
    +                        if not (sc.name and search(sc.name.lower()) or
    +                                node.prompt and 
search(node.prompt[0].lower())):
    +
    +                            # Give up on the first regex that doesn't 
match, to
    +                            # speed things up a bit when multiple regexes 
are
    +                            # entered
    +                            break
    +
    +                    else:
    +                        add_match(node)
    +
    +                # Search menus and comments
    +
    +                for node in _sorted_menu_comment_nodes():
    +                    for search in regex_searches:
    +                        if not search(node.prompt[0].lower()):
    +                            break
    +                    else:
    +                        add_match(node)
    +
    +            except re.error as e:
    +                # Bad regex. Remember the error message so we can show it.
    +                bad_re = "Bad regular expression"
    +                # re.error.msg was added in Python 3.5
    +                if hasattr(e, "msg"):
    +                    bad_re += ": " + e.msg
    +
    +                matches = []
    +
    +            # Reset scroll and jump to the top of the list of matches
    +            sel_node_i = scroll = 0
    +
    +        _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
    +                             s, s_i, hscroll,
    +                             bad_re, matches, sel_node_i, scroll)
    +        curses.doupdate()
    +
    +
    +        c = _getch_compat(edit_box)
    +
    +        if c == "\n":
    +            if matches:
    +                _jump_to(matches[sel_node_i])
    +                _safe_curs_set(0)
    +                return True
    +
    +        elif c == "\x1B":  # \x1B = ESC
    +            _safe_curs_set(0)
    +            return False
    +
    +        elif c == curses.KEY_RESIZE:
    +            # We adjust the scroll so that the selected node stays visible 
in
    +            # the list when the terminal is resized, hence the 'scroll'
    +            # assignment
    +            scroll = _resize_jump_to_dialog(
    +                edit_box, matches_win, bot_sep_win, help_win,
    +                sel_node_i, scroll)
    +
    +        elif c == "\x06":  # \x06 = Ctrl-F
    +            if matches:
    +                _safe_curs_set(0)
    +                _info_dialog(matches[sel_node_i], True)
    +                _safe_curs_set(2)
    +
    +                scroll = _resize_jump_to_dialog(
    +                    edit_box, matches_win, bot_sep_win, help_win,
    +                    sel_node_i, scroll)
    +
    +        elif c == curses.KEY_DOWN:
    +            sel_node_i, scroll = select_next_match()
    +
    +        elif c == curses.KEY_UP:
    +            sel_node_i, scroll = select_prev_match()
    +
    +        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
    +            # Keep it simple. This way we get sane behavior for small 
windows,
    +            # etc., for free.
    +            for _ in range(_PG_JUMP):
    +                sel_node_i, scroll = select_next_match()
    +
    +        # Page Up (no Ctrl-U, as it's already used by the edit box)
    +        elif c == curses.KEY_PPAGE:
    +            for _ in range(_PG_JUMP):
    +                sel_node_i, scroll = select_prev_match()
    +
    +        elif c == curses.KEY_END:
    +            sel_node_i = len(matches) - 1
    +            scroll = _max_scroll(matches, matches_win)
    +
    +        elif c == curses.KEY_HOME:
    +            sel_node_i = scroll = 0
    +
    +        else:
    +            s, s_i, hscroll = _edit_text(c, s, s_i, hscroll,
    +                                         _width(edit_box) - 2)
    +
    +
    +# Obscure Python: We never pass a value for cached_nodes, and it keeps 
pointing
    +# to the same list. This avoids a global.
    +def _sorted_sc_nodes(cached_nodes=[]):
    +    # Returns a sorted list of symbol and choice nodes to search. The 
symbol
    +    # nodes appear first, sorted by name, and then the choice nodes, 
sorted by
    +    # prompt and (secondarily) name.
    +
    +    if not cached_nodes:
    +        # Add symbol nodes
    +        for sym in sorted(_kconf.unique_defined_syms,
    +                          key=lambda sym: sym.name):
    +            # += is in-place for lists
    +            cached_nodes += sym.nodes
    +
    +        # Add choice nodes
    +
    +        choices = sorted(_kconf.unique_choices,
    +                         key=lambda choice: choice.name or "")
    +
    +        cached_nodes += sorted(
    +            [node
    +             for choice in choices
    +                 for node in choice.nodes],
    +            key=lambda node: node.prompt[0] if node.prompt else "")
    +
    +    return cached_nodes
    +
    +
    +def _sorted_menu_comment_nodes(cached_nodes=[]):
    +    # Returns a list of menu and comment nodes to search, sorted by prompt,
    +    # with the menus first
    +
    +    if not cached_nodes:
    +        def prompt_text(mc):
    +            return mc.prompt[0]
    +
    +        cached_nodes += sorted(_kconf.menus, key=prompt_text)
    +        cached_nodes += sorted(_kconf.comments, key=prompt_text)
    +
    +    return cached_nodes
    +
    +
    +def _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
    +                           sel_node_i, scroll):
    +    # Resizes the jump-to dialog to fill the terminal.
    +    #
    +    # Returns the new scroll index. We adjust the scroll if needed so that 
the
    +    # selected node stays visible.
    +
    +    screen_height, screen_width = _stdscr.getmaxyx()
    +
    +    bot_sep_win.resize(1, screen_width)
    +
    +    help_win_height = len(_JUMP_TO_HELP_LINES)
    +    matches_win_height = screen_height - help_win_height - 4
    +
    +    if matches_win_height >= 1:
    +        edit_box.resize(3, screen_width)
    +        matches_win.resize(matches_win_height, screen_width)
    +        help_win.resize(help_win_height, screen_width)
    +
    +        matches_win.mvwin(3, 0)
    +        bot_sep_win.mvwin(3 + matches_win_height, 0)
    +        help_win.mvwin(3 + matches_win_height + 1, 0)
    +    else:
    +        # Degenerate case. Give up on nice rendering and just prevent 
errors.
    +
    +        matches_win_height = 1
    +
    +        edit_box.resize(screen_height, screen_width)
    +        matches_win.resize(1, screen_width)
    +        help_win.resize(1, screen_width)
    +
    +        for win in matches_win, bot_sep_win, help_win:
    +            win.mvwin(0, 0)
    +
    +    # Adjust the scroll so that the selected row is still within the 
window, if
    +    # needed
    +    if sel_node_i - scroll >= matches_win_height:
    +        return sel_node_i - matches_win_height + 1
    +    return scroll
    +
    +
    +def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
    +                         s, s_i, hscroll,
    +                         bad_re, matches, sel_node_i, scroll):
    +
    +    edit_width = _width(edit_box) - 2
    +
    +
    +    #
    +    # Update list of matches
    +    #
    +
    +    matches_win.erase()
    +
    +    if matches:
    +        for i in range(scroll,
    +                       min(scroll + _height(matches_win), len(matches))):
    +
    +            node = matches[i]
    +
    +            if isinstance(node.item, (Symbol, Choice)):
    +                node_str = _name_and_val_str(node.item)
    +                if node.prompt:
    +                    node_str += ' "{}"'.format(node.prompt[0])
    +            elif node.item == MENU:
    +                node_str = 'menu "{}"'.format(node.prompt[0])
    +            else:  # node.item == COMMENT
    +                node_str = 'comment "{}"'.format(node.prompt[0])
    +
    +            _safe_addstr(matches_win, i - scroll, 0, node_str,
    +                         _style["selection" if i == sel_node_i else 
"list"])
    +
    +    else:
    +        # bad_re holds the error message from the re.error exception on 
errors
    +        _safe_addstr(matches_win, 0, 0, bad_re or "No matches")
    +
    +    matches_win.noutrefresh()
    +
    +
    +    #
    +    # Update bottom separator line
    +    #
    +
    +    bot_sep_win.erase()
    +
    +    # Draw arrows pointing down if the symbol list is scrolled up
    +    if scroll < _max_scroll(matches, matches_win):
    +        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
    +
    +    bot_sep_win.noutrefresh()
    +
    +
    +    #
    +    # Update help window at bottom
    +    #
    +
    +    help_win.erase()
    +
    +    for i, line in enumerate(_JUMP_TO_HELP_LINES):
    +        _safe_addstr(help_win, i, 0, line)
    +
    +    help_win.noutrefresh()
    +
    +
    +    #
    +    # Update edit box. We do this last since it makes it handy to position 
the
    +    # cursor.
    +    #
    +
    +    edit_box.erase()
    +
    +    _draw_frame(edit_box, "Jump to symbol/choice/menu/comment")
    +
    +    # Draw arrows pointing up if the symbol list is scrolled down
    +    if scroll > 0:
    +        # TODO: Bit ugly that _style["frame"] is repeated here
    +        _safe_hline(edit_box, 2, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS,
    +                    _style["frame"])
    +
    +    visible_s = s[hscroll:hscroll + edit_width]
    +    _safe_addstr(edit_box, 1, 1, visible_s)
    +
    +    _safe_move(edit_box, 1, 1 + s_i - hscroll)
    +
    +    edit_box.noutrefresh()
    +
    +
    +def _info_dialog(node, from_jump_to_dialog):
    +    # Shows a fullscreen window with information about 'node'.
    +    #
    +    # If 'from_jump_to_dialog' is True, the information dialog was opened 
from
    +    # within the jump-to-dialog. In this case, we make '/' from within the
    +    # information dialog just return, to avoid a confusing recursive 
invocation
    +    # of the jump-to-dialog.
    +
    +    # Top row, with title and arrows point up
    +    top_line_win = _styled_win("separator")
    +
    +    # Text display
    +    text_win = _styled_win("text")
    +    text_win.keypad(True)
    +
    +    # Bottom separator, with arrows pointing down
    +    bot_sep_win = _styled_win("separator")
    +
    +    # Help window with keys at the bottom
    +    help_win = _styled_win("help")
    +
    +    # Give windows their initial size
    +    _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
    +
    +
    +    # Get lines of help text
    +    lines = _info_str(node).split("\n")
    +
    +    # Index of first row in 'lines' to show
    +    scroll = 0
    +
    +    while True:
    +        _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
    +                          bot_sep_win, help_win)
    +        curses.doupdate()
    +
    +
    +        c = _getch_compat(text_win)
    +
    +        if c == curses.KEY_RESIZE:
    +            _resize_info_dialog(top_line_win, text_win, bot_sep_win, 
help_win)
    +
    +        elif c in (curses.KEY_DOWN, "j", "J"):
    +            if scroll < _max_scroll(lines, text_win):
    +                scroll += 1
    +
    +        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
    +            scroll = min(scroll + _PG_JUMP, _max_scroll(lines, text_win))
    +
    +        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
    +            scroll = max(scroll - _PG_JUMP, 0)
    +
    +        elif c in (curses.KEY_END, "G"):
    +            scroll = _max_scroll(lines, text_win)
    +
    +        elif c in (curses.KEY_HOME, "g"):
    +            scroll = 0
    +
    +        elif c in (curses.KEY_UP, "k", "K"):
    +            if scroll > 0:
    +                scroll -= 1
    +
    +        elif c == "/":
    +            # Support starting a search from within the information dialog
    +
    +            if from_jump_to_dialog:
    +                # Avoid recursion
    +                return
    +
    +            if _jump_to_dialog():
    +                # Jumped to a symbol. Cancel the information dialog.
    +                return
    +
    +            # Stay in the information dialog if the jump-to dialog was
    +            # canceled. Resize it in case the terminal was resized while 
the
    +            # fullscreen jump-to dialog was open.
    +            _resize_info_dialog(top_line_win, text_win, bot_sep_win, 
help_win)
    +
    +        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
    +                   "\x1B",  # \x1B = ESC
    +                   "q", "Q", "h", "H"):
    +
    +            return
    +
    +
    +def _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win):
    +    # Resizes the info dialog to fill the terminal
    +
    +    screen_height, screen_width = _stdscr.getmaxyx()
    +
    +    top_line_win.resize(1, screen_width)
    +    bot_sep_win.resize(1, screen_width)
    +
    +    help_win_height = len(_INFO_HELP_LINES)
    +    text_win_height = screen_height - help_win_height - 2
    +
    +    if text_win_height >= 1:
    +        text_win.resize(text_win_height, screen_width)
    +        help_win.resize(help_win_height, screen_width)
    +
    +        text_win.mvwin(1, 0)
    +        bot_sep_win.mvwin(1 + text_win_height, 0)
    +        help_win.mvwin(1 + text_win_height + 1, 0)
    +    else:
    +        # Degenerate case. Give up on nice rendering and just prevent 
errors.
    +
    +        text_win.resize(1, screen_width)
    +        help_win.resize(1, screen_width)
    +
    +        for win in text_win, bot_sep_win, help_win:
    +            win.mvwin(0, 0)
    +
    +
    +def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
    +                      bot_sep_win, help_win):
    +
    +    text_win_height, text_win_width = text_win.getmaxyx()
    +
    +
    +    # Note: The top row is deliberately updated last. See _draw_main().
    +
    +    #
    +    # Update text display
    +    #
    +
    +    text_win.erase()
    +
    +    for i, line in enumerate(lines[scroll:scroll + text_win_height]):
    +        _safe_addstr(text_win, i, 0, line)
    +
    +    text_win.noutrefresh()
    +
    +
    +    #
    +    # Update bottom separator line
    +    #
    +
    +    bot_sep_win.erase()
    +
    +    # Draw arrows pointing down if the symbol window is scrolled up
    +    if scroll < _max_scroll(lines, text_win):
    +        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
    +
    +    bot_sep_win.noutrefresh()
    +
    +
    +    #
    +    # Update help window at bottom
    +    #
    +
    +    help_win.erase()
    +
    +    for i, line in enumerate(_INFO_HELP_LINES):
    +        _safe_addstr(help_win, i, 0, line)
    +
    +    help_win.noutrefresh()
    +
    +
    +    #
    +    # Update top row
    +    #
    +
    +    top_line_win.erase()
    +
    +    # Draw arrows pointing up if the information window is scrolled down. 
Draw
    +    # them before drawing the title, so the title ends up on top for small
    +    # windows.
    +    if scroll > 0:
    +        _safe_hline(top_line_win, 0, 4, curses.ACS_UARROW, 
_N_SCROLL_ARROWS)
    +
    +    title = ("Symbol" if isinstance(node.item, Symbol) else
    +             "Choice" if isinstance(node.item, Choice) else
    +             "Menu"   if node.item == MENU else
    +             "Comment") + " information"
    +    _safe_addstr(top_line_win, 0, max((text_win_width - len(title))//2, 0),
    +                 title)
    +
    +    top_line_win.noutrefresh()
    +
    +
    +def _info_str(node):
    +    # Returns information about the menu node 'node' as a string.
    +    #
    +    # The helper functions are responsible for adding newlines. This allows
    +    # them to return "" if they don't want to add any output.
    +
    +    if isinstance(node.item, Symbol):
    +        sym = node.item
    +
    +        return (
    +            _name_info(sym) +
    +            _prompt_info(sym) +
    +            "Type: {}\n".format(TYPE_TO_STR[sym.type]) +
    +            _value_info(sym) +
    +            _help_info(sym) +
    +            _direct_dep_info(sym) +
    +            _defaults_info(sym) +
    +            _select_imply_info(sym) +
    +            _kconfig_def_info(sym)
    +        )
    +
    +    if isinstance(node.item, Choice):
    +        choice = node.item
    +
    +        return (
    +            _name_info(choice) +
    +            _prompt_info(choice) +
    +            "Type: {}\n".format(TYPE_TO_STR[choice.type]) +
    +            'Mode: {}\n'.format(choice.str_value) +
    +            _help_info(choice) +
    +            _choice_syms_info(choice) +
    +            _direct_dep_info(choice) +
    +            _defaults_info(choice) +
    +            _kconfig_def_info(choice)
    +        )
    +
    +    # node.item in (MENU, COMMENT)
    +    return _kconfig_def_info(node)
    +
    +
    +def _name_info(sc):
    +    # Returns a string with the name of the symbol/choice. Names are 
optional
    +    # for choices.
    +
    +    return "Name: {}\n".format(sc.name) if sc.name else ""
    +
    +
    +def _prompt_info(sc):
    +    # Returns a string listing the prompts of 'sc' (Symbol or Choice)
    +
    +    s = ""
    +
    +    for node in sc.nodes:
    +        if node.prompt:
    +            s += "Prompt: {}\n".format(node.prompt[0])
    +
    +    return s
    +
    +
    +def _value_info(sym):
    +    # Returns a string showing 'sym's value
    +
    +    # Only put quotes around the value for string symbols
    +    return "Value: {}\n".format(
    +        '"{}"'.format(sym.str_value)
    +        if sym.orig_type == STRING
    +        else sym.str_value)
    +
    +
    +def _choice_syms_info(choice):
    +    # Returns a string listing the choice symbols in 'choice'. Adds
    +    # "(selected)" next to the selected one.
    +
    +    s = "Choice symbols:\n"
    +
    +    for sym in choice.syms:
    +        s += "  - " + sym.name
    +        if sym is choice.selection:
    +            s += " (selected)"
    +        s += "\n"
    +
    +    return s + "\n"
    +
    +
    +def _help_info(sc):
    +    # Returns a string with the help text(s) of 'sc' (Symbol or Choice).
    +    # Symbols and choices defined in multiple locations can have multiple 
help
    +    # texts.
    +
    +    s = "\n"
    +
    +    for node in sc.nodes:
    +        if node.help is not None:
    +            s += "Help:\n\n{}\n\n".format(_indent(node.help, 2))
    +
    +    return s
    +
    +
    +def _direct_dep_info(sc):
    +    # Returns a string describing the direct dependencies of 'sc' (Symbol 
or
    +    # Choice). The direct dependencies are the OR of the dependencies from 
each
    +    # definition location. The dependencies at each definition location 
come
    +    # from 'depends on' and dependencies inherited from parent items.
    +
    +    return "" if sc.direct_dep is _kconf.y else \
    +        'Direct dependencies (={}):\n{}\n' \
    +        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
    +                _split_expr_info(sc.direct_dep, 2))
    +
    +
    +def _defaults_info(sc):
    +    # Returns a string describing the defaults of 'sc' (Symbol or Choice)
    +
    +    if not sc.defaults:
    +        return ""
    +
    +    s = "Default"
    +    if len(sc.defaults) > 1:
    +        s += "s"
    +    s += ":\n"
    +
    +    for val, cond in sc.orig_defaults:
    +        s += "  - "
    +        if isinstance(sc, Symbol):
    +            s += _expr_str(val)
    +
    +            # Skip the tristate value hint if the expression is just a 
single
    +            # symbol. _expr_str() already shows its value as a string.
    +            #
    +            # This also avoids showing the tristate value for 
string/int/hex
    +            # defaults, which wouldn't make any sense.
    +            if isinstance(val, tuple):
    +                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
    +        else:
    +            # Don't print the value next to the symbol name for choice
    +            # defaults, as it looks a bit confusing
    +            s += val.name
    +        s += "\n"
    +
    +        if cond is not _kconf.y:
    +            s += "    Condition (={}):\n{}" \
    +                 .format(TRI_TO_STR[expr_value(cond)],
    +                         _split_expr_info(cond, 4))
    +
    +    return s + "\n"
    +
    +
    +def _split_expr_info(expr, indent):
    +    # Returns a string with 'expr' split into its top-level && or || 
operands,
    +    # with one operand per line, together with the operand's value. This is
    +    # usually enough to get something readable for long expressions. A 
fancier
    +    # recursive thingy would be possible too.
    +    #
    +    # indent:
    +    #   Number of leading spaces to add before the split expression.
    +
    +    if len(split_expr(expr, AND)) > 1:
    +        split_op = AND
    +        op_str = "&&"
    +    else:
    +        split_op = OR
    +        op_str = "||"
    +
    +    s = ""
    +    for i, term in enumerate(split_expr(expr, split_op)):
    +        s += "{}{} {}".format(indent*" ",
    +                              "  " if i == 0 else op_str,
    +                              _expr_str(term))
    +
    +        # Don't bother showing the value hint if the expression is just a
    +        # single symbol. _expr_str() already shows its value.
    +        if isinstance(term, tuple):
    +            s += "  (={})".format(TRI_TO_STR[expr_value(term)])
    +
    +        s += "\n"
    +
    +    return s
    +
    +
    +def _select_imply_info(sym):
    +    # Returns a string with information about which symbols 'select' or 
'imply'
    +    # 'sym'. The selecting/implying symbols are grouped according to which
    +    # value they select/imply 'sym' to (n/m/y).
    +
    +    def sis(expr, val, title):
    +        # sis = selects/implies
    +        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
    +        if not sis:
    +            return ""
    +
    +        res = title
    +        for si in sis:
    +            res += "  - {}\n".format(split_expr(si, AND)[0].name)
    +        return res + "\n"
    +
    +    s = ""
    +
    +    if sym.rev_dep is not _kconf.n:
    +        s += sis(sym.rev_dep, 2,
    +                 "Symbols currently y-selecting this symbol:\n")
    +        s += sis(sym.rev_dep, 1,
    +                 "Symbols currently m-selecting this symbol:\n")
    +        s += sis(sym.rev_dep, 0,
    +                 "Symbols currently n-selecting this symbol (no 
effect):\n")
    +
    +    if sym.weak_rev_dep is not _kconf.n:
    +        s += sis(sym.weak_rev_dep, 2,
    +                 "Symbols currently y-implying this symbol:\n")
    +        s += sis(sym.weak_rev_dep, 1,
    +                 "Symbols currently m-implying this symbol:\n")
    +        s += sis(sym.weak_rev_dep, 0,
    +                 "Symbols currently n-implying this symbol (no effect):\n")
    +
    +    return s
    +
    +
    +def _kconfig_def_info(item):
    +    # Returns a string with the definition of 'item' in Kconfig syntax,
    +    # together with the definition location(s) and their include and menu 
paths
    +
    +    nodes = [item] if isinstance(item, MenuNode) else item.nodes
    +
    +    s = "Kconfig definition{}, with parent deps. propagated to 'depends 
on'\n" \
    +        .format("s" if len(nodes) > 1 else "")
    +    s += (len(s) - 1)*"="
    +
    +    for node in nodes:
    +        s += "\n\n" \
    +             "At {}:{}\n" \
    +             "{}" \
    +             "Menu path: {}\n\n" \
    +             "{}" \
    +             .format(node.filename, node.linenr,
    +                     _include_path_info(node),
    +                     _menu_path_info(node),
    +                     _indent(node.custom_str(_name_and_val_str), 2))
    +
    +    return s
    +
    +
    +def _include_path_info(node):
    +    if not node.include_path:
    +        # In the top-level Kconfig file
    +        return ""
    +
    +    return "Included via {}\n".format(
    +        " -> ".join("{}:{}".format(filename, linenr)
    +                    for filename, linenr in node.include_path))
    +
    +
    +def _menu_path_info(node):
    +    # Returns a string describing the menu path leading up to 'node'
    +
    +    path = ""
    +
    +    while node.parent is not _kconf.top_node:
    +        node = node.parent
    +
    +        # Promptless choices might appear among the parents. Use
    +        # standard_sc_expr_str() for them, so that they show up as
    +        # '<choice (name if any)>'.
    +        path = " -> " + (node.prompt[0] if node.prompt else
    +                         standard_sc_expr_str(node.item)) + path
    +
    +    return "(Top)" + path
    +
    +
    +def _indent(s, n):
    +    # Returns 's' with each line indented 'n' spaces. textwrap.indent() is 
not
    +    # available in Python 2 (it's 3.3+).
    +
    +    return "\n".join(n*" " + line for line in s.split("\n"))
    +
    +
    +def _name_and_val_str(sc):
    +    # Custom symbol/choice printer that shows symbol values after symbols
    +
    +    # Show the values of non-constant (non-quoted) symbols that don't look 
like
    +    # numbers. Things like 123 are actually symbol references, and only 
work as
    +    # expected due to undefined symbols getting their name as their value.
    +    # Showing the symbol value for those isn't helpful though.
    +    if isinstance(sc, Symbol) and not sc.is_constant and not 
_is_num(sc.name):
    +        if not sc.nodes:
    +            # Undefined symbol reference
    +            return "{}(undefined/n)".format(sc.name)
    +
    +        return '{}(={})'.format(sc.name, sc.str_value)
    +
    +    # For other items, use the standard format
    +    return standard_sc_expr_str(sc)
    +
    +
    +def _expr_str(expr):
    +    # Custom expression printer that shows symbol values
    +    return expr_str(expr, _name_and_val_str)
    +
    +
    +def _styled_win(style):
    +    # Returns a new curses window with style 'style' and space as the fill
    +    # character. The initial dimensions are (1, 1), so the window needs to 
be
    +    # sized and positioned separately.
    +
    +    win = curses.newwin(1, 1)
    +    _set_style(win, style)
    +    return win
    +
    +
    +def _set_style(win, style):
    +    # Changes the style of an existing window
    +
    +    win.bkgdset(" ", _style[style])
    +
    +
    +def _max_scroll(lst, win):
    +    # Assuming 'lst' is a list of items to be displayed in 'win',
    +    # returns the maximum number of steps 'win' can be scrolled down.
    +    # We stop scrolling when the bottom item is visible.
    +
    +    return max(0, len(lst) - _height(win))
    +
    +
    +def _edit_text(c, s, i, hscroll, width):
    +    # Implements text editing commands for edit boxes. Takes a character 
(which
    +    # could also be e.g. curses.KEY_LEFT) and the edit box state, and 
returns
    +    # the new state after the character has been processed.
    +    #
    +    # c:
    +    #   Character from user
    +    #
    +    # s:
    +    #   Current contents of string
    +    #
    +    # i:
    +    #   Current cursor index in string
    +    #
    +    # hscroll:
    +    #   Index in s of the leftmost character in the edit box, for 
horizontal
    +    #   scrolling
    +    #
    +    # width:
    +    #   Width in characters of the edit box
    +    #
    +    # Return value:
    +    #   An (s, i, hscroll) tuple for the new state
    +
    +    if c == curses.KEY_LEFT:
    +        if i > 0:
    +            i -= 1
    +
    +    elif c == curses.KEY_RIGHT:
    +        if i < len(s):
    +            i += 1
    +
    +    elif c in (curses.KEY_HOME, "\x01"):  # \x01 = CTRL-A
    +        i = 0
    +
    +    elif c in (curses.KEY_END, "\x05"):  # \x05 = CTRL-E
    +        i = len(s)
    +
    +    elif c in (curses.KEY_BACKSPACE, _ERASE_CHAR):
    +        if i > 0:
    +            s = s[:i-1] + s[i:]
    +            i -= 1
    +
    +    elif c == curses.KEY_DC:
    +        s = s[:i] + s[i+1:]
    +
    +    elif c == "\x17":  # \x17 = CTRL-W
    +        # The \W removes characters like ',' one at a time
    +        new_i = re.search(r"(?:\w*|\W)\s*$", s[:i]).start()
    +        s = s[:new_i] + s[i:]
    +        i = new_i
    +
    +    elif c == "\x0B":  # \x0B = CTRL-K
    +        s = s[:i]
    +
    +    elif c == "\x15":  # \x15 = CTRL-U
    +        s = s[i:]
    +        i = 0
    +
    +    elif isinstance(c, str):
    +        # Insert character
    +        s = s[:i] + c + s[i:]
    +        i += 1
    +
    +    # Adjust the horizontal scroll so that the cursor never touches the 
left or
    +    # right edges of the edit box, except when it's at the beginning or 
the end
    +    # of the string
    +    if i < hscroll + _SCROLL_OFFSET:
    +        hscroll = max(i - _SCROLL_OFFSET, 0)
    +    elif i >= hscroll + width - _SCROLL_OFFSET:
    +        max_scroll = max(len(s) - width + 1, 0)
    +        hscroll = min(i - width + _SCROLL_OFFSET + 1, max_scroll)
    +
    +
    +    return s, i, hscroll
    +
    +
    +def _load_save_info():
    +    # Returns an information string for load/save dialog boxes
    +
    +    return "(Relative to {})\n\nRefer to your home directory with ~" \
    +           .format(os.path.join(os.getcwd(), ""))
    +
    +
    +def _msg(title, text):
    +    # Pops up a message dialog that can be dismissed with Space/Enter/ESC
    +
    +    _key_dialog(title, text, " \n")
    +
    +
    +def _error(text):
    +    # Pops up an error dialog that can be dismissed with Space/Enter/ESC
    +
    +    _msg("Error", text)
    +
    +
    +def _node_str(node):
    +    # Returns the complete menu entry text for a menu node.
    +    #
    +    # Example return value: "[*] Support for X"
    +
    +    # Calculate the indent to print the item with by checking how many 
levels
    +    # above it the closest 'menuconfig' item is (this includes menus and
    +    # choices as well as menuconfig symbols)
    +    indent = 0
    +    parent = node.parent
    +    while not parent.is_menuconfig:
    +        indent += _SUBMENU_INDENT
    +        parent = parent.parent
    +
    +    # This approach gives nice alignment for empty string symbols ("()  
Foo")
    +    s = "{:{}}".format(_value_str(node), 3 + indent)
    +
    +    if _should_show_name(node):
    +        if isinstance(node.item, Symbol):
    +            s += " <{}>".format(node.item.name)
    +        else:
    +            # For choices, use standard_sc_expr_str(). That way they show 
up as
    +            # '<choice (name if any)>'.
    +            s += " " + standard_sc_expr_str(node.item)
    +
    +    if node.prompt:
    +        if node.item == COMMENT:
    +            s += " *** {} ***".format(node.prompt[0])
    +        else:
    +            s += " " + node.prompt[0]
    +
    +        if isinstance(node.item, Symbol):
    +            sym = node.item
    +
    +            # Print "(NEW)" next to symbols without a user value (from 
e.g. a
    +            # .config), but skip it for choice symbols in choices in y 
mode,
    +            # and for symbols of UNKNOWN type (which generate a warning 
though)
    +            if sym.user_value is None and sym.orig_type and \
    +               not (sym.choice and sym.choice.tri_value == 2):
    +
    +                s += " (NEW)"
    +
    +    if isinstance(node.item, Choice) and node.item.tri_value == 2:
    +        # Print the prompt of the selected symbol after the choice for
    +        # choices in y mode
    +        sym = node.item.selection
    +        if sym:
    +            for sym_node in sym.nodes:
    +                # Use the prompt used at this choice location, in case the
    +                # choice symbol is defined in multiple locations
    +                if sym_node.parent is node and sym_node.prompt:
    +                    s += " ({})".format(sym_node.prompt[0])
    +                    break
    +            else:
    +                # If the symbol isn't defined at this choice location, then
    +                # just use whatever prompt we can find for it
    +                for sym_node in sym.nodes:
    +                    if sym_node.prompt:
    +                        s += " ({})".format(sym_node.prompt[0])
    +                        break
    +
    +    # Print "--->" next to nodes that have menus that can potentially be
    +    # entered. Print "----" if the menu is empty. We don't allow those to 
be
    +    # entered.
    +    if node.is_menuconfig:
    +        s += "  --->" if _shown_nodes(node) else "  ----"
    +
    +    return s
    +
    +
    +def _should_show_name(node):
    +    # Returns True if 'node' is a symbol or choice whose name should shown 
(if
    +    # any, as names are optional for choices)
    +
    +    # The 'not node.prompt' case only hits in show-all mode, for promptless
    +    # symbols and choices
    +    return not node.prompt or \
    +           (_show_name and isinstance(node.item, (Symbol, Choice)))
    +
    +
    +def _value_str(node):
    +    # Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu node
    +
    +    item = node.item
    +
    +    if item in (MENU, COMMENT):
    +        return ""
    +
    +    # Wouldn't normally happen, and generates a warning
    +    if not item.orig_type:
    +        return ""
    +
    +    if item.orig_type in (STRING, INT, HEX):
    +        return "({})".format(item.str_value)
    +
    +    # BOOL or TRISTATE
    +
    +    if _is_y_mode_choice_sym(item):
    +        return "(X)" if item.choice.selection is item else "( )"
    +
    +    tri_val_str = (" ", "M", "*")[item.tri_value]
    +
    +    if len(item.assignable) <= 1:
    +        # Pinned to a single value
    +        return "" if isinstance(item, Choice) else 
"-{}-".format(tri_val_str)
    +
    +    if item.type == BOOL:
    +        return "[{}]".format(tri_val_str)
    +
    +    # item.type == TRISTATE
    +    if item.assignable == (1, 2):
    +        return "{{{}}}".format(tri_val_str)  # {M}/{*}
    +    return "<{}>".format(tri_val_str)
    +
    +
    +def _is_y_mode_choice_sym(item):
    +    # The choice mode is an upper bound on the visibility of choice 
symbols, so
    +    # we can check the choice symbols' own visibility to see if the choice 
is
    +    # in y mode
    +    return isinstance(item, Symbol) and item.choice and item.visibility == 
2
    +
    +
    +def _check_valid(sym, s):
    +    # Returns True if the string 's' is a well-formed value for 'sym'.
    +    # Otherwise, displays an error and returns False.
    +
    +    if sym.orig_type not in (INT, HEX):
    +        # Anything goes for non-int/hex symbols
    +        return True
    +
    +    base = 10 if sym.orig_type == INT else 16
    +    try:
    +        int(s, base)
    +    except ValueError:
    +        _error("'{}' is a malformed {} value"
    +               .format(s, TYPE_TO_STR[sym.orig_type]))
    +        return False
    +
    +    for low_sym, high_sym, cond in sym.ranges:
    +        if expr_value(cond):
    +            low_s = low_sym.str_value
    +            high_s = high_sym.str_value
    +
    +            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
    +                _error("{} is outside the range {}-{}"
    +                       .format(s, low_s, high_s))
    +                return False
    +
    +            break
    +
    +    return True
    +
    +
    +def _range_info(sym):
    +    # Returns a string with information about the valid range for the 
symbol
    +    # 'sym', or None if 'sym' doesn't have a range
    +
    +    if sym.orig_type in (INT, HEX):
    +        for low, high, cond in sym.ranges:
    +            if expr_value(cond):
    +                return "Range: {}-{}".format(low.str_value, high.str_value)
    +
    +    return None
    +
    +
    +def _is_num(name):
    +    # Heuristic to see if a symbol name looks like a number, for nicer 
output
    +    # when printing expressions. Things like 16 are actually symbol names, 
only
    +    # they get their name as their value when the symbol is undefined.
    +
    +    try:
    +        int(name)
    +    except ValueError:
    +        if not name.startswith(("0x", "0X")):
    +            return False
    +
    +        try:
    +            int(name, 16)
    +        except ValueError:
    +            return False
    +
    +    return True
    +
    +
    +def _getch_compat(win):
    +    # Uses get_wch() if available (Python 3.3+) and getch() otherwise. Also
    +    # handles a PDCurses resizing quirk.
    +
    +    if hasattr(win, "get_wch"):
    +        c = win.get_wch()
    +    else:
    +        c = win.getch()
    +        if 0 <= c <= 255:
    +            c = chr(c)
    +
    +    # Decent resizing behavior on PDCurses requires calling resize_term(0, 
0)
    +    # after receiving KEY_RESIZE, while ncurses (usually) handles terminal
    +    # resizing automatically in get(_w)ch() (see the end of the
    +    # resizeterm(3NCURSES) man page).
    +    #
    +    # resize_term(0, 0) reliably fails and does nothing on ncurses, so this
    +    # hack gives ncurses/PDCurses compatibility for resizing. I don't know
    +    # whether it would cause trouble for other implementations.
    +    if c == curses.KEY_RESIZE:
    +        try:
    +            curses.resize_term(0, 0)
    +        except curses.error:
    +            pass
    +
    +    return c
    +
    +
    +def _warn(*args):
    +    # Temporarily returns from curses to shell mode and prints a warning to
    +    # stderr. The warning would get lost in curses mode.
    +    curses.endwin()
    +    print("menuconfig warning: ", end="", file=sys.stderr)
    +    print(*args, file=sys.stderr)
    +    curses.doupdate()
    +
    +
    +# Ignore exceptions from some functions that might fail, e.g. for small
    +# windows. They usually do reasonable things anyway.
    +
    +
    +def _safe_curs_set(visibility):
    +    try:
    +        curses.curs_set(visibility)
    +    except curses.error:
    +        pass
    +
    +
    +def _safe_addstr(win, *args):
    +    # Clip the line to avoid wrapping to the next line, which looks 
glitchy.
    +    # addchstr() would do it for us, but it's not available in the 'curses'
    +    # module.
    +
    +    attr = None
    +    if isinstance(args[0], str):
    +        y, x = win.getyx()
    +        s = args[0]
    +        if len(args) == 2:
    +            attr = args[1]
    +    else:
    +        y, x, s = args[:3]
    +        if len(args) == 4:
    +            attr = args[3]
    +
    +    maxlen = _width(win) - x
    +    s = s.expandtabs()
    +
    +    try:
    +        # The 'curses' module uses wattr_set() internally if you pass 
'attr',
    +        # overwriting the background style, so setting 'attr' to 0 in the 
first
    +        # case won't do the right thing
    +        if attr is None:
    +            win.addnstr(y, x, s, maxlen)
    +        else:
    +            win.addnstr(y, x, s, maxlen, attr)
    +    except curses.error:
    +        pass
    +
    +
    +def _safe_addch(win, *args):
    +    try:
    +        win.addch(*args)
    +    except curses.error:
    +        pass
    +
    +
    +def _safe_hline(win, *args):
    +    try:
    +        win.hline(*args)
    +    except curses.error:
    +        pass
    +
    +
    +def _safe_vline(win, *args):
    +    try:
    +        win.vline(*args)
    +    except curses.error:
    +        pass
    +
    +
    +def _safe_move(win, *args):
    +    try:
    +        win.move(*args)
    +    except curses.error:
    +        pass
    +
    +
    +def _change_c_lc_ctype_to_utf8():
    +    # See _CHANGE_C_LC_CTYPE_TO_UTF8
    +
    +    if _IS_WINDOWS:
    +        # Windows rarely has issues here, and the PEP 538 implementation 
avoids
    +        # changing the locale on it. None of the UTF-8 locales below were
    +        # supported from some quick testing either. Play it safe.
    +        return
    +
    +    def try_set_locale(loc):
    +        try:
    +            locale.setlocale(locale.LC_CTYPE, loc)
    +            return True
    +        except locale.Error:
    +            return False
    +
    +    # Is LC_CTYPE set to the C locale?
    +    if locale.setlocale(locale.LC_CTYPE) == "C":
    +        # This list was taken from the PEP 538 implementation in the 
CPython
    +        # code, in Python/pylifecycle.c
    +        for loc in "C.UTF-8", "C.utf8", "UTF-8":
    +            if try_set_locale(loc):
    +                # LC_CTYPE successfully changed
    +                return
    +
    +
    +# Are we running on Windows?
    +_IS_WINDOWS = os.name == "nt"
    +
    +if __name__ == "__main__":
    +    _main()
    diff --git a/support/kconfiglib/oldconfig.py 
b/support/kconfiglib/oldconfig.py
    new file mode 100755
    index 0000000..042ab44
    --- /dev/null
    +++ b/support/kconfiglib/oldconfig.py
    @@ -0,0 +1,246 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Implements oldconfig functionality.
    +
    +  1. Loads existing .config
    +  2. Prompts for the value of all modifiable symbols/choices that
    +     aren't already set in the .config
    +  3. Writes an updated .config
    +
    +The default input/output filename is '.config'. A different filename can be
    +passed in the KCONFIG_CONFIG environment variable.
    +
    +When overwriting a configuration file, the old version is saved to
    +<filename>.old (e.g. .config.old).
    +
    +Entering '?' displays the help text of the symbol/choice, if any.
    +
    +Unlike 'make oldconfig', this script doesn't print menu titles and 
comments,
    +but gives Kconfig definition locations. Printing menus and comments would 
be
    +pretty easy to add: Look at the parents of each item, and print all menu
    +prompts and comments unless they have already been printed (assuming you 
want
    +to skip "irrelevant" menus).
    +"""
    +from __future__ import print_function
    +
    +import sys
    +
    +from kconfiglib import Symbol, Choice, BOOL, TRISTATE, HEX, 
standard_kconfig
    +
    +
    +# Python 2/3 compatibility hack
    +if sys.version_info[0] < 3:
    +    input = raw_input
    +
    +
    +def _main():
    +    # Earlier symbols in Kconfig files might depend on later symbols and 
become
    +    # visible if their values change. This flag is set to True if the 
value of
    +    # any symbol changes, in which case we rerun the oldconfig to check 
for new
    +    # visible symbols.
    +    global conf_changed
    +
    +    kconf = standard_kconfig()
    +    print(kconf.load_config())
    +
    +    while True:
    +        conf_changed = False
    +
    +        for node in kconf.node_iter():
    +            oldconfig(node)
    +
    +        if not conf_changed:
    +            break
    +
    +    print(kconf.write_config())
    +
    +
    +def oldconfig(node):
    +    """
    +    Prompts the user for a value if node.item is a visible symbol/choice 
with
    +    no user value.
    +    """
    +    # See main()
    +    global conf_changed
    +
    +    # Only symbols and choices can be configured
    +    if not isinstance(node.item, (Symbol, Choice)):
    +        return
    +
    +    # Skip symbols and choices that aren't visible
    +    if not node.item.visibility:
    +        return
    +
    +    # Skip symbols and choices that don't have a prompt (at this location)
    +    if not node.prompt:
    +        return
    +
    +    if isinstance(node.item, Symbol):
    +        sym = node.item
    +
    +        # Skip symbols that already have a user value
    +        if sym.user_value is not None:
    +            return
    +
    +        # Skip symbols that can only have a single value, due to selects
    +        if len(sym.assignable) == 1:
    +            return
    +
    +        # Skip symbols in choices in y mode. We ask once for the entire 
choice
    +        # instead.
    +        if sym.choice and sym.choice.tri_value == 2:
    +            return
    +
    +        # Loop until the user enters a valid value or enters a blank string
    +        # (for the default value)
    +        while True:
    +            val = input("{} ({}) [{}] ".format(
    +                node.prompt[0], _name_and_loc_str(sym),
    +                _default_value_str(sym)))
    +
    +            if val == "?":
    +                _print_help(node)
    +                continue
    +
    +            # Substitute a blank string with the default value the symbol
    +            # would get
    +            if not val:
    +                val = sym.str_value
    +
    +            # Automatically add a "0x" prefix for hex symbols, like the
    +            # menuconfig interface does. This isn't done when loading 
.config
    +            # files, hence why set_value() doesn't do it automatically.
    +            if sym.type == HEX and not val.startswith(("0x", "0X")):
    +                val = "0x" + val
    +
    +            old_str_val = sym.str_value
    +
    +            # Kconfiglib itself will print a warning here if the value
    +            # is invalid, so we don't need to bother
    +            if sym.set_value(val):
    +                # Valid value input. We're done with this node.
    +
    +                if sym.str_value != old_str_val:
    +                    conf_changed = True
    +
    +                return
    +
    +    else:
    +        choice = node.item
    +
    +        # Skip choices that already have a visible user selection...
    +        if choice.user_selection and choice.user_selection.visibility == 2:
    +            # ...unless there are new visible symbols in the choice. (We 
know
    +            # they have y (2) visibility in that case, because m-visible
    +            # symbols get demoted to n-visibility in y-mode choices, and 
the
    +            # user-selected symbol had visibility y.)
    +            for sym in choice.syms:
    +                if sym is not choice.user_selection and sym.visibility and 
\
    +                   sym.user_value is None:
    +                    # New visible symbols in the choice
    +                    break
    +            else:
    +                # No new visible symbols in the choice
    +                return
    +
    +        # Get a list of available selections. The mode of the choice limits
    +        # the visibility of the choice value symbols, so this will 
indirectly
    +        # skip choices in n and m mode.
    +        options = [sym for sym in choice.syms if sym.visibility == 2]
    +
    +        if not options:
    +            # No y-visible choice value symbols
    +            return
    +
    +        # Loop until the user enters a valid selection or a blank string 
(for
    +        # the default selection)
    +        while True:
    +            print("{} ({})".format(node.prompt[0], 
_name_and_loc_str(choice)))
    +
    +            for i, sym in enumerate(options, 1):
    +                print("{} {}. {} ({})".format(
    +                    ">" if sym is choice.selection else " ",
    +                    i,
    +                    # Assume people don't define choice symbols with 
multiple
    +                    # prompts. That generates a warning anyway.
    +                    sym.nodes[0].prompt[0],
    +                    sym.name))
    +
    +            sel_index = input("choice[1-{}]: ".format(len(options)))
    +
    +            if sel_index == "?":
    +                _print_help(node)
    +                continue
    +
    +            # Pick the default selection if the string is blank
    +            if not sel_index:
    +                choice.selection.set_value(2)
    +                break
    +
    +            try:
    +                sel_index = int(sel_index)
    +            except ValueError:
    +                print("Bad index", file=sys.stderr)
    +                continue
    +
    +            if not 1 <= sel_index <= len(options):
    +                print("Bad index", file=sys.stderr)
    +                continue
    +
    +            # Valid selection
    +
    +            if options[sel_index - 1].tri_value != 2:
    +                conf_changed = True
    +
    +            options[sel_index - 1].set_value(2)
    +            break
    +
    +        # Give all of the non-selected visible choice symbols the user 
value n.
    +        # This makes it so that the choice is no longer considered new 
once we
    +        # do additional passes, if the reason that it was considered new 
was
    +        # that it had new visible choice symbols.
    +        #
    +        # Only giving visible choice symbols the user value n means we will
    +        # prompt for the choice again if later user selections make more 
new
    +        # choice symbols visible, which is correct.
    +        for sym in choice.syms:
    +            if sym is not choice.user_selection and sym.visibility:
    +                sym.set_value(0)
    +
    +
    +def _name_and_loc_str(sc):
    +    # Helper for printing the name of the symbol/choice 'sc' along with the
    +    # location(s) in the Kconfig files where it is defined. Unnamed choices
    +    # return "choice" instead of the name.
    +
    +    return "{}, defined at {}".format(
    +        sc.name or "choice",
    +        ", ".join("{}:{}".format(node.filename, node.linenr)
    +                  for node in sc.nodes))
    +
    +
    +def _print_help(node):
    +    print("\n" + (node.help or "No help text\n"))
    +
    +
    +def _default_value_str(sym):
    +    # Returns the "m/M/y" string in e.g.
    +    #
    +    #   TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]:
    +    #
    +    # For string/int/hex, returns the default value as-is.
    +
    +    if sym.type in (BOOL, TRISTATE):
    +        return "/".join(("NMY" if sym.tri_value == tri else "nmy")[tri]
    +                        for tri in sym.assignable)
    +
    +    # string/int/hex
    +    return sym.str_value
    +
    +
    +if __name__ == "__main__":
    +    _main()
    diff --git a/support/kconfiglib/olddefconfig.py 
b/support/kconfiglib/olddefconfig.py
    new file mode 100755
    index 0000000..a59a7d4
    --- /dev/null
    +++ b/support/kconfiglib/olddefconfig.py
    @@ -0,0 +1,28 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2018-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Updates an old .config file or creates a new one, by filling in default 
values
    +for all new symbols. This is the same as picking the default selection for 
all
    +symbols in oldconfig, or entering the menuconfig interface and immediately
    +saving.
    +
    +The default input/output filename is '.config'. A different filename can be
    +passed in the KCONFIG_CONFIG environment variable.
    +
    +When overwriting a configuration file, the old version is saved to
    +<filename>.old (e.g. .config.old).
    +"""
    +import kconfiglib
    +
    +
    +def main():
    +    kconf = kconfiglib.standard_kconfig()
    +    print(kconf.load_config())
    +    print(kconf.write_config())
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/savedefconfig.py 
b/support/kconfiglib/savedefconfig.py
    new file mode 100755
    index 0000000..c388f1a
    --- /dev/null
    +++ b/support/kconfiglib/savedefconfig.py
    @@ -0,0 +1,49 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Saves a minimal configuration file that only lists symbols that differ in 
value
    +from their defaults. Loading such a configuration file is equivalent to 
loading
    +the "full" configuration file.
    +
    +Minimal configuration files are handy to start from when editing 
configuration
    +files by hand.
    +
    +The default input configuration file is '.config'. A different input 
filename
    +can be passed in the KCONFIG_CONFIG environment variable.
    +
    +Note: Minimal configurations can also be generated from within the 
menuconfig
    +interface.
    +"""
    +import argparse
    +
    +import kconfiglib
    +
    +
    +def main():
    +    parser = argparse.ArgumentParser(
    +        formatter_class=argparse.RawDescriptionHelpFormatter,
    +        description=__doc__)
    +
    +    parser.add_argument(
    +        "--kconfig",
    +        default="Kconfig",
    +        help="Base Kconfig file (default: Kconfig)")
    +
    +    parser.add_argument(
    +        "--out",
    +        metavar="MINIMAL_CONFIGURATION",
    +        default="defconfig",
    +        help="Output filename for minimal configuration (default: 
defconfig)")
    +
    +    args = parser.parse_args()
    +
    +    kconf = kconfiglib.Kconfig(args.kconfig)
    +    print(kconf.load_config())
    +    print(kconf.write_min_config(args.out))
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/setconfig.py 
b/support/kconfiglib/setconfig.py
    new file mode 100755
    index 0000000..b689754
    --- /dev/null
    +++ b/support/kconfiglib/setconfig.py
    @@ -0,0 +1,92 @@
    +#!/usr/bin/env python
    +
    +# Copyright (c) 2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +"""
    +Simple utility for setting configuration values from the command line.
    +
    +Sample usage:
    +
    +  $ setconfig FOO_SUPPORT=y BAR_BITS=8
    +
    +Note: Symbol names should not be prefixed with 'CONFIG_'.
    +
    +The exit status on errors is 1.
    +
    +The default input/output configuration file is '.config'. A different 
filename
    +can be passed in the KCONFIG_CONFIG environment variable.
    +
    +When overwriting a configuration file, the old version is saved to
    +<filename>.old (e.g. .config.old).
    +"""
    +import argparse
    +import sys
    +
    +import kconfiglib
    +
    +
    +def main():
    +    parser = argparse.ArgumentParser(
    +        formatter_class=argparse.RawDescriptionHelpFormatter,
    +        description=__doc__)
    +
    +    parser.add_argument(
    +        "--kconfig",
    +        default="Kconfig",
    +        help="Base Kconfig file (default: Kconfig)")
    +
    +    parser.add_argument(
    +        "--no-check-exists",
    +        dest="check_exists",
    +        action="store_false",
    +        help="Ignore assignments to non-existent symbols instead of 
erroring "
    +             "out")
    +
    +    parser.add_argument(
    +        "--no-check-value",
    +        dest="check_value",
    +        action="store_false",
    +        help="Ignore assignments that didn't \"take\" (where the symbol 
got a "
    +             "different value, e.g. due to unsatisfied dependencies) 
instead "
    +             "of erroring out")
    +
    +    parser.add_argument(
    +        "assignments",
    +        metavar="ASSIGNMENT",
    +        nargs="*",
    +        help="A 'NAME=value' assignment")
    +
    +    args = parser.parse_args()
    +
    +    kconf = kconfiglib.Kconfig(args.kconfig)
    +    print(kconf.load_config())
    +
    +    for arg in args.assignments:
    +        if "=" not in arg:
    +            sys.exit("error: no '=' in assignment: '{}'".format(arg))
    +        name, value = arg.split("=", 1)
    +
    +        if name not in kconf.syms:
    +            if not args.check_exists:
    +                continue
    +            sys.exit("error: no symbol '{}' in configuration".format(name))
    +
    +        sym = kconf.syms[name]
    +
    +        if not sym.set_value(value):
    +            sys.exit("error: '{}' is an invalid value for the {} symbol {}"
    +                     .format(value, kconfiglib.TYPE_TO_STR[sym.orig_type],
    +                             name))
    +
    +        if args.check_value and sym.str_value != value:
    +            sys.exit("error: {} was assigned the value '{}', but got the "
    +                     "value '{}'. Check the symbol's dependencies, and 
make "
    +                     "sure that it has a prompt."
    +                     .format(name, value, sym.str_value))
    +
    +    print(kconf.write_config())
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/support/kconfiglib/setup.cfg b/support/kconfiglib/setup.cfg
    new file mode 100644
    index 0000000..185845e
    --- /dev/null
    +++ b/support/kconfiglib/setup.cfg
    @@ -0,0 +1,7 @@
    +[bdist_wheel]
    +# We support both Python 2 and Python 3
    +universal = 1
    +
    +[metadata]
    +# Include the license file in wheels
    +license_file = LICENSE.txt
    diff --git a/support/kconfiglib/setup.py b/support/kconfiglib/setup.py
    new file mode 100644
    index 0000000..9a3947e
    --- /dev/null
    +++ b/support/kconfiglib/setup.py
    @@ -0,0 +1,100 @@
    +import io
    +import os
    +
    +import setuptools
    +
    +
    +setuptools.setup(
    +    name="kconfiglib",
    +    # MAJOR.MINOR.PATCH, per http://semver.org
    +    version="12.13.0",
    +    description="A flexible Python Kconfig implementation",
    +
    +    # Make sure that README.rst decodes on Python 3 in environments that 
use
    +    # the C locale (which implies ASCII), by explicitly giving the 
encoding.
    +    #
    +    # io.open() has the 'encoding' parameter on both Python 2 and 3. open()
    +    # doesn't have it on Python 2. This lets us use the same code for both.
    +    long_description=io.open(
    +        os.path.join(os.path.dirname(__file__), "README.rst"),
    +        encoding="utf-8"
    +    ).read(),
    +
    +    url="https://github.com/ulfalizer/Kconfiglib";,
    +    author='Ulf "Ulfalizer" Magnusson',
    +    author_email="ulfalizer@xxxxxxxxx",
    +    keywords="kconfig, kbuild, menuconfig, configuration-management",
    +    license="ISC",
    +
    +    py_modules=(
    +        "kconfiglib",
    +        "menuconfig",
    +        "guiconfig",
    +        "genconfig",
    +        "oldconfig",
    +        "olddefconfig",
    +        "savedefconfig",
    +        "defconfig",
    +        "alldefconfig",
    +        "allnoconfig",
    +        "allmodconfig",
    +        "allyesconfig",
    +        "listnewconfig",
    +        "setconfig",
    +    ),
    +
    +    entry_points={
    +        "console_scripts": (
    +            "menuconfig = menuconfig:_main",
    +            "guiconfig = guiconfig:_main",
    +            "genconfig = genconfig:main",
    +            "oldconfig = oldconfig:_main",
    +            "olddefconfig = olddefconfig:main",
    +            "savedefconfig = savedefconfig:main",
    +            "defconfig = defconfig:main",
    +            "alldefconfig = alldefconfig:main",
    +            "allnoconfig = allnoconfig:main",
    +            "allmodconfig = allmodconfig:main",
    +            "allyesconfig = allyesconfig:main",
    +            "listnewconfig = listnewconfig:main",
    +            "setconfig = setconfig:main",
    +        )
    +    },
    +
    +    # The terminal menuconfig implementation uses the standard Python 
'curses'
    +    # module. The windows-curses package makes it available on Windows. See
    +    # https://github.com/zephyrproject-rtos/windows-curses.
    +    install_requires=(
    +        'windows-curses; sys_platform == "win32"',
    +    ),
    +
    +    # Needs support for unnumbered {} in format() and argparse
    +    python_requires=">=2.7,!=3.0.*,!=3.1.*",
    +
    +    project_urls={
    +        "GitHub repository": "https://github.com/ulfalizer/Kconfiglib";,
    +        "Examples": 
"https://github.com/ulfalizer/Kconfiglib/tree/master/examples";,
    +    },
    +
    +    classifiers=[
    +        "Development Status :: 5 - Production/Stable",
    +        "Intended Audience :: Developers",
    +        "Topic :: Software Development :: Build Tools",
    +        "Topic :: System :: Operating System Kernels :: Linux",
    +        "License :: OSI Approved :: ISC License (ISCL)",
    +        "Operating System :: POSIX",
    +        "Operating System :: Microsoft :: Windows",
    +        "Programming Language :: Python :: 2",
    +        "Programming Language :: Python :: 2.7",
    +        "Programming Language :: Python :: 3",
    +        "Programming Language :: Python :: 3.2",
    +        "Programming Language :: Python :: 3.3",
    +        "Programming Language :: Python :: 3.4",
    +        "Programming Language :: Python :: 3.5",
    +        "Programming Language :: Python :: 3.6",
    +        "Programming Language :: Python :: 3.7",
    +        "Programming Language :: Python :: 3.8",
    +        "Programming Language :: Python :: Implementation :: CPython",
    +        "Programming Language :: Python :: Implementation :: PyPy",
    +    ]
    +)
    diff --git a/support/kconfiglib/tests/Kappend 
b/support/kconfiglib/tests/Kappend
    new file mode 100644
    index 0000000..ce1478f
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kappend
    @@ -0,0 +1,12 @@
    +config MODULES
    +    def_bool y
    +
    +config BOOL
    +    bool "bool 1"
    +
    +config STRING
    +    string "string"
    +
    +config IGNOREME
    +    bool "ignore me"
    +    default y
    diff --git a/support/kconfiglib/tests/Kassignable 
b/support/kconfiglib/tests/Kassignable
    new file mode 100644
    index 0000000..26b4177
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kassignable
    @@ -0,0 +1,230 @@
    +config MODULES
    +   bool "modules"
    +   option modules
    +
    +
    +# Things that should never be .assignable
    +
    +if UNDEFINED && "const"
    +endif
    +
    +config NO_PROMPT
    +   bool
    +
    +config STRING
    +   string "string"
    +
    +config INT
    +   int "int"
    +
    +config HEX
    +   hex "hex"
    +
    +
    +# Non-selected symbols
    +
    +config Y_VIS_BOOL
    +   bool "y-vis bool"
    +
    +config M_VIS_BOOL
    +   bool "m-vis bool" if m
    +
    +config N_VIS_BOOL
    +   bool "n-vis bool" if n
    +
    +config Y_VIS_TRI
    +   tristate "y-vis tri"
    +
    +config M_VIS_TRI
    +   tristate "m-vis tri" if m
    +
    +config N_VIS_TRI
    +   tristate "n-vis tri" if n
    +
    +
    +# Symbols selected to y
    +
    +config Y_SELECTOR
    +   def_tristate y
    +
    +   select Y_SEL_Y_VIS_BOOL
    +   select Y_SEL_M_VIS_BOOL
    +   select Y_SEL_N_VIS_BOOL
    +
    +   select Y_SEL_Y_VIS_TRI
    +   select Y_SEL_M_VIS_TRI
    +   select Y_SEL_N_VIS_TRI
    +
    +config Y_SEL_Y_VIS_BOOL
    +   bool "y-sel y-vis bool"
    +
    +config Y_SEL_M_VIS_BOOL
    +   bool "y-sel m-vis bool" if m
    +
    +config Y_SEL_N_VIS_BOOL
    +   bool "y-sel n-vis bool" if n
    +
    +config Y_SEL_Y_VIS_TRI
    +   tristate "y-sel y-vis tri"
    +
    +config Y_SEL_M_VIS_TRI
    +   tristate "y-sel m-vis tri" if m
    +
    +config Y_SEL_N_VIS_TRI
    +   tristate "y-sel n-vis tri" if n
    +
    +
    +# Symbols selected to m
    +
    +config M_SELECTOR
    +   def_tristate m
    +
    +   select M_SEL_Y_VIS_BOOL
    +   select M_SEL_M_VIS_BOOL
    +   select M_SEL_N_VIS_BOOL
    +
    +   select M_SEL_Y_VIS_TRI
    +   select M_SEL_M_VIS_TRI
    +   select M_SEL_N_VIS_TRI
    +
    +config M_SEL_Y_VIS_BOOL
    +   bool "m-sel y-vis bool"
    +
    +config M_SEL_M_VIS_BOOL
    +   bool "m-sel m-vis bool" if m
    +
    +config M_SEL_N_VIS_BOOL
    +   bool "m-sel n-vis bool" if n
    +
    +config M_SEL_Y_VIS_TRI
    +   tristate "m-sel y-vis tri"
    +
    +config M_SEL_M_VIS_TRI
    +   tristate "m-sel m-vis tri" if m
    +
    +config M_SEL_N_VIS_TRI
    +   tristate "m-sel n-vis tri" if n
    +
    +
    +# Symbols implied to y
    +
    +config Y_IMPLIER
    +   def_tristate y
    +
    +   imply Y_IMP_Y_VIS_BOOL
    +   imply Y_IMP_M_VIS_BOOL
    +   imply Y_IMP_N_VIS_BOOL
    +
    +   imply Y_IMP_Y_VIS_TRI
    +   imply Y_IMP_M_VIS_TRI
    +   imply Y_IMP_N_VIS_TRI
    +
    +config Y_IMP_Y_VIS_BOOL
    +   bool "y-imp y-vis bool"
    +
    +config Y_IMP_M_VIS_BOOL
    +   bool "y-imp m-vis bool" if m
    +
    +config Y_IMP_N_VIS_BOOL
    +   bool "y-imp n-vis bool" if n
    +
    +config Y_IMP_Y_VIS_TRI
    +   tristate "y-imp y-vis tri"
    +
    +config Y_IMP_M_VIS_TRI
    +   tristate "y-imp m-vis tri" if m
    +
    +config Y_IMP_N_VIS_TRI
    +   tristate "y-imp n-vis tri" if n
    +
    +
    +# Symbols implied to m (never affects assignable values)
    +
    +config M_IMPLIER
    +   def_tristate m
    +
    +   imply M_IMP_Y_VIS_BOOL
    +   imply M_IMP_M_VIS_BOOL
    +   imply M_IMP_N_VIS_BOOL
    +
    +   imply M_IMP_Y_VIS_TRI
    +   imply M_IMP_M_VIS_TRI
    +   imply M_IMP_N_VIS_TRI
    +
    +config M_IMP_Y_VIS_BOOL
    +   bool "m-imp y-vis bool"
    +
    +config M_IMP_M_VIS_BOOL
    +   bool "m-imp m-vis bool" if m
    +
    +config M_IMP_N_VIS_BOOL
    +   bool "m-imp n-vis bool" if n
    +
    +config M_IMP_Y_VIS_TRI
    +   tristate "m-imp y-vis tri"
    +
    +config M_IMP_M_VIS_TRI
    +   tristate "m-imp m-vis tri" if m
    +
    +config M_IMP_N_VIS_TRI
    +   tristate "m-imp n-vis tri" if n
    +
    +
    +# Symbols in y-mode choice
    +
    +choice Y_CHOICE
    +   bool "y-mode choice"
    +
    +config Y_CHOICE_BOOL
    +   bool "y-mode choice bool"
    +
    +config Y_CHOICE_TRISTATE
    +   tristate "y-mode choice tristate"
    +
    +config Y_CHOICE_N_VIS_TRISTATE
    +   tristate "y-mode choice tristate invisible" if n
    +
    +endchoice
    +
    +
    +# Symbols in m/y-mode choice
    +
    +choice MY_CHOICE
    +   tristate "m/y-mode choice"
    +
    +config MY_CHOICE_BOOL
    +   bool "m/y-mode choice bool"
    +
    +config MY_CHOICE_TRISTATE
    +   tristate "m/y-mode choice tristate"
    +
    +config MY_CHOICE_N_VIS_TRISTATE
    +   tristate "m/y-mode choice tristate invisible" if n
    +
    +endchoice
    +
    +
    +# Choices with some other possible modes
    +
    +choice NMY_CHOICE
    +   tristate "n/m/y-mode choice"
    +   optional
    +endchoice
    +
    +choice NY_CHOICE
    +   bool "n/y-mode choice"
    +   optional
    +endchoice
    +
    +choice NM_CHOICE
    +   tristate "n/m-mode choice" if m
    +   optional
    +endchoice
    +
    +choice M_CHOICE
    +   tristate "m-mode choice" if m
    +endchoice
    +
    +choice N_CHOICE
    +   tristate "n-mode choice" if n
    +endchoice
    diff --git a/support/kconfiglib/tests/Kchoice 
b/support/kconfiglib/tests/Kchoice
    new file mode 100644
    index 0000000..16b38d4
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kchoice
    @@ -0,0 +1,198 @@
    +config MODULES
    +    bool "modules"
    +
    +# bool/tristate and optional
    +
    +choice BOOL
    +    bool "bool"
    +config B_1
    +    tristate "B_1"
    +config B_2
    +    tristate "B_2"
    +endchoice
    +
    +choice BOOL_OPT
    +    bool "bool optional"
    +    optional
    +config BO_1
    +    tristate "BO_1"
    +config BO_2
    +    tristate "BO_2"
    +endchoice
    +
    +choice TRISTATE
    +    tristate "tristate"
    +config T_1
    +    tristate "T_1"
    +config T_2
    +    tristate "T_2"
    +endchoice
    +
    +choice TRISTATE_OPT
    +    tristate "tristate optional"
    +    optional
    +config TO_1
    +    tristate "TO_1"
    +config TO_2
    +    tristate "TO_2"
    +endchoice
    +
    +# m-visibility
    +
    +choice BOOL_M
    +    bool "bool m" if m
    +config BM_1
    +    tristate "BM_1"
    +config BM_2
    +    tristate "BM_2"
    +endchoice
    +
    +choice TRISTATE_M
    +    tristate "tristate m" if m
    +config TM_1
    +    tristate "TM_1"
    +config TM_2
    +    tristate "TM_2"
    +endchoice
    +
    +# Defaults
    +
    +config TRISTATE_SYM
    +    tristate "tristate"
    +
    +choice DEFAULTS
    +    bool "defaults"
    +    default OPT_1 if n
    +    default OPT_2 if TRISTATE_SYM
    +    default OPT_4
    +config OPT_1
    +    tristate "OPT_1"
    +config OPT_2
    +    tristate "OPT_2"
    +config OPT_3
    +    tristate "OPT_3"
    +config OPT_4
    +    tristate "OPT_4"
    +endchoice
    +
    +choice DEFAULTS_NOT_VISIBLE
    +    bool "defaults not visible"
    +    # Skipped due to condition
    +    default OPT_6 if n
    +    # Skipped because OPT_7 is not visible
    +    default OPT_7
    +    # This one should apply
    +    default OPT_8
    +config OPT_5
    +    tristate "OPT_5"
    +config OPT_6
    +    tristate "OPT_6"
    +config OPT_7
    +    tristate "OPT_7" if n
    +config OPT_8
    +    tristate "OPT_8"
    +config OPT_9
    +    tristate "OPT_9"
    +endchoice
    +
    +# Choices without an explicitly specified type should get the type of the 
first
    +# symbol with a type
    +
    +choice NO_TYPE_BOOL
    +    prompt "no type bool"
    +config NTB_1
    +    bool "NTB_1"
    +config NTB_2
    +    tristate "NTB_2"
    +endchoice
    +
    +choice NO_TYPE_TRISTATE
    +    prompt "no type tristate"
    +config NTT_1
    +config NTT_2
    +    tristate "NTB_2"
    +config NTT_3
    +    bool "NTT_3"
    +endchoice
    +
    +# Choice items without an explicitly specified type should get the type of 
the
    +# choice
    +
    +choice MISSING_MEMBER_TYPES_1
    +    bool "missing member types"
    +config MMT_1
    +config MMT_2
    +config MMT_3
    +    tristate
    +endchoice
    +
    +choice MISSING_MEMBER_TYPES_2
    +config MMT_4
    +config MMT_5
    +    bool
    +endchoice
    +
    +# Choice where the default selection (the first symbol) depends on another
    +# symbol. If that symbol becomes 'n', the default selection should change 
to
    +# the first visible symbol in the choice.
    +
    +choice DEFAULT_WITH_DEP
    +    bool "default with dep"
    +
    +config A
    +    bool "A"
    +    depends on DEP
    +
    +config B
    +    bool "B"
    +
    +endchoice
    +
    +config DEP
    +    bool "dep"
    +
    +# Choice with symbols that shouldn't be considered choice symbols because 
they
    +# depend on the preceding symbol. This might be a kconfig bug, but some 
things
    +# use it, so we need to emulate it.
    +
    +choice WEIRD_SYMS
    +    bool "weird symbols that aren't considered part of the choice"
    +
    +# Only WS1 is part of the choice
    +config WS1
    +    bool "WS1"
    +
    +config WS2
    +    bool "WS2"
    +    depends on WS1
    +
    +config WS3
    +    bool
    +    depends on WS2
    +
    +config WS4
    +    bool
    +    depends on WS1
    +
    +config WS5
    +    bool "WS5" if WS1
    +
    +# 'if' has the same effect, so only WS6 is part of the choice
    +config WS6
    +    bool "WS6"
    +
    +if WS6
    +
    +config WS7
    +    bool
    +
    +config WS8
    +    bool "WS8"
    +
    +endif
    +
    +# Should also be part of the choice
    +config WS9
    +    bool "WS9"
    +
    +endchoice
    diff --git a/support/kconfiglib/tests/Kdefconfig_existent 
b/support/kconfiglib/tests/Kdefconfig_existent
    new file mode 100644
    index 0000000..304cae6
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdefconfig_existent
    @@ -0,0 +1,8 @@
    +# $FOO is "defconfig_2"
    +
    +config A
    +    string
    +    option defconfig_list
    +    default "Kconfiglib/tests/defconfig_1" if y && !n && n
    +    default "Kconfiglib/tests/$FOO"
    +    default "Kconfiglib/tests/defconfig_1"
    diff --git a/support/kconfiglib/tests/Kdefconfig_existent_but_n 
b/support/kconfiglib/tests/Kdefconfig_existent_but_n
    new file mode 100644
    index 0000000..2fdaaa9
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdefconfig_existent_but_n
    @@ -0,0 +1,10 @@
    +# $FOO is "defconfig_2"
    +# Should produce None due to the "depends on n"
    +
    +config A
    +    string
    +    depends on n
    +    option defconfig_list
    +    default "Kconfiglib/tests/defconfig_1" if y && !n && n
    +    default "Kconfiglib/tests/$FOO"
    +    default "Kconfiglib/tests/defconfig_1"
    diff --git a/support/kconfiglib/tests/Kdefconfig_nonexistent 
b/support/kconfiglib/tests/Kdefconfig_nonexistent
    new file mode 100644
    index 0000000..5b7230f
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdefconfig_nonexistent
    @@ -0,0 +1,5 @@
    +config A
    +    string
    +    option defconfig_list
    +    default "Kconfiglib/tests/non_existent_1"
    +    default "Kconfiglib/tests/non_existent_2"
    diff --git a/support/kconfiglib/tests/Kdefconfig_srctree 
b/support/kconfiglib/tests/Kdefconfig_srctree
    new file mode 100644
    index 0000000..d2591fa
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdefconfig_srctree
    @@ -0,0 +1,5 @@
    +config A
    +    string
    +    option defconfig_list
    +    default "sub/defconfig_in_sub" # Assume this doesn't exist
    +    default "Kconfiglib/tests/defconfig_2"
    diff --git a/support/kconfiglib/tests/Kdepcopy 
b/support/kconfiglib/tests/Kdepcopy
    new file mode 100644
    index 0000000..6abe898
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdepcopy
    @@ -0,0 +1,173 @@
    +# We verify that the properties below end up in definition order
    +
    +config MULTIDEF
    +    bool
    +    default A
    +    default B
    +    select AA
    +    imply AA
    +
    +if FOO
    +
    +config MULTIDEF
    +    default C
    +    default D
    +    select BB
    +    imply BB
    +
    +if BAR
    +
    +config MULTIDEF
    +    default E
    +    default F
    +    select CC
    +    imply CC
    +
    +menu "menu"
    +
    +config MULTIDEF
    +    default G
    +    default H
    +    select DD
    +    imply DD
    +
    +config MULTIDEF
    +    default I
    +    default J
    +    select EE
    +    imply EE
    +
    +endmenu
    +
    +config MULTIDEF
    +    default K
    +    default L
    +    select FF
    +    imply FF
    +
    +config MULTIDEF
    +    default M
    +    default N
    +    select GG
    +    imply GG
    +
    +endif
    +
    +config MULTIDEF
    +    default O
    +    default P
    +    select HH
    +    select II
    +    imply HH
    +    imply II
    +
    +endif
    +
    +config MULTIDEF
    +    default Q
    +    default R
    +    select JJ
    +    imply JJ
    +
    +
    +# Same test with choice symbols involved
    +
    +config MULTIDEF_CHOICE
    +    bool
    +    select A
    +
    +choice
    +    bool "choice"
    +
    +config MULTIDEF_CHOICE
    +    bool "multidef choice"
    +    select B
    +
    +endchoice
    +
    +config MULTIDEF_CHOICE
    +    bool
    +    select C
    +
    +
    +# Same test with ranges involved
    +
    +config MULTIDEF_RANGE
    +    int
    +    range A _
    +
    +menu "menu"
    +
    +config MULTIDEF_RANGE
    +    int
    +    range B _
    +
    +if FOO
    +
    +config MULTIDEF_RANGE
    +    int
    +    range C _
    +
    +endif
    +
    +config MULTIDEF_RANGE
    +    int
    +    range D _
    +
    +endmenu
    +
    +config MULTIDEF_RANGE
    +    int
    +    range E _
    +
    +config MULTIDEF_RANGE
    +    int
    +    range F _
    +
    +
    +# Same test for a choice
    +
    +choice MULTICHOICE
    +    bool "choice"
    +    default A
    +
    +config A
    +    bool "A"
    +
    +config B
    +    bool "B"
    +
    +config C
    +    bool "C"
    +
    +config D
    +    bool "C"
    +
    +config E
    +    bool "C"
    +
    +endchoice
    +
    +if FOO
    +
    +choice MULTICHOICE
    +    default B
    +endchoice
    +
    +menu "menu"
    +
    +choice MULTICHOICE
    +    default C
    +endchoice
    +
    +endmenu
    +
    +choice MULTICHOICE
    +    default D
    +endchoice
    +
    +endif
    +
    +choice MULTICHOICE
    +    default E
    +endchoice
    diff --git a/support/kconfiglib/tests/Kdeploop0 
b/support/kconfiglib/tests/Kdeploop0
    new file mode 100644
    index 0000000..98d3e3c
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop0
    @@ -0,0 +1,3 @@
    +config FOO
    +    bool
    +    depends on FOO
    diff --git a/support/kconfiglib/tests/Kdeploop1 
b/support/kconfiglib/tests/Kdeploop1
    new file mode 100644
    index 0000000..134cd29
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop1
    @@ -0,0 +1,3 @@
    +config FOO
    +    bool
    +    select FOO
    diff --git a/support/kconfiglib/tests/Kdeploop10 
b/support/kconfiglib/tests/Kdeploop10
    new file mode 100644
    index 0000000..2e616ae
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop10
    @@ -0,0 +1,48 @@
    +config A
    +    bool
    +    depends on B
    +
    +config B
    +    bool
    +    depends on C = 7
    +
    +config C
    +    int
    +    range D 8
    +
    +config D
    +    int
    +    default 3 if E
    +    default 8
    +
    +config E
    +    bool
    +
    +config F
    +    bool
    +    select E if G
    +
    +config G
    +    bool
    +    depends on H
    +
    +choice
    +    bool "choice"
    +
    +config H
    +    bool "H"
    +    depends on I
    +
    +endchoice
    +
    +choice
    +    bool "choice" if J
    +
    +config I
    +    bool "I"
    +
    +endchoice
    +
    +config J
    +    bool
    +    depends on A
    diff --git a/support/kconfiglib/tests/Kdeploop2 
b/support/kconfiglib/tests/Kdeploop2
    new file mode 100644
    index 0000000..c997243
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop2
    @@ -0,0 +1,3 @@
    +config FOO
    +    bool
    +    default FOO
    diff --git a/support/kconfiglib/tests/Kdeploop3 
b/support/kconfiglib/tests/Kdeploop3
    new file mode 100644
    index 0000000..90c83d5
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop3
    @@ -0,0 +1,3 @@
    +config FOO
    +    bool
    +    default y if FOO
    diff --git a/support/kconfiglib/tests/Kdeploop4 
b/support/kconfiglib/tests/Kdeploop4
    new file mode 100644
    index 0000000..789d8b7
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop4
    @@ -0,0 +1,7 @@
    +config FOO
    +    bool
    +    depends on BAR
    +
    +config BAR
    +    bool
    +    depends on FOO
    diff --git a/support/kconfiglib/tests/Kdeploop5 
b/support/kconfiglib/tests/Kdeploop5
    new file mode 100644
    index 0000000..f12fe6b
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop5
    @@ -0,0 +1,7 @@
    +config FOO
    +    bool
    +    select BAR
    +
    +config BAR
    +    bool
    +    select FOO
    diff --git a/support/kconfiglib/tests/Kdeploop6 
b/support/kconfiglib/tests/Kdeploop6
    new file mode 100644
    index 0000000..cb1e701
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop6
    @@ -0,0 +1,6 @@
    +config FOO
    +    bool
    +
    +config BAR
    +    bool
    +    select FOO if FOO
    diff --git a/support/kconfiglib/tests/Kdeploop7 
b/support/kconfiglib/tests/Kdeploop7
    new file mode 100644
    index 0000000..63d2c57
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop7
    @@ -0,0 +1,11 @@
    +choice
    +    bool "choice"
    +
    +config FOO
    +    bool "foo"
    +    depends on BAR
    +
    +config BAR
    +    bool "bar"
    +
    +endchoice
    diff --git a/support/kconfiglib/tests/Kdeploop8 
b/support/kconfiglib/tests/Kdeploop8
    new file mode 100644
    index 0000000..84efd8d
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop8
    @@ -0,0 +1,8 @@
    +choice
    +    bool "choice"
    +    default FOO if FOO
    +
    +config FOO
    +    bool "foo"
    +
    +endchoice
    diff --git a/support/kconfiglib/tests/Kdeploop9 
b/support/kconfiglib/tests/Kdeploop9
    new file mode 100644
    index 0000000..939f7f4
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdeploop9
    @@ -0,0 +1,7 @@
    +choice
    +    bool "choice" if FOO
    +
    +config FOO
    +    bool "foo"
    +
    +endchoice
    diff --git a/support/kconfiglib/tests/Kdirdep 
b/support/kconfiglib/tests/Kdirdep
    new file mode 100644
    index 0000000..cbb88b9
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kdirdep
    @@ -0,0 +1,30 @@
    +config NO_DEP_SYM
    +    bool
    +
    +config DEP_SYM
    +    bool
    +    depends on A
    +
    +config DEP_SYM
    +    depends on B && C
    +
    +config DEP_SYM
    +    depends on !D
    +
    +
    +choice NO_DEP_CHOICE
    +    bool "no dep. choice"
    +endchoice
    +
    +choice DEP_CHOICE
    +    bool "dep. choice"
    +    depends on A
    +endchoice
    +
    +choice DEP_CHOICE
    +    depends on B
    +endchoice
    +
    +choice DEP_CHOICE
    +    depends on C
    +endchoice
    diff --git a/support/kconfiglib/tests/Kescape 
b/support/kconfiglib/tests/Kescape
    new file mode 100644
    index 0000000..ee6b2b6
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kescape
    @@ -0,0 +1,3 @@
    +config STRING
    +    string "string"
    +    default "\"\\"
    diff --git a/support/kconfiglib/tests/Keval b/support/kconfiglib/tests/Keval
    new file mode 100644
    index 0000000..1e64b83
    --- /dev/null
    +++ b/support/kconfiglib/tests/Keval
    @@ -0,0 +1,34 @@
    +# Enabled/disabled in the test
    +config MODULES
    +    bool "modules"
    +    option modules
    +
    +config N
    +    def_tristate n
    +
    +config M
    +    def_tristate m
    +
    +menuconfig Y
    +    def_tristate y
    +    prompt "foo"
    +
    +config Y_STRING
    +    string
    +    default "y"
    +
    +config FOO_BAR_STRING
    +    string
    +    default "foo bar"
    +
    +config INT_37
    +    int
    +    default 37
    +
    +config HEX_0X37
    +    hex
    +    default 0x37
    +
    +config HEX_37
    +    hex
    +    default 37
    diff --git a/support/kconfiglib/tests/Kexpr_items 
b/support/kconfiglib/tests/Kexpr_items
    new file mode 100644
    index 0000000..fb97ace
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kexpr_items
    @@ -0,0 +1,11 @@
    +config TEST
    +    bool
    +    default A && (B || !C && D = "E") || F > G || !!!H
    +
    +choice CHOICE
    +    bool "choice"
    +
    +config TEST_CHOICE
    +    bool "test choice" if A
    +
    +endchoice
    diff --git a/support/kconfiglib/tests/Khelp b/support/kconfiglib/tests/Khelp
    new file mode 100644
    index 0000000..b80c2eb
    --- /dev/null
    +++ b/support/kconfiglib/tests/Khelp
    @@ -0,0 +1,50 @@
    +config TWO_HELP_STRINGS
    +    bool
    +    help
    +      first help string
    +
    +
    +
    +
    +config TWO_HELP_STRINGS
    +    help
    +      second help string
    +
    +config NO_BLANK_AFTER_HELP
    +    bool
    +    help
    +      help for
    +      NO_BLANK_AFTER_HELP
    +choice CHOICE_HELP
    +    bool "choice with help"
    +    help
    +  help for
    +  CHOICE_HELP
    +endchoice
    +
    +config HELP_TERMINATED_BY_COMMENT
    +  bool
    +  help
    + a
    + b
    + c
    +#
    +
    +config TRICKY_HELP
    +  bool
    +  -help---
    +
    +
    +  a
    +   b
    +    c
    +
    +   d
    +    e
    +     f
    +
    +
    +  g
    +   h
    +    i
    +#
    diff --git a/support/kconfiglib/tests/Kifremoval 
b/support/kconfiglib/tests/Kifremoval
    new file mode 100644
    index 0000000..f94472c
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kifremoval
    @@ -0,0 +1,99 @@
    +# Test some tricky cases that give consecutive 'if' nodes even after
    +# flattening. Simple cases are exercised a ton elsewhere.
    +
    +if X
    +endif
    +if X
    +endif
    +
    +config A
    +
    +if X
    +endif
    +if X
    +endif
    +
    +config B
    +
    +if X
    +endif
    +if X
    +endif
    +if X
    +endif
    +
    +config C
    +
    +if X
    +  if X
    +    if X
    +    endif
    +    if X
    +    endif
    +  endif
    +  if X
    +    if X
    +    endif
    +    if X
    +    endif
    +  endif
    +  config D
    +endif
    +if X
    +endif
    +
    +menu "E"
    +  if X
    +    if X
    +    endif
    +  endif
    +  if X
    +    if X
    +    endif
    +  endif
    +endmenu
    +
    +menu "F"
    +  if X
    +  endif
    +  if X
    +  endif
    +  if X
    +    if X
    +    endif
    +    if X
    +    endif
    +    menu "G"
    +    endmenu
    +  endif
    +endmenu
    +
    +choice H
    +  if X
    +    if X
    +    endif
    +  endif
    +  if X
    +    if X
    +    endif
    +  endif
    +endchoice
    +
    +choice I
    +  if X
    +  endif
    +  if X
    +  endif
    +  if X
    +    if X
    +    endif
    +    if X
    +    endif
    +    config J
    +  endif
    +endchoice
    +
    +if X
    +endif
    +if X
    +endif
    diff --git a/support/kconfiglib/tests/Kimply 
b/support/kconfiglib/tests/Kimply
    new file mode 100644
    index 0000000..3ce346f
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kimply
    @@ -0,0 +1,145 @@
    +config MODULES
    +    def_bool y
    +    option modules
    +
    +#
    +# Implied symbols with unmet and met direct dependencies
    +#
    +
    +config IMPLY_DIRECT_DEPS
    +    def_tristate y
    +    imply UNMET_DIRECT_1
    +    imply UNMET_DIRECT_2
    +    imply UNMET_DIRECT_3
    +    imply MET_DIRECT_1
    +    imply MET_DIRECT_2
    +    imply MET_DIRECT_3
    +    imply MET_DIRECT_4
    +
    +config UNMET_DIRECT_1
    +    tristate
    +    depends on n
    +
    +if n
    +config UNMET_DIRECT_2
    +    tristate
    +endif
    +
    +menu "menu"
    +    depends on n
    +
    +config UNMET_DIRECT_3
    +    tristate
    +
    +endmenu
    +
    +config MET_DIRECT_1
    +    tristate
    +
    +config MET_DIRECT_2
    +    depends on y
    +    tristate
    +
    +if y
    +config MET_DIRECT_3
    +    tristate
    +endif
    +
    +menu "menu"
    +    depends on y
    +
    +config MET_DIRECT_4
    +    tristate
    +
    +endmenu
    +
    +#
    +# 'imply' with condition
    +#
    +
    +config IMPLY_COND
    +    def_tristate y
    +    tristate
    +    imply IMPLIED_N_COND if n
    +    imply IMPLIED_M_COND if m
    +    imply IMPLIED_Y_COND if y
    +
    +config IMPLIED_N_COND
    +    tristate
    +
    +config IMPLIED_M_COND
    +    tristate
    +
    +config IMPLIED_Y_COND
    +    tristate
    +
    +#
    +# Implying from symbol with value n
    +#
    +
    +# Will default to 'n'
    +config IMPLY_N_1
    +    tristate
    +    imply IMPLIED_FROM_N_1
    +
    +# This test also disables the imply, so it's kinda redundant, but why not
    +if n
    +config IMPLY_N_2
    +    tristate
    +    imply IMPLIED_FROM_N_2
    +endif
    +
    +config IMPLIED_FROM_N_1
    +    tristate
    +
    +config IMPLIED_FROM_N_2
    +    tristate
    +
    +#
    +# Implying from symbol with value m
    +#
    +
    +config IMPLY_M
    +    def_tristate m
    +    imply IMPLIED_M
    +    # Implying a bool to 'm' makes it default to 'y'
    +    imply IMPLIED_M_BOOL
    +
    +config IMPLIED_M
    +    tristate
    +
    +config IMPLIED_M_BOOL
    +    bool
    +
    +#
    +# 'imply' which should raise an 'm' default to 'y'
    +#
    +
    +config IMPLY_M_TO_Y
    +    tristate
    +    default y
    +    imply IMPLIED_M_TO_Y
    +
    +config IMPLIED_M_TO_Y
    +    tristate
    +    default m
    +
    +#
    +# Used for testing user values
    +#
    +
    +config DIRECT_DEP
    +    tristate "direct dep"
    +
    +config IMPLY
    +    tristate "imply"
    +    imply IMPLIED_TRISTATE
    +    imply IMPLIED_BOOL
    +
    +config IMPLIED_TRISTATE
    +    tristate "implied tristate"
    +    depends on DIRECT_DEP
    +
    +config IMPLIED_BOOL
    +    bool "implied bool"
    +    depends on DIRECT_DEP
    diff --git a/support/kconfiglib/tests/Kinclude_path 
b/support/kconfiglib/tests/Kinclude_path
    new file mode 100644
    index 0000000..7a3badb
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kinclude_path
    @@ -0,0 +1,12 @@
    +config TOP
    +   bool
    +
    +source "Kinclude_path_sourced_1"
    +
    +config TOP
    +   bool
    +
    +source "Kinclude_path_sourced_1"
    +
    +config TOP
    +   bool
    diff --git a/support/kconfiglib/tests/Kinclude_path_sourced_1 
b/support/kconfiglib/tests/Kinclude_path_sourced_1
    new file mode 100644
    index 0000000..f4dee98
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kinclude_path_sourced_1
    @@ -0,0 +1,12 @@
    +config ONE_DOWN
    +   bool
    +
    +source "Kinclude_path_sourced_2"
    +
    +config ONE_DOWN
    +   bool
    +
    +source "Kinclude_path_sourced_2"
    +
    +config ONE_DOWN
    +   bool
    diff --git a/support/kconfiglib/tests/Kinclude_path_sourced_2 
b/support/kconfiglib/tests/Kinclude_path_sourced_2
    new file mode 100644
    index 0000000..068f18d
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kinclude_path_sourced_2
    @@ -0,0 +1,11 @@
    +config TWO_DOWN
    +   bool
    +
    +menu "menu"
    +endmenu
    +
    +comment "comment"
    +
    +choice
    +   bool "choice"
    +endchoice
    diff --git a/support/kconfiglib/tests/Kitemlists 
b/support/kconfiglib/tests/Kitemlists
    new file mode 100644
    index 0000000..8aa7107
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kitemlists
    @@ -0,0 +1,36 @@
    +comment "comment 1"
    +
    +choice
    +    bool "choice 1"
    +endchoice
    +
    +menu "menu 1"
    +
    +choice NAMED
    +    bool "choice 2"
    +endchoice
    +
    +menu "menu 2"
    +menu "menu 3"
    +comment "comment 2"
    +endmenu
    +
    +choice
    +    bool "choice 3"
    +endchoice
    +
    +choice NAMED
    +    bool
    +endchoice
    +
    +endmenu
    +
    +menu "menu 4"
    +endmenu
    +
    +comment "comment 3"
    +
    +endmenu
    +
    +menu "menu 5"
    +endmenu
    diff --git a/support/kconfiglib/tests/Klocation 
b/support/kconfiglib/tests/Klocation
    new file mode 100644
    index 0000000..f997055
    --- /dev/null
    +++ b/support/kconfiglib/tests/Klocation
    @@ -0,0 +1,72 @@
    +if UNDEFINED
    +endif
    +
    +config SINGLE_DEF
    +    bool
    +
    +config MULTI_DEF
    +    bool
    +
    +# Throw in some line continuations too to make sure it doesn't mess up the 
line
    +# numbers
    +if y && \
    +   y
    +if y && \
    +   y && \
    +   y
    +
    +# Throw in some help texts too
    +
    +config HELP_1
    +    bool "help 1"
    +    help
    +config HELP_2
    +    bool "help 2"
    +    help
    +      foo
    +      bar
    +
    +        baz
    +
    +config HELP_3
    +    help
    +      foo
    +      bar
    +    bool
    +config \
    +MULTI_DEF
    +
    +config MULTI_DEF
    +
    +endif
    +endif
    +
    +# 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}
    +source "$TESTS_DIR_FROM_ENV/*ub/Klocation$_GSOURCED[12]"
    +# Test old syntax too
    +gsource "$TESTS_DIR_FROM_ENV/*ub/Klocation$_GSOURCED[12]"
    +
    +# Expands to "sub/Klocation_grsourced[12]", matching
    +# tests/sub/Klocation_grsourced{1,2}
    +rsource "$SUB_DIR_FROM_ENV/Klocation$_GRSOURCED[12]"
    +# Test old syntax too
    +grsource "$SUB_DIR_FROM_ENV/Klocation$_GRSOURCED[12]"
    +
    +# No-ops
    +osource "nonexistent"
    +osource "nonexistent*"
    +gsource "nonexistent"
    +gsource "nonexistent*"
    +orsource "nonexistent"
    +orsource "nonexistent*"
    +grsource "nonexistent"
    +grsource "nonexistent*"
    +
    +config MULTI_DEF
    diff --git a/support/kconfiglib/tests/Klocation_sourced 
b/support/kconfiglib/tests/Klocation_sourced
    new file mode 100644
    index 0000000..6e19f76
    --- /dev/null
    +++ b/support/kconfiglib/tests/Klocation_sourced
    @@ -0,0 +1,18 @@
    +
    +
    +config MULTI_DEF
    +
    +choice CHOICE
    +   bool "choice"
    +endchoice
    +
    +config MENU_HOOK
    +   bool
    +
    +menu "menu"
    +endmenu
    +
    +config COMMENT_HOOK
    +   bool
    +
    +comment "comment"
    diff --git a/support/kconfiglib/tests/Kmainmenu 
b/support/kconfiglib/tests/Kmainmenu
    new file mode 100644
    index 0000000..80713c0
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kmainmenu
    @@ -0,0 +1,5 @@
    +config FOO
    +    string
    +    option env="FOO"
    +
    +mainmenu "---$FOO---"
    diff --git a/support/kconfiglib/tests/Kmenuconfig 
b/support/kconfiglib/tests/Kmenuconfig
    new file mode 100644
    index 0000000..9a4cc11
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kmenuconfig
    @@ -0,0 +1,37 @@
    +# Menu nodes with is_menuconfig False
    +
    +config NOT_MENUCONFIG_1
    +   bool
    +
    +config NOT_MENUCONFIG_2
    +   bool "not menuconfig 2"
    +
    +config MENUCONFIG_MULTI_DEF
    +   bool "menuconfig multi def 1"
    +
    +config COMMENT_HOOK
    +   bool
    +
    +comment "not menuconfig 3"
    +
    +
    +# Menu nodes with is_menuconfig True
    +
    +menuconfig MENUCONFIG_1
    +   bool "menuconfig 1"
    +
    +menuconfig MENUCONFIG_MULTI_DEF
    +   bool "menuconfig multi def 2"
    +
    +config MENU_HOOK
    +   bool
    +
    +menu "menuconfig 2"
    +endmenu
    +
    +config CHOICE_HOOK
    +   bool
    +
    +choice
    +   bool "menuconfig 3"
    +endchoice
    diff --git a/support/kconfiglib/tests/Kmisc b/support/kconfiglib/tests/Kmisc
    new file mode 100644
    index 0000000..d9a799d
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kmisc
    @@ -0,0 +1,82 @@
    +# For testing various minor APIs
    +
    +# optional choices
    +
    +choice NOT_OPTIONAL
    +    bool "not optional"
    +config A
    +    bool "A"
    +config B
    +    bool "B"
    +endchoice
    +
    +choice OPTIONAL
    +    tristate "optional"
    +    optional
    +config C
    +    tristate "C"
    +config D
    +    tristate "D"
    +# Quirky symbols - not proper choice symbol
    +
    +config Q1
    +    tristate "Q1"
    +    depends on D
    +
    +config Q2
    +    tristate "Q2"
    +    depends on Q1
    +
    +config Q3
    +    tristate "Q3"
    +    depends on D
    +
    +endchoice
    +
    +# User values
    +
    +config BOOL
    +    bool "bool" if NOT_DEFINED_1
    +
    +config TRISTATE
    +    tristate # Visibility should not affect user value
    +
    +config STRING
    +    string "string"
    +
    +config INT
    +    int # Visibility should not affect user value
    +
    +config HEX
    +    hex "hex"
    +    depends on NOT_DEFINED_2
    +
    +config COMMENT_HOOK
    +comment "comment"
    +
    +config MENU_HOOK
    +menu "menu"
    +    depends on NOT_DEFINED_3 || NOT_DEFINED_2
    +    depends on !NOT_DEFINED_4
    +endmenu
    +
    +config FROM_ENV
    +    string "from env"
    +    option env="ENV_VAR"
    +
    +config FROM_ENV_MISSING
    +    string "from env missing"
    +    option env="MISSING_ENV_VAR"
    +    default "missing"
    +
    +config FROM_ENV_WEIRD
    +    string
    +    default "weird"
    +    option env="ENV_VAR"
    +
    +config NOT_ALLNOCONFIG_Y
    +    bool "not allnoconfig_y"
    +
    +config ALLNOCONFIG_Y
    +    bool "allnoconfig_y"
    +    option allnoconfig_y
    diff --git a/support/kconfiglib/tests/Kmissingrsource 
b/support/kconfiglib/tests/Kmissingrsource
    new file mode 100644
    index 0000000..924b0b3
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kmissingrsource
    @@ -0,0 +1 @@
    +rsource "nonexistent"
    diff --git a/support/kconfiglib/tests/Kmissingsource 
b/support/kconfiglib/tests/Kmissingsource
    new file mode 100644
    index 0000000..a3a6c25
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kmissingsource
    @@ -0,0 +1 @@
    +source "nonexistent"
    diff --git a/support/kconfiglib/tests/Korder 
b/support/kconfiglib/tests/Korder
    new file mode 100644
    index 0000000..3a8dffa
    --- /dev/null
    +++ b/support/kconfiglib/tests/Korder
    @@ -0,0 +1,35 @@
    +config O
    +   int "O"
    +   default 0
    +
    +config R
    +   int "R"
    +   default 1
    +
    +config D
    +   int "D"
    +   default 2
    +
    +config E
    +   int "E"
    +   default 3
    +
    +# Defined twice
    +config R
    +   int "R"
    +
    +config R2
    +   int "R2"
    +   default 4
    +
    +config I
    +   int "I"
    +   default 5
    +
    +config N
    +   int "N"
    +   default 6
    +
    +config G
    +   int "G"
    +   default 7
    diff --git a/support/kconfiglib/tests/Kpreprocess 
b/support/kconfiglib/tests/Kpreprocess
    new file mode 100644
    index 0000000..e30b389
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kpreprocess
    @@ -0,0 +1,147 @@
    +# Simple assignments (with bad formatting, as an additional test)
    +
    +simple-recursive=foo
    +simple-immediate:=bar
    +# Should become recursive
    +simple-recursive-2+=baz
    +
    +    whitespaced     =       foo
    +
    +
    +# Simple += test. += should preserve the flavor of the variable (simple vs.
    +# recursive).
    +
    +preserve-recursive = foo
    +preserve-recursive += bar
    +
    +preserve-immediate := foo
    +preserve-immediate += bar
    +
    +
    +# Recursive substitution
    +
    +recursive = $(foo) $(bar) $($(b-char)a$(z-char))
    +recursive += $(indir)
    +
    +foo = abc
    +bar = def
    +baz = ghi
    +
    +b-char = b
    +z-char = z
    +
    +indir = jkl $(indir-2)
    +indir-2 = mno
    +
    +
    +# Immediate substitution
    +
    +def = foo
    +immediate := $(undef)$(def)$(undef)$(def)
    +def = bar
    +undef = bar
    +
    +
    +# Function calls
    +
    +# Chained function call
    +quote = "$(1)" "$(2)"
    +rev-quote = $(quote,$(2),$(1))
    +surround-rev-quote = $(0) $(rev-quote,$(1),$(2)) $(0)
    +surround-rev-quote-unused-arg = $(surround-rev-quote,$(1),$(2)) $(3)
    +# No value is passed for $(3), so it expands to nothing
    +fn-indir = surround-rev-quote
    +messy-fn-res = $($(fn-indir)-unused-arg, a  b , c  d )
    +
    +# Special characters in function call
    +comma = ,
    +right-paren = )
    +dollar = $
    +left-paren = (
    +fn = "$(1)"
    +special-chars-fn-res = 
$(fn,$(comma)$(dollar)$(left-paren)foo$(right-paren))
    +
    +
    +# Variable expansions in various locations (verified by checking how the 
symbol
    +# prints)
    +
    +qaz = QAZ
    +echo = $(1)
    +ignore-first = $(2)
    +
    +config PRINT_ME
    +   string "$(ENV_1)" if ($(echo,FOO) && $(echo,BAR)) || !$(echo,BAZ) || 
!(($(qaz)))
    +   default "$(echo,"foo")" if "foo $(echo,"bar") baz" = "$(undefined)"
    +
    +# Expansion within a symbol token, with deliberate sloppiness
    +config PRINT_$(ignore-first,  ,ME)_TOO
    +   bool "foo"
    +   default FOO$(ignore-first,    ,BAR)BAZ$(qaz) if 
$(qaz)&&$(qaz)FOO&&x$(ignore-first,  ,xx)
    +
    +
    +# Recursive expansion (throws an exception)
    +
    +rec-1 = x $(rec-2) y
    +rec-2 = x $(rec-3) y
    +rec-3 = x $(rec-1) y
    +
    +# Functions are allowed to reference themselves, but an exception is 
thrown if
    +# the function seems to be stuck (the recursion gets too deep)
    +safe-fn-rec = $($(1))
    +safe-fn-rec-2 = $(safe-fn-rec,safe-fn-rec-3)
    +safe-fn-rec-3 = foo
    +safe-fn-rec-res = $(safe-fn-rec,safe-fn-rec-2)
    +
    +unsafe-fn-rec = $(unsafe-fn-rec,$(1))
    +
    +
    +# Expansion in the left-hand side of assignments
    +
    +dummy-arg-fn = bar
    +lhs-indir-1 = lhs-indir-2
    +lhs-indir-2 = -baz
    +rhs = value
    +# LHS expands to foo-bar-baz
    +foo-$(dummy-arg-fn, ignored argument )$($(lhs-indir-1)) = $(rhs)
    +# Expands to empty string, accepted
    +  $(undefined)  
    +
    +# Variable with a space in its name
    +empty =
    +space = $(empty) $(empty)
    +foo$(space)bar = value
    +space-var-res = $(foo bar)
    +
    +
    +# Built-in functions
    +
    +# Expands to "baz qaz"
    +shell-res = $(shell,false && echo foo bar || echo baz qaz)
    +
    +# Warns about output on stderr, expands to nothing
    +shell-stderr-res := $(shell,echo message on stderr >&2)
    +
    +# Expands to the current location
    +location-res := $(filename):$(lineno)
    +
    +# Adds one warning, expands to nothing
    +$(warning-if,,no warning)
    +$(warning-if,n,no warning)
    +warning-res := $(warning-if,y,a warning)
    +
    +# Does not cause an error, expands to nothing
    +error-n-res := $(error-if,n,oops)
    +
    +# Causes an error when expanded
    +error-y-res = $(error-if,y,oops)
    +
    +
    +# Environment variables (for testing Kconfig.env_vars). ENV_1 is already
    +# referenced above.
    +env_ref_1 := xxx $(ENV_2) xxx
    +env_ref_2 := $(shell,echo $(ENV_3))
    +env_ref_3 :=
    +env_ref_3 += $(ENV_4)
    +$(warning-if,$(ENV_5),$(ENV_UNDEFINED))
    +source "$(ENV_6)"
    +env_ref_4 = $(ENV_7)  # Never evaluated
    diff --git a/support/kconfiglib/tests/Krange 
b/support/kconfiglib/tests/Krange
    new file mode 100644
    index 0000000..3057483
    --- /dev/null
    +++ b/support/kconfiglib/tests/Krange
    @@ -0,0 +1,133 @@
    +#
    +# No ranges
    +#
    +
    +config HEX_NO_RANGE
    +    hex "hex no range"
    +
    +config INT_NO_RANGE
    +    int "int no range"
    +
    +#
    +# Disabled ranges
    +#
    +
    +config HEX_ALL_RANGES_DISABLED
    +    hex "hex all ranges disabled"
    +    range 0x10 0x20 if n
    +    range 0x30 0x40 if n
    +
    +config INT_ALL_RANGES_DISABLED
    +    int "int all ranges disabled"
    +    range 10 20 if n
    +    range 30 40 if n
    +
    +#
    +# Ranges with defaults
    +#
    +
    +# hex
    +
    +config HEX_RANGE_10_20_LOW_DEFAULT
    +    hex "int range 10-20 low default"
    +    range 0x10 0x20
    +    default 0x9
    +
    +config HEX_RANGE_10_20_HIGH_DEFAULT
    +    hex "int range 10-20 high default"
    +    range 0x10 0x20
    +    default 0x21
    +
    +config HEX_RANGE_10_20_OK_DEFAULT
    +    hex "int range 10-20 ok default"
    +    range 0x10 0x20
    +    default 0x15
    +
    +config HEX_RANGE_10_20_OK_DEFAULT_ALTERNATE
    +    hex "int range 10-20 ok default alternate"
    +    range 0x10 0x20
    +    default 15
    +
    +# int
    +
    +config INT_RANGE_10_20_LOW_DEFAULT
    +    int "int range 10-20 low default"
    +    range 10 20
    +    default 9
    +
    +config INT_RANGE_10_20_HIGH_DEFAULT
    +    int "int range 10-20 high default"
    +    range 10 20
    +    default 21
    +
    +config INT_RANGE_10_20_OK_DEFAULT
    +    int "int range 10-20 ok default"
    +    range 10 20
    +    default 15
    +
    +#
    +# Ranges with no defaults (should default to low end of range if > 0)
    +#
    +
    +config HEX_RANGE_10_20
    +    hex "hex range 10-20"    
    +    range 0x10 0x20
    +
    +config HEX_RANGE_0_10
    +    hex "hex range 0-10"
    +    range 0x0 0x10
    +
    +config INT_RANGE_10_20
    +    int "int range 10-20"    
    +    range 10 20
    +
    +config INT_RANGE_0_10
    +    int "int range 0-10"
    +    range 0 10
    +
    +config INT_RANGE_NEG_10_10
    +    int "int range -10-10"
    +    range -10 10
    +
    +#
    +# Dependent ranges
    +#
    +
    +config HEX_40
    +    hex
    +    default 40
    +
    +config HEX_RANGE_10_40_DEPENDENT
    +    hex "hex range 10-40 dependent"
    +    range HEX_RANGE_10_20 HEX_40
    +
    +config INT_40
    +    int
    +    default 40
    +
    +config INT_RANGE_10_40_DEPENDENT
    +    int "int range 10-40 dependent"
    +    range INT_RANGE_10_20 INT_40
    +
    +#
    +# Ranges on symbols defined in multiple locations
    +#
    +
    +if n
    +config INACTIVE_RANGE
    +    range 0 1
    +endif
    +
    +config INACTIVE_RANGE
    +    int
    +    # Default will apply and should not get clamped,
    +    # because the range does not apply
    +    default 2
    +
    +config ACTIVE_RANGE
    +    range 0 1
    +
    +config ACTIVE_RANGE
    +    int
    +    # Default will apply and should be clamped to 1
    +    default 2
    diff --git a/support/kconfiglib/tests/Krecursive1 
b/support/kconfiglib/tests/Krecursive1
    new file mode 100644
    index 0000000..35b521a
    --- /dev/null
    +++ b/support/kconfiglib/tests/Krecursive1
    @@ -0,0 +1 @@
    +source "tests/Krecursive2"
    diff --git a/support/kconfiglib/tests/Krecursive2 
b/support/kconfiglib/tests/Krecursive2
    new file mode 100644
    index 0000000..9f9f00e
    --- /dev/null
    +++ b/support/kconfiglib/tests/Krecursive2
    @@ -0,0 +1 @@
    +source "tests/Krecursive1"
    diff --git a/support/kconfiglib/tests/Kreferenced 
b/support/kconfiglib/tests/Kreferenced
    new file mode 100644
    index 0000000..9da94c8
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kreferenced
    @@ -0,0 +1,63 @@
    +config NO_REFS
    +    bool
    +
    +config JUST_DEPENDS_ON_REFS
    +    bool
    +    depends on A && B
    +
    +if A
    +
    +menu "menu"
    +    depends on B
    +    visible if C
    +    visible if D
    +
    +config LOTS_OF_REFS
    +    bool "lots" if C || D
    +    default E || F if G || H
    +    default I || J if K || L
    +    select M if N || O
    +    select P if Q || R
    +    imply S if T || U
    +    imply V if W || X
    +    depends on Y || Z
    +
    +endmenu
    +
    +endif
    +
    +config INT_REFS
    +    int "int"
    +    range A B if C && D
    +    range E F if G && H
    +
    +choice CHOICE
    +    bool "choice"
    +
    +config CHOICE_REF
    +    bool "choice ref"
    +
    +endchoice
    +
    +comment "comment"
    +    depends on A || B
    +
    +
    +config MULTI_DEF_SYM
    +    def_bool A && B
    +
    +config MULTI_DEF_SYM
    +    depends on C
    +
    +
    +choice MULTI_DEF_CHOICE
    +    bool "choice"
    +    depends on A && B
    +
    +endchoice
    +
    +choice MULTI_DEF_CHOICE
    +    bool "choice"
    +    depends on C
    +
    +endchoice
    diff --git a/support/kconfiglib/tests/Krelation 
b/support/kconfiglib/tests/Krelation
    new file mode 100644
    index 0000000..940eff8
    --- /dev/null
    +++ b/support/kconfiglib/tests/Krelation
    @@ -0,0 +1,36 @@
    +config A
    +    bool
    +    depends on UNDEFINED
    +
    +choice CHOICE_1
    +    bool "C"
    +config B
    +    bool "B"
    +config C
    +    bool "C" if B
    +config D
    +    bool "D"
    +endchoice
    +
    +menu "m1"
    +config E
    +    bool
    +menu "m2"
    +config F
    +    bool
    +choice CHOICE_2
    +    tristate "foo"
    +config G
    +    bool "g"
    +config H
    +    bool "h"
    +endchoice
    +endmenu
    +config I
    +    bool
    +endmenu
    +
    +menu "m3"
    +endmenu
    +menu "m4"
    +endmenu
    diff --git a/support/kconfiglib/tests/Krepr b/support/kconfiglib/tests/Krepr
    new file mode 100644
    index 0000000..fe6d8f0
    --- /dev/null
    +++ b/support/kconfiglib/tests/Krepr
    @@ -0,0 +1,64 @@
    +config MODULES
    +    bool
    +    option modules
    +    default y
    +
    +if UNDEFINED
    +endif
    +
    +config BASIC
    +    bool
    +    default y
    +    ---help---
    +
    +config VISIBLE
    +    bool "visible"
    +
    +config STRING
    +    string "visible"
    +
    +config DIR_DEP_N
    +    depends on n
    +
    +config OPTIONS
    +    option allnoconfig_y
    +    option defconfig_list
    +    option env="ENV"
    +
    +config MULTI_DEF
    +config MULTI_DEF
    +
    +menuconfig MENUCONFIG
    +
    +choice CHOICE
    +    tristate "choice"
    +
    +config CHOICE_1
    +    tristate "choice sym"
    +
    +config CHOICE_2
    +    tristate "choice sym"
    +
    +endchoice
    +
    +config CHOICE_HOOK
    +
    +choice
    +    tristate "optional choice" if n
    +    optional
    +endchoice
    +
    +config NO_VISIBLE_IF_HOOK
    +
    +menu "no visible if"
    +endmenu
    +
    +config VISIBLE_IF_HOOK
    +
    +menu "visible if"
    +    visible if m
    +endmenu
    +
    +config COMMENT_HOOK
    +
    +comment "comment"
    diff --git a/support/kconfiglib/tests/Kstr b/support/kconfiglib/tests/Kstr
    new file mode 100644
    index 0000000..f55c830
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kstr
    @@ -0,0 +1,293 @@
    +if UNDEFINED
    +endif
    +
    +config NO_TYPE
    +
    +config BASIC_NO_PROMPT
    +    bool
    +    help
    +      blah blah
    +
    +        blah blah blah
    +
    +       blah
    +
    +config BASIC_PROMPT
    +    bool "basic"
    +
    +config ADVANCED
    +    tristate "prompt" if DEP
    +    default DEFAULT_1
    +    default DEFAULT_2 if DEP
    +    select SELECTED_1
    +    select SELECTED_2 if DEP
    +    imply IMPLIED_1
    +    imply IMPLIED_2 if DEP
    +    help
    +      first help text
    +
    +config ADVANCED
    +    prompt "prompt 2"
    +
    +menuconfig ADVANCED
    +    prompt "prompt 3"
    +
    +if DEP3
    +
    +menu "foo"
    +    depends on DEP4
    +    visible if VIS
    +
    +config ADVANCED
    +    help
    +      second help text
    +    depends on A || !B || (C && D) || !(E && F) || G = H || \
    +              (I && !J && (K || L) && !(M || N) && O = P)
    +
    +config ADVANCED
    +    # Used to verify that the direct dependencies appear to the right of 
VIS
    +    prompt "prompt 4"
    +
    +endmenu
    +
    +endif
    +
    +config ONLY_DIRECT_DEPS
    +    int
    +    depends on DEP1
    +    depends on DEP2
    +
    +config STRING
    +    def_string "foo"
    +    default "bar" if DEP
    +    default STRING2
    +    default STRING3 if DEP
    +
    +config INT
    +    def_int 7 if DEP
    +    range 1 2
    +    range FOO BAR
    +    range BAZ QAZ if DEP
    +
    +config HEX
    +    def_hex 0x123
    +    range 0x100 0x200
    +    range FOO BAR
    +    range BAZ QAZ if DEP
    +
    +config MODULES
    +    bool "MODULES"
    +    option modules
    +
    +config OPTIONS
    +    option allnoconfig_y
    +    option defconfig_list
    +    option env="ENV"
    +
    +if LOC_1
    +config CORRECT_PROP_LOCS_BOOL
    +    prompt "prompt 1"
    +    default DEFAULT_1
    +    default DEFAULT_2
    +    select SELECT_1
    +    select SELECT_2
    +    imply IMPLY_1
    +    imply IMPLY_2
    +    help
    +      help 1
    +endif
    +
    +if LOC_2
    +menuconfig CORRECT_PROP_LOCS_BOOL
    +    bool "prompt 2"
    +    default DEFAULT_3
    +    default DEFAULT_4
    +    select SELECT_3
    +    select SELECT_4
    +    imply IMPLY_3
    +    imply IMPLY_4
    +    help
    +      help 2
    +endif
    +
    +if LOC_3
    +config CORRECT_PROP_LOCS_BOOL
    +    prompt "prompt 3"
    +    default DEFAULT_5
    +    default DEFAULT_6
    +    select SELECT_5
    +    select SELECT_6
    +    imply IMPLY_5
    +    imply IMPLY_6
    +    help
    +      help 2
    +endif
    +
    +if LOC_1
    +config CORRECT_PROP_LOCS_INT
    +    int
    +    range 1 2
    +    range 3 4
    +endif
    +
    +if LOC_2
    +config CORRECT_PROP_LOCS_INT
    +    range 5 6
    +    range 7 8
    +endif
    +
    +choice CHOICE
    +    tristate "foo"
    +    default CHOICE_1
    +    default CHOICE_2 if dep
    +
    +config CHOICE_1
    +    tristate "choice 1"
    +
    +config CHOICE_2
    +    tristate "choice 2"
    +
    +endchoice
    +
    +choice
    +    tristate "no name"
    +    optional
    +endchoice
    +
    +if LOC_1
    +choice CORRECT_PROP_LOCS_CHOICE
    +    bool
    +    default CHOICE_3
    +
    +config CHOICE_3
    +    bool "choice 3"
    +
    +config CHOICE_4
    +    bool "choice 3"
    +
    +config CHOICE_5
    +    bool "choice 3"
    +
    +endchoice
    +endif
    +
    +if LOC_2
    +choice CORRECT_PROP_LOCS_CHOICE
    +    default CHOICE_4
    +endchoice
    +endif
    +
    +if LOC_3
    +choice CORRECT_PROP_LOCS_CHOICE
    +    default CHOICE_5
    +endchoice
    +endif
    +
    +config SIMPLE_MENU_HOOK
    +menu "simple menu"
    +endmenu
    +
    +config ADVANCED_MENU_HOOK
    +menu "advanced menu"
    +    depends on A
    +    visible if B
    +    visible if C || D
    +endmenu
    +
    +config SIMPLE_COMMENT_HOOK
    +comment "simple comment"
    +
    +config ADVANCED_COMMENT_HOOK
    +comment "advanced comment"
    +    depends on A
    +    depends on B
    +
    +# Corner cases when removing direct dependencies
    +
    +config DEP_REM_CORNER_CASES
    +    bool
    +    default A
    +    depends on n
    +
    +config DEP_REM_CORNER_CASES
    +    default B if n
    +
    +config DEP_REM_CORNER_CASES
    +    default C
    +    depends on m
    +
    +config DEP_REM_CORNER_CASES
    +    default D if A && y
    +    depends on y
    +
    +config DEP_REM_CORNER_CASES
    +    default E if !E1
    +    default F if F1 = F2
    +    default G if G1 || H1
    +    depends on !H
    +
    +config DEP_REM_CORNER_CASES
    +    default H
    +    depends on "foo" = "bar"
    +
    +menu "menu"
    +    visible if FOO || BAR
    +
    +config DEP_REM_CORNER_CASES
    +    prompt "prompt"
    +    depends on BAZ && QAZ
    +
    +endmenu
    +
    +# Only prompt, no type
    +config PROMPT_ONLY
    +    prompt "prompt only"
    +
    +# {Symbol,Choice}.orig_*
    +
    +if BASE_DEP
    +
    +config BOOL_SYM_ORIG
    +    bool
    +    default D1 if DEP
    +    default D2
    +    select S1
    +    select S2 if DEP
    +    imply I1
    +    imply I1
    +
    +config BOOL_SYM_ORIG
    +    default D3
    +    select S3
    +    imply I3 if DEP
    +
    +config INT_SYM_ORIG
    +    int
    +    range 1 2 if DEP
    +    range 3 4
    +
    +config INT_SYM_ORIG
    +    range 5 6 if DEP
    +
    +choice CHOICE_ORIG
    +    bool "choice orig"
    +    default A
    +    default B if DEP
    +
    +config A
    +    bool
    +
    +config B
    +    bool
    +
    +endchoice
    +
    +choice CHOICE_ORIG
    +    default C if DEP
    +
    +config C
    +    bool
    +
    +endchoice
    +
    +endif
    diff --git a/support/kconfiglib/tests/Kundef 
b/support/kconfiglib/tests/Kundef
    new file mode 100644
    index 0000000..fae521a
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kundef
    @@ -0,0 +1,23 @@
    +config DEF
    +   bool
    +
    +config BOOL
    +   bool "foo" if DEF || !UNDEF_1
    +   default UNDEF_2
    +
    +config INT
    +   int
    +   range UNDEF_2 8
    +   default 10
    +   range 5 15
    +
    +config HEX
    +   hex
    +   range 0x123 0X456
    +   default 0x200
    +
    +menu "menu"
    +   depends on UNDEF_1
    +   visible if UNDEF_3
    +
    +endmenu
    diff --git a/support/kconfiglib/tests/Kuserfunctions 
b/support/kconfiglib/tests/Kuserfunctions
    new file mode 100644
    index 0000000..89b4442
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kuserfunctions
    @@ -0,0 +1,11 @@
    +add-zero  = $(add)
    +add-one   = $(add,1)
    +add-three = $(add,1,-1,2,1)
    +
    +one-zero = $(one)
    +one-one  = $(one,foo bar)
    +one-two  = $(one,foo bar,baz)
    +
    +one-or-more-zero  = $(one-or-more)
    +one-or-more-one   = $(one-or-more,foo)
    +one-or-more-three = $(one-or-more,foo,bar,baz)
    diff --git a/support/kconfiglib/tests/Kvisibility 
b/support/kconfiglib/tests/Kvisibility
    new file mode 100644
    index 0000000..91def0a
    --- /dev/null
    +++ b/support/kconfiglib/tests/Kvisibility
    @@ -0,0 +1,342 @@
    +config MODULES
    +    bool "MODULES"
    +    option modules
    +
    +#
    +# Symbol visibility
    +#
    +
    +config NO_PROMPT
    +    bool
    +
    +# Not rewritten, so MOD will have the value 'y' when running without 
modules
    +config MOD
    +    def_tristate m
    +
    +config BOOL_N
    +    bool "bool n" if n
    +
    +config BOOL_M
    +    # Rewritten to m && MODULES
    +    bool "bool m" if m
    +
    +config BOOL_MOD
    +    bool "bool MOD"
    +    # Not rewritten
    +    depends on MOD
    +
    +config BOOL_Y
    +    bool "bool y"
    +    # Rewritten to m && MODULES
    +    depends on y || m
    +
    +config TRISTATE_N
    +    tristate "tristate n" if n
    +
    +config TRISTATE_M
    +    # Rewritten to m && MODULES
    +    tristate "tristate m" if m
    +
    +config TRISTATE_MOD
    +    tristate "tristate MOD"
    +    # Not rewritten
    +    depends on MOD
    +
    +config TRISTATE_Y
    +    bool "tristate y"
    +    # Rewritten to m && MODULES
    +    depends on y || m
    +
    +# Symbols nested in 'if'
    +
    +if n
    +
    +config BOOL_IF_N
    +    bool "bool if n"
    +
    +config TRISTATE_IF_N
    +    tristate "tristate if n"
    +
    +endif
    +
    +if m
    +
    +config BOOL_IF_M
    +    bool "bool if m"
    +
    +config TRISTATE_IF_M
    +    tristate "tristate if n"
    +
    +endif
    +
    +if y
    +
    +config BOOL_IF_Y
    +    bool "bool if y"
    +
    +config TRISTATE_IF_Y
    +    tristate "tristate if y"
    +
    +endif
    +
    +# Symbols nested in 'menu'
    +
    +menu "menu 1"
    +    depends on n
    +
    +config BOOL_MENU_N
    +    bool "bool menu n"
    +
    +config TRISTATE_MENU_N
    +    tristate "tristate menu n"
    +
    +endmenu
    +
    +menu "menu 2"
    +    depends on m
    +
    +config BOOL_MENU_M
    +    bool "bool menu m"
    +
    +config TRISTATE_MENU_M
    +    tristate "tristate menu n"
    +
    +endmenu
    +
    +menu "menu 3"
    +    depends on y
    +
    +config BOOL_MENU_Y
    +    bool "bool menu y"
    +
    +config TRISTATE_MENU_Y
    +    tristate "tristate menu y"
    +
    +endmenu
    +
    +# Symbols nested in choices
    +
    +choice C1
    +    tristate "choice n" if n
    +
    +config BOOL_CHOICE_N
    +    bool "bool choice n"
    +
    +config TRISTATE_CHOICE_N
    +    tristate "tristate choice n"
    +
    +endchoice
    +
    +choice C2
    +    tristate "choice m" if m
    +
    +config BOOL_CHOICE_M
    +    bool "bool choice m"
    +
    +config TRISTATE_CHOICE_M
    +    tristate "tristate choice n"
    +
    +endchoice
    +
    +choice C3
    +    tristate "choice y" if y
    +
    +config BOOL_CHOICE_Y
    +    bool "bool choice y"
    +
    +config TRISTATE_CHOICE_Y
    +    tristate "tristate choice y"
    +
    +endchoice
    +
    +#
    +# Choice visibility
    +#
    +
    +choice BOOL_CHOICE_N
    +    bool "bool choice n" if n
    +endchoice
    +
    +choice BOOL_CHOICE_M
    +    bool "bool choice m" if m
    +endchoice
    +
    +choice BOOL_CHOICE_Y
    +    bool "bool choice y" if y
    +endchoice
    +
    +choice TRISTATE_CHOICE_N
    +    tristate "tristate choice n" if n
    +endchoice
    +
    +choice TRISTATE_CHOICE_M
    +    tristate "tristate choice m" if m
    +endchoice
    +
    +choice TRISTATE_CHOICE_Y
    +    tristate "tristate choice y" if y
    +
    +config K
    +    tristate "K"
    +
    +config L
    +    tristate "L"
    +
    +endchoice
    +
    +if m
    +choice TRISTATE_CHOICE_IF_M_AND_Y
    +    tristate "tristate choice if m and y" if y
    +
    +config M
    +    bool "M"
    +
    +config N
    +    bool "N"
    +
    +endchoice
    +endif
    +
    +menu "choice-containing menu"
    +    depends on n && y
    +
    +choice TRISTATE_CHOICE_MENU_N_AND_Y
    +    tristate "tristate choice if n and y"
    +
    +config O
    +    tristate "O"
    +
    +config P
    +    tristate "P"
    +
    +endchoice
    +
    +endmenu
    +
    +#
    +# Menu visibility
    +#
    +
    +menu "menu n"
    +    depends on n
    +endmenu
    +
    +menu "menu m"
    +    depends on m
    +endmenu
    +
    +menu "menu y"
    +    depends on y
    +endmenu
    +
    +if n
    +menu "menu if n"
    +endmenu
    +endif
    +
    +if m
    +menu "menu if m"
    +endmenu
    +endif
    +
    +if y
    +menu "menu if y"
    +endmenu
    +endif
    +
    +if m
    +menu "menu if m and y"
    +    depends on y
    +endmenu
    +endif
    +
    +#
    +# Comment visibility
    +#
    +
    +comment "comment n"
    +    depends on n
    +comment "comment m"
    +    depends on m
    +comment "comment y"
    +    depends on y
    +
    +if n
    +comment "comment if n"
    +endif
    +
    +if m
    +comment "comment if m"
    +endif
    +
    +if y
    +comment "comment if y"
    +endif
    +
    +if "y"
    +
    +menu "menu with comment"
    +    depends on m
    +
    +comment "double-nested m comment"
    +    depends on y
    +
    +endmenu
    +
    +endif
    +
    +# Used to verify that string/int/hex symbols with m visibility accept a 
user
    +# value
    +
    +if m
    +
    +config STRING_m
    +    string "string"
    +
    +config INT_m
    +    int "int"
    +
    +config HEX_m
    +    hex "hex"
    +
    +endif
    +
    +# Menu 'visible if' visibility
    +
    +menu "n-visible menu"
    +    visible if n
    +
    +config VISIBLE_IF_N
    +    tristate "visible if n"
    +
    +endmenu
    +
    +menu "m-visible menu"
    +    visible if m
    +
    +config VISIBLE_IF_M
    +    tristate "visible if m"
    +
    +endmenu
    +
    +menu "y-visible menu"
    +    visible if y
    +
    +config VISIBLE_IF_Y
    +    tristate "visible if m"
    +
    +endmenu
    +
    +menu "m-visible menu 2"
    +    visible if y || n
    +    visible if m && y
    +    visible if y
    +
    +if y
    +
    +config VISIBLE_IF_M_2
    +    tristate "visible if m 2"
    +
    +endif
    +
    +endmenu
    diff --git a/support/kconfiglib/tests/config_indented 
b/support/kconfiglib/tests/config_indented
    new file mode 100644
    index 0000000..d57d8b4
    --- /dev/null
    +++ b/support/kconfiglib/tests/config_indented
    @@ -0,0 +1,3 @@
    +# Indented assignments should be ignored to be compatible with the C
    +# implementation
    + CONFIG_IGNOREME=n
    diff --git a/support/kconfiglib/tests/config_set_bool 
b/support/kconfiglib/tests/config_set_bool
    new file mode 100644
    index 0000000..d2dccbf
    --- /dev/null
    +++ b/support/kconfiglib/tests/config_set_bool
    @@ -0,0 +1 @@
    +CONFIG_BOOL=y
    diff --git a/support/kconfiglib/tests/config_set_string 
b/support/kconfiglib/tests/config_set_string
    new file mode 100644
    index 0000000..3a1250a
    --- /dev/null
    +++ b/support/kconfiglib/tests/config_set_string
    @@ -0,0 +1 @@
    +CONFIG_STRING="foo bar"
    diff --git a/support/kconfiglib/tests/defconfig_1 
b/support/kconfiglib/tests/defconfig_1
    new file mode 100644
    index 0000000..e69de29
    diff --git a/support/kconfiglib/tests/defconfig_2 
b/support/kconfiglib/tests/defconfig_2
    new file mode 100644
    index 0000000..e69de29
    diff --git a/support/kconfiglib/tests/empty b/support/kconfiglib/tests/empty
    new file mode 100644
    index 0000000..e69de29
    diff --git a/support/kconfiglib/tests/kconfigfunctions.py 
b/support/kconfiglib/tests/kconfigfunctions.py
    new file mode 100644
    index 0000000..e760468
    --- /dev/null
    +++ b/support/kconfiglib/tests/kconfigfunctions.py
    @@ -0,0 +1,14 @@
    +def add(kconf, name, *args):
    +    return str(sum(map(int, args)))
    +
    +def one(kconf, name, s):
    +    return name + 2*s
    +
    +def one_or_more(kconf, name, arg, *args):
    +    return arg + " + " + ",".join(args)
    +
    +functions = {
    +    "add":         (add,         0, None),
    +    "one":         (one,         1,    1),
    +    "one-or-more": (one_or_more, 1, None),
    +}
    diff --git a/support/kconfiglib/tests/reltest 
b/support/kconfiglib/tests/reltest
    new file mode 100755
    index 0000000..3e09e5f
    --- /dev/null
    +++ b/support/kconfiglib/tests/reltest
    @@ -0,0 +1,51 @@
    +#!/usr/bin/env bash
    +
    +# Runs the test suite and all examples scripts with Python 2 and Python 3,
    +# bailing immediately if anything fails. For the examples that aren't 
tested in
    +# the test suite, we just confirm that they at least run.
    +#
    +# Should be run from the kernel root with  $ Kconfiglib/tests/reltest
    +
    +test_script() {
    +    echo -e "\n================= $1 with $py =================\n"
    +    if (($# == 1)); then
    +        make scriptconfig PYTHONCMD=$py SCRIPT=$1
    +    else
    +        make scriptconfig PYTHONCMD=$py SCRIPT=$1 SCRIPT_ARG="$2"
    +    fi
    +
    +    if (($?)); then
    +        echo "$1 failed to run with $py"
    +        exit 1
    +    fi
    +}
    +
    +for py in python2 python3; do
    +    echo -e "\n================= Test suite with $py =================\n"
    +
    +    if ! $py Kconfiglib/testsuite.py; then
    +        echo "test suite failed for $py"
    +        exit 1
    +    fi
    +
    +    # Check that the example scripts that aren't tested by the test suite 
run
    +    # at least
    +
    +    test_script Kconfiglib/examples/defconfig_oldconfig.py
    +    test_script Kconfiglib/examples/eval_expr.py MODULES
    +    test_script Kconfiglib/examples/find_symbol.py X86
    +    test_script Kconfiglib/examples/help_grep.py general
    +    test_script Kconfiglib/examples/print_sym_info.py MODULES
    +    test_script Kconfiglib/examples/print_tree.py
    +
    +    $py Kconfiglib/examples/menuconfig_example.py 
Kconfiglib/examples/Kmenuconfig <<END
    +BOOL
    +n
    +END
    +    if (($?)); then
    +        echo "menuconfig_example.py failed with $py"
    +        exit 1
    +    fi
    +done
    +
    +echo "everything okay"
    diff --git a/support/kconfiglib/tests/sub/Kconfig_symlink_2 
b/support/kconfiglib/tests/sub/Kconfig_symlink_2
    new file mode 100644
    index 0000000..aeba985
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/Kconfig_symlink_2
    @@ -0,0 +1 @@
    +rsource "Kconfig_symlink_3"
    diff --git a/support/kconfiglib/tests/sub/Kconfig_symlink_3 
b/support/kconfiglib/tests/sub/Kconfig_symlink_3
    new file mode 100644
    index 0000000..20b4e06
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/Kconfig_symlink_3
    @@ -0,0 +1,2 @@
    +config FOUNDME
    +    bool
    diff --git a/support/kconfiglib/tests/sub/Klocation_grsourced1 
b/support/kconfiglib/tests/sub/Klocation_grsourced1
    new file mode 100644
    index 0000000..49dac36
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/Klocation_grsourced1
    @@ -0,0 +1 @@
    +config MULTI_DEF
    diff --git a/support/kconfiglib/tests/sub/Klocation_grsourced2 
b/support/kconfiglib/tests/sub/Klocation_grsourced2
    new file mode 100644
    index 0000000..49dac36
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/Klocation_grsourced2
    @@ -0,0 +1 @@
    +config MULTI_DEF
    diff --git a/support/kconfiglib/tests/sub/Klocation_gsourced1 
b/support/kconfiglib/tests/sub/Klocation_gsourced1
    new file mode 100644
    index 0000000..49dac36
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/Klocation_gsourced1
    @@ -0,0 +1 @@
    +config MULTI_DEF
    diff --git a/support/kconfiglib/tests/sub/Klocation_gsourced2 
b/support/kconfiglib/tests/sub/Klocation_gsourced2
    new file mode 100644
    index 0000000..49dac36
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/Klocation_gsourced2
    @@ -0,0 +1 @@
    +config MULTI_DEF
    diff --git a/support/kconfiglib/tests/sub/Klocation_rsourced 
b/support/kconfiglib/tests/sub/Klocation_rsourced
    new file mode 100644
    index 0000000..c6f185d
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/Klocation_rsourced
    @@ -0,0 +1,2 @@
    +
    +config MULTI_DEF
    diff --git a/support/kconfiglib/tests/sub/defconfig_in_sub 
b/support/kconfiglib/tests/sub/defconfig_in_sub
    new file mode 100644
    index 0000000..e69de29
    diff --git a/support/kconfiglib/tests/sub/sub/Kconfig_symlink_1 
b/support/kconfiglib/tests/sub/sub/Kconfig_symlink_1
    new file mode 100644
    index 0000000..bceec8b
    --- /dev/null
    +++ b/support/kconfiglib/tests/sub/sub/Kconfig_symlink_1
    @@ -0,0 +1,2 @@
    +# Sources tests/sub/Kconfig_symlink_2, with an absolute path
    +source "$(KCONFIG_SYMLINK_2)"
    diff --git a/support/kconfiglib/tests/symlink 
b/support/kconfiglib/tests/symlink
    new file mode 120000
    index 0000000..565623e
    --- /dev/null
    +++ b/support/kconfiglib/tests/symlink
    @@ -0,0 +1 @@
    +sub/sub
    \ No newline at end of file
    diff --git a/support/kconfiglib/testsuite.py 
b/support/kconfiglib/testsuite.py
    new file mode 100644
    index 0000000..3a6a3a6
    --- /dev/null
    +++ b/support/kconfiglib/testsuite.py
    @@ -0,0 +1,3136 @@
    +# Copyright (c) 2011-2019, Ulf Magnusson
    +# SPDX-License-Identifier: ISC
    +
    +# This is the Kconfiglib test suite. It runs selftests on Kconfigs 
provided by
    +# us and tests compatibility with the C Kconfig implementation by 
comparing the
    +# output of Kconfiglib with the output of the scripts/kconfig/*conf 
utilities
    +# for different targets and defconfigs. It should be run from the top-level
    +# kernel directory with
    +#
    +#   $ python Kconfiglib/testsuite.py
    +#
    +# Some additional options can be turned on by passing them as arguments. 
They
    +# default to off.
    +#
    +#  - obsessive:
    +#    By default, only valid arch/defconfig pairs are tested. In obsessive 
mode,
    +#    every arch will be tested with every defconfig. Increases the testing 
time
    +#    by an order of magnitude. Occasionally finds (usually obscure) bugs, 
and I
    +#    make sure everything passes with it.
    +#
    +#  - obsessive-min-config:
    +#    Like obsessive, for the minimal configuation (defconfig) tests.
    +#
    +#  - log:
    +#    Log timestamped defconfig test failures to the file 
test_defconfig_fails.
    +#    Handy in obsessive mode.
    +#
    +# For example, this commands runs the test suite in obsessive mode with 
logging
    +# enabled:
    +#
    +#   $ python(3) Kconfiglib/testsuite.py obsessive log
    +#
    +# pypy works too, and runs most tests much faster than CPython.
    +#
    +# All tests should pass. Report regressions to ulfalizer a.t Google's email
    +# service.
    +
    +import difflib
    +import errno
    +import os
    +import platform
    +import re
    +import shutil
    +import subprocess
    +import sys
    +import tempfile
    +import textwrap
    +
    +from kconfiglib import Kconfig, Symbol, Choice, COMMENT, MENU, MenuNode, \
    +                       BOOL, TRISTATE, HEX, STRING, \
    +                       TRI_TO_STR, \
    +                       escape, unescape, \
    +                       expr_str, expr_value, expr_items, split_expr, \
    +                       _ordered_unique, \
    +                       OR, AND, \
    +                       KconfigError
    +
    +
    +def shell(cmd):
    +    with open(os.devnull, "w") as devnull:
    +        subprocess.call(cmd, shell=True, stdout=devnull, stderr=devnull)
    +
    +
    +all_passed = True
    +
    +
    +def fail(msg=None):
    +    global all_passed
    +    all_passed = False
    +    if msg is not None:
    +        print("fail: " + msg)
    +
    +
    +def verify(cond, msg):
    +    if not cond:
    +        fail(msg)
    +
    +
    +def verify_equal(x, y):
    +    if x != y:
    +        fail("'{}' does not equal '{}'".format(x, y))
    +
    +
    +# Prevent accidental loading of configuration files by removing
    +# KCONFIG_ALLCONFIG from the environment
    +os.environ.pop("KCONFIG_ALLCONFIG", None)
    +
    +obsessive = False
    +obsessive_min_config = False
    +log = False
    +
    +
    +def run_tests():
    +    global obsessive, log
    +    for s in sys.argv[1:]:
    +        if s == "obsessive":
    +            obsessive = True
    +            print("Obsessive mode enabled")
    +        elif s == "obsessive-min-config":
    +            obsessive_min_config = True
    +            print("Obsessive minimal config mode enabled")
    +        elif s == "log":
    +            log = True
    +            print("Log mode enabled")
    +        else:
    +            print("Unrecognized option '{}'".format(s))
    +            return
    +
    +    run_selftests()
    +    run_compatibility_tests()
    +
    +
    +def run_selftests():
    +    #
    +    # Common helper functions. These all expect 'c' to hold the current
    +    # configuration.
    +    #
    +
    +    def verify_value(sym_name, val):
    +        # Verifies that a symbol has a particular value.
    +
    +        if isinstance(val, int):
    +            val = TRI_TO_STR[val]
    +
    +        sym = c.syms[sym_name]
    +        verify(sym.str_value == val,
    +               'expected {} to have the value "{}", had the value "{}"'
    +               .format(sym_name, val, sym.str_value))
    +
    +    def assign_and_verify_value(sym_name, val, new_val):
    +        # Assigns 'val' to a symbol and verifies that its value becomes
    +        # 'new_val'. Assumes (and tests) that 'val' is valid for the
    +        # symbol type.
    +
    +        if isinstance(new_val, int):
    +            new_val = TRI_TO_STR[new_val]
    +
    +        sym = c.syms[sym_name]
    +        old_val = sym.str_value
    +        verify(sym.set_value(val),
    +               "assigning '{}' to {} unexpectedly failed"
    +               .format(val, sym_name))
    +        verify(sym.str_value == new_val,
    +               "expected {} to have the value '{}' after being assigned 
the "
    +               "value '{}'. Instead, the value is '{}'. The old value was "
    +               "'{}'."
    +               .format(sym_name, new_val, val, sym.str_value, old_val))
    +
    +    def assign_and_verify(sym_name, user_val):
    +        # Like assign_and_verify_value(), with the expected value being the
    +        # value just set.
    +
    +        assign_and_verify_value(sym_name, user_val, user_val)
    +
    +    def assign_and_verify_user_value(sym_name, val, user_val, valid):
    +        # Assigns a user value to the symbol and verifies the new user 
value.
    +        # If valid is True, the user value is valid for the type, otherwise
    +        # not. This is used to test the set_value() return value.
    +
    +        sym = c.syms[sym_name]
    +        sym_old_user_val = sym.user_value
    +
    +        verify(sym.set_value(val) == valid,
    +               "expected the user value '{}' to be {} for {}, was not"
    +               .format(val, "valid" if valid else "invalid", sym_name))
    +        verify(sym.user_value == user_val,
    +               "the assigned user value '{}' wasn't reflected in 
user_value "
    +               "on the symbol {}. Instead, the new user_value was '{}'. 
The "
    +               "old user value was '{}'."
    +               .format(user_val, sym_name, sym.user_value, 
sym_old_user_val))
    +
    +    #
    +    # Selftests
    +    #
    +
    +    print("Testing string literal lexing")
    +
    +    # Dummy empty configuration just to get a Kconfig object
    +    c = Kconfig("Kconfiglib/tests/empty")
    +
    +    def verify_string_lex(s, expected):
    +        # Verifies that a constant symbol with the name 'res' is produced 
from
    +        # lexing 's'
    +
    +        res = c._tokenize("if " + s)[1].name
    +        verify(res == expected,
    +               "expected <{}> to produced the constant symbol <{}>, "
    +               'produced <{}>'.format(s[1:-1], expected, res))
    +
    +    verify_string_lex(r""" "" """, "")
    +    verify_string_lex(r""" '' """, "")
    +
    +    verify_string_lex(r""" "a" """, "a")
    +    verify_string_lex(r""" 'a' """, "a")
    +    verify_string_lex(r""" "ab" """, "ab")
    +    verify_string_lex(r""" 'ab' """, "ab")
    +    verify_string_lex(r""" "abc" """, "abc")
    +    verify_string_lex(r""" 'abc' """, "abc")
    +
    +    verify_string_lex(r""" "'" """, "'")
    +    verify_string_lex(r""" '"' """, '"')
    +
    +    verify_string_lex(r""" "\"" """, '"')
    +    verify_string_lex(r""" '\'' """, "'")
    +
    +    verify_string_lex(r""" "\"\"" """, '""')
    +    verify_string_lex(r""" '\'\'' """, "''")
    +
    +    verify_string_lex(r""" "\'" """, "'")
    +    verify_string_lex(r""" '\"' """, '"')
    +
    +    verify_string_lex(r""" "\\" """, "\\")
    +    verify_string_lex(r""" '\\' """, "\\")
    +
    +    verify_string_lex(r""" "\a\\'\b\c\"'d" """, 'a\\\'bc"\'d')
    +    verify_string_lex(r""" '\a\\"\b\c\'"d' """, "a\\\"bc'\"d")
    +
    +    def verify_string_bad(s):
    +        # Verifies that tokenizing 's' throws a KconfigError. Strips the 
first
    +        # and last characters from 's' so we can use readable raw strings 
as
    +        # input.
    +
    +        try:
    +            c.eval_string(s)
    +        except KconfigError:
    +            pass
    +        else:
    +            fail("expected tokenization of {} to fail, 
didn't".format(s[1:-1]))
    +
    +    verify_string_bad(r""" " """)
    +    verify_string_bad(r""" ' """)
    +    verify_string_bad(r""" "' """)
    +    verify_string_bad(r""" '" """)
    +    verify_string_bad(r""" "\" """)
    +    verify_string_bad(r""" '\' """)
    +    verify_string_bad(r""" "foo """)
    +    verify_string_bad(r""" 'foo """)
    +
    +
    +    print("Testing escape() and unescape()")
    +
    +    def verify_escape_unescape(s, sesc):
    +        # Verify that 's' escapes to 'sesc' and that 'sesc' unescapes to 
's'
    +        verify_equal(escape(s), sesc)
    +        verify_equal(unescape(sesc), s)
    +
    +    verify_escape_unescape(r''          , r''              )
    +    verify_escape_unescape(r'foo'       , r'foo'           )
    +    verify_escape_unescape(r'"'         , r'\"'            )
    +    verify_escape_unescape(r'""'        , r'\"\"'          )
    +    verify_escape_unescape('\\'         , r'\\'            )
    +    verify_escape_unescape(r'\\'        , r'\\\\'          )
    +    verify_escape_unescape(r'\"'        , r'\\\"'          )
    +    verify_escape_unescape(r'"ab\cd"ef"', r'\"ab\\cd\"ef\"')
    +
    +    # Backslashes before any character should be unescaped, not just 
before "
    +    # and \
    +    verify_equal(unescape(r"\afoo\b\c\\d\\\e\\\\f"), r"afoobc\d\e\\f")
    +
    +
    +    print("Testing _ordered_unique()")
    +
    +    verify_equal(_ordered_unique([]), [])
    +    verify_equal(_ordered_unique([1]), [1])
    +    verify_equal(_ordered_unique([1, 2]), [1, 2])
    +    verify_equal(_ordered_unique([1, 1]), [1])
    +    verify_equal(_ordered_unique([1, 1, 2]), [1, 2])
    +    verify_equal(_ordered_unique([1, 2, 1]), [1, 2])
    +    verify_equal(_ordered_unique([1, 2, 2]), [1, 2])
    +    verify_equal(_ordered_unique([1, 2, 3, 2, 1, 2, 3, 4, 3, 2, 1, 0]),
    +                                 [1, 2, 3, 4, 0])
    +
    +
    +    print("Testing expression evaluation")
    +
    +    c = Kconfig("Kconfiglib/tests/Keval", warn=False)
    +
    +    def verify_eval(expr, val):
    +        res = c.eval_string(expr)
    +        verify(res == val,
    +               "'{}' evaluated to {}, expected {}".format(expr, res, val))
    +
    +    # No modules
    +    verify_eval("n", 0)
    +    verify_eval("m", 0)
    +    verify_eval("y", 2)
    +    verify_eval("'n'", 0)
    +    verify_eval("'m'", 0)
    +    verify_eval("'y'", 2)
    +    verify_eval("M", 2)
    +
    +    # Modules
    +    c.modules.set_value(2)
    +    verify_eval("n", 0)
    +    verify_eval("m", 1)
    +    verify_eval("y", 2)
    +    verify_eval("'n'", 0)
    +    verify_eval("'m'", 1)
    +    verify_eval("'y'", 2)
    +    verify_eval("M", 1)
    +    verify_eval("(Y || N) && (m && y)", 1)
    +
    +    # Non-bool/non-tristate symbols are always n in a tristate sense
    +    verify_eval("Y_STRING", 0)
    +    verify_eval("Y_STRING || m", 1)
    +
    +    # As are all constants besides y and m
    +    verify_eval('"foo"', 0)
    +    verify_eval('"foo" || "bar"', 0)
    +    verify_eval('"foo" || m', 1)
    +
    +    # Test equality for symbols
    +
    +    verify_eval("N = N", 2)
    +    verify_eval("N = n", 2)
    +    verify_eval("N = 'n'", 2)
    +    verify_eval("N != N", 0)
    +    verify_eval("N != n", 0)
    +    verify_eval("N != 'n'", 0)
    +
    +    verify_eval("M = M", 2)
    +    verify_eval("M = m", 2)
    +    verify_eval("M = 'm'", 2)
    +    verify_eval("M != M", 0)
    +    verify_eval("M != m", 0)
    +    verify_eval("M != 'm'", 0)
    +
    +    verify_eval("Y = Y", 2)
    +    verify_eval("Y = y", 2)
    +    verify_eval("Y = 'y'", 2)
    +    verify_eval("Y != Y", 0)
    +    verify_eval("Y != y", 0)
    +    verify_eval("Y != 'y'", 0)
    +
    +    verify_eval("N != M", 2)
    +    verify_eval("N != Y", 2)
    +    verify_eval("M != Y", 2)
    +
    +    verify_eval("Y_STRING = y", 2)
    +    verify_eval("Y_STRING = 'y'", 2)
    +    verify_eval('FOO_BAR_STRING = "foo bar"', 2)
    +    verify_eval('FOO_BAR_STRING != "foo bar baz"', 2)
    +    verify_eval('INT_37 = 37', 2)
    +    verify_eval("INT_37 = '37'", 2)
    +    verify_eval('HEX_0X37 = 0x37', 2)
    +    verify_eval("HEX_0X37 = '0x37'", 2)
    +
    +    # These should also hold after 31847b67 (kconfig: allow use of 
relations
    +    # other than (in)equality)
    +    verify_eval("HEX_0X37 = '0x037'", 2)
    +    verify_eval("HEX_0X37 = '0x0037'", 2)
    +
    +    # Constant symbol comparisons
    +    verify_eval('"foo" != "bar"', 2)
    +    verify_eval('"foo" = "bar"', 0)
    +    verify_eval('"foo" = "foo"', 2)
    +
    +    # Undefined symbols get their name as their value
    +    c.warn = False
    +    verify_eval("'not_defined' = not_defined", 2)
    +    verify_eval("not_defined_2 = not_defined_2", 2)
    +    verify_eval("not_defined_1 != not_defined_2", 2)
    +
    +    # Test less than/greater than
    +
    +    # Basic evaluation
    +    verify_eval("INT_37 < 38", 2)
    +    verify_eval("38 < INT_37", 0)
    +    verify_eval("INT_37 < '38'", 2)
    +    verify_eval("'38' < INT_37", 0)
    +    verify_eval("INT_37 < 138", 2)
    +    verify_eval("138 < INT_37", 0)
    +    verify_eval("INT_37 < '138'", 2)
    +    verify_eval("'138' < INT_37", 0)
    +    verify_eval("INT_37 < -138", 0)
    +    verify_eval("-138 < INT_37", 2)
    +    verify_eval("INT_37 < '-138'", 0)
    +    verify_eval("'-138' < INT_37", 2)
    +    verify_eval("INT_37 < 37", 0)
    +    verify_eval("37 < INT_37", 0)
    +    verify_eval("INT_37 < 36", 0)
    +    verify_eval("36 < INT_37", 2)
    +
    +    # Different formats in comparison
    +    verify_eval("INT_37 < 0x26", 2) # 38
    +    verify_eval("INT_37 < 0x25", 0) # 37
    +    verify_eval("INT_37 < 0x24", 0) # 36
    +    verify_eval("HEX_0X37 < 56", 2) # 0x38
    +    verify_eval("HEX_0X37 < 55", 0) # 0x37
    +    verify_eval("HEX_0X37 < 54", 0) # 0x36
    +
    +    # Other int comparisons
    +    verify_eval("INT_37 <= 38", 2)
    +    verify_eval("INT_37 <= 37", 2)
    +    verify_eval("INT_37 <= 36", 0)
    +    verify_eval("INT_37 >  38", 0)
    +    verify_eval("INT_37 >  37", 0)
    +    verify_eval("INT_37 >  36", 2)
    +    verify_eval("INT_37 >= 38", 0)
    +    verify_eval("INT_37 >= 37", 2)
    +    verify_eval("INT_37 >= 36", 2)
    +
    +    # Other hex comparisons
    +    verify_eval("HEX_0X37 <= 0x38", 2)
    +    verify_eval("HEX_0X37 <= 0x37", 2)
    +    verify_eval("HEX_0X37 <= 0x36", 0)
    +    verify_eval("HEX_0X37 >  0x38", 0)
    +    verify_eval("HEX_0X37 >  0x37", 0)
    +    verify_eval("HEX_0X37 >  0x36", 2)
    +    verify_eval("HEX_0X37 >= 0x38", 0)
    +    verify_eval("HEX_0X37 >= 0x37", 2)
    +    verify_eval("HEX_0X37 >= 0x36", 2)
    +
    +    # A hex holding a value without a "0x" prefix should still be treated 
as
    +    # hexadecimal
    +    verify_eval("HEX_37 < 0x38", 2)
    +    verify_eval("HEX_37 < 0x37", 0)
    +    verify_eval("HEX_37 < 0x36", 0)
    +
    +    # Symbol comparisons
    +    verify_eval("INT_37   <  HEX_0X37", 2)
    +    verify_eval("INT_37   >  HEX_0X37", 0)
    +    verify_eval("HEX_0X37 <  INT_37  ", 0)
    +    verify_eval("HEX_0X37 >  INT_37  ", 2)
    +    verify_eval("INT_37   <  INT_37  ", 0)
    +    verify_eval("INT_37   <= INT_37  ", 2)
    +    verify_eval("INT_37   >  INT_37  ", 0)
    +    verify_eval("INT_37   <= INT_37  ", 2)
    +
    +    # Tristate value comparisons
    +    verify_eval("n < n", 0)
    +    verify_eval("n < m", 2)
    +    verify_eval("n < y", 2)
    +    verify_eval("n < N", 0)
    +    verify_eval("n < M", 2)
    +    verify_eval("n < Y", 2)
    +    verify_eval("0 > n", 0)
    +    verify_eval("1 > n", 2)
    +    verify_eval("2 > n", 2)
    +    verify_eval("m < n", 0)
    +    verify_eval("m < m", 0)
    +    verify_eval("m < y", 2)
    +
    +    # Strings compare lexicographically
    +    verify_eval("'aa' < 'ab'", 2)
    +    verify_eval("'aa' > 'ab'", 0)
    +    verify_eval("'ab' < 'aa'", 0)
    +    verify_eval("'ab' > 'aa'", 2)
    +
    +    # Comparisons where one of the operands doesn't parse as a number also 
give
    +    # a lexicographic comparison
    +    verify_eval("INT_37 <  '37a' ", 2)
    +    verify_eval("'37a'  >  INT_37", 2)
    +    verify_eval("INT_37 <= '37a' ", 2)
    +    verify_eval("'37a'  >= INT_37", 2)
    +    verify_eval("INT_37 >= '37a' ", 0)
    +    verify_eval("INT_37 >  '37a' ", 0)
    +    verify_eval("'37a'  <  INT_37", 0)
    +    verify_eval("'37a'  <= INT_37", 0)
    +
    +    def verify_eval_bad(expr):
    +        try:
    +            c.eval_string(expr)
    +        except KconfigError:
    +            pass
    +        else:
    +            fail('expected eval_string("{}") to throw KconfigError, '
    +                 "didn't".format(expr))
    +
    +    # Verify that some bad stuff throws KconfigError's
    +    verify_eval_bad("")
    +    verify_eval_bad("&")
    +    verify_eval_bad("|")
    +    verify_eval_bad("!")
    +    verify_eval_bad("(")
    +    verify_eval_bad(")")
    +    verify_eval_bad("=")
    +    verify_eval_bad("(X")
    +    verify_eval_bad("X)")
    +    verify_eval_bad("X X")
    +    verify_eval_bad("!X X")
    +    verify_eval_bad("X !X")
    +    verify_eval_bad("(X) X")
    +    verify_eval_bad("X &&")
    +    verify_eval_bad("&& X")
    +    verify_eval_bad("X && && X")
    +    verify_eval_bad("X && !&&")
    +    verify_eval_bad("X ||")
    +    verify_eval_bad("|| X")
    +
    +
    +    print("Testing Symbol.__str__()/custom_str() and def_{int,hex,string}")
    +
    +    def verify_str(item, s):
    +        verify_equal(str(item), s[1:-1])
    +
    +    def verify_custom_str(item, s):
    +        verify_equal(item.custom_str(lambda sc: "[{}]".format(sc.name)),
    +                     s[1:-1])
    +
    +    c = Kconfig("Kconfiglib/tests/Kstr", warn=False)
    +
    +    c.modules.set_value(2)
    +
    +    verify_str(c.syms["UNDEFINED"], """
    +""")
    +
    +    verify_str(c.syms["BASIC_NO_PROMPT"], """
    +config BASIC_NO_PROMPT
    +   bool
    +   help
    +     blah blah
    +     
    +       blah blah blah
    +     
    +      blah
    +""")
    +
    +    verify_str(c.syms["BASIC_PROMPT"], """
    +config BASIC_PROMPT
    +   bool "basic"
    +""")
    +
    +    verify_str(c.syms["ADVANCED"], """
    +config ADVANCED
    +   tristate "prompt" if DEP
    +   default DEFAULT_1
    +   default DEFAULT_2 if DEP
    +   select SELECTED_1
    +   select SELECTED_2 if DEP
    +   imply IMPLIED_1
    +   imply IMPLIED_2 if DEP
    +   help
    +     first help text
    +
    +config ADVANCED
    +   tristate "prompt 2"
    +
    +menuconfig ADVANCED
    +   tristate "prompt 3"
    +
    +config ADVANCED
    +   tristate
    +   depends on (A || !B || (C && D) || !(E && F) || G = H || (I && !J && (K 
|| L) && !(M || N) && O = P)) && DEP4 && DEP3
    +   help
    +     second help text
    +
    +config ADVANCED
    +   tristate "prompt 4" if VIS
    +   depends on DEP4 && DEP3
    +""")
    +
    +    verify_custom_str(c.syms["ADVANCED"], """
    +config ADVANCED
    +   tristate "prompt" if [DEP]
    +   default [DEFAULT_1]
    +   default [DEFAULT_2] if [DEP]
    +   select [SELECTED_1]
    +   select [SELECTED_2] if [DEP]
    +   imply [IMPLIED_1]
    +   imply [IMPLIED_2] if [DEP]
    +   help
    +     first help text
    +
    +config ADVANCED
    +   tristate "prompt 2"
    +
    +menuconfig ADVANCED
    +   tristate "prompt 3"
    +
    +config ADVANCED
    +   tristate
    +   depends on ([A] || ![B] || ([C] && [D]) || !([E] && [F]) || [G] = [H] 
|| ([I] && ![J] && ([K] || [L]) && !([M] || [N]) && [O] = [P])) && [DEP4] && 
[DEP3]
    +   help
    +     second help text
    +
    +config ADVANCED
    +   tristate "prompt 4" if [VIS]
    +   depends on [DEP4] && [DEP3]
    +""")
    +
    +
    +    verify_str(c.syms["ONLY_DIRECT_DEPS"], """
    +config ONLY_DIRECT_DEPS
    +   int
    +   depends on DEP1 && DEP2
    +""")
    +
    +    verify_str(c.syms["STRING"], """
    +config STRING
    +   string
    +   default "foo"
    +   default "bar" if DEP
    +   default STRING2
    +   default STRING3 if DEP
    +""")
    +
    +    verify_str(c.syms["INT"], """
    +config INT
    +   int
    +   range 1 2
    +   range FOO BAR
    +   range BAZ QAZ if DEP
    +   default 7 if DEP
    +""")
    +
    +    verify_str(c.syms["HEX"], """
    +config HEX
    +   hex
    +   range 0x100 0x200
    +   range FOO BAR
    +   range BAZ QAZ if DEP
    +   default 0x123
    +""")
    +
    +    verify_str(c.modules, """
    +config MODULES
    +   bool "MODULES"
    +   option modules
    +""")
    +
    +    verify_str(c.syms["OPTIONS"], """
    +config OPTIONS
    +   option allnoconfig_y
    +   option defconfig_list
    +   option env="ENV"
    +""")
    +
    +    verify_str(c.syms["CORRECT_PROP_LOCS_BOOL"], """
    +config CORRECT_PROP_LOCS_BOOL
    +   bool "prompt 1"
    +   default DEFAULT_1
    +   default DEFAULT_2
    +   select SELECT_1
    +   select SELECT_2
    +   imply IMPLY_1
    +   imply IMPLY_2
    +   depends on LOC_1
    +   help
    +     help 1
    +
    +menuconfig CORRECT_PROP_LOCS_BOOL
    +   bool "prompt 2"
    +   default DEFAULT_3
    +   default DEFAULT_4
    +   select SELECT_3
    +   select SELECT_4
    +   imply IMPLY_3
    +   imply IMPLY_4
    +   depends on LOC_2
    +   help
    +     help 2
    +
    +config CORRECT_PROP_LOCS_BOOL
    +   bool "prompt 3"
    +   default DEFAULT_5
    +   default DEFAULT_6
    +   select SELECT_5
    +   select SELECT_6
    +   imply IMPLY_5
    +   imply IMPLY_6
    +   depends on LOC_3
    +   help
    +     help 2
    +""")
    +
    +    verify_str(c.syms["CORRECT_PROP_LOCS_INT"], """
    +config CORRECT_PROP_LOCS_INT
    +   int
    +   range 1 2
    +   range 3 4
    +   depends on LOC_1
    +
    +config CORRECT_PROP_LOCS_INT
    +   int
    +   range 5 6
    +   range 7 8
    +   depends on LOC_2
    +""")
    +
    +    verify_str(c.syms["PROMPT_ONLY"], """
    +config PROMPT_ONLY
    +   prompt "prompt only"
    +""")
    +
    +    verify_custom_str(c.syms["CORRECT_PROP_LOCS_INT"], """
    +config CORRECT_PROP_LOCS_INT
    +   int
    +   range [1] [2]
    +   range [3] [4]
    +   depends on [LOC_1]
    +
    +config CORRECT_PROP_LOCS_INT
    +   int
    +   range [5] [6]
    +   range [7] [8]
    +   depends on [LOC_2]
    +""")
    +
    +
    +
    +    print("Testing Choice.__str__()/custom_str()")
    +
    +    verify_str(c.named_choices["CHOICE"], """
    +choice CHOICE
    +   tristate "foo"
    +   default CHOICE_1
    +   default CHOICE_2 if dep
    +""")
    +
    +    verify_str(c.named_choices["CHOICE"].nodes[0].next.item, """
    +choice
    +   tristate "no name"
    +   optional
    +""")
    +
    +    verify_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
    +choice CORRECT_PROP_LOCS_CHOICE
    +   bool
    +   default CHOICE_3
    +   depends on LOC_1
    +
    +choice CORRECT_PROP_LOCS_CHOICE
    +   bool
    +   default CHOICE_4
    +   depends on LOC_2
    +
    +choice CORRECT_PROP_LOCS_CHOICE
    +   bool
    +   default CHOICE_5
    +   depends on LOC_3
    +""")
    +
    +    verify_custom_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
    +choice CORRECT_PROP_LOCS_CHOICE
    +   bool
    +   default [CHOICE_3]
    +   depends on [LOC_1]
    +
    +choice CORRECT_PROP_LOCS_CHOICE
    +   bool
    +   default [CHOICE_4]
    +   depends on [LOC_2]
    +
    +choice CORRECT_PROP_LOCS_CHOICE
    +   bool
    +   default [CHOICE_5]
    +   depends on [LOC_3]
    +""")
    +
    +
    +    print("Testing MenuNode.__str__()/custom_str() for menus and comments")
    +
    +    verify_str(c.syms["SIMPLE_MENU_HOOK"].nodes[0].next, """
    +menu "simple menu"
    +""")
    +
    +    verify_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
    +menu "advanced menu"
    +   depends on A
    +   visible if B && (C || D)
    +""")
    +
    +    verify_custom_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
    +menu "advanced menu"
    +   depends on [A]
    +   visible if [B] && ([C] || [D])
    +""")
    +
    +    verify_str(c.syms["SIMPLE_COMMENT_HOOK"].nodes[0].next, """
    +comment "simple comment"
    +""")
    +
    +    verify_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
    +comment "advanced comment"
    +   depends on A && B
    +""")
    +
    +    verify_custom_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
    +comment "advanced comment"
    +   depends on [A] && [B]
    +""")
    +
    +
    +    print("Testing {MenuNode,Symbol,Choice}.orig_*")
    +
    +    # Just test some corner cases here re. MenuNode.orig_*. They are 
already
    +    # indirectly tested above. Use MenuNode.__str__() as a proxy.
    +
    +    verify_str(c.syms["DEP_REM_CORNER_CASES"], """
    +config DEP_REM_CORNER_CASES
    +   bool
    +   default A
    +   depends on n
    +
    +config DEP_REM_CORNER_CASES
    +   bool
    +   default B if n
    +
    +config DEP_REM_CORNER_CASES
    +   bool
    +   default C
    +   depends on m && MODULES
    +
    +config DEP_REM_CORNER_CASES
    +   bool
    +   default D if A
    +
    +config DEP_REM_CORNER_CASES
    +   bool
    +   default E if !E1
    +   default F if F1 = F2
    +   default G if G1 || H1
    +   depends on !H
    +
    +config DEP_REM_CORNER_CASES
    +   bool
    +   default H
    +   depends on "foo" = "bar"
    +
    +config DEP_REM_CORNER_CASES
    +   bool "prompt" if FOO || BAR
    +   depends on BAZ && QAZ
    +""")
    +
    +    # Test {Symbol,Choice}.orig_*
    +
    +    def verify_deps(elms, dep_index, expected):
    +        verify_equal(" ".join(expr_str(elm[dep_index]) for elm in elms),
    +                     expected)
    +
    +    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_defaults,        1, "DEP y y")
    +    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_selects,         1, "y DEP y")
    +    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_implies,         1, "y y DEP")
    +    verify_deps(c.syms["INT_SYM_ORIG"].orig_ranges,           2, "DEP y 
DEP")
    +    verify_deps(c.named_choices["CHOICE_ORIG"].orig_defaults, 1, "y DEP 
DEP")
    +
    +
    +    print("Testing Symbol.__repr__()")
    +
    +    def verify_repr(item, s):
    +        verify_equal(repr(item) + "\n", s[1:])
    +
    +    c = Kconfig("Kconfiglib/tests/Krepr", warn=False)
    +
    +    verify_repr(c.n, """
    +<symbol n, tristate, value n, constant>
    +""")
    +
    +    verify_repr(c.m, """
    +<symbol m, tristate, value m, constant>
    +""")
    +
    +    verify_repr(c.y, """
    +<symbol y, tristate, value y, constant>
    +""")
    +
    +    verify_repr(c.syms["UNDEFINED"], """
    +<symbol UNDEFINED, unknown, value "UNDEFINED", visibility n, direct deps 
n, undefined>
    +""")
    +
    +    verify_repr(c.syms["BASIC"], """
    +<symbol BASIC, bool, value y, visibility n, direct deps y, 
Kconfiglib/tests/Krepr:9>
    +""")
    +
    +    verify_repr(c.syms["VISIBLE"], """
    +<symbol VISIBLE, bool, "visible", value n, visibility y, direct deps y, 
Kconfiglib/tests/Krepr:14>
    +""")
    +
    +    c.syms["VISIBLE"].set_value(2)
    +    c.syms["STRING"].set_value("foo")
    +
    +    verify_repr(c.syms["VISIBLE"], """
    +<symbol VISIBLE, bool, "visible", value y, user value y, visibility y, 
direct deps y, Kconfiglib/tests/Krepr:14>
    +""")
    +
    +    verify_repr(c.syms["STRING"], """
    +<symbol STRING, string, "visible", value "foo", user value "foo", 
visibility y, direct deps y, Kconfiglib/tests/Krepr:17>
    +""")
    +
    +    verify_repr(c.syms["DIR_DEP_N"], """
    +<symbol DIR_DEP_N, unknown, value "DIR_DEP_N", visibility n, direct deps 
n, Kconfiglib/tests/Krepr:20>
    +""")
    +
    +    verify_repr(c.syms["OPTIONS"], """
    +<symbol OPTIONS, unknown, value "OPTIONS", visibility n, allnoconfig_y, is 
the defconfig_list symbol, from environment variable ENV, direct deps y, 
Kconfiglib/tests/Krepr:23>
    +""")
    +
    +    verify_repr(c.syms["MULTI_DEF"], """
    +<symbol MULTI_DEF, unknown, value "MULTI_DEF", visibility n, direct deps 
y, Kconfiglib/tests/Krepr:28, Kconfiglib/tests/Krepr:29>
    +""")
    +
    +    verify_repr(c.syms["CHOICE_1"], """
    +<symbol CHOICE_1, tristate, "choice sym", value n, visibility m, choice 
symbol, direct deps m, Kconfiglib/tests/Krepr:36>
    +""")
    +
    +    verify_repr(c.modules, """
    +<symbol MODULES, bool, value y, visibility n, is the modules symbol, 
direct deps y, Kconfiglib/tests/Krepr:1>
    +""")
    +
    +
    +    print("Testing Choice.__repr__()")
    +
    +    verify_repr(c.named_choices["CHOICE"], """
    +<choice CHOICE, tristate, "choice", mode m, visibility y, 
Kconfiglib/tests/Krepr:33>
    +""")
    +
    +    c.named_choices["CHOICE"].set_value(2)
    +
    +    verify_repr(c.named_choices["CHOICE"], """
    +<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_1 
selected, visibility y, Kconfiglib/tests/Krepr:33>
    +""")
    +
    +    c.syms["CHOICE_2"].set_value(2)
    +
    +    verify_repr(c.named_choices["CHOICE"], """
    +<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_2 
selected, CHOICE_2 selected by user, visibility y, Kconfiglib/tests/Krepr:33>
    +""")
    +
    +    c.named_choices["CHOICE"].set_value(1)
    +
    +    verify_repr(c.named_choices["CHOICE"], """
    +<choice CHOICE, tristate, "choice", mode m, user mode m, CHOICE_2 selected 
by user (overridden), visibility y, Kconfiglib/tests/Krepr:33>
    +""")
    +
    +    verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next.item, """
    +<choice, tristate, "optional choice", mode n, visibility n, optional, 
Kconfiglib/tests/Krepr:46>
    +""")
    +
    +
    +    print("Testing MenuNode.__repr__()")
    +
    +    verify_repr(c.syms["BASIC"].nodes[0], """
    +<menu node for symbol BASIC, deps y, has help, has next, 
Kconfiglib/tests/Krepr:9>
    +""")
    +
    +    verify_repr(c.syms["DIR_DEP_N"].nodes[0], """
    +<menu node for symbol DIR_DEP_N, deps n, has next, 
Kconfiglib/tests/Krepr:20>
    +""")
    +
    +    verify_repr(c.syms["MULTI_DEF"].nodes[0], """
    +<menu node for symbol MULTI_DEF, deps y, has next, 
Kconfiglib/tests/Krepr:28>
    +""")
    +
    +    verify_repr(c.syms["MULTI_DEF"].nodes[1], """
    +<menu node for symbol MULTI_DEF, deps y, has next, 
Kconfiglib/tests/Krepr:29>
    +""")
    +
    +    verify_repr(c.syms["MENUCONFIG"].nodes[0], """
    +<menu node for symbol MENUCONFIG, is menuconfig, deps y, has next, 
Kconfiglib/tests/Krepr:31>
    +""")
    +
    +    verify_repr(c.named_choices["CHOICE"].nodes[0], """
    +<menu node for choice CHOICE, prompt "choice" (visibility y), deps y, has 
child, has next, Kconfiglib/tests/Krepr:33>
    +""")
    +
    +    verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next, """
    +<menu node for choice, prompt "optional choice" (visibility n), deps y, 
has next, Kconfiglib/tests/Krepr:46>
    +""")
    +
    +    verify_repr(c.syms["NO_VISIBLE_IF_HOOK"].nodes[0].next, """
    +<menu node for menu, prompt "no visible if" (visibility y), deps y, 
'visible if' deps y, has next, Kconfiglib/tests/Krepr:53>
    +""")
    +
    +    verify_repr(c.syms["VISIBLE_IF_HOOK"].nodes[0].next, """
    +<menu node for menu, prompt "visible if" (visibility y), deps y, 'visible 
if' deps m, has next, Kconfiglib/tests/Krepr:58>
    +""")
    +
    +    verify_repr(c.syms["COMMENT_HOOK"].nodes[0].next, """
    +<menu node for comment, prompt "comment" (visibility y), deps y, 
Kconfiglib/tests/Krepr:64>
    +""")
    +
    +
    +    print("Testing Kconfig.__repr__()")
    +
    +    verify_repr(c, """
    +<configuration with 15 symbols, main menu prompt "Main menu", srctree is 
current directory, config symbol prefix "CONFIG_", warnings disabled, printing 
of warnings to stderr enabled, undef. symbol assignment warnings disabled, 
overriding symbol assignment warnings enabled, redundant symbol assignment 
warnings enabled>
    +""")
    +
    +    os.environ["srctree"] = "Kconfiglib"
    +    os.environ["CONFIG_"] = "CONFIG_ value"
    +
    +    c = Kconfig("tests/Krepr", warn=False)
    +    c.warn = True
    +    c.warn_to_stderr = False
    +    c.warn_assign_override = False
    +    c.warn_assign_redun = False
    +    c.warn_assign_undef = True
    +
    +    verify_repr(c, """
    +<configuration with 15 symbols, main menu prompt "Main menu", srctree 
"Kconfiglib", config symbol prefix "CONFIG_ value", warnings enabled, printing 
of warnings to stderr disabled, undef. symbol assignment warnings enabled, 
overriding symbol assignment warnings disabled, redundant symbol assignment 
warnings disabled>
    +""")
    +
    +    os.environ.pop("srctree", None)
    +    os.environ.pop("CONFIG_", None)
    +
    +
    +    print("Testing tricky help strings")
    +
    +    c = Kconfig("Kconfiglib/tests/Khelp")
    +
    +    def verify_help(node, s):
    +        verify_equal(node.help, s[1:-1])
    +
    +    verify_help(c.syms["TWO_HELP_STRINGS"].nodes[0], """
    +first help string
    +""")
    +
    +    verify_help(c.syms["TWO_HELP_STRINGS"].nodes[1], """
    +second help string
    +""")
    +
    +    verify_help(c.syms["NO_BLANK_AFTER_HELP"].nodes[0], """
    +help for
    +NO_BLANK_AFTER_HELP
    +""")
    +
    +    verify_help(c.named_choices["CHOICE_HELP"].nodes[0], """
    +help for
    +CHOICE_HELP
    +""")
    +
    +    verify_help(c.syms["HELP_TERMINATED_BY_COMMENT"].nodes[0], """
    +a
    +b
    +c
    +""")
    +
    +    verify_help(c.syms["TRICKY_HELP"].nodes[0], """
    +a
    + b
    +  c
    +
    + d
    +  e
    +   f
    +
    +
    +g
    + h
    +  i
    +""")
    +
    +
    +    print("Testing locations, source/rsource/gsource/grsource, and "
    +          "Kconfig.kconfig_filenames")
    +
    +    def verify_locations(nodes, *expected_locs):
    +        verify(len(nodes) == len(expected_locs),
    +               "Wrong number of locations for " + repr(nodes))
    +
    +        for node, expected_loc in zip(nodes, expected_locs):
    +            node_loc = "{}:{}".format(node.filename, node.linenr)
    +            verify(node_loc == expected_loc,
    +                   "expected {} to have the location {}, had the location 
{}"
    +                   .format(repr(node), expected_loc, node_loc))
    +
    +    # Expanded in the 'source' statement in Klocation
    +
    +    os.environ["TESTS_DIR_FROM_ENV"] = "tests"
    +    os.environ["SUB_DIR_FROM_ENV"] = "sub"
    +
    +    os.environ["_SOURCED"] = "_sourced"
    +    os.environ["_RSOURCED"] = "_rsourced"
    +    os.environ["_GSOURCED"] = "_gsourced"
    +    os.environ["_GRSOURCED"] = "_grsourced"
    +
    +    # Test twice, with $srctree as a relative and an absolute path,
    +    # respectively
    +    for srctree in "Kconfiglib", os.path.abspath("Kconfiglib"):
    +        os.environ["srctree"] = srctree
    +
    +        # Has symbol with empty help text, so disable warnings
    +        c = Kconfig("tests/Klocation", warn=False)
    +
    +        verify_locations(c.syms["SINGLE_DEF"].nodes, "tests/Klocation:4")
    +
    +        verify_locations(c.syms["MULTI_DEF"].nodes,
    +          "tests/Klocation:7",
    +          "tests/Klocation:37",
    +          "tests/Klocation:39",
    +          "tests/Klocation_sourced:3",
    +          "tests/sub/Klocation_rsourced:2",
    +          "tests/sub/Klocation_gsourced1:1",
    +          "tests/sub/Klocation_gsourced2:1",
    +          "tests/sub/Klocation_gsourced1:1",
    +          "tests/sub/Klocation_gsourced2:1",
    +          "tests/sub/Klocation_grsourced1:1",
    +          "tests/sub/Klocation_grsourced2:1",
    +          "tests/sub/Klocation_grsourced1:1",
    +          "tests/sub/Klocation_grsourced2:1",
    +          "tests/Klocation:72")
    +
    +        verify_locations(c.named_choices["CHOICE"].nodes,
    +                         "tests/Klocation_sourced:5")
    +
    +        verify_locations([c.syms["MENU_HOOK"].nodes[0].next],
    +                         "tests/Klocation_sourced:12")
    +
    +        verify_locations([c.syms["COMMENT_HOOK"].nodes[0].next],
    +                         "tests/Klocation_sourced:18")
    +
    +        # Test Kconfig.kconfig_filenames
    +
    +        verify_equal(c.kconfig_filenames, [
    +            "tests/Klocation",
    +            "tests/Klocation_sourced",
    +            "tests/sub/Klocation_rsourced",
    +            "tests/sub/Klocation_gsourced1",
    +            "tests/sub/Klocation_gsourced2",
    +            "tests/sub/Klocation_gsourced1",
    +            "tests/sub/Klocation_gsourced2",
    +            "tests/sub/Klocation_grsourced1",
    +            "tests/sub/Klocation_grsourced2",
    +            "tests/sub/Klocation_grsourced1",
    +            "tests/sub/Klocation_grsourced2"
    +        ])
    +
    +        # Test recursive 'source' detection
    +
    +        try:
    +            Kconfig("tests/Krecursive1")
    +        except KconfigError as e:
    +            verify_equal(str(e), """
    +tests/Krecursive2:1: recursive 'source' of 'tests/Krecursive1' detected. 
Check that environment variables are set correctly.
    +Include path:
    +tests/Krecursive1:1
    +tests/Krecursive2:1
    +"""[:-1])
    +        except:
    +            fail("recursive 'source' raised wrong exception")
    +        else:
    +            fail("recursive 'source' did not raise exception")
    +
    +        # Verify that source and rsource throw exceptions for missing files
    +
    +        # TODO: Make an exception test helper
    +
    +        try:
    +            Kconfig("tests/Kmissingsource")
    +        except KconfigError as e:
    +            if "not found" not in str(e):
    +                fail("'source' with missing file raised wrong 
KconfigError")
    +        except:
    +            fail("'source' with missing file raised wrong exception")
    +        else:
    +            fail("'source' with missing file did not raise exception")
    +
    +        try:
    +            Kconfig("tests/Kmissingrsource")
    +        except KconfigError as e:
    +            if "not found" not in str(e):
    +                fail("'rsource' with missing file raised wrong 
KconfigError")
    +        except:
    +            fail("'rsource' with missing file raised wrong exception")
    +        else:
    +            fail("'rsource' with missing file did not raise exception")
    +
    +    # Test a tricky case involving symlinks. $srctree is tests/symlink, 
which
    +    # points to tests/sub/sub, meaning tests/symlink/.. != tests/. 
Previously,
    +    # using 'rsource' from a file sourced with an absolute path triggered 
an
    +    # unsafe relpath() with tests/symlink/.. in it, crashing.
    +
    +    os.environ["srctree"] = "Kconfiglib/tests/symlink"
    +    os.environ["KCONFIG_SYMLINK_2"] = os.path.abspath(
    +        "Kconfiglib/tests/sub/Kconfig_symlink_2")
    +    if not os.path.isabs(
    +        Kconfig("Kconfig_symlink_1").syms["FOUNDME"].nodes[0].filename):
    +
    +        fail("Symlink + rsource issues")
    +
    +
    +    print("Testing Kconfig.node_iter()")
    +
    +    # Reuse tests/Klocation. The node_iter(unique_syms=True) case already 
gets
    +    # plenty of testing from write_config() as well.
    +
    +    os.environ["srctree"] = "Kconfiglib"
    +    c = Kconfig("tests/Klocation", warn=False)
    +
    +    verify_equal(
    +        [node.item.name for node in c.node_iter()
    +         if isinstance(node.item, Symbol)],
    +        ["SINGLE_DEF", "MULTI_DEF", "HELP_1", "HELP_2", "HELP_3", 
"MULTI_DEF",
    +         "MULTI_DEF", "MULTI_DEF", "MENU_HOOK", "COMMENT_HOOK"] + \
    +        10*["MULTI_DEF"])
    +
    +    verify_equal(
    +        [node.item.name for node in c.node_iter(True)
    +         if isinstance(node.item, Symbol)],
    +        ["SINGLE_DEF", "MULTI_DEF", "HELP_1", "HELP_2", "HELP_3", 
"MENU_HOOK",
    +         "COMMENT_HOOK"])
    +
    +    verify_equal(
    +        [node.prompt[0] for node in c.node_iter()
    +         if not isinstance(node.item, Symbol)],
    +        ["choice", "menu", "comment"])
    +
    +    verify_equal(
    +        [node.prompt[0] for node in c.node_iter(True)
    +         if not isinstance(node.item, Symbol)],
    +        ["choice", "menu", "comment"])
    +
    +
    +    print("Testing MenuNode.include_path")
    +
    +    os.environ["srctree"] = "Kconfiglib/tests"
    +
    +    c = Kconfig("Kinclude_path")
    +
    +    def verify_node_path(node, *expected):
    +        if node.include_path != expected:
    +            fail("Wrong include path for node {!r}. Got {}, expected {}."
    +                 .format(node, node.include_path, expected))
    +
    +    def verify_sym_path(sym_name, node_i, *expected):
    +        verify_node_path(c.syms[sym_name].nodes[node_i], *expected)
    +
    +    verify_sym_path("TOP", 0)
    +    verify_sym_path("TOP", 1)
    +    verify_sym_path("TOP", 2)
    +
    +    verify_sym_path("ONE_DOWN", 0, ("Kinclude_path", 4))
    +    verify_sym_path("ONE_DOWN", 1, ("Kinclude_path", 4))
    +    verify_sym_path("ONE_DOWN", 2, ("Kinclude_path", 4))
    +    verify_sym_path("ONE_DOWN", 3, ("Kinclude_path", 9))
    +    verify_sym_path("ONE_DOWN", 4, ("Kinclude_path", 9))
    +    verify_sym_path("ONE_DOWN", 5, ("Kinclude_path", 9))
    +
    +    verify_sym_path("TWO_DOWN", 0,
    +                    ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
    +    verify_sym_path("TWO_DOWN", 1,
    +                    ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 9))
    +    verify_sym_path("TWO_DOWN", 2,
    +                    ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 4))
    +    verify_sym_path("TWO_DOWN", 3,
    +                    ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 9))
    +
    +    verify_node_path(c.top_node)
    +    verify_node_path(c.menus[0], ("Kinclude_path", 4), 
("Kinclude_path_sourced_1", 4))
    +    verify_node_path(c.comments[0], ("Kinclude_path", 4), 
("Kinclude_path_sourced_1", 4))
    +    verify_node_path(c.choices[0].nodes[0], ("Kinclude_path", 4), 
("Kinclude_path_sourced_1", 4))
    +
    +    os.environ.pop("srctree", None)
    +
    +
    +    print("Testing Kconfig.choices/menus/comments")
    +
    +    c = Kconfig("Kconfiglib/tests/Kitemlists")
    +
    +    def verify_prompts(items, *expected_prompts):
    +        verify(len(items) == len(expected_prompts),
    +               "Wrong number of prompts for {}".format(items))
    +
    +        for item, expected_prompt in zip(items, expected_prompts):
    +            if not isinstance(item, MenuNode):
    +                item = item.nodes[0]
    +
    +            verify(item.prompt[0] == expected_prompt,
    +                   "Wrong prompt for {}, expected '{}'"
    +                   .format(repr(item), expected_prompt))
    +
    +    verify_prompts(c.choices, "choice 1", "choice 2", "choice 3", "choice 
2")
    +    verify_prompts(c.menus, "menu 1", "menu 2", "menu 3", "menu 4", "menu 
5")
    +    verify_prompts(c.comments, "comment 1", "comment 2", "comment 3")
    +
    +
    +    print("Testing Symbol/Choice.direct_dep")
    +
    +    c = Kconfig("Kconfiglib/tests/Kdirdep")
    +
    +    verify_equal(expr_str(c.syms["NO_DEP_SYM"].direct_dep), 'y')
    +    verify_equal(expr_str(c.syms["DEP_SYM"].direct_dep), "A || (B && C) || 
!D")
    +
    +    verify_equal(expr_str(c.named_choices["NO_DEP_CHOICE"].direct_dep), 
'y')
    +    verify_equal(expr_str(c.named_choices["DEP_CHOICE"].direct_dep),
    +                 "A || B || C")
    +
    +
    +    print("Testing expr_items()")
    +
    +    c = Kconfig("Kconfiglib/tests/Kexpr_items")
    +
    +    def verify_expr_items(expr, *sym_names):
    +        verify_equal(tuple(sorted(item.name for item in expr_items(expr))),
    +                     sym_names)
    +
    +    verify_expr_items(
    +        c.syms["TEST"].defaults[0][0],
    +        "A", "B", "C", "D", "E", "F", "G", "H"
    +    )
    +
    +    verify_expr_items(
    +        c.syms["TEST_CHOICE"].nodes[0].prompt[1],
    +        "A", "CHOICE"
    +    )
    +
    +
    +    print("Testing MenuNode/Symbol/Choice.referenced")
    +
    +    c = Kconfig("Kconfiglib/tests/Kreferenced", warn=False)
    +
    +    def verify_deps(item, *dep_names):
    +        verify_equal(tuple(sorted(item.name for item in item.referenced)),
    +                     dep_names)
    +
    +    verify_deps(c.top_node, "y")
    +
    +    verify_deps(c.syms["NO_REFS"].nodes[0], "y")
    +
    +    verify_deps(c.syms["JUST_DEPENDS_ON_REFS"].nodes[0], "A", "B")
    +
    +    verify_deps(c.syms["LOTS_OF_REFS"].nodes[0],
    +                *(chr(n) for n in range(ord("A"), ord("Z") + 1)))
    +
    +    verify_deps(c.syms["INT_REFS"].nodes[0],
    +                "A", "B", "C", "D", "E", "F", "G", "H", "y")
    +
    +    verify_deps(c.syms["CHOICE_REF"].nodes[0], "CHOICE")
    +
    +    verify_deps(c.menus[0], "A", "B", "C", "D")
    +
    +    verify_deps(c.comments[0], "A", "B")
    +
    +    verify_deps(c.syms["MULTI_DEF_SYM"], "A", "B", "C", "y")
    +    verify_deps(c.named_choices["MULTI_DEF_CHOICE"], "A", "B", "C")
    +
    +
    +    print("Testing split_expr()")
    +
    +    c = Kconfig("Kconfiglib/tests/empty")
    +    c.warn = False
    +
    +    def verify_split(to_split, op, operand_strs):
    +        # The same hackage as in Kconfig.eval_string()
    +        c._tokens = c._tokenize("if " + to_split)[1:]
    +        c._tokens_i = 0
    +
    +        operands = split_expr(c._parse_expr(False), op)
    +
    +        verify(len(operands) == len(operand_strs),
    +               "Wrong number of operands when {} was split by {}"
    +               .format(to_split, "OR" if op == OR else "AND"))
    +
    +        for operand, operand_str in zip(operands, operand_strs):
    +            verify_equal(expr_str(operand), operand_str)
    +
    +    verify_split("A",                    OR, ("A",                ))
    +    verify_split("!A",                   OR, ("!A",               ))
    +    verify_split("A = B",                OR, ("A = B",            ))
    +    verify_split("A && B",               OR, ("A && B",           ))
    +    verify_split("A || B",               OR, ("A", "B"            ))
    +    verify_split("(A || B) || C",        OR, ("A", "B", "C"       ))
    +    verify_split("A || (B || C)",        OR, ("A", "B", "C"       ))
    +    verify_split("A || !(B || C)",       OR, ("A", "!(B || C)"    ))
    +    verify_split("A || (B && (C || D))", OR, ("A", "B && (C || D)"))
    +    verify_split("(A && (B || C)) || D", OR, ("A && (B || C)", "D"))
    +
    +    verify_split("A",                    AND, ("A",                ))
    +    verify_split("!A",                   AND, ("!A",               ))
    +    verify_split("A = B",                AND, ("A = B",            ))
    +    verify_split("A || B",               AND, ("A || B",           ))
    +    verify_split("A && B",               AND, ("A", "B"            ))
    +    verify_split("(A && B) && C",        AND, ("A", "B", "C"       ))
    +    verify_split("A && (B && C)",        AND, ("A", "B", "C"       ))
    +    verify_split("A && !(B && C)",       AND, ("A", "!(B && C)"    ))
    +    verify_split("A && (B || (C && D))", AND, ("A", "B || (C && D)"))
    +    verify_split("(A || (B && C)) && D", AND, ("A || (B && C)", "D"))
    +
    +
    +    print("Testing visibility")
    +
    +    c = Kconfig("Kconfiglib/tests/Kvisibility")
    +
    +    def verify_visibility(item, no_module_vis, module_vis):
    +        c.modules.set_value(0)
    +        verify(item.visibility == no_module_vis,
    +               "expected {} to have visibility {} without modules, had "
    +               "visibility {}".
    +               format(repr(item), no_module_vis, item.visibility))
    +
    +        c.modules.set_value(2)
    +        verify(item.visibility == module_vis,
    +               "expected {} to have visibility {} with modules, had "
    +               "visibility {}".
    +               format(repr(item), module_vis, item.visibility))
    +
    +    # Symbol visibility
    +
    +    verify_visibility(c.syms["NO_PROMPT"],     0, 0)
    +    verify_visibility(c.syms["BOOL_N"],        0, 0)
    +    verify_visibility(c.syms["BOOL_M"],        0, 2)
    +    verify_visibility(c.syms["BOOL_MOD"],      2, 2)
    +    verify_visibility(c.syms["BOOL_Y"],        2, 2)
    +    verify_visibility(c.syms["TRISTATE_M"],    0, 1)
    +    verify_visibility(c.syms["TRISTATE_MOD"],  2, 1)
    +    verify_visibility(c.syms["TRISTATE_Y"],    2, 2)
    +    verify_visibility(c.syms["BOOL_IF_N"],     0, 0)
    +    verify_visibility(c.syms["BOOL_IF_M"],     0, 2)
    +    verify_visibility(c.syms["BOOL_IF_Y"],     2, 2)
    +    verify_visibility(c.syms["BOOL_MENU_N"],   0, 0)
    +    verify_visibility(c.syms["BOOL_MENU_M"],   0, 2)
    +    verify_visibility(c.syms["BOOL_MENU_Y"],   2, 2)
    +    verify_visibility(c.syms["BOOL_CHOICE_N"], 0, 0)
    +
    +    # Non-tristate symbols in tristate choices are only visible if the 
choice
    +    # is in y mode
    +
    +    # The choice can't be brought to y mode because of the 'if m'
    +    verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
    +    c.syms["BOOL_CHOICE_M"].choice.set_value(2)
    +    verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
    +
    +    # The choice gets y mode only when running without modules, because it
    +    # defaults to m mode
    +    verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 0)
    +    c.syms["BOOL_CHOICE_Y"].choice.set_value(2)
    +    # When set to y mode, the choice symbol becomes visible both with and
    +    # without modules
    +    verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 2)
    +
    +    verify_visibility(c.syms["TRISTATE_IF_N"],     0, 0)
    +    verify_visibility(c.syms["TRISTATE_IF_M"],     0, 1)
    +    verify_visibility(c.syms["TRISTATE_IF_Y"],     2, 2)
    +    verify_visibility(c.syms["TRISTATE_MENU_N"],   0, 0)
    +    verify_visibility(c.syms["TRISTATE_MENU_M"],   0, 1)
    +    verify_visibility(c.syms["TRISTATE_MENU_Y"],   2, 2)
    +    verify_visibility(c.syms["TRISTATE_CHOICE_N"], 0, 0)
    +    verify_visibility(c.syms["TRISTATE_CHOICE_M"], 0, 1)
    +    verify_visibility(c.syms["TRISTATE_CHOICE_Y"], 2, 2)
    +
    +    verify_visibility(c.named_choices["BOOL_CHOICE_N"],     0, 0)
    +    verify_visibility(c.named_choices["BOOL_CHOICE_M"],     0, 2)
    +    verify_visibility(c.named_choices["BOOL_CHOICE_Y"],     2, 2)
    +    verify_visibility(c.named_choices["TRISTATE_CHOICE_N"], 0, 0)
    +    verify_visibility(c.named_choices["TRISTATE_CHOICE_M"], 0, 1)
    +    verify_visibility(c.named_choices["TRISTATE_CHOICE_Y"], 2, 2)
    +
    +    verify_visibility(c.named_choices["TRISTATE_CHOICE_IF_M_AND_Y"],   0, 
1)
    +    verify_visibility(c.named_choices["TRISTATE_CHOICE_MENU_N_AND_Y"], 0, 
0)
    +
    +    # Verify that 'visible if' visibility gets propagated to prompts
    +
    +    verify_visibility(c.syms["VISIBLE_IF_N"], 0, 0)
    +    verify_visibility(c.syms["VISIBLE_IF_M"], 0, 1)
    +    verify_visibility(c.syms["VISIBLE_IF_Y"], 2, 2)
    +    verify_visibility(c.syms["VISIBLE_IF_M_2"], 0, 1)
    +
    +    # Verify that string/int/hex symbols with m visibility accept a user 
value
    +
    +    assign_and_verify("STRING_m", "foo bar")
    +    assign_and_verify("INT_m", "123")
    +    assign_and_verify("HEX_m", "0x123")
    +
    +
    +    print("Testing .assignable")
    +
    +    c = Kconfig("Kconfiglib/tests/Kassignable")
    +
    +    def verify_assignable_imp(item, assignable_no_modules, 
assignable_modules):
    +        # Verifies the assignable values for 'item', with and without 
modules.
    +
    +        for modules_val, assignable in (0, assignable_no_modules), \
    +                                       (2, assignable_modules):
    +
    +            c.modules.set_value(modules_val)
    +            module_msg = "without modules" if modules_val == 0 else \
    +                         "with modules"
    +
    +            verify(item.assignable == assignable,
    +                   "Incorrect assignable values for {} {}. Should be {}, "
    +                   "was {}."
    +                   .format(item.name, module_msg, assignable, 
item.assignable))
    +
    +            # Verify that the values can actually be assigned too
    +
    +            for val in item.assignable:
    +                item.set_value(val)
    +                verify(item.tri_value == val,
    +                       "Unable to set {} to {} {}, even though it was in "
    +                       ".assignable".format(item.name, val, module_msg))
    +
    +    def verify_assignable(sym_name, assignable_no_modules, 
assignable_modules):
    +        verify_assignable_imp(c.syms[sym_name],
    +                              assignable_no_modules,
    +                              assignable_modules)
    +
    +    def verify_const_unassignable(sym_name):
    +        verify_assignable_imp(c.const_syms[sym_name], (), ())
    +
    +    # Things that shouldn't be .assignable
    +    verify_const_unassignable("n")
    +    verify_const_unassignable("m")
    +    verify_const_unassignable("y")
    +    verify_const_unassignable("const")
    +    verify_assignable("UNDEFINED", (), ())
    +    verify_assignable("NO_PROMPT", (), ())
    +    verify_assignable("STRING", (), ())
    +    verify_assignable("INT", (), ())
    +    verify_assignable("HEX", (), ())
    +
    +    # Non-selected symbols
    +    verify_assignable("Y_VIS_BOOL", (0, 2), (0,    2))
    +    verify_assignable("M_VIS_BOOL", (    ), (0,    2))  # Vis. promoted
    +    verify_assignable("N_VIS_BOOL", (    ), (       ))
    +    verify_assignable("Y_VIS_TRI",  (0, 2), (0, 1, 2))
    +    verify_assignable("M_VIS_TRI",  (    ), (0, 1   ))
    +    verify_assignable("N_VIS_TRI",  (    ), (       ))
    +
    +    # Symbols selected to y
    +    verify_assignable("Y_SEL_Y_VIS_BOOL", (2,), (2,))
    +    verify_assignable("Y_SEL_M_VIS_BOOL", (  ), (2,))  # Vis. promoted
    +    verify_assignable("Y_SEL_N_VIS_BOOL", (  ), (  ))
    +    verify_assignable("Y_SEL_Y_VIS_TRI",  (2,), (2,))
    +    verify_assignable("Y_SEL_M_VIS_TRI",  (  ), (2,))
    +    verify_assignable("Y_SEL_N_VIS_TRI",  (  ), (  ))
    +
    +    # Symbols selected to m
    +    verify_assignable("M_SEL_Y_VIS_BOOL", (2,), (   2,))  # Value promoted
    +    verify_assignable("M_SEL_M_VIS_BOOL", (  ), (   2,))  # Vis./value 
promoted
    +    verify_assignable("M_SEL_N_VIS_BOOL", (  ), (     ))
    +    verify_assignable("M_SEL_Y_VIS_TRI",  (2,), (1, 2 ))
    +    verify_assignable("M_SEL_M_VIS_TRI",  (  ), (1,   ))
    +    verify_assignable("M_SEL_N_VIS_TRI",  (  ), (     ))
    +
    +    # Symbols implied to y
    +    verify_assignable("Y_IMP_Y_VIS_BOOL", (0, 2), (0, 2))
    +    verify_assignable("Y_IMP_M_VIS_BOOL", (    ), (0, 2))  # Vis. promoted
    +    verify_assignable("Y_IMP_N_VIS_BOOL", (    ), (    ))
    +    verify_assignable("Y_IMP_Y_VIS_TRI",  (0, 2), (0, 2))  # m removed by 
imply
    +    verify_assignable("Y_IMP_M_VIS_TRI",  (    ), (0, 2))  # m promoted to 
y by imply
    +    verify_assignable("Y_IMP_N_VIS_TRI",  (    ), (    ))
    +
    +    # Symbols implied to m (never affects assignable values)
    +    verify_assignable("M_IMP_Y_VIS_BOOL", (0, 2), (0,    2))
    +    verify_assignable("M_IMP_M_VIS_BOOL", (    ), (0,    2))  # Vis. 
promoted
    +    verify_assignable("M_IMP_N_VIS_BOOL", (    ), (       ))
    +    verify_assignable("M_IMP_Y_VIS_TRI",  (0, 2), (0, 1, 2))
    +    verify_assignable("M_IMP_M_VIS_TRI",  (    ), (0, 1   ))
    +    verify_assignable("M_IMP_N_VIS_TRI",  (    ), (       ))
    +
    +    # Symbols in y-mode choice
    +    verify_assignable("Y_CHOICE_BOOL",           (2,), (2,))
    +    verify_assignable("Y_CHOICE_TRISTATE",       (2,), (2,))
    +    verify_assignable("Y_CHOICE_N_VIS_TRISTATE", (  ), (  ))
    +
    +    # Symbols in m/y-mode choice, starting out in m mode, or y mode when
    +    # running without modules
    +    verify_assignable("MY_CHOICE_BOOL",           (2,), (    ))
    +    verify_assignable("MY_CHOICE_TRISTATE",       (2,), (0, 1))
    +    verify_assignable("MY_CHOICE_N_VIS_TRISTATE", (  ), (    ))
    +
    +    c.named_choices["MY_CHOICE"].set_value(2)
    +
    +    # Symbols in m/y-mode choice, now in y mode
    +    verify_assignable("MY_CHOICE_BOOL",           (2,), (2,))
    +    verify_assignable("MY_CHOICE_TRISTATE",       (2,), (2,))
    +    verify_assignable("MY_CHOICE_N_VIS_TRISTATE", (  ), (  ))
    +
    +    def verify_choice_assignable(choice_name, assignable_no_modules,
    +                                 assignable_modules):
    +        verify_assignable_imp(c.named_choices[choice_name],
    +                              assignable_no_modules,
    +                              assignable_modules)
    +
    +    # Choices with various possible modes
    +    verify_choice_assignable("Y_CHOICE",   (2,  ), (      2,))
    +    verify_choice_assignable("MY_CHOICE",  (2,  ), (   1, 2 ))
    +    verify_choice_assignable("NMY_CHOICE", (0, 2), (0, 1, 2 ))
    +    verify_choice_assignable("NY_CHOICE",  (0, 2), (0,    2 ))
    +    verify_choice_assignable("NM_CHOICE",  (    ), (0, 1    ))
    +    verify_choice_assignable("M_CHOICE",   (    ), (   1,   ))
    +    verify_choice_assignable("N_CHOICE",   (    ), (        ))
    +
    +
    +    print("Testing object relations")
    +
    +    c = Kconfig("Kconfiglib/tests/Krelation")
    +
    +    verify(c.syms["A"].nodes[0].parent is c.top_node,
    +           "A's parent should be the top node")
    +
    +    verify(c.syms["B"].nodes[0].parent.item is c.named_choices["CHOICE_1"],
    +           "B's parent should be the first choice")
    +
    +    verify(c.syms["C"].nodes[0].parent.item is c.syms["B"],
    +           "C's parent should be B (due to auto menus)")
    +
    +    verify(c.syms["E"].nodes[0].parent.item == MENU,
    +           "E's parent should be a menu")
    +
    +    verify(c.syms["E"].nodes[0].parent.parent is c.top_node,
    +           "E's grandparent should be the top node")
    +
    +    verify(c.syms["G"].nodes[0].parent.item is c.named_choices["CHOICE_2"],
    +           "G's parent should be the second choice")
    +
    +    verify(c.syms["G"].nodes[0].parent.parent.item == MENU,
    +           "G's grandparent should be a menu")
    +
    +
    +    print("Testing hex/int ranges")
    +
    +    c = Kconfig("Kconfiglib/tests/Krange", warn=False)
    +
    +    for sym_name in "HEX_NO_RANGE", "INT_NO_RANGE", "HEX_40", "INT_40":
    +        sym = c.syms[sym_name]
    +        verify(not sym.ranges,
    +               "{} should not have ranges".format(sym_name))
    +
    +    for sym_name in "HEX_ALL_RANGES_DISABLED", "INT_ALL_RANGES_DISABLED", \
    +                    "HEX_RANGE_10_20_LOW_DEFAULT", \
    +                    "INT_RANGE_10_20_LOW_DEFAULT":
    +        sym = c.syms[sym_name]
    +        verify(sym.ranges, "{} should have ranges".format(sym_name))
    +
    +    # hex/int symbols without defaults should get no default value
    +    verify_value("HEX_NO_RANGE", "")
    +    verify_value("INT_NO_RANGE", "")
    +    # And neither if all ranges are disabled
    +    verify_value("HEX_ALL_RANGES_DISABLED", "")
    +    verify_value("INT_ALL_RANGES_DISABLED", "")
    +    # Make sure they are assignable though, and test that the form of the 
user
    +    # value is reflected in the value for hex symbols
    +    assign_and_verify("HEX_NO_RANGE", "0x123")
    +    assign_and_verify("HEX_NO_RANGE", "123")
    +    assign_and_verify("INT_NO_RANGE", "123")
    +
    +    # Defaults outside of the valid range should be clamped
    +    verify_value("HEX_RANGE_10_20_LOW_DEFAULT", "0x10")
    +    verify_value("HEX_RANGE_10_20_HIGH_DEFAULT", "0x20")
    +    verify_value("INT_RANGE_10_20_LOW_DEFAULT", "10")
    +    verify_value("INT_RANGE_10_20_HIGH_DEFAULT", "20")
    +    # Defaults inside the valid range should be preserved. For hex symbols,
    +    # they should additionally use the same form as in the assignment.
    +    verify_value("HEX_RANGE_10_20_OK_DEFAULT", "0x15")
    +    verify_value("HEX_RANGE_10_20_OK_DEFAULT_ALTERNATE", "15")
    +    verify_value("INT_RANGE_10_20_OK_DEFAULT", "15")
    +
    +    # hex/int symbols with no defaults but valid ranges should default to 
the
    +    # lower end of the range if it's > 0
    +    verify_value("HEX_RANGE_10_20", "0x10")
    +    verify_value("HEX_RANGE_0_10", "")
    +    verify_value("INT_RANGE_10_20", "10")
    +    verify_value("INT_RANGE_0_10", "")
    +    verify_value("INT_RANGE_NEG_10_10", "")
    +
    +    # User values and dependent ranges
    +
    +    # Avoid warnings for assigning values outside the active range
    +    c.warn = False
    +
    +    def verify_range(sym_name, low, high, default):
    +        # Verifies that all values in the range 'low'-'high' can be 
assigned,
    +        # and that assigning values outside the range reverts the value 
back to
    +        # 'default' (None if it should revert back to "").
    +
    +        is_hex = (c.syms[sym_name].type == HEX)
    +
    +        for i in range(low, high + 1):
    +            assign_and_verify_user_value(sym_name, str(i), str(i), True)
    +            if is_hex:
    +                # The form of the user value should be preserved for hex
    +                # symbols
    +                assign_and_verify_user_value(sym_name, hex(i), hex(i), 
True)
    +
    +        # Verify that assigning a user value just outside the range causes
    +        # defaults to be used
    +
    +        if default is None:
    +            default_str = ""
    +        else:
    +            default_str = hex(default) if is_hex else str(default)
    +
    +        if is_hex:
    +            too_low_str = hex(low - 1)
    +            too_high_str = hex(high + 1)
    +        else:
    +            too_low_str = str(low - 1)
    +            too_high_str = str(high + 1)
    +
    +        assign_and_verify_value(sym_name, too_low_str, default_str)
    +        assign_and_verify_value(sym_name, too_high_str, default_str)
    +
    +    verify_range("HEX_RANGE_10_20_LOW_DEFAULT",  0x10, 0x20,  0x10)
    +    verify_range("HEX_RANGE_10_20_HIGH_DEFAULT", 0x10, 0x20,  0x20)
    +    verify_range("HEX_RANGE_10_20_OK_DEFAULT",   0x10, 0x20,  0x15)
    +
    +    verify_range("INT_RANGE_10_20_LOW_DEFAULT",  10,   20,    10)
    +    verify_range("INT_RANGE_10_20_HIGH_DEFAULT", 10,   20,    20)
    +    verify_range("INT_RANGE_10_20_OK_DEFAULT",   10,   20,    15)
    +
    +    verify_range("HEX_RANGE_10_20",              0x10, 0x20,  0x10)
    +
    +    verify_range("INT_RANGE_10_20",              10,  20,     10)
    +    verify_range("INT_RANGE_0_10",               0,   10,     None)
    +    verify_range("INT_RANGE_NEG_10_10",          -10, 10,     None)
    +
    +    # Dependent ranges
    +
    +    verify_value("HEX_40", "40")
    +    verify_value("INT_40", "40")
    +
    +    c.syms["HEX_RANGE_10_20"].unset_value()
    +    c.syms["INT_RANGE_10_20"].unset_value()
    +    verify_value("HEX_RANGE_10_40_DEPENDENT", "0x10")
    +    verify_value("INT_RANGE_10_40_DEPENDENT", "10")
    +    c.syms["HEX_RANGE_10_20"].set_value("15")
    +    c.syms["INT_RANGE_10_20"].set_value("15")
    +    verify_value("HEX_RANGE_10_40_DEPENDENT", "0x15")
    +    verify_value("INT_RANGE_10_40_DEPENDENT", "15")
    +    c.unset_values()
    +    verify_range("HEX_RANGE_10_40_DEPENDENT", 0x10, 0x40,  0x10)
    +    verify_range("INT_RANGE_10_40_DEPENDENT", 10,   40,    10)
    +
    +    # Ranges and symbols defined in multiple locations
    +
    +    verify_value("INACTIVE_RANGE", "2")
    +    verify_value("ACTIVE_RANGE", "1")
    +
    +
    +    print("Testing defconfig_filename")
    +
    +    c = Kconfig("Kconfiglib/tests/empty")
    +    verify(c.defconfig_filename is None,
    +           "defconfig_filename should be None with no defconfig_list 
symbol")
    +
    +    c = Kconfig("Kconfiglib/tests/Kdefconfig_nonexistent")
    +    verify(c.defconfig_filename is None,
    +           "defconfig_filename should be None when none of the files in 
the "
    +           "defconfig_list symbol exist")
    +
    +    # Referenced in Kdefconfig_existent(_but_n)
    +    os.environ["FOO"] = "defconfig_2"
    +
    +    c = Kconfig("Kconfiglib/tests/Kdefconfig_existent_but_n")
    +    verify(c.defconfig_filename is None,
    +           "defconfig_filename should be None when the condition is n for 
all "
    +           "the defaults")
    +
    +    c = Kconfig("Kconfiglib/tests/Kdefconfig_existent")
    +    verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
    +           "defconfig_filename should return the existing file "
    +           "Kconfiglib/tests/defconfig_2")
    +
    +    # Should also look relative to $srctree if the specified defconfig is a
    +    # relative path and can't be opened
    +
    +    c = Kconfig("Kconfiglib/tests/Kdefconfig_srctree")
    +    verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
    +           "defconfig_filename gave wrong file with $srctree unset")
    +
    +    os.environ["srctree"] = "Kconfiglib/tests"
    +    c = Kconfig("Kdefconfig_srctree")
    +    verify(c.defconfig_filename == "Kconfiglib/tests/sub/defconfig_in_sub",
    +           "defconfig_filename gave wrong file with $srctree set")
    +
    +    os.environ.pop("srctree", None)
    +
    +
    +    print("Testing mainmenu_text")
    +
    +    c = Kconfig("Kconfiglib/tests/empty")
    +    verify(c.mainmenu_text == "Main menu",
    +           "An empty Kconfig should get a default main menu prompt")
    +
    +    # Expanded in the mainmenu text
    +    os.environ["FOO"] = "bar baz"
    +    c = Kconfig("Kconfiglib/tests/Kmainmenu")
    +    verify(c.mainmenu_text == "---bar baz---",
    +           "Wrong mainmenu text")
    +
    +
    +    print("Testing user_value")
    +
    +    # References undefined env. var. Disable warnings.
    +    c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
    +
    +    # Avoid warnings from assigning invalid user values and assigning user
    +    # values to symbols without prompts
    +    c.warn = False
    +
    +    syms = [c.syms[name] for name in
    +            ("BOOL", "TRISTATE", "STRING", "INT", "HEX")]
    +
    +    for sym in syms:
    +        verify(sym.user_value is None,
    +               "{} should not have a user value to begin with")
    +
    +    # Assign valid values for the types
    +
    +    assign_and_verify_user_value("BOOL", 0, 0, True)
    +    assign_and_verify_user_value("BOOL", 2, 2, True)
    +    assign_and_verify_user_value("TRISTATE", 0, 0, True)
    +    assign_and_verify_user_value("TRISTATE", 1, 1, True)
    +    assign_and_verify_user_value("TRISTATE", 2, 2, True)
    +    assign_and_verify_user_value("STRING", "foo bar", "foo bar", True)
    +    assign_and_verify_user_value("INT", "123", "123", True)
    +    assign_and_verify_user_value("HEX", "0x123", "0x123", True)
    +
    +    # Assign invalid values for the types. They should retain their old 
user
    +    # value.
    +
    +    assign_and_verify_user_value("BOOL", 1, 2, False)
    +    assign_and_verify_user_value("BOOL", "foo", 2, False)
    +    assign_and_verify_user_value("BOOL", "1", 2, False)
    +    assign_and_verify_user_value("TRISTATE", "foo", 2, False)
    +    assign_and_verify_user_value("TRISTATE", "1", 2, False)
    +    assign_and_verify_user_value("STRING", 0, "foo bar", False)
    +    assign_and_verify_user_value("INT", "foo", "123", False)
    +    assign_and_verify_user_value("INT", 0, "123", False)
    +    assign_and_verify_user_value("HEX", "foo", "0x123", False)
    +    assign_and_verify_user_value("HEX", 0, "0x123", False)
    +    assign_and_verify_user_value("HEX", "-0x1", "0x123", False)
    +
    +    for s in syms:
    +        s.unset_value()
    +        verify(s.user_value is None,
    +               "{} should not have a user value after being reset".
    +               format(s.name))
    +
    +
    +    print("Testing is_menuconfig")
    +
    +    c = Kconfig("Kconfiglib/tests/Kmenuconfig")
    +
    +    for not_menuconfig in c.syms["NOT_MENUCONFIG_1"].nodes[0], \
    +                          c.syms["NOT_MENUCONFIG_2"].nodes[0], \
    +                          c.syms["MENUCONFIG_MULTI_DEF"].nodes[0], \
    +                          c.syms["COMMENT_HOOK"].nodes[0].next:
    +
    +        verify(not not_menuconfig.is_menuconfig,
    +               "'{}' should have is_menuconfig 
False".format(not_menuconfig))
    +
    +    for menuconfig in c.top_node, \
    +                      c.syms["MENUCONFIG_1"].nodes[0], \
    +                      c.syms["MENUCONFIG_MULTI_DEF"].nodes[1], \
    +                      c.syms["MENU_HOOK"].nodes[0].next, \
    +                      c.syms["CHOICE_HOOK"].nodes[0].next:
    +
    +        verify(menuconfig.is_menuconfig,
    +               "'{}' should have is_menuconfig True".format(menuconfig))
    +
    +
    +    print("Testing 'option env' semantics")
    +
    +    os.environ["ENV_VAR"] = "ENV_VAR value"
    +
    +    # References undefined env. var., so disable warnings
    +    c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
    +
    +    # Verify that 'option env' is treated like a default
    +    verify_value("FROM_ENV", "ENV_VAR value")
    +    verify_value("FROM_ENV_MISSING", "missing")
    +
    +    verify_value("FROM_ENV_WEIRD", "weird")
    +
    +
    +    print("Testing defined vs undefined symbols")
    +
    +    for name in "A", "B", "C", "D", "BOOL", "TRISTATE", "STRING", "INT", 
"HEX":
    +        verify(c.syms[name].nodes,
    +               "{} should be defined".format(name))
    +
    +    for name in "NOT_DEFINED_1", "NOT_DEFINED_2", "NOT_DEFINED_3", \
    +                "NOT_DEFINED_4":
    +        sym = c.syms[name]
    +        verify(not c.syms[name].nodes,
    +               "{} should not be defined".format(name))
    +
    +
    +    print("Testing Symbol.choice")
    +
    +    for name in "A", "B", "C", "D":
    +        verify(c.syms[name].choice is not None,
    +               "{} should be a choice symbol".format(name))
    +
    +    for name in "Q1", "Q2", "Q3", "BOOL", "TRISTATE", "STRING", "INT", 
"HEX", \
    +                "FROM_ENV", "FROM_ENV_MISSING", "NOT_DEFINED_1", \
    +                "NOT_DEFINED_2", "NOT_DEFINED_3", "NOT_DEFINED_4":
    +        verify(c.syms[name].choice is None,
    +               "{} should not be a choice symbol".format(name))
    +
    +
    +    print("Testing is_allnoconfig_y")
    +
    +    verify(not c.syms["NOT_ALLNOCONFIG_Y"].is_allnoconfig_y,
    +           "NOT_ALLNOCONFIG_Y should not be allnoconfig_y")
    +    verify(c.syms["ALLNOCONFIG_Y"].is_allnoconfig_y,
    +           "ALLNOCONFIG_Y should be allnoconfig_y")
    +
    +
    +    print("Testing .config reading and writing")
    +
    +    config_test_file = "Kconfiglib/tests/config_test"
    +
    +    def verify_file_contents(fname, contents):
    +        with open(fname, "r") as f:
    +            file_contents = f.read()
    +            verify(file_contents == contents,
    +                   "{} contains '{}'. Expected '{}'."
    +                   .format(fname, file_contents, contents))
    +
    +    # Writing/reading strings with characters that need to be escaped
    +
    +    c = Kconfig("Kconfiglib/tests/Kescape")
    +
    +    # Test the default value
    +    c.write_config(config_test_file + "_from_def", header="")
    +    verify_file_contents(config_test_file + "_from_def",
    +                         r'''CONFIG_STRING="\"\\"''' "\n")
    +    # Write our own value
    +    c.syms["STRING"].set_value(r'''\"a'\\''')
    +    c.write_config(config_test_file + "_from_user", header="")
    +    verify_file_contents(config_test_file + "_from_user",
    +                         r'''CONFIG_STRING="\\\"a'\\\\"''' "\n")
    +
    +    # Read back the two configs and verify the respective values
    +    c.load_config(config_test_file + "_from_def")
    +    verify_value("STRING", '"\\')
    +    c.load_config(config_test_file + "_from_user")
    +    verify_value("STRING", r'''\"a'\\''')
    +
    +    # Appending values from a .config
    +
    +    c = Kconfig("Kconfiglib/tests/Kappend")
    +
    +    # Values before assigning
    +    verify_value("BOOL", "n")
    +    verify_value("STRING", "")
    +
    +    # Assign BOOL
    +    c.load_config("Kconfiglib/tests/config_set_bool", replace=False)
    +    verify_value("BOOL", "y")
    +    verify_value("STRING", "")
    +
    +    # Assign STRING
    +    c.load_config("Kconfiglib/tests/config_set_string", replace=False)
    +    verify_value("BOOL", "y")
    +    verify_value("STRING", "foo bar")
    +
    +    # Reset BOOL
    +    c.load_config("Kconfiglib/tests/config_set_string")
    +    verify_value("BOOL", "n")
    +    verify_value("STRING", "foo bar")
    +
    +    # Loading a completely empty .config should reset values
    +    c.load_config("Kconfiglib/tests/empty")
    +    verify_value("STRING", "")
    +
    +    # An indented assignment in a .config should be ignored
    +    c.load_config("Kconfiglib/tests/config_indented")
    +    verify_value("IGNOREME", "y")
    +
    +    # Symbol order in headers and minimal configuration files should match
    +    # definition order, like in .config files
    +
    +    c = Kconfig("Kconfiglib/tests/Korder")
    +
    +    c.write_autoconf(config_test_file, header="")
    +    verify_file_contents(config_test_file, """
    +#define CONFIG_O 0
    +#define CONFIG_R 1
    +#define CONFIG_D 2
    +#define CONFIG_E 3
    +#define CONFIG_R2 4
    +#define CONFIG_I 5
    +#define CONFIG_N 6
    +#define CONFIG_G 7
    +"""[1:])
    +
    +    # Differs from defaults
    +    c.syms["O"].set_value("-1")
    +    c.syms["R"].set_value("-1")
    +    c.syms["E"].set_value("-1")
    +    c.syms["R2"].set_value("-1")
    +    c.syms["N"].set_value("-1")
    +    c.syms["G"].set_value("-1")
    +    c.write_min_config(config_test_file, header="")
    +    verify_file_contents(config_test_file, """
    +CONFIG_O=-1
    +CONFIG_R=-1
    +CONFIG_E=-1
    +CONFIG_R2=-1
    +CONFIG_N=-1
    +CONFIG_G=-1
    +"""[1:])
    +
    +
    +    print("Testing Kconfig fetching and separation")
    +
    +    for c in Kconfig("Kconfiglib/tests/Kmisc", warn=False), \
    +             Kconfig("Kconfiglib/tests/Kmisc", warn=False):
    +        for item in c.syms["BOOL"], \
    +                    c.syms["BOOL"].nodes[0], \
    +                    c.named_choices["OPTIONAL"], \
    +                    c.named_choices["OPTIONAL"].nodes[0], \
    +                    c.syms["MENU_HOOK"].nodes[0].next, \
    +                    c.syms["COMMENT_HOOK"].nodes[0].next:
    +            verify(item.kconfig is c,
    +                   ".kconfig not properly set for " + repr(item))
    +
    +
    +    print("Testing imply semantics")
    +
    +    c = Kconfig("Kconfiglib/tests/Kimply")
    +
    +    verify_value("IMPLY_DIRECT_DEPS", "y")
    +    verify_value("UNMET_DIRECT_1", "n")
    +    verify_value("UNMET_DIRECT_2", "n")
    +    verify_value("UNMET_DIRECT_3", "n")
    +    verify_value("MET_DIRECT_1", "y")
    +    verify_value("MET_DIRECT_2", "y")
    +    verify_value("MET_DIRECT_3", "y")
    +    verify_value("MET_DIRECT_4", "y")
    +
    +    verify_value("IMPLY_COND", "y")
    +    verify_value("IMPLIED_N_COND", "n")
    +    verify_value("IMPLIED_M_COND", "m")
    +    verify_value("IMPLIED_Y_COND", "y")
    +
    +    verify_value("IMPLY_N_1", "n")
    +    verify_value("IMPLY_N_2", "n")
    +    verify_value("IMPLIED_FROM_N_1", "n")
    +    verify_value("IMPLIED_FROM_N_2", "n")
    +
    +    verify_value("IMPLY_M", "m")
    +    verify_value("IMPLIED_M", "m")
    +    verify_value("IMPLIED_M_BOOL", "y")
    +
    +    verify_value("IMPLY_M_TO_Y", "y")
    +    verify_value("IMPLIED_M_TO_Y", "y")
    +
    +    # Test user value semantics
    +
    +    # Verify that IMPLIED_TRISTATE is invalidated if the direct
    +    # dependencies change
    +
    +    assign_and_verify("IMPLY", 2)
    +    assign_and_verify("DIRECT_DEP", 2)
    +    verify_value("IMPLIED_TRISTATE", 2)
    +    assign_and_verify("DIRECT_DEP", 0)
    +    verify_value("IMPLIED_TRISTATE", 0)
    +    # Set back for later tests
    +    assign_and_verify("DIRECT_DEP", 2)
    +
    +    # Verify that IMPLIED_TRISTATE can be set to anything when IMPLY has 
value
    +    # n, and that it gets the value n by default (for non-imply-related
    +    # reasons)
    +
    +    assign_and_verify("IMPLY", 0)
    +    assign_and_verify("IMPLIED_TRISTATE", 0)
    +    assign_and_verify("IMPLIED_TRISTATE", 1)
    +    assign_and_verify("IMPLIED_TRISTATE", 2)
    +    c.syms["IMPLIED_TRISTATE"].unset_value()
    +    verify_value("IMPLIED_TRISTATE", "n")
    +
    +    # Same as above for m. Anything still goes, but m by default now.
    +
    +    assign_and_verify("IMPLY", 1)
    +    assign_and_verify("IMPLIED_TRISTATE", 0)
    +    assign_and_verify("IMPLIED_TRISTATE", 1)
    +    assign_and_verify("IMPLIED_TRISTATE", 2)
    +    c.syms["IMPLIED_TRISTATE"].unset_value()
    +    verify_value("IMPLIED_TRISTATE", 1)
    +
    +    # Same as above for y. Only n and y should be accepted. m gets 
promoted to
    +    # y. Default should be y.
    +
    +    assign_and_verify("IMPLY", 2)
    +    assign_and_verify("IMPLIED_TRISTATE", 0)
    +    assign_and_verify_value("IMPLIED_TRISTATE", 1, 2)
    +    assign_and_verify("IMPLIED_TRISTATE", 2)
    +    c.syms["IMPLIED_TRISTATE"].unset_value()
    +    verify_value("IMPLIED_TRISTATE", 2)
    +
    +    # Being implied to either m or y should give a bool the value y
    +
    +    c.syms["IMPLY"].unset_value()
    +    verify_value("IMPLIED_BOOL", 0)
    +    assign_and_verify("IMPLY", 0)
    +    verify_value("IMPLIED_BOOL", 0)
    +    assign_and_verify("IMPLY", 1)
    +    verify_value("IMPLIED_BOOL", 2)
    +    assign_and_verify("IMPLY", 2)
    +    verify_value("IMPLIED_BOOL", 2)
    +
    +    # A bool implied to m or y can take the values n and y
    +
    +    c.syms["IMPLY"].set_value(1)
    +    assign_and_verify("IMPLIED_BOOL", 0)
    +    assign_and_verify("IMPLIED_BOOL", 2)
    +
    +    c.syms["IMPLY"].set_value(2)
    +    assign_and_verify("IMPLIED_BOOL", 0)
    +    assign_and_verify("IMPLIED_BOOL", 2)
    +
    +
    +    print("Testing choice semantics")
    +
    +    # Would warn for choice value symbols defined without a type, even
    +    # though the type is automatically derived. This is probably more
    +    # helpful than ignoring those cases, as this feature isn't used
    +    # deliberately anywhere from what I've seen.
    +    c = Kconfig("Kconfiglib/tests/Kchoice", warn=False)
    +
    +    for name in "BOOL", "BOOL_OPT", "BOOL_M", "DEFAULTS":
    +        verify(c.named_choices[name].orig_type == BOOL,
    +               "choice {} should have type bool".format(name))
    +
    +    for name in "TRISTATE", "TRISTATE_OPT", "TRISTATE_M":
    +        verify(c.named_choices[name].orig_type == TRISTATE,
    +               "choice {} should have type tristate".format(name))
    +
    +    def select_and_verify(sym):
    +        choice = sym.nodes[0].parent.item
    +        choice.set_value(2)
    +
    +        sym.set_value(2)
    +
    +        verify(sym.choice.selection is sym,
    +               sym.name + " should be the selected symbol")
    +
    +        verify(choice.user_selection is sym,
    +               sym.name + " should be the user selection of the choice")
    +
    +        verify(sym.tri_value == 2,
    +               sym.name + " should have value y when selected")
    +
    +        verify(sym.user_value == 2,
    +               sym.name + " should have user value y when selected")
    +
    +        for sibling in choice.syms:
    +            if sibling is not sym:
    +                verify(sibling.tri_value == 0,
    +                       sibling.name + " should be n when not selected")
    +
    +    def select_and_verify_all(choice_name):
    +        choice = c.named_choices[choice_name]
    +
    +        # Select in forward order
    +        for sym in choice.syms:
    +            select_and_verify(sym)
    +
    +        # Select in reverse order
    +        for sym in reversed(choice.syms):
    +            select_and_verify(sym)
    +
    +    def verify_mode(choice_name, no_modules_mode, modules_mode):
    +        choice = c.named_choices[choice_name]
    +
    +        c.modules.set_value(0)
    +        verify(choice.tri_value == no_modules_mode,
    +               'Wrong mode for choice {} with no modules. Expected {}, got 
{}.'
    +               .format(choice.name, no_modules_mode, choice.tri_value))
    +
    +        c.modules.set_value(2)
    +        verify(choice.tri_value == modules_mode,
    +               'Wrong mode for choice {} with modules. Expected {}, got 
{}.'
    +               .format(choice.name, modules_mode, choice.tri_value))
    +
    +    verify_mode("BOOL",         2, 2)
    +    verify_mode("BOOL_OPT",     0, 0)
    +    verify_mode("TRISTATE",     2, 1)
    +    verify_mode("TRISTATE_OPT", 0, 0)
    +    verify_mode("BOOL_M",       0, 2)
    +    verify_mode("TRISTATE_M",   0, 1)
    +
    +    # Test defaults
    +
    +    choice = c.named_choices["DEFAULTS"]
    +
    +    c.syms["TRISTATE_SYM"].set_value(0)
    +    verify(choice.selection is c.syms["OPT_4"],
    +           "Wrong choice default with TRISTATE_SYM = n")
    +
    +    c.syms["TRISTATE_SYM"].set_value(2)
    +    verify(choice.selection is c.syms["OPT_2"],
    +           "Wrong choice default with TRISTATE_SYM = y")
    +
    +    c.syms["OPT_1"].set_value(2)
    +    verify(choice.selection is c.syms["OPT_1"],
    +           "User selection should override defaults")
    +
    +    verify(c.named_choices["DEFAULTS_NOT_VISIBLE"].selection
    +           is c.syms["OPT_8"],
    +           "Non-visible choice symbols should cause the next default to be 
"
    +           "considered")
    +
    +    # Test y mode selection
    +
    +    c.modules.set_value(2)
    +
    +    select_and_verify_all("BOOL")
    +    select_and_verify_all("BOOL_OPT")
    +    select_and_verify_all("TRISTATE")
    +    select_and_verify_all("TRISTATE_OPT")
    +    # For BOOL_M, the mode should have been promoted
    +    select_and_verify_all("BOOL_M")
    +
    +    # Test m mode selection
    +
    +    c.named_choices["TRISTATE"].set_value(1)
    +
    +    verify(c.named_choices["TRISTATE"].tri_value == 1,
    +           "TRISTATE choice should have mode m after explicit mode 
assignment")
    +
    +    assign_and_verify_value("T_1", 0, 0)
    +    assign_and_verify_value("T_2", 0, 0)
    +    assign_and_verify_value("T_1", 1, 1)
    +    assign_and_verify_value("T_2", 1, 1)
    +    assign_and_verify_value("T_1", 2, 1)
    +    assign_and_verify_value("T_2", 2, 1)
    +
    +    # Switching to y mode should cause T_2 to become selected
    +    c.named_choices["TRISTATE"].set_value(2)
    +    verify_value("T_1", 0)
    +    verify_value("T_2", 2)
    +
    +    # Verify that choices with no explicitly specified type get the type 
of the
    +    # first contained symbol with a type
    +
    +    verify(c.named_choices["NO_TYPE_BOOL"].orig_type == BOOL,
    +           "Expected first choice without explicit type to have type bool")
    +
    +    verify(c.named_choices["NO_TYPE_TRISTATE"].orig_type == TRISTATE,
    +           "Expected second choice without explicit type to have type "
    +           "tristate")
    +
    +    # Verify that symbols without a type in the choice get the type of the
    +    # choice
    +
    +    for name in "MMT_1", "MMT_2", "MMT_4", "MMT_5":
    +        verify(c.syms[name].orig_type == BOOL,
    +               "Expected {} to get type bool".format(name))
    +
    +    verify(c.syms["MMT_3"].orig_type == TRISTATE,
    +           "Expected MMT_3 to have type tristate")
    +
    +    # Verify that the default selection can change depending on the
    +    # visibility of the choice symbols
    +
    +    default_with_dep_choice = c.named_choices["DEFAULT_WITH_DEP"]
    +
    +    verify(default_with_dep_choice.selection is c.syms["B"],
    +           "Wrong choice default with unsatisfied deps on default")
    +
    +    c.syms["DEP"].set_value("y")
    +
    +    verify(default_with_dep_choice.selection is c.syms["A"],
    +           "Wrong choice default with satisfied deps on default")
    +
    +    c.syms["DEP"].set_value("n")
    +
    +    verify(default_with_dep_choice.selection is c.syms["B"],
    +           "Wrong choice default with unsatisfied deps on default (round 
two)")
    +
    +    # Verify that symbols in choices that depend on the preceding symbol 
aren't
    +    # considered choice symbols
    +
    +    weird_choice = c.named_choices["WEIRD_SYMS"]
    +
    +    def verify_is_normal_choice_symbol(name):
    +        sym = c.syms[name]
    +        verify(sym.choice is not None and
    +               sym in weird_choice.syms and
    +               sym.nodes[0].parent.item is weird_choice,
    +               "{} should be a normal choice symbol".format(sym.name))
    +
    +    def verify_is_weird_choice_symbol(name):
    +        sym = c.syms[name]
    +        verify(sym.choice is None and
    +               sym not in weird_choice.syms,
    +               "{} should be a weird (non-)choice symbol"
    +               .format(sym.name))
    +
    +    verify_is_normal_choice_symbol("WS1")
    +    verify_is_weird_choice_symbol("WS2")
    +    verify_is_weird_choice_symbol("WS3")
    +    verify_is_weird_choice_symbol("WS4")
    +    verify_is_weird_choice_symbol("WS5")
    +    verify_is_normal_choice_symbol("WS6")
    +    verify_is_weird_choice_symbol("WS7")
    +    verify_is_weird_choice_symbol("WS8")
    +    verify_is_normal_choice_symbol("WS9")
    +
    +
    +    print("Testing 'if' node removal")
    +
    +    c = Kconfig("Kconfiglib/tests/Kifremoval", warn=False)
    +
    +    nodes = tuple(c.node_iter())
    +    verify_equal(nodes[0].item.name, "A")
    +    verify_equal(nodes[1].item.name, "B")
    +    verify_equal(nodes[2].item.name, "C")
    +    verify_equal(nodes[3].item.name, "D")
    +    verify_equal(nodes[4].prompt[0], "E")
    +    verify_equal(nodes[5].prompt[0], "F")
    +    verify_equal(nodes[6].prompt[0], "G")
    +    verify_equal(nodes[7].item.name, "H")
    +    verify_equal(nodes[8].item.name, "I")
    +    verify_equal(nodes[9].item.name, "J")
    +    verify(len(nodes) == 10,
    +           "Wrong number of nodes after 'if' removal")
    +
    +
    +    print("Testing multi.def. property copying")
    +
    +    c = Kconfig("Kconfiglib/tests/Kdepcopy", warn=False)
    +
    +    def verify_props(desc, props, prop_names):
    +        actual = [prop[0].name for prop in props]
    +        expected = prop_names.split()
    +
    +        verify(actual == expected,
    +               "Wrong {} properties, expected '{}', got '{}'"
    +               .format(desc, expected, actual))
    +
    +    verify_props("default", c.syms["MULTIDEF"].defaults,
    +                 "A B C D E F G H I J K L M N O P Q R")
    +
    +    verify_props("select", c.syms["MULTIDEF"].selects,
    +                 "AA BB CC DD EE FF GG HH II JJ")
    +
    +    verify_props("imply", c.syms["MULTIDEF"].selects,
    +                 "AA BB CC DD EE FF GG HH II JJ")
    +
    +    verify_props("select", c.syms["MULTIDEF_CHOICE"].selects,
    +                 "A B C")
    +
    +    verify_props("range", c.syms["MULTIDEF_RANGE"].ranges,
    +                 "A B C D E F")
    +
    +    verify_props("default", c.choices[1].defaults,
    +                 "A B C D E")
    +
    +
    +    print("Testing dependency loop detection")
    +
    +    # These are all expected to raise dependency loop errors
    +    for i in range(11):
    +        filename = "Kconfiglib/tests/Kdeploop" + str(i)
    +        try:
    +            Kconfig(filename)
    +        except KconfigError as e:
    +            if "Dependency loop" not in str(e):
    +                fail("dependency loop in {} raised wrong KconfigError"
    +                     .format(filename))
    +        except:
    +            fail("dependency loop in {} raised wrong exception"
    +                 .format(filename))
    +        else:
    +            fail("dependency loop in {} not detected".format(filename))
    +
    +    # Check the most complicated message completely
    +    try:
    +        Kconfig("Kconfiglib/tests/Kdeploop10")
    +    except KconfigError as e:
    +        verify_equal(str(e), """
    +Dependency loop
    +===============
    +
    +A (defined at Kconfiglib/tests/Kdeploop10:1), with definition...
    +
    +config A
    +   bool
    +   depends on B
    +
    +...depends on B (defined at Kconfiglib/tests/Kdeploop10:5), with 
definition...
    +
    +config B
    +   bool
    +   depends on C = 7
    +
    +...depends on C (defined at Kconfiglib/tests/Kdeploop10:9), with 
definition...
    +
    +config C
    +   int
    +   range D 8
    +
    +...depends on D (defined at Kconfiglib/tests/Kdeploop10:13), with 
definition...
    +
    +config D
    +   int
    +   default 3 if E
    +   default 8
    +
    +...depends on E (defined at Kconfiglib/tests/Kdeploop10:18), with 
definition...
    +
    +config E
    +   bool
    +
    +(select-related dependencies: F && G)
    +
    +...depends on G (defined at Kconfiglib/tests/Kdeploop10:25), with 
definition...
    +
    +config G
    +   bool
    +   depends on H
    +
    +...depends on the choice symbol H (defined at 
Kconfiglib/tests/Kdeploop10:32), with definition...
    +
    +config H
    +   bool "H"
    +   depends on I && <choice>
    +
    +...depends on the choice symbol I (defined at 
Kconfiglib/tests/Kdeploop10:41), with definition...
    +
    +config I
    +   bool "I"
    +   depends on <choice>
    +
    +...depends on <choice> (defined at Kconfiglib/tests/Kdeploop10:38), with 
definition...
    +
    +choice
    +   bool "choice" if J
    +
    +...depends on J (defined at Kconfiglib/tests/Kdeploop10:46), with 
definition...
    +
    +config J
    +   bool
    +   depends on A
    +
    +...depends again on A (defined at Kconfiglib/tests/Kdeploop10:1)
    +"""[:-1])
    +    except:
    +        fail("Loop detection message check raised wrong exception")
    +    else:
    +        fail("Loop detection message check did not raise exception")
    +
    +
    +    print("Testing preprocessor")
    +
    +    os.environ["ENV_1"] = "env_1"
    +    os.environ["ENV_2"] = "env_2"
    +    os.environ["ENV_3"] = "env_3"
    +    os.environ["ENV_4"] = "env_4"
    +    os.environ["ENV_5"] = "n"
    +    os.environ["ENV_6"] = "Kconfiglib/tests/empty"
    +    os.environ["ENV_7"] = "env_7"
    +    # We verify warnings manually
    +    c = Kconfig("Kconfiglib/tests/Kpreprocess", warn_to_stderr=False)
    +
    +    def verify_variable(name, unexp_value, exp_value, recursive, *args):
    +        var = c.variables[name]
    +
    +        verify(var.value == unexp_value,
    +               "expected variable '{}' to have the unexpanded value '{}', 
had "
    +               "the value '{}'".format(name, unexp_value, var.value))
    +
    +        if not args:
    +            verify(var.expanded_value == exp_value,
    +                   "expected expanded_value for {} to be '{}', was '{}'"
    +                   .format(name, exp_value, var.expanded_value))
    +
    +        verify(var.expanded_value_w_args(*args) == exp_value,
    +               "expected expanded_value_w_args() for '{}' to be '{}', was 
'{}'"
    +               .format(name, exp_value, var.expanded_value_w_args(*args)))
    +
    +        verify(var.is_recursive == recursive,
    +               "{} was {}, shouldn't be"
    +               .format(name, "recursive" if var.is_recursive else 
"simple"))
    +
    +    verify_variable("simple-recursive", "foo", "foo", True)
    +    verify_variable("simple-immediate", "bar", "bar", False)
    +    verify_variable("simple-recursive-2", "baz", "baz", True)
    +
    +    verify_variable("whitespaced", "foo", "foo", True)
    +
    +    verify_variable("preserve-recursive", "foo bar", "foo bar", True)
    +    verify_variable("preserve-immediate", "foo bar", "foo bar", False)
    +
    +    verify_variable("recursive",
    +                    "$(foo) $(bar) $($(b-char)a$(z-char)) $(indir)",
    +                    "abc def ghi jkl mno",
    +                    True)
    +
    +    verify_variable("immediate", "foofoo", "foofoo", False)
    +
    +    verify_variable("messy-fn-res",
    +                    "$($(fn-indir)-unused-arg, a  b , c  d )",
    +                    'surround-rev-quote " c  d " " a  b " 
surround-rev-quote ',
    +                    True)
    +
    +    verify_variable("special-chars-fn-res",
    +                    
"$(fn,$(comma)$(dollar)$(left-paren)foo$(right-paren))",
    +                    '",$(foo)"',
    +                    True)
    +
    +    verify_variable("quote", '"$(1)" "$(2)"', '"" ""', True)
    +    verify_variable("quote", '"$(1)" "$(2)"', '"one" ""', True,
    +                    "one")
    +    verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
    +                    "one", "two")
    +    verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
    +                    "one", "two", "three")
    +
    +    verify_str(c.syms["PRINT_ME"], r"""
    +config PRINT_ME
    +   string "env_1" if (FOO && BAR) || !BAZ || !QAZ
    +   default "\"foo\"" if "foo \"bar\" baz" = ""
    +""")
    +
    +    verify_str(c.syms["PRINT_ME_TOO"], r"""
    +config PRINT_ME_TOO
    +   bool "foo"
    +   default FOOBARBAZQAZ if QAZ && QAZFOO && xxx
    +""")
    +
    +    def verify_repr(name, s):
    +        verify_equal(repr(c.variables[name]), s)
    +
    +    verify_repr(
    +        "simple-immediate",
    +        "<variable simple-immediate, immediate, value 'bar'>")
    +
    +    verify_repr(
    +        "messy-fn-res",
    +        "<variable messy-fn-res, recursive, value 
'$($(fn-indir)-unused-arg, a  b , c  d )'>")
    +
    +    def verify_recursive(name):
    +        try:
    +            c.variables[name].expanded_value
    +        except KconfigError:
    +            pass
    +        else:
    +            fail("Expected '{}' expansion to flag recursive expansion, 
didn't"
    +                 .format(name))
    +
    +    verify_recursive("rec-1")
    +    # Indirectly verifies that it's not recursive
    +    verify_variable("safe-fn-rec-res",
    +                    "$(safe-fn-rec,safe-fn-rec-2)",
    +                    "foo",
    +                    True)
    +    verify_recursive("unsafe-fn-rec")
    +
    +    verify_variable("foo-bar-baz", "$(rhs)", "value", True)
    +
    +    verify_variable("space-var-res", "$(foo bar)", "value", True)
    +
    +    verify_variable("shell-res",
    +                    "$(shell,false && echo foo bar || echo baz qaz)",
    +                    "baz qaz",
    +                    True)
    +
    +    verify_variable("shell-stderr-res", "", "", False)
    +
    +    verify_variable("location-res",
    +                    "Kconfiglib/tests/Kpreprocess:125",
    +                    "Kconfiglib/tests/Kpreprocess:125",
    +                    False)
    +
    +    verify_variable("warning-res", "", "", False)
    +    verify_variable("error-n-res", "", "", False)
    +
    +    try:
    +        c.variables["error-y-res"].expanded_value
    +    except KconfigError:
    +        pass
    +    else:
    +        fail("expanding error-y-res didn't raise an exception")
    +
    +    # Check Kconfig.env_vars
    +    verify_equal(c.env_vars,
    +                 set(("ENV_1", "ENV_2", "ENV_3", "ENV_4", "ENV_5", 
"ENV_6")))
    +
    +    # Check that the expected warnings were generated
    +    verify_equal(c.warnings, [
    +        "Kconfiglib/tests/Kpreprocess:122: warning: 'echo message on 
stderr >&2' wrote to stderr: message on stderr",
    +        "Kconfiglib/tests/Kpreprocess:130: warning: a warning"
    +    ])
    +
    +
    +    print("Testing user-defined preprocessor functions")
    +
    +    # Make Kconfiglib/tests/kconfigfunctions.py importable
    +    sys.path.insert(0, "Kconfiglib/tests")
    +
    +    c = Kconfig("Kconfiglib/tests/Kuserfunctions")
    +
    +    verify_variable("add-zero",  "$(add)",          "0", True)
    +    verify_variable("add-one",   "$(add,1)",        "1", True)
    +    verify_variable("add-three", "$(add,1,-1,2,1)", "3", True)
    +
    +    verify_variable("one-one", "$(one,foo bar)", "onefoo barfoo bar", True)
    +
    +    verify_variable("one-or-more-one", "$(one-or-more,foo)", "foo + ", 
True)
    +    verify_variable("one-or-more-three", "$(one-or-more,foo,bar,baz)",
    +                    "foo + bar,baz", True)
    +
    +    def verify_bad_argno(name):
    +        try:
    +            c.variables[name].expanded_value
    +        except KconfigError:
    +            pass
    +        else:
    +            fail("Expected '{}' expansion to flag wrong number of 
arguments, "
    +                 "didn't".format(name))
    +
    +    verify_bad_argno("one-zero")
    +    verify_bad_argno("one-two")
    +    verify_bad_argno("one-or-more-zero")
    +
    +    sys.path.pop(0)
    +
    +    # This test can fail on older Python 3.x versions, because they don't
    +    # preserve dict insertion order during iteration. The output is still
    +    # correct, just different.
    +    if not (3, 0) <= sys.version_info <= (3, 5):
    +        print("Testing KCONFIG_WARN_UNDEF")
    +
    +        os.environ["KCONFIG_WARN_UNDEF"] = "y"
    +        c = Kconfig("Kconfiglib/tests/Kundef", warn_to_stderr=False)
    +
    +        verify_equal("\n".join(c.warnings), """
    +warning: the int symbol INT (defined at Kconfiglib/tests/Kundef:8) has a 
non-int range [UNDEF_2 (undefined), 8 (undefined)]
    +warning: undefined symbol UNDEF_1:
    +
    +- Referenced at Kconfiglib/tests/Kundef:4:
    +
    +config BOOL
    +   bool "foo" if DEF || !UNDEF_1
    +   default UNDEF_2
    +
    +- Referenced at Kconfiglib/tests/Kundef:19:
    +
    +menu "menu"
    +   depends on UNDEF_1
    +   visible if UNDEF_3
    +warning: undefined symbol UNDEF_2:
    +
    +- Referenced at Kconfiglib/tests/Kundef:4:
    +
    +config BOOL
    +   bool "foo" if DEF || !UNDEF_1
    +   default UNDEF_2
    +
    +- Referenced at Kconfiglib/tests/Kundef:8:
    +
    +config INT
    +   int
    +   range UNDEF_2 8
    +   range 5 15
    +   default 10
    +warning: undefined symbol UNDEF_3:
    +
    +- Referenced at Kconfiglib/tests/Kundef:19:
    +
    +menu "menu"
    +   depends on UNDEF_1
    +   visible if UNDEF_3
    +"""[1:-1])
    +
    +        os.environ.pop("KCONFIG_WARN_UNDEF")
    +
    +
    +    print("\nAll selftests passed\n" if all_passed else
    +          "\nSome selftests failed\n")
    +
    +
    +def run_compatibility_tests():
    +    # Runs tests on configurations from the kernel. Tests compability with 
the
    +    # C implementation by comparing outputs.
    +
    +    # Referenced inside the kernel Kconfig files.
    +    #
    +    # The str() makes the type of the value 'str' on both Python 2 and 
Python 3,
    +    # which is nice for some later dictionary key sanity checks.
    +
    +    os.environ["KERNELVERSION"] = str(
    +        subprocess.check_output("make kernelversion", shell=True)
    +            .decode("utf-8").rstrip()
    +    )
    +
    +    os.environ["CC_VERSION_TEXT"] = str(
    +        subprocess.check_output("gcc --version | head -n1", shell=True)
    +            .decode("utf-8").rstrip()
    +    )
    +
    +    os.environ["srctree"] = "."
    +    os.environ["CC"] = "gcc"
    +    os.environ["LD"] = "ld"
    +
    +
    +    if not os.path.exists("scripts/kconfig/conf"):
    +        print("\nscripts/kconfig/conf does not exist -- running "
    +              "'make allnoconfig' to build it...")
    +        shell("make allnoconfig")
    +
    +
    +    print("Running compatibility tests...\n")
    +
    +    test_fns = (test_defconfig,
    +                # Fails for a few defconfigs due to a bug in the C tools. 
Will
    +                # be enabled once patches get in.
    +                #test_min_config,
    +                test_alldefconfig,
    +                test_allnoconfig,
    +                test_allnoconfig_walk,
    +                test_allmodconfig,
    +                test_allyesconfig,
    +                test_sanity)
    +
    +    for test_fn in test_fns:
    +        # The test description is taken from the docstring of the 
corresponding
    +        # function
    +        print(textwrap.dedent(test_fn.__doc__))
    +
    +        for arch, srcarch in all_arch_srcarch():
    +            # Referenced inside the Kconfig files
    +            os.environ["ARCH"] = arch
    +            os.environ["SRCARCH"] = srcarch
    +
    +            rm_configs()
    +
    +            test_fn(arch, srcarch)
    +
    +    if all_passed:
    +        print("All selftests and compatibility tests passed")
    +    else:
    +        sys.exit("Some tests failed")
    +
    +
    +def all_arch_srcarch():
    +    for srcarch in os.listdir("arch"):
    +        # arc and h8300 are currently broken with the C tools on 
linux-next as
    +        # well. Perhaps they require cross-compilers to be installed.
    +        #
    +        # User-mode Linux has an unorthodox Kconfig setup that would 
require a
    +        # different testing setup. Skip it too.
    +        if srcarch in ("arc", "h8300", "um"):
    +            continue
    +
    +        if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
    +            yield (srcarch, srcarch)
    +
    +    # Some arches define additional ARCH settings with ARCH != SRCARCH
    +    # (search for "Additional ARCH settings for" in the top-level Makefile)
    +
    +    yield ("i386", "x86")
    +    yield ("x86_64", "x86")
    +
    +    yield ("sparc32", "sparc")
    +    yield ("sparc64", "sparc")
    +
    +    yield ("sh64", "sh")
    +
    +
    +def test_allnoconfig(arch, srcarch):
    +    """
    +    Verify that allnoconfig.py generates the same .config as
    +    'make allnoconfig', for each architecture. Runs the script via
    +    'make scriptconfig'.
    +    """
    +    shell("make scriptconfig SCRIPT=Kconfiglib/allnoconfig.py "
    +          "PYTHONCMD='{}'".format(sys.executable))
    +    shell("mv .config ._config")
    +    shell("scripts/kconfig/conf --allnoconfig Kconfig")
    +
    +    compare_configs(arch)
    +
    +
    +def test_allnoconfig_walk(arch, srcarch):
    +    """
    +    Verify that examples/allnoconfig_walk.py generates the same .config as
    +    'make allnoconfig', for each architecture. Runs the script via
    +    'make scriptconfig'.
    +    """
    +    shell("make scriptconfig 
SCRIPT=Kconfiglib/examples/allnoconfig_walk.py "
    +          "PYTHONCMD='{}'".format(sys.executable))
    +    shell("mv .config ._config")
    +    shell("scripts/kconfig/conf --allnoconfig Kconfig")
    +
    +    compare_configs(arch)
    +
    +
    +def test_allmodconfig(arch, srcarch):
    +    """
    +    Verify that allmodconfig.py generates the same .config as
    +    'make allmodconfig', for each architecture. Runs the script via
    +    'make scriptconfig'.
    +    """
    +    shell("make scriptconfig SCRIPT=Kconfiglib/allmodconfig.py "
    +          "PYTHONCMD='{}'".format(sys.executable))
    +    shell("mv .config ._config")
    +    shell("scripts/kconfig/conf --allmodconfig Kconfig")
    +
    +    compare_configs(arch)
    +
    +
    +def test_allyesconfig(arch, srcarch):
    +    """
    +    Verify that allyesconfig.py generates the same .config as
    +    'make allyesconfig', for each architecture. Runs the script via
    +    'make scriptconfig'.
    +    """
    +    shell("make scriptconfig SCRIPT=Kconfiglib/allyesconfig.py "
    +          "PYTHONCMD='{}'".format(sys.executable))
    +    shell("mv .config ._config")
    +    shell("scripts/kconfig/conf --allyesconfig Kconfig")
    +
    +    compare_configs(arch)
    +
    +
    +def test_sanity(arch, srcarch):
    +    """
    +    Do sanity checks on each configuration and call all public methods on 
all
    +    symbols, choices, and menu nodes for all architectures to make sure we
    +    never crash or hang.
    +    """
    +    print("For {}...".format(arch))
    +
    +    kconf = Kconfig()
    +
    +    for sym in kconf.defined_syms:
    +        verify(sym._visited == 2,
    +               "{} has broken dependency loop detection (_visited = {})"
    +               .format(sym.name, sym._visited))
    +
    +    kconf.modules
    +    kconf.defconfig_list
    +    kconf.defconfig_filename
    +
    +    # Legacy warning functions
    +    kconf.enable_redun_warnings()
    +    kconf.disable_redun_warnings()
    +    kconf.enable_undef_warnings()
    +    kconf.disable_undef_warnings()
    +    kconf.enable_warnings()
    +    kconf.disable_warnings()
    +    kconf.enable_stderr_warnings()
    +    kconf.disable_stderr_warnings()
    +
    +    kconf.mainmenu_text
    +    kconf.unset_values()
    +
    +    kconf.write_autoconf("/dev/null")
    +
    +    # No tempfile.TemporaryDirectory in Python 2
    +    tmpdir = tempfile.mkdtemp()
    +    kconf.sync_deps(os.path.join(tmpdir, "deps"))  # Create
    +    kconf.sync_deps(os.path.join(tmpdir, "deps"))  # Update
    +    shutil.rmtree(tmpdir)
    +
    +    # Python 2/3 compatible
    +    for key, sym in kconf.syms.items():
    +        verify(isinstance(key, str), "weird key '{}' in syms 
dict".format(key))
    +
    +        verify(not sym.is_constant, sym.name + " in 'syms' and constant")
    +
    +        verify(sym not in kconf.const_syms,
    +               sym.name + " in both 'syms' and 'const_syms'")
    +
    +        for dep in sym._dependents:
    +            verify(not dep.is_constant,
    +                   "the constant symbol {} depends on {}"
    +                   .format(dep.name, sym.name))
    +
    +        sym.__repr__()
    +        sym.__str__()
    +        sym.assignable
    +        kconf.disable_warnings()
    +        sym.set_value(2)
    +        sym.set_value("foo")
    +        sym.unset_value()
    +        kconf.enable_warnings()  # Legacy warning function
    +        sym.str_value
    +        sym.tri_value
    +        sym.type
    +        sym.user_value
    +        sym.visibility
    +
    +    for sym in kconf.defined_syms:
    +        verify(sym.nodes, sym.name + " is defined but lacks menu nodes")
    +
    +        verify(not (sym.orig_type not in (BOOL, TRISTATE) and sym.choice),
    +               sym.name + " is a choice symbol but not bool/tristate")
    +
    +    for key, sym in kconf.const_syms.items():
    +        verify(isinstance(key, str),
    +               "weird key '{}' in const_syms dict".format(key))
    +
    +        verify(sym.is_constant,
    +               '"{}" is in const_syms but not marked constant'
    +               .format(sym.name))
    +
    +        verify(not sym.nodes,
    +               '"{}" is constant but has menu nodes'.format(sym.name))
    +
    +        verify(not sym._dependents,
    +               '"{}" is constant but is a dependency of some symbol'
    +               .format(sym.name))
    +
    +        verify(not sym.choice,
    +               '"{}" is constant and a choice symbol'.format(sym.name))
    +
    +        sym.__repr__()
    +        sym.__str__()
    +        sym.assignable
    +        kconf.disable_warnings()
    +        sym.set_value(2)
    +        sym.set_value("foo")
    +        sym.unset_value()
    +        kconf.enable_warnings()  # Legacy warning function
    +        sym.str_value
    +        sym.tri_value
    +        sym.type
    +        sym.visibility
    +
    +    for choice in kconf.choices:
    +        for sym in choice.syms:
    +            verify(sym.choice is choice,
    +                   "{0} is in choice.syms but 'sym.choice' is not the 
choice"
    +                   .format(sym.name))
    +
    +            verify(sym.type in (BOOL, TRISTATE),
    +                   "{} is a choice symbol but is not a bool/tristate"
    +                   .format(sym.name))
    +
    +        choice.__str__()
    +        choice.__repr__()
    +        choice.str_value
    +        choice.tri_value
    +        choice.user_value
    +        choice.assignable
    +        choice.selection
    +        choice.type
    +        choice.visibility
    +
    +    # Menu nodes
    +
    +    node = kconf.top_node
    +
    +    while 1:
    +        # Everything else should be well exercised elsewhere
    +        node.__repr__()
    +        node.__str__()
    +        verify(isinstance(node.item, (Symbol, Choice)) or \
    +               node.item in (MENU, COMMENT),
    +               "'{}' appeared as a menu item".format(node.item))
    +
    +        if node.list is not None:
    +            node = node.list
    +
    +        elif node.next is not None:
    +            node = node.next
    +
    +        else:
    +            while node.parent is not None:
    +                node = node.parent
    +                if node.next is not None:
    +                    node = node.next
    +                    break
    +            else:
    +                break
    +
    +
    +def test_alldefconfig(arch, srcarch):
    +    """
    +    Verify that alldefconfig.py generates the same .config as
    +    'make alldefconfig', for each architecture. Runs the script via
    +    'make scriptconfig'.
    +    """
    +    shell("make scriptconfig SCRIPT=Kconfiglib/alldefconfig.py "
    +          "PYTHONCMD='{}'".format(sys.executable))
    +    shell("mv .config ._config")
    +    shell("scripts/kconfig/conf --alldefconfig Kconfig")
    +
    +    compare_configs(arch)
    +
    +
    +def test_defconfig(arch, srcarch):
    +    """
    +    Verify that Kconfiglib generates the same .config as 
scripts/kconfig/conf,
    +    for each architecture/defconfig pair. In obsessive mode, this test 
includes
    +    nonsensical groupings of arches with defconfigs from other arches 
(every
    +    arch/defconfig combination) and takes an order of magnitude longer 
time to
    +    run.
    +
    +    With logging enabled, this test appends any failures to a file
    +    test_defconfig_fails in the root.
    +    """
    +    kconf = Kconfig()
    +
    +    if obsessive:
    +        defconfigs = []
    +
    +        # Collect all defconfigs. This could be done once instead, but it's
    +        # a speedy operation comparatively.
    +        for srcarch_ in os.listdir("arch"):
    +            defconfigs.extend(defconfig_files(srcarch_))
    +    else:
    +        defconfigs = defconfig_files(srcarch)
    +
    +    # Test architecture for each defconfig
    +
    +    for defconfig in defconfigs:
    +        rm_configs()
    +
    +        kconf.load_config(defconfig)
    +        kconf.write_config("._config")
    +        shell("scripts/kconfig/conf --defconfig='{}' Kconfig".
    +              format(defconfig))
    +
    +        arch_defconfig_str = "  {:14}with {:60} ".format(arch, defconfig)
    +
    +        if equal_configs():
    +            print(arch_defconfig_str + "OK")
    +        else:
    +            print(arch_defconfig_str + "FAIL")
    +            fail()
    +            if log:
    +                with open("test_defconfig_fails", "a") as fail_log:
    +                    fail_log.write("{} with {} did not match\n"
    +                                   .format(arch, defconfig))
    +
    +
    +def test_min_config(arch, srcarch):
    +    """
    +    Verify that Kconfiglib generates the same .config as 'make 
savedefconfig'
    +    for each architecture/defconfig pair.
    +    """
    +    kconf = Kconfig()
    +
    +    if obsessive_min_config:
    +        defconfigs = []
    +        for srcarch_ in os.listdir("arch"):
    +            defconfigs.extend(defconfig_files(srcarch_))
    +    else:
    +        defconfigs = defconfig_files(srcarch)
    +
    +    for defconfig in defconfigs:
    +        rm_configs()
    +
    +        kconf.load_config(defconfig)
    +        kconf.write_min_config("._config")
    +
    +        shell("cp {} .config".format(defconfig))
    +
    +        shell("scripts/kconfig/conf --savedefconfig=.config Kconfig")
    +
    +        arch_defconfig_str = "  {:14}with {:60} ".format(arch, defconfig)
    +
    +        if equal_configs():
    +            print(arch_defconfig_str + "OK")
    +        else:
    +            print(arch_defconfig_str + "FAIL")
    +
    +
    +#
    +# Helper functions
    +#
    +
    +
    +def defconfig_files(srcarch):
    +    # Yields a list of defconfig file filenames for a particular srcarch
    +    # subdirectory (arch/<srcarch>/)
    +
    +    srcarch_dir = os.path.join("arch", srcarch)
    +
    +    # Some arches have a defconfig in the root of their arch/<arch>/ 
directory
    +    root_defconfig = os.path.join(srcarch_dir, "defconfig")
    +    if os.path.exists(root_defconfig):
    +        yield root_defconfig
    +
    +    # Assume all files in the arch/<arch>/configs/ directory (if it 
exists) are
    +    # configurations
    +    defconfigs_dir = os.path.join(srcarch_dir, "configs")
    +
    +    if not os.path.isdir(defconfigs_dir):
    +        return
    +
    +    for dirpath, _, filenames in os.walk(defconfigs_dir):
    +        for filename in filenames:
    +            yield os.path.join(dirpath, filename)
    +
    +
    +def rm_configs():
    +    # Delete any old ".config" (generated by the C implementation) and
    +    # "._config" (generated by us), if present.
    +
    +    def rm_if_exists(f):
    +        if os.path.exists(f):
    +            os.remove(f)
    +
    +    rm_if_exists(".config")
    +    rm_if_exists("._config")
    +
    +
    +def compare_configs(arch):
    +    if equal_configs():
    +        print("{:14}OK".format(arch))
    +    else:
    +        print("{:14}FAIL".format(arch))
    +        fail()
    +
    +
    +def equal_configs():
    +    with open(".config") as f:
    +        their = f.readlines()
    +
    +    # Strip the header generated by 'conf'
    +    i = 0
    +    for line in their:
    +        if not line.startswith("#") or \
    +           re.match(r"# CONFIG_(\w+) is not set", line):
    +            break
    +        i += 1
    +    their = their[i:]
    +
    +    try:
    +        f = open("._config")
    +    except EnvironmentError as e:
    +        if e.errno != errno.ENOENT:
    +            raise
    +        print("._config not found. Did you forget to apply the Makefile 
patch?")
    +        return False
    +    else:
    +        with f:
    +            # [1:] strips the default header
    +            our = f.readlines()[1:]
    +
    +    if their == our:
    +        return True
    +
    +    # Print a unified diff to help debugging
    +    print("Mismatched .config's! Unified diff:")
    +    sys.stdout.writelines(difflib.unified_diff(their, our, 
fromfile="their",
    +                                               tofile="our"))
    +
    +    return False
    +
    +
    +if __name__ == "__main__":
    +    run_tests()
    -- 
    2.7.4
    
    
    
    

_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.