-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Declarative per-task configuration #302
Comments
FWIW - tried this, but, proc-macros cannot currently get the output dir location to generate code / generate the right |
I made an attempt and got something running in the It looks like this in the task: task_config::task_config! {
user_leds, // config block name
count: usize,
leds: &'static [(drv_stm32xx_sys_api::PinSet, bool)],
} And looks like this in [config.user_leds]
count = 4
leds = [
["drv_stm32xx_sys_api::Port::C.pin(6)", true],
["drv_stm32xx_sys_api::Port::I.pin(8)", false],
["drv_stm32xx_sys_api::Port::I.pin(9)", false],
["drv_stm32xx_sys_api::Port::I.pin(10)", false],
["drv_stm32xx_sys_api::Port::I.pin(11)", false],
] (only implemented for This is closely modeled after Rick's suggestion, so it just generates a single |
Draft PR in #466 |
Merged! |
Fairly often, tasks need some form of configuration for a given application (target device, target board, etc). A few examples are user_leds, dependencies on other tasks, and i2c topology.
Previous approaches
user_leds currently manages with cfg_if! blocks that look at cfg(target_board). While this provides static knowledge of the board configuration at compile-time (good), it also requires modifying user_leds' source for every new board. Discoverability is OK-ish in that user_leds will just fail to compile on an unsupported target board but the error messages will be unhelpful (use of undeclared variable) even though it points you in the correct general direction.
Inter-task dependencies originally used an enum called userlib::Task that was generated by userlib's build.rs. Again, this provided static knowledge at compile time. Discoverability was poor as the enum only existed in generated code. Searching for Task would turn up lots of usages but no declaration which made figuring out what variants existed difficult. Further, the variant needed to be Rust identifiers but they were generated from app.toml which was more relaxed so looking in app.toml only got you close to the actual variant name. Lastly, the calling task needed to name the specific variant to use at compile-time. This led to the same problem as user_leds where each task would have cfg_if! blocks to handle per-target, per-board, or per-application configuration.
userlib::task_slot was introduced to address some of userlib::Task's limitations. task_slot allows the calling task to declare a generic dependency on another task without naming the specific dependency. This improves discoverability since the task_slot macro is invoked in the calling task and it introduces a clearly named static variable in the module where the macro is invoked. Configuration of the target task for a slot is moved to app.toml which feels natural for per-application configuration details. Unfortunately, task_slot's implementation is more dynamic and requires intentionally preventing some compile-time optimizations (constant propagation on task_slot static variable contents). For the limited use case of task IDs used in IPCs, the impact on code size and performance is minimal.
I2C topology is another case that has recently received some attention with #238. build.rs is once again used to generate code from app.toml. Just as with userlib::Task, this provides poor discoverability but good static knowledge at compile-time while also allowing per-application configuration. Unlike userlib::Task, each task that wishes to use i2c topology information is required to write their own build.rs that invokes build_i2c::codegen().
Suggested approach
Combine the declarative nature of userlib::task_slot with the compile-time knowledge of build.rs codegen. How? proc_macro. Consider the use case of user_leds. If there was a proc_macro named task_config!, user_leds could declare a dependency on application-specific configuration data like so:
and then in app.toml, the configuration would be supplied via the same struct-like key/vals:
The task_config! proc_macro would read app.toml, extract the config information, and generate a struct w/ initialization such as:
The text was updated successfully, but these errors were encountered: