3 LunaConfig is a configuration language build on top of python. It allows user
4 to define options and the dependencies then reuse these options in their source
5 code for customisable build.
7 Lunaix use LunaConfig to toggle various optional modules, subsystems or features
8 and allow parameterised kernel build.
13 LunaConfig is designed to be an improvement over the old-school Kconfig, which in
14 particular to address the issue of lacking hierchical representation. This is
15 because Kconfig organise options into a big flat list, while the options are
16 organised into menu which is hierachical in nature. It is very difficult to
17 identify membership of a config without diving into menuconfig.
21 LunaConfig is presented as a file named `LConfig` and thus limited to one LConfig per directory. This is because LunaConfig enforce user to organise each directory to be a module or logical packaging of set of relavent functionalities.
23 In each LunaConfig files, these major concepts you will usually seen.
25 1. Config Component: `Terms` and `Groups`
29 We will describe them in details in the following sections
33 > The object that hold the value for customisation
36 def feature1() -> bool:
40 This defined an option named `feature1` with type of `bool` and the default
45 > The logical object that is used to organise the `terms` or other `groups`
47 A group is similar to term but without return type indication and return statement
49 And it is usually nested with other groups or terms.
53 def feature1() -> bool:
59 Multiple `LConfig`s may be defined across different sub-directory in large scale
60 project for better maintainability
62 LunaConfig allow you to import content of other LConfig using python's relative import
69 This import mechanism works like `#include` directive in C preprocessor,
70 the `from . import` construct will automatically intercepted by the LConfig interpreter and
71 be replaced with the content from `./module/LConfig`
73 You can also address file in the deeper hierarchy of the directory tree, for example
76 from .sub1.sub2 import module
79 This will be translated into `./sub1/sub2/module/LConfig`
81 It can also be used in any valid python conditional branches to support
93 Native Python code is fully supported in LunaConfig, this include everything like packages import, functions, and class definitions. LunaConfig has the ability to distinguish these code transparently from legitimate config code.
95 However, there is one exception for this ability. Since LunaConfig treat function definition as declaration of config component, to define a python native function you will need to decorate it with `@native`. For example:
102 def feature1() -> int:
106 If a native function is nested in a config component, it will not be affected by the scope and still avaliable globally. But this is not the case if it is nested by another native function.
108 If a config component is nested in a native function, then it is ignored by LunaConfig
112 A config term require it's value type to be specified explicitly. The type can be a literal type, primitive type or composite type
116 A term can take a literal as it's type, doing this will ensure the value taken by the term to be exactly same as the given type
120 def feature1() -> "value":
125 def feature1() -> "value":
131 A term can take any python's primitive type, the value taken by the term will be type checked rather than value checked
135 def feature1() -> int:
139 def feature1() -> int:
145 Any literal type or primitive type can be composite together via some structure to form composite type. The checking on these type is depends on the composite structure used.
149 A Union structure realised through binary disjunctive connector `|`. The term value must satisfy the type check against one of the composite type:
152 def feature1() -> "a" | "b" | int
153 # value can be either:
154 # "a" or "b" or any integer
158 ## Component Attributes
160 Each component have set of attributes to modify its behaviour and apperance, these
161 attributes are conveyed through decorators
165 > usage: `Groups` and `Terms`
167 Label provide a user friendly name for a component, which will be the first choice
168 of the name displayed by the interactive configuration tool
172 def feature1() -> bool:
180 Marking a term to be readonly prevent explicit value update, that is, manual update by user. Implicit value update initiated by `constrains` (more on this later) is still allowed.
184 def feature1() -> bool:
190 > usage: `Groups` and `Terms`
192 A component can be marked to be hidden thus prevent it from displayed by the configuration tool, it does not affect the visibility in the code.
194 If the decorated target is a group, then it is inheritated by all it's subordinates.
198 def feature1() -> bool:
206 This is a short hand of both `@hidden` and `@readonly`
210 def feature1() -> bool:
219 def feature1() -> bool:
225 > usage: `Groups` and `Terms`
227 Any component can be defined outside of the logical hierachial structure (i.e., the nested function) but still attached to it's physical hierachial structure.
230 @parent := parent_group
231 def feature1() -> bool:
235 def feature2() -> bool:
239 This will assigned `feature1` to be a subordinate of `parent_group`. Note that the reference to `parent_group` does not required to be after the declaration.
245 def feature2() -> bool:
248 def feature1() -> bool:
254 > usage: `Groups` and `Terms`
256 A help message will provide explainantion or comment of a component, to be used and displayed by the configuration tool.
258 The form of message is expressed using python's doc-string
261 def feature1() -> bool:
263 This is help message for feature1
271 There are builtin directives that is used inside the component.
275 > usage: `Groups` and `Terms`
277 The dependency between components is described by various `require(...)` directives.
279 This directive follows the python function call syntax and accept one argument of a boolean expression as depedency predicate.
281 Multiple `require` directives will be chainned together with logical conjunction (i.e., `and`)
284 def feature1() -> bool:
285 require (feature2 or feature3)
291 This composition is equivalent to `feature5 and (feature2 or feature3)`, indicate that the `feature1` require presences of both `feature5` and at least one of `feature2` or `feature3`.
293 If a dependency can not be satisfied, then the feature is disabled. This will cause it neither to be shown in configuration tool nor referencable in source code.
295 Note that the dependency check only perform on the enablement of the node but not visibility.
297 ### Auto Toggling (Inverse Dependency)
299 > usage: `Terms` with `bool` value type
301 The `when(...)` directive allow changing the default value based on the predicate evaluation on the value of other terms. Therefore, it can be only used by `Terms` with `bool` as value type.
303 Similar to `require`, it is based on the function call syntax and takes one argument of conjunctive expression (i.e., boolean expression with only `and` connectors).
305 Multiple `when` will be chained together using logical disjunction (i.e., `or`)
308 def feature1() -> bool:
309 when (feature2 is "value1" and feature5 is "value1")
313 Which means `feature1` will takes value of `True` if one of the following is satisfied:
314 + both `feature2` and `feature5` has value of `"value1"`
315 + `feature3` has value of True
317 Notice that we do not have `return` statement at the end as the precense of `when` will add a return automatically (or replace it if one exists)
322 For configuration language being a python superset will have a risk of abusing due to the high flexibility. This include complex logic, non-trivial operation being used in a component to derive the final value. Thus shifting the style from declarative to imperative and greatly reduce the overall reability.
324 For prevention of this potential drawback, LunaConfig implemented a syntactical validator to identify these possible bad-practice and issue warning (or rise a fatal-error depending on the user setting).
326 Currently, LunaConfig detect the misuses based on these rules:
328 + `dynamic-logic`: The presence of conditional branching that could lead to complex logic. However, pattern matching is allowed.
329 + `while-loop`, `for-loop`: The presence of any loop structure.
330 + `class-def`: The presence of class definition
331 + `complex-struct`: The present of complicated data structure such as `dict`.
332 + `side-effect`: The presence of dynamic assignment of other config terms value.
333 + `non-trivial-value`: The presence of non-trivial value being used as default value. This include every things other than:
336 + comparison between constant or variable
337 + single boolean operation
338 + ternary operator with constant/refernece and trivial boolean operation