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 as an improvement over the old-school Kconfig,
14 particularly to address its lack of hierarchical representation. Kconfig
15 organises options into a large, flat list, even though they appear in a
16 hierarchical menu structure. As a result, it can be very difficult to
17 determine which menu a configuration option belongs to without diving into
22 LunaConfig is presented as a file named `LConfig` and thus limited to one
23 LConfig per directory. This is because LunaConfig enforce user to organise each
24 directory to be a module or logical packaging of set of relavent functionalities.
26 Each LunaConfig files is comprised by these major constructs:
28 1. Config Component: `Terms` and `Groups`
32 We will describe them in details in the following sections
36 > The object that hold the value for customisation
39 def feature1() -> bool:
43 This defined an option named `feature1` with type of `bool` and the default
48 > The logical object that is used to organise the `terms` or other `groups`
50 A group is similar to term but without return type indication and return
53 And it is usually nested with other groups or terms.
57 def feature1() -> bool:
63 Multiple `LConfig`s may be defined across different sub-directory in large scale
64 project for better maintainability
66 LunaConfig allow you to import content of other LConfig using python's relative
73 This import mechanism works like `#include` directive in C preprocessor,
74 the `from . import` construct will be automatically intercepted by the LConfig
75 interpreter and be replaced with the content from `./module/LConfig`
77 You can also address file in the deeper hierarchy of the directory tree, for
81 from .sub1.sub2 import module
84 This will be translated into `./sub1/sub2/module/LConfig`
86 It can also be used in any valid python conditional branches to support
98 Native Python code is fully supported in LunaConfig, this include everything
99 like packages import, functions, and class definitions. LunaConfig has the
100 ability to distinguish these code transparently from legitimate config code.
102 However, there is one exception for this ability. Since LunaConfig treat
103 function definition as declaration of config component, to define a python
104 native function you will need to decorate it with `@native`. For example:
111 def feature1() -> int:
115 If a native function is nested in a config component, it will not be affected
116 by the scope and still avaliable globally. But this is not the case if it is
117 nested by another native function.
119 If a config component is nested in a native function, then it is ignored by
124 A config term require it's value type to be specified explicitly. The type can
125 be a literal type, primitive type or composite type
129 A term can take a literal as it's type, doing this will ensure the value taken
130 by the term to be exactly same as the given type
134 def feature1() -> "value":
139 def feature1() -> "value":
145 A term can take any python's primitive type, the value taken by the term will
146 be type checked rather than value checked
150 def feature1() -> int:
154 def feature1() -> int:
160 Any literal type or primitive type can be composite together via some structure
161 to form composite type. The checking on these type is depends on the composite
166 A Union structure realised through binary disjunctive connector `|`. The term
167 value must satisfy the type check against one of the composite type:
170 def feature1() -> "a" | "b" | int:
171 # value can be either:
172 # "a" or "b" or any integer
176 ## Component Attributes
178 Each component have set of attributes to modify its behaviour and apperance,
179 these attributes are conveyed through decorators
183 > usage: `Groups` and `Terms`
185 Label provide a user friendly name for a component, which will be the first
186 choice of the name displayed by the interactive configuration tool
190 def feature1() -> bool:
198 Marking a term to be readonly prevent explicit value update, that is, manual
199 update by user. Implicit value update initiated by `constrains` (more on this
200 later) is still allowed.
204 def feature1() -> bool:
210 > usage: `Groups` and `Terms`
212 A component can be marked to be hidden thus prevent it from displayed by the
213 configuration tool, it does not affect the visibility in the code.
215 If the decorated target is a group, then it is inherited by all it's
220 def feature1() -> bool:
228 This is a short hand of both `@hidden` and `@readonly`
232 def feature1() -> bool:
241 def feature1() -> bool:
247 > usage: `Groups` and `Terms`
249 Any component can be defined outside of the logical hierachial structure (i.e.,
250 the nested function) but still attached to it's physical hierachial structure.
253 @parent := parent_group
254 def feature1() -> bool:
258 def feature2() -> bool:
262 This will assigned `feature1` to be a subordinate of `parent_group`. Note that
263 the reference to `parent_group` does not required to be after the declaration.
269 def feature2() -> bool:
272 def feature1() -> bool:
278 > usage: `Groups` and `Terms`
280 A help message will provide explainantion or comment of a component, to be used
281 and displayed by the configuration tool.
283 The form of message is expressed using python's doc-string
286 def feature1() -> bool:
288 This is help message for feature1
296 There are builtin directives that is used inside the component.
300 > usage: `Groups` and `Terms`
302 The dependency between components is described by various `require(...)`
305 This directive follows the python function call syntax and accept one argument
306 of a boolean expression as depedency predicate.
308 Multiple `require` directives will be chainned together with logical conjunction
312 def feature1() -> bool:
313 require (feature2 or feature3)
319 This composition is equivalent to `feature5 and (feature2 or feature3)`,
320 indicate that the `feature1` require presences of both `feature5` and at least
321 one of `feature2` or `feature3`.
323 If a dependency can not be satisfied, then the feature is disabled. This will
324 cause it neither to be shown in configuration tool nor referencable in source
327 Note that the dependency check only perform on the enablement of the node but
330 ### Constrains (Inverse Dependency)
332 > usage: `Terms` with `bool` value type
334 The `when(...)` directive allow changing the default value based on the
335 predicate evaluation on the value of other terms. Therefore, it can be only
336 used by `Terms` with `bool` as value type.
338 Similar to `require`, it is based on the function call syntax and takes one
339 argument of conjunctive expression (i.e., boolean expression with only `and`
342 Multiple `when` will be chained together using logical disjunction (i.e., `or`)
345 def feature1() -> bool:
346 when (feature2 is "value1" and feature5 is "value1")
350 Which means `feature1` will takes value of `True` if one of the following is
352 + both `feature2` and `feature5` has value of `"value1"`
353 + `feature3` has value of True
355 Notice that we do not have `return` statement at the end as the precense of
356 `when` will add a return automatically (or replace it if one exists)
361 Since the language is based on Python, it's very tempting to pack in advanced
362 features or messy logic into the config file, which can make it harder to
363 follow—especially as the file grows in size.
365 LunaConfig recognises this issue and includes a built-in linter to help users
366 identify such bad practices. It shows warnings by default but can be configured
367 to raise errors instead.
369 Currently, LunaConfig detect the misuses based on these rules:
371 + `dynamic-logic`: The presence of conditional branching that could lead to
372 complex logic. However, pattern matching is allowed.
373 + `while-loop`, `for-loop`: The presence of any loop structure.
374 + `class-def`: The presence of class definition
375 + `complex-struct`: The present of complicated data structure such as `dict`.
376 + `side-effect`: The presence of dynamic assignment of other config terms value.
377 + `non-trivial-value`: The presence of non-trivial value being used as default
378 value. This include every things **other than**: