[riot-notifications] [RIOT-OS/RIOT] Modelling modules in Kconfig (#15429)

Leandro Lanzieri notifications at github.com
Wed Nov 11 16:14:44 CET 2020


### Introduction
There is currently an ongoing effort to model all RIOT modules' dependencies in Kconfig, in order to use this system for module configuration and selection. This would replace our current Make-based dependency resolution system.

Currently the dependency resolution is done in a sort of top-down fashion (with some caveats), where a module is enabled by adding it to the `USEMODULE` list, and that module is responsible for pulling all the modules it needs, and indicating the features it needs. This happens iteratively until no new modules are added to the list.

Many RIOT developers are used to 'just enable module FOO', which would in many cases (although not all) bring the needed modules to use it. There are some cases where intermediate modules need to be explicitly added, but the logic is not always defined.

### The Kconfig options

The Kconfig approach is based on modelling RIOT modules as Kconfig symbols, leveraging Kconfig's dependency system. Kconfig allows to express dependencies among symbols in the following ways:

#### `depends on`

Expresses a normal dependency, reducing the upper limit of a symbol. In the following example if `MODULE_BAR` is not set to `y`, `MODULE_FOO` can't be enabled by the user (either via `menuconfig` or a configuration file).

<details><summary><b>Example of `depends on`</b></summary>

```Kconfig
config MODULE_BAR
    bool "Bar support"

config MODULE_FOO
    bool "Foo support"
    depends on MODULE_BAR
```

</details>

#### `select`

This expresses a reverse dependency. From the [Linux Kconfig language](https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html) definition:

> While normal dependencies reduce the upper limit of a symbol (see below), reverse dependencies can be used to force a lower limit of another symbol.

The caveat with this attribute is that it does not take into account the direct dependencies of the `select`ed symbol. That's why this note is also present:

> Note: select should be used with care. select will force a symbol to a value without visiting the dependencies. By abusing select you are able to select a symbol FOO even if FOO depends on BAR that is not set. In general use select only for non-visible symbols (no prompts anywhere) and for symbols with no dependencies. That will limit the usefulness but on the other hand avoid the illegal configurations all over.

Currently we are using `select` in our Kconfig files to indicate features, CPUs and to select other *non visible* symbols, and with peripheral drivers. This is because the drivers map 1 to 1 to the features, so the user of the peripheral driver can depend on the feature to avoid selecting the module when the feature is not present.

<details><summary><b>Example of `select`</b></summary>

```Kconfig
config MODULE_PERIPH_I2C
    bool "I2C support"
    depends on HAS_PERIPH_I2C

config MODULE_APDS99XX
    bool "APDS99XX light sensor"
    depends on HAS_PERIPH_I2C
    select MODULE_PERIPH_I2C
```

</details>

A good explanation with examples and alternatives can be found in Zephyr's [Kconfig tips](https://docs.zephyrproject.org/latest/guides/kconfig/tips.html#select-statements).

#### `imply`

This attribute expresses a weak reverse dependency between two symbols:

>This is similar to “select” as it enforces a lower limit on another symbol except that the “implied” symbol’s value may still be set to n from a direct dependency or with a visible prompt.

The `imply`ed symbol is enabled only when its dependencies are met, but being disabled is also a valid configuration for the symbol that implies it. This is usually used to enable "nice to have" modules.

### How should we model?

The current make approach has much more in common to dependencies modelled using `select` (top-down) than with `depends on` (bottom-up). There are some cases where this does not work. For instance, when there can be multiple modules that provide a certain feature, or that implement a certain API, or when there are complex conditions for a module to be enabled.

When modelling dependencies using `depends on` more steps may be needed to enable the module we want to use, but there is no risk of illegal conditions (provided that the dependencies are modelled correctly :). Having long chains of `select` does not scale well, and tends to get out of sync when updating dependencies. With `depends on` there is always more control on which is being included on the binary. In both cases interfaces like `menuconfig` can be used to check dependencies and which
symbols select each other.

To try to mitigate the need for extra steps certain modules that are commonly needed can be `default`ed to `y`. This would cause that users that do not need the module will have to explicitly disable it. In the end this is a trade-off between what is enabled usually and what not.

#### Proposal

To me the safest way is to limit the usage of `select` to:
   - non-visible symbols
   - modules that have no dependencies 
   - particular cases where the dependencies are controlled

The rest should be modelled with `depends on`. Of course common sense always applies.

<details><summary><b>Example of dependencies of AT driver modules</b></summary>

Note that:
-  `MODULE_AT_ISR` depends on `MODULE_EVENT_THREAD` that is why I assumed it was
    safe to select `MODULE_EVENT_THREAD_%` modules from the choices.
- `MODULE_FMT` does not present dependencies, so it's selected.

```Kconfig
# drivers/at/Kconfig

menuconfig MODULE_AT
    bool "AT (Hayes) command set library"
    depends on HAS_PERIPH_UART
    depends on TEST_KCONFIG
    depends on MODULE_ISRPIPE
    depends on MODULE_ISRPIPE_READ_TIMEOUT
    select MODULE_FMT
    select MODULE_PERIPH_UART

if MODULE_AT

config MODULE_AT_URC
    bool "Support Unsolicited Result Codes (URC)"

choice MODULE_AT_URC_ISR
    bool "Process URCs when they arrive"
    depends on MODULE_AT_URC
    depends on MODULE_EVENT_THREAD
    optional
    help
        To process URCs upon arrival an event thread is used. The
        MODULE_EVENT_THREAD symbol should be set. Choose a priority for the
        thread that processes the URCs.

config MODULE_AT_URC_ISR_LOW
    bool "Low"
    select MODULE_EVENT_THREAD_LOW

config MODULE_AT_URC_ISR_MEDIUM
    bool "Medium"
    select MODULE_EVENT_THREAD_MEDIUM

config MODULE_AT_URC_ISR_HIGHEST
    bool "Highest"
    select MODULE_EVENT_THREAD_HIGHEST

endchoice # MODULE_AT_URC_ISR

endif # MODULE_AT
```

```Kconfig
# sys/event/Kconfig

menuconfig MODULE_EVENT
    bool "Event queue"
    depends on TEST_KCONFIG
    select CORE_THREAD_FLAGS
    help
        This module offers an event queue framework like libevent or libuev.
        An event queue is basically a FIFO queue of events, with some functions
        to efficiently and safely handle adding and getting events to / from
        such a queue.

if MODULE_EVENT

menuconfig MODULE_EVENT_THREAD
    bool "Support for event handler threads"
    help
        There are three threads of different priorities that can be enabled.

if MODULE_EVENT_THREAD

config MODULE_EVENT_THREAD_LOW
    bool "Low priority thread"

config MODULE_EVENT_THREAD_MEDIUM
    bool "Medium priority thread"

config MODULE_EVENT_THREAD_HIGHEST
    bool "Highest priority thread"

endif # MODULE_EVENT_THREAD

[...]

endif # MODULE_EVENT
```
</details>

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/RIOT-OS/RIOT/issues/15429
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.riot-os.org/pipermail/notifications/attachments/20201111/57ed5408/attachment.htm>


More information about the notifications mailing list