mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-27 04:10:28 +00:00
Define configs in YAML files (#3504)
* Define configs in YAML files * Fix error message string * Cleanup * Fix rebase * Experiment: Value is String * More i64 -> i128 * More i64 -> i128 * yml -> yaml * Clippy * Expect * Test more * Explicit `trunc` * fmt * Typo * `is_tooling` -> `ignore_feature_gates` * Fix * Briefly explain the config format * Evaluate conditions in order, first match wins * Address review * Move evalexpr I128 support into separate file
This commit is contained in:
parent
891a5a4a8c
commit
b87cd34456
@ -1,5 +1,5 @@
|
||||
use esp_build::assert_unique_used_features;
|
||||
use esp_config::{ConfigOption, generate_config};
|
||||
use esp_config::generate_config_from_yaml_definition;
|
||||
|
||||
fn main() {
|
||||
// Ensure that only a single chip is specified:
|
||||
@ -13,14 +13,8 @@ fn main() {
|
||||
}
|
||||
|
||||
// emit config
|
||||
generate_config(
|
||||
"esp_backtrace",
|
||||
&[ConfigOption::new(
|
||||
"backtrace-frames",
|
||||
"The maximum number of frames that will be printed in a backtrace",
|
||||
10,
|
||||
)],
|
||||
true,
|
||||
true,
|
||||
);
|
||||
println!("cargo:rerun-if-changed=./esp_config.yml");
|
||||
let cfg_yaml = std::fs::read_to_string("./esp_config.yml")
|
||||
.expect("Failed to read esp_config.yml for esp-backtrace");
|
||||
generate_config_from_yaml_definition(&cfg_yaml, true, true, None).unwrap();
|
||||
}
|
||||
|
7
esp-backtrace/esp_config.yml
Normal file
7
esp-backtrace/esp_config.yml
Normal file
@ -0,0 +1,7 @@
|
||||
crate: esp-backtrace
|
||||
|
||||
options:
|
||||
- name: backtrace_frames
|
||||
description: The maximum number of frames that will be printed in a backtrace.
|
||||
default:
|
||||
- value: 10
|
@ -1,7 +1,7 @@
|
||||
use std::env;
|
||||
|
||||
use chrono::{TimeZone, Utc};
|
||||
use esp_config::{ConfigOption, Validator, generate_config};
|
||||
use esp_config::generate_config_from_yaml_definition;
|
||||
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(embedded_test)");
|
||||
@ -18,38 +18,8 @@ fn main() {
|
||||
println!("cargo::rustc-env=ESP_BOOTLOADER_BUILD_DATE={build_date_formatted}");
|
||||
|
||||
// emit config
|
||||
generate_config(
|
||||
"esp-bootloader-esp-idf",
|
||||
&[
|
||||
ConfigOption::new(
|
||||
"mmu_page_size",
|
||||
"ESP32-C2, ESP32-C6 and ESP32-H2 support configurable page sizes. \
|
||||
This is currently only used to populate the app descriptor.",
|
||||
"64k",
|
||||
)
|
||||
.constraint(Validator::Enumeration(vec![
|
||||
String::from("8k"),
|
||||
String::from("16k"),
|
||||
String::from("32k"),
|
||||
String::from("64k"),
|
||||
])), // .active(true) TODO we need to know the device here
|
||||
ConfigOption::new(
|
||||
"esp_idf_version",
|
||||
"ESP-IDF version used in the application descriptor. Currently it's \
|
||||
not checked by the bootloader.",
|
||||
"0.0.0",
|
||||
),
|
||||
ConfigOption::new(
|
||||
"partition-table-offset",
|
||||
"The address of partition table (by default 0x8000). Allows you to \
|
||||
move the partition table, it gives more space for the bootloader. Note that the \
|
||||
bootloader and app will both need to be compiled with the same \
|
||||
PARTITION_TABLE_OFFSET value.",
|
||||
0x8000,
|
||||
)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
],
|
||||
true,
|
||||
true,
|
||||
);
|
||||
println!("cargo:rerun-if-changed=./esp_config.yml");
|
||||
let cfg_yaml = std::fs::read_to_string("./esp_config.yml")
|
||||
.expect("Failed to read esp_config.yml for esp-bootloader-esp-idf");
|
||||
generate_config_from_yaml_definition(&cfg_yaml, true, true, None).unwrap();
|
||||
}
|
||||
|
28
esp-bootloader-esp-idf/esp_config.yml
Normal file
28
esp-bootloader-esp-idf/esp_config.yml
Normal file
@ -0,0 +1,28 @@
|
||||
crate: esp-bootloader-esp-idf
|
||||
|
||||
options:
|
||||
- name: mmu_page_size
|
||||
description: ESP32-C2, ESP32-C6 and ESP32-H2 support configurable page sizes. This is currently only used to populate the app descriptor.
|
||||
default:
|
||||
- value: '"64k"'
|
||||
constraints:
|
||||
- type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 8k
|
||||
- 16k
|
||||
- 32k
|
||||
- 64k
|
||||
|
||||
- name: esp_idf_version
|
||||
description: ESP-IDF version used in the application descriptor. Currently it's not checked by the bootloader.
|
||||
default:
|
||||
- value: '"0.0.0"'
|
||||
|
||||
- name: partition-table-offset
|
||||
description: "The address of partition table (by default 0x8000). Allows you to
|
||||
move the partition table, it gives more space for the bootloader. Note that the
|
||||
bootloader and app will both need to be compiled with the same
|
||||
PARTITION_TABLE_OFFSET value."
|
||||
default:
|
||||
- value: 32768
|
@ -15,7 +15,9 @@ test = true
|
||||
[dependencies]
|
||||
document-features = "0.2.11"
|
||||
serde = { version = "1.0.197", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1.0.0", optional = true }
|
||||
serde_yaml = { version = "0.9", optional = true }
|
||||
evalexpr = { version = "12.0.2", optional = true }
|
||||
esp-metadata = { version = "0.7.0", path = "../esp-metadata", default-features = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
temp-env = "0.3.6"
|
||||
@ -23,4 +25,4 @@ pretty_assertions = "1.4.1"
|
||||
|
||||
[features]
|
||||
## Enable the generation and parsing of a config
|
||||
build = ["dep:serde","dep:serde_json"]
|
||||
build = ["dep:serde", "dep:serde_yaml", "dep:evalexpr", "dep:esp-metadata"]
|
||||
|
@ -44,6 +44,75 @@ For all supported data types, there are helper macros that emit `const` code for
|
||||
|
||||
In addition to environment variables, for boolean types rust `cfg`'s are emitted in snake case _without_ the prefix.
|
||||
|
||||
## Defining Configuration Options
|
||||
|
||||
Config options should be defined declaratively in a file called `esp_config.yml`.
|
||||
|
||||
Such a file looks like this:
|
||||
```yaml
|
||||
crate: esp-bootloader-esp-idf
|
||||
|
||||
options:
|
||||
- name: mmu_page_size
|
||||
description: ESP32-C2, ESP32-C6 and ESP32-H2 support configurable page sizes. This is currently only used to populate the app descriptor.
|
||||
default:
|
||||
- value: '"64k"'
|
||||
stability: !Stable stable-since-version
|
||||
constraints:
|
||||
- if: true
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 8k
|
||||
- 16k
|
||||
- 32k
|
||||
- 64k
|
||||
|
||||
- name: esp_idf_version
|
||||
description: ESP-IDF version used in the application descriptor. Currently it's not checked by the bootloader.
|
||||
default:
|
||||
- if: 'chip == "esp32c6"'
|
||||
value: '"esp32c6"'
|
||||
- if: 'chip == "esp32"'
|
||||
value: '"other"'
|
||||
active: true
|
||||
|
||||
- name: partition-table-offset
|
||||
description: "The address of partition table (by default 0x8000). Allows you to \
|
||||
move the partition table, it gives more space for the bootloader. Note that the \
|
||||
bootloader and app will both need to be compiled with the same \
|
||||
PARTITION_TABLE_OFFSET value."
|
||||
default:
|
||||
- if: true
|
||||
value: 32768
|
||||
stability: Unstable
|
||||
active: 'chip == "esp32c6"'
|
||||
|
||||
checks:
|
||||
- 'ESP_BOOTLOADER_ESP_IDF_CONFIG_PARTITION_TABLE_OFFSET >= 32768'
|
||||
```
|
||||
|
||||
`if` and `active` are [evalexpr](https://crates.io/crates/evalexpr) expressions returning a boolean.
|
||||
|
||||
The expression supports these custom functions:
|
||||
|Function|Description|
|
||||
|---|---|
|
||||
|feature(String)|`true` if the given chip feature is present|
|
||||
|cargo_feature(String)|`true` if the given Cargo feature is active|
|
||||
|ignore_feature_gates()|Usually `false` but tooling will set this to `true` to hint that the expression is evaluated by e.g. a TUI|
|
||||
|
||||
`ignore_feature_gates()` is useful to enable otherwise disabled functionality - e.g. to offer all possible options regardless of any active / non-active features.
|
||||
|
||||
The `chip` variable is populated with the name of the targeted chip (if the crate is using chip specific features).
|
||||
|
||||
The conditions for `default` and `constraints` are evaluated in order and the first match is taken no matter if there is more.
|
||||
This way you could have a catch-all condition as the last item by just specifying `true`.
|
||||
|
||||
`checks` is a list of checks which needs to pass for a valid configuration and is checked after all config values for the current config are applied.
|
||||
You can access the currently configured values to check them.
|
||||
|
||||
For more examples see the various `esp_config.yml` files in this repository.
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile when using the latest stable Rust version at the time of the crate's release. It _might_ compile with older versions, but that may change in any new release, including patches.
|
||||
|
163
esp-config/src/generate/evalexpr_extensions.rs
Normal file
163
esp-config/src/generate/evalexpr_extensions.rs
Normal file
@ -0,0 +1,163 @@
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct I128NumericTypes;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct I128(pub i128);
|
||||
|
||||
impl FromStr for I128 {
|
||||
type Err = core::num::ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(I128(s.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for I128 {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
use core::str::FromStr;
|
||||
|
||||
use evalexpr::{EvalexprError, EvalexprResult};
|
||||
|
||||
impl<NumericTypes: evalexpr::EvalexprNumericTypes<Int = Self>> evalexpr::EvalexprInt<NumericTypes>
|
||||
for I128
|
||||
{
|
||||
const MIN: Self = I128(i128::MIN);
|
||||
|
||||
const MAX: Self = I128(i128::MAX);
|
||||
|
||||
fn from_usize(int: usize) -> EvalexprResult<Self, NumericTypes> {
|
||||
Ok(I128(int.try_into().map_err(|_| {
|
||||
EvalexprError::IntFromUsize { usize_int: int }
|
||||
})?))
|
||||
}
|
||||
|
||||
fn into_usize(&self) -> EvalexprResult<usize, NumericTypes> {
|
||||
if self.0 >= 0 {
|
||||
(self.0 as u64)
|
||||
.try_into()
|
||||
.map_err(|_| EvalexprError::IntIntoUsize { int: *self })
|
||||
} else {
|
||||
Err(EvalexprError::IntIntoUsize { int: *self })
|
||||
}
|
||||
}
|
||||
|
||||
fn from_hex_str(literal: &str) -> Result<Self, ()> {
|
||||
Ok(I128(i128::from_str_radix(literal, 16).map_err(|_| ())?))
|
||||
}
|
||||
|
||||
fn checked_add(&self, rhs: &Self) -> EvalexprResult<Self, NumericTypes> {
|
||||
let result = (self.0).checked_add(rhs.0);
|
||||
if let Some(result) = result {
|
||||
Ok(I128(result))
|
||||
} else {
|
||||
Err(EvalexprError::AdditionError {
|
||||
augend: evalexpr::Value::<NumericTypes>::from_int(*self),
|
||||
addend: evalexpr::Value::<NumericTypes>::from_int(*rhs),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_sub(&self, rhs: &Self) -> EvalexprResult<Self, NumericTypes> {
|
||||
let result = (self.0).checked_sub(rhs.0);
|
||||
if let Some(result) = result {
|
||||
Ok(I128(result))
|
||||
} else {
|
||||
Err(EvalexprError::SubtractionError {
|
||||
minuend: evalexpr::Value::<NumericTypes>::from_int(*self),
|
||||
subtrahend: evalexpr::Value::<NumericTypes>::from_int(*rhs),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_neg(&self) -> EvalexprResult<Self, NumericTypes> {
|
||||
let result = (self.0).checked_neg();
|
||||
if let Some(result) = result {
|
||||
Ok(I128(result))
|
||||
} else {
|
||||
Err(EvalexprError::NegationError {
|
||||
argument: evalexpr::Value::<NumericTypes>::from_int(*self),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_mul(&self, rhs: &Self) -> EvalexprResult<Self, NumericTypes> {
|
||||
let result = (self.0).checked_mul(rhs.0);
|
||||
if let Some(result) = result {
|
||||
Ok(I128(result))
|
||||
} else {
|
||||
Err(EvalexprError::MultiplicationError {
|
||||
multiplicand: evalexpr::Value::<NumericTypes>::from_int(*self),
|
||||
multiplier: evalexpr::Value::<NumericTypes>::from_int(*rhs),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_div(&self, rhs: &Self) -> EvalexprResult<Self, NumericTypes> {
|
||||
let result = (self.0).checked_div(rhs.0);
|
||||
if let Some(result) = result {
|
||||
Ok(I128(result))
|
||||
} else {
|
||||
Err(EvalexprError::DivisionError {
|
||||
dividend: evalexpr::Value::<NumericTypes>::from_int(*self),
|
||||
divisor: evalexpr::Value::<NumericTypes>::from_int(*rhs),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_rem(&self, rhs: &Self) -> EvalexprResult<Self, NumericTypes> {
|
||||
let result = (self.0).checked_rem(rhs.0);
|
||||
if let Some(result) = result {
|
||||
Ok(I128(result))
|
||||
} else {
|
||||
Err(EvalexprError::ModulationError {
|
||||
dividend: evalexpr::Value::<NumericTypes>::from_int(*self),
|
||||
divisor: evalexpr::Value::<NumericTypes>::from_int(*rhs),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn abs(&self) -> EvalexprResult<Self, NumericTypes> {
|
||||
Ok(I128(self.0.abs()))
|
||||
}
|
||||
|
||||
fn bitand(&self, rhs: &Self) -> Self {
|
||||
I128(std::ops::BitAnd::bitand(self.0, rhs.0))
|
||||
}
|
||||
|
||||
fn bitor(&self, rhs: &Self) -> Self {
|
||||
I128(std::ops::BitOr::bitor(self.0, rhs.0))
|
||||
}
|
||||
|
||||
fn bitxor(&self, rhs: &Self) -> Self {
|
||||
I128(std::ops::BitXor::bitxor(self.0, rhs.0))
|
||||
}
|
||||
|
||||
fn bitnot(&self) -> Self {
|
||||
I128(std::ops::Not::not(self.0))
|
||||
}
|
||||
|
||||
fn bit_shift_left(&self, rhs: &Self) -> Self {
|
||||
I128(std::ops::Shl::shl(self.0, rhs.0))
|
||||
}
|
||||
|
||||
fn bit_shift_right(&self, rhs: &Self) -> Self {
|
||||
I128(std::ops::Shr::shr(self.0, rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl evalexpr::EvalexprNumericTypes for I128NumericTypes {
|
||||
type Int = I128;
|
||||
type Float = f64;
|
||||
|
||||
fn int_as_float(int: &Self::Int) -> Self::Float {
|
||||
int.0 as Self::Float
|
||||
}
|
||||
|
||||
fn float_as_int(float: &Self::Float) -> Self::Int {
|
||||
I128(float.trunc() as i128)
|
||||
}
|
||||
}
|
@ -1,10 +1,16 @@
|
||||
use core::fmt::Display;
|
||||
use std::{collections::HashMap, env, fmt, fs, io::Write, path::PathBuf};
|
||||
|
||||
use evalexpr::{ContextWithMutableFunctions, ContextWithMutableVariables};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::generate::{validator::Validator, value::Value};
|
||||
use crate::generate::{
|
||||
evalexpr_extensions::{I128, I128NumericTypes},
|
||||
validator::Validator,
|
||||
value::Value,
|
||||
};
|
||||
|
||||
pub(crate) mod evalexpr_extensions;
|
||||
mod markdown;
|
||||
pub(crate) mod validator;
|
||||
pub(crate) mod value;
|
||||
@ -45,8 +51,287 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate and parse config from a prefix, and an array tuples containing the
|
||||
/// name, description, default value, and an optional validator.
|
||||
/// The root node of a configuration.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
/// The crate name.
|
||||
#[serde(rename = "crate")]
|
||||
pub krate: String,
|
||||
/// The config options for this crate.
|
||||
pub options: Vec<CfgOption>,
|
||||
/// Optionally additional checks.
|
||||
pub checks: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
fn true_default() -> String {
|
||||
"true".to_string()
|
||||
}
|
||||
|
||||
fn unstable_default() -> Stability {
|
||||
Stability::Unstable
|
||||
}
|
||||
|
||||
/// A default value for a configuration option.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CfgDefaultValue {
|
||||
/// Condition which makes this default value used.
|
||||
/// You can and have to have exactly one active default value.
|
||||
#[serde(rename = "if")]
|
||||
#[serde(default = "true_default")]
|
||||
pub if_: String,
|
||||
/// The default value.
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
/// A configuration option.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CfgOption {
|
||||
/// Name of the configuration option
|
||||
pub name: String,
|
||||
/// Description of the configuration option.
|
||||
/// This will be visible in the documentation and in the tooling.
|
||||
pub description: String,
|
||||
/// A condition which specified when this option is active.
|
||||
#[serde(default = "true_default")]
|
||||
pub active: String,
|
||||
/// The default value.
|
||||
/// Exactly one of the items needs to be active at any time.
|
||||
pub default: Vec<CfgDefaultValue>,
|
||||
/// Constraints (Validators) to use.
|
||||
/// If given at most one item is allowed to be active at any time.
|
||||
pub constraints: Option<Vec<CfgConstraint>>,
|
||||
/// A display hint for the value.
|
||||
/// This is meant for tooling and/or documentation.
|
||||
pub display_hint: Option<DisplayHint>,
|
||||
/// The stability guarantees of this option.
|
||||
#[serde(default = "unstable_default")]
|
||||
pub stability: Stability,
|
||||
}
|
||||
|
||||
/// A conditional constraint / validator for a config option.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CfgConstraint {
|
||||
/// Condition which makes this validator used.
|
||||
#[serde(rename = "if")]
|
||||
#[serde(default = "true_default")]
|
||||
if_: String,
|
||||
/// The validator to be used.
|
||||
#[serde(rename = "type")]
|
||||
type_: Validator,
|
||||
}
|
||||
|
||||
/// Generate the config from a YAML definition.
|
||||
///
|
||||
/// The YAML follows the format outlined by [Config].
|
||||
///
|
||||
/// After deserializing the config and normalizing it, this will call
|
||||
/// [generate_config] to finally get the currently active configuration.
|
||||
pub fn generate_config_from_yaml_definition(
|
||||
yaml: &str,
|
||||
enable_unstable: bool,
|
||||
emit_md_tables: bool,
|
||||
chip: Option<esp_metadata::Config>,
|
||||
) -> Result<HashMap<String, Value>, Error> {
|
||||
let features: Vec<String> = env::vars()
|
||||
.filter(|(k, _)| k.starts_with("CARGO_FEATURE_"))
|
||||
.map(|(k, _)| k)
|
||||
.map(|v| {
|
||||
v.strip_prefix("CARGO_FEATURE_")
|
||||
.unwrap_or_default()
|
||||
.to_string()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (config, options) = evaluate_yaml_config(yaml, chip, features, false)?;
|
||||
|
||||
let cfg = generate_config(&config.krate, &options, enable_unstable, emit_md_tables);
|
||||
|
||||
if let Some(checks) = config.checks {
|
||||
let mut eval_ctx = evalexpr::HashMapContext::<I128NumericTypes>::new();
|
||||
for (k, v) in cfg.iter() {
|
||||
eval_ctx
|
||||
.set_value(
|
||||
k.clone(),
|
||||
match v {
|
||||
Value::Bool(v) => evalexpr::Value::Boolean(*v),
|
||||
Value::Integer(v) => evalexpr::Value::Int(I128(*v)),
|
||||
Value::String(v) => evalexpr::Value::String(v.clone()),
|
||||
},
|
||||
)
|
||||
.map_err(|err| Error::Parse(format!("Error setting value for {k} ({err})")))?;
|
||||
}
|
||||
for check in checks {
|
||||
if !evalexpr::eval_with_context(&check, &eval_ctx)
|
||||
.and_then(|v| v.as_boolean())
|
||||
.map_err(|err| Error::Validation(format!("Validation error: '{check}' ({err})")))?
|
||||
{
|
||||
return Err(Error::Validation(format!("Validation error: '{check}'")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
/// Evaluate the given YAML representation of a config definition.
|
||||
pub fn evaluate_yaml_config(
|
||||
yaml: &str,
|
||||
chip: Option<esp_metadata::Config>,
|
||||
features: Vec<String>,
|
||||
ignore_feature_gates: bool,
|
||||
) -> Result<(Config, Vec<ConfigOption>), Error> {
|
||||
let config: Config = serde_yaml::from_str(yaml).map_err(|err| Error::Parse(err.to_string()))?;
|
||||
let mut options = Vec::new();
|
||||
let mut eval_ctx = evalexpr::HashMapContext::<evalexpr::DefaultNumericTypes>::new();
|
||||
if let Some(config) = chip {
|
||||
eval_ctx
|
||||
.set_value("chip".into(), evalexpr::Value::String(config.name()))
|
||||
.map_err(|err| Error::Parse(err.to_string()))?;
|
||||
|
||||
eval_ctx
|
||||
.set_function(
|
||||
"feature".into(),
|
||||
evalexpr::Function::<evalexpr::DefaultNumericTypes>::new(move |arg| {
|
||||
if let evalexpr::Value::String(which) = arg {
|
||||
let res = config.contains(which);
|
||||
Ok(evalexpr::Value::Boolean(res))
|
||||
} else {
|
||||
Err(evalexpr::EvalexprError::CustomMessage(format!(
|
||||
"Bad argument: {arg:?}"
|
||||
)))
|
||||
}
|
||||
}),
|
||||
)
|
||||
.map_err(|err| Error::Parse(err.to_string()))?;
|
||||
|
||||
eval_ctx
|
||||
.set_function(
|
||||
"cargo_feature".into(),
|
||||
evalexpr::Function::<evalexpr::DefaultNumericTypes>::new(move |arg| {
|
||||
if let evalexpr::Value::String(which) = arg {
|
||||
let res = features.contains(&which.to_uppercase().replace("-", "_"));
|
||||
Ok(evalexpr::Value::Boolean(res))
|
||||
} else {
|
||||
Err(evalexpr::EvalexprError::CustomMessage(format!(
|
||||
"Bad argument: {arg:?}"
|
||||
)))
|
||||
}
|
||||
}),
|
||||
)
|
||||
.map_err(|err| Error::Parse(err.to_string()))?;
|
||||
|
||||
eval_ctx
|
||||
.set_function(
|
||||
"ignore_feature_gates".into(),
|
||||
evalexpr::Function::<evalexpr::DefaultNumericTypes>::new(move |arg| {
|
||||
if let evalexpr::Value::Empty = arg {
|
||||
Ok(evalexpr::Value::Boolean(ignore_feature_gates))
|
||||
} else {
|
||||
Err(evalexpr::EvalexprError::CustomMessage(format!(
|
||||
"Bad argument: {arg:?}"
|
||||
)))
|
||||
}
|
||||
}),
|
||||
)
|
||||
.map_err(|err| Error::Parse(err.to_string()))?;
|
||||
}
|
||||
for option in config.options.clone() {
|
||||
let active = evalexpr::eval_with_context(&option.active, &eval_ctx)
|
||||
.map_err(|err| {
|
||||
Error::Parse(format!(
|
||||
"Error evaluating '{}', error = {:?}",
|
||||
option.active, err
|
||||
))
|
||||
})?
|
||||
.as_boolean()
|
||||
.map_err(|err| {
|
||||
Error::Parse(format!(
|
||||
"Error evaluating '{}', error = {:?}",
|
||||
option.active, err
|
||||
))
|
||||
})?;
|
||||
|
||||
let constraint = {
|
||||
let mut active_constraint = None;
|
||||
if let Some(constraints) = &option.constraints {
|
||||
for constraint in constraints {
|
||||
if evalexpr::eval_with_context(&constraint.if_, &eval_ctx)
|
||||
.map_err(|err| {
|
||||
Error::Parse(format!(
|
||||
"Error evaluating '{}', error = {err:?}",
|
||||
constraint.if_
|
||||
))
|
||||
})?
|
||||
.as_boolean()
|
||||
.map_err(|err| {
|
||||
Error::Parse(format!(
|
||||
"Error evaluating '{}', error = {:?}",
|
||||
constraint.if_, err
|
||||
))
|
||||
})?
|
||||
{
|
||||
active_constraint = Some(constraint.type_.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if option.constraints.is_some() && active_constraint.is_none() {
|
||||
panic!(
|
||||
"No constraint active for crate {}, option {}",
|
||||
config.krate, option.name
|
||||
);
|
||||
}
|
||||
|
||||
active_constraint
|
||||
};
|
||||
|
||||
let default_value = {
|
||||
let mut default_value = None;
|
||||
for value in option.default.clone() {
|
||||
if evalexpr::eval_with_context(&value.if_, &eval_ctx)
|
||||
.and_then(|v| v.as_boolean())
|
||||
.map_err(|err| {
|
||||
Error::Parse(format!("Error evaluating '{}', error = {err:?}", value.if_))
|
||||
})?
|
||||
{
|
||||
default_value = Some(value.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if default_value.is_none() {
|
||||
panic!(
|
||||
"No default value active for crate {}, option {}",
|
||||
config.krate, option.name
|
||||
);
|
||||
}
|
||||
|
||||
default_value
|
||||
};
|
||||
|
||||
let option = ConfigOption {
|
||||
name: option.name.clone(),
|
||||
description: option.description,
|
||||
default_value: default_value.ok_or(Error::Parse(format!(
|
||||
"No default value found for {}",
|
||||
option.name
|
||||
)))?,
|
||||
constraint,
|
||||
stability: option.stability,
|
||||
active,
|
||||
display_hint: option.display_hint.unwrap_or(DisplayHint::None),
|
||||
};
|
||||
options.push(option);
|
||||
}
|
||||
Ok((config, options))
|
||||
}
|
||||
|
||||
/// Generate and parse config from a prefix, and an array of [ConfigOption].
|
||||
///
|
||||
/// This function will parse any `SCREAMING_SNAKE_CASE` environment variables
|
||||
/// that match the given prefix. It will then attempt to parse the [`Value`] and
|
||||
@ -125,37 +410,9 @@ pub fn generate_config_internal<'a>(
|
||||
|
||||
emit_configuration(&mut stdout, &configs);
|
||||
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
let config_json = config_json(&configs, false);
|
||||
write_out_file(format!("{crate_name}_config_data.json"), config_json);
|
||||
}
|
||||
|
||||
configs
|
||||
}
|
||||
|
||||
fn config_json(config: &[(String, &ConfigOption, Value)], pretty: bool) -> String {
|
||||
#[derive(Serialize)]
|
||||
struct Item<'a> {
|
||||
option: &'a ConfigOption,
|
||||
actual_value: Value,
|
||||
}
|
||||
|
||||
let mut to_write = Vec::new();
|
||||
for (_, option, value) in config.iter() {
|
||||
to_write.push(Item {
|
||||
actual_value: value.clone(),
|
||||
option,
|
||||
})
|
||||
}
|
||||
|
||||
if pretty {
|
||||
serde_json::to_string_pretty(&to_write).unwrap()
|
||||
} else {
|
||||
serde_json::to_string(&to_write).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// The stability of the configuration option.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Stability {
|
||||
@ -224,51 +481,6 @@ pub struct ConfigOption {
|
||||
}
|
||||
|
||||
impl ConfigOption {
|
||||
/// Create a new config option.
|
||||
///
|
||||
/// Unstable, active, no display-hint and not constrained by default.
|
||||
pub fn new(name: &str, description: &str, default_value: impl Into<Value>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
description: description.to_string(),
|
||||
default_value: default_value.into(),
|
||||
constraint: None,
|
||||
stability: Stability::Unstable,
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constrain the config option
|
||||
pub fn constraint(mut self, validator: Validator) -> Self {
|
||||
self.constraint = Some(validator);
|
||||
self
|
||||
}
|
||||
|
||||
/// Constrain the config option
|
||||
pub fn constraint_by(mut self, validator: Option<Validator>) -> Self {
|
||||
self.constraint = validator;
|
||||
self
|
||||
}
|
||||
|
||||
/// Mark this config option as stable
|
||||
pub fn stable(mut self, version: &str) -> Self {
|
||||
self.stability = Stability::Stable(version.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the active flag of this config option
|
||||
pub fn active(mut self, active: bool) -> Self {
|
||||
self.active = active;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the display hint
|
||||
pub fn display_hint(mut self, display_hint: DisplayHint) -> Self {
|
||||
self.display_hint = display_hint;
|
||||
self
|
||||
}
|
||||
|
||||
fn env_var(&self, prefix: &str) -> String {
|
||||
format!("{}{}", prefix, screaming_snake_case(&self.name))
|
||||
}
|
||||
@ -708,85 +920,6 @@ mod test {
|
||||
assert!(cargo_lines.contains(&"cargo:rustc-cfg=some_key_variant_0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_output() {
|
||||
let mut stdout = Vec::new();
|
||||
let config = [
|
||||
ConfigOption {
|
||||
name: String::from("some-key"),
|
||||
description: String::from("NA"),
|
||||
default_value: Value::String("variant-0".to_string()),
|
||||
constraint: Some(Validator::Enumeration(vec![
|
||||
"variant-0".to_string(),
|
||||
"variant-1".to_string(),
|
||||
])),
|
||||
stability: Stability::Stable(String::from("testing")),
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
ConfigOption {
|
||||
name: String::from("some-key2"),
|
||||
description: String::from("NA"),
|
||||
default_value: Value::Bool(true),
|
||||
constraint: None,
|
||||
stability: Stability::Unstable,
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
];
|
||||
let configs =
|
||||
temp_env::with_vars([("ESP_TEST_CONFIG_SOME_KEY", Some("variant-0"))], || {
|
||||
generate_config_internal(&mut stdout, "esp-test", &config, false)
|
||||
});
|
||||
|
||||
let json_output = config_json(&configs, true);
|
||||
println!("{json_output}");
|
||||
pretty_assertions::assert_eq!(
|
||||
r#"[
|
||||
{
|
||||
"option": {
|
||||
"name": "some-key",
|
||||
"description": "NA",
|
||||
"default_value": {
|
||||
"String": "variant-0"
|
||||
},
|
||||
"constraint": {
|
||||
"Enumeration": [
|
||||
"variant-0",
|
||||
"variant-1"
|
||||
]
|
||||
},
|
||||
"stability": {
|
||||
"Stable": "testing"
|
||||
},
|
||||
"active": true,
|
||||
"display_hint": "None"
|
||||
},
|
||||
"actual_value": {
|
||||
"String": "variant-0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"option": {
|
||||
"name": "some-key2",
|
||||
"description": "NA",
|
||||
"default_value": {
|
||||
"Bool": true
|
||||
},
|
||||
"constraint": null,
|
||||
"stability": "Unstable",
|
||||
"active": true,
|
||||
"display_hint": "None"
|
||||
},
|
||||
"actual_value": {
|
||||
"Bool": true
|
||||
}
|
||||
}
|
||||
]"#,
|
||||
json_output
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn unstable_option_panics_unless_enabled() {
|
||||
@ -838,61 +971,190 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convenience_constructors() {
|
||||
fn deserialization() {
|
||||
let yml = r#"
|
||||
crate: esp-bootloader-esp-idf
|
||||
|
||||
options:
|
||||
- name: mmu_page_size
|
||||
description: ESP32-C2, ESP32-C6 and ESP32-H2 support configurable page sizes. This is currently only used to populate the app descriptor.
|
||||
default:
|
||||
- value: '"64k"'
|
||||
stability: !Stable xxxx
|
||||
constraints:
|
||||
- if: true
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 8k
|
||||
- 16k
|
||||
- 32k
|
||||
- 64k
|
||||
|
||||
- name: esp_idf_version
|
||||
description: ESP-IDF version used in the application descriptor. Currently it's not checked by the bootloader.
|
||||
default:
|
||||
- if: 'chip == "esp32c6"'
|
||||
value: '"esp32c6"'
|
||||
- if: 'chip == "esp32"'
|
||||
value: '"other"'
|
||||
active: true
|
||||
|
||||
- name: partition-table-offset
|
||||
description: "The address of partition table (by default 0x8000). Allows you to \
|
||||
move the partition table, it gives more space for the bootloader. Note that the \
|
||||
bootloader and app will both need to be compiled with the same \
|
||||
PARTITION_TABLE_OFFSET value."
|
||||
default:
|
||||
- if: true
|
||||
value: 32768
|
||||
stability: Unstable
|
||||
active: 'chip == "esp32c6"'
|
||||
"#;
|
||||
|
||||
let (cfg, options) = evaluate_yaml_config(
|
||||
yml,
|
||||
Some(esp_metadata::Config::for_chip(&esp_metadata::Chip::Esp32c6).clone()),
|
||||
vec![],
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!("esp-bootloader-esp-idf", cfg.krate);
|
||||
|
||||
assert_eq!(
|
||||
ConfigOption {
|
||||
name: String::from("number"),
|
||||
description: String::from("NA"),
|
||||
default_value: Value::Integer(999),
|
||||
constraint: None,
|
||||
vec![
|
||||
ConfigOption {
|
||||
name: "mmu_page_size".to_string(),
|
||||
description: "ESP32-C2, ESP32-C6 and ESP32-H2 support configurable page sizes. This is currently only used to populate the app descriptor.".to_string(),
|
||||
default_value: Value::String("64k".to_string()),
|
||||
constraint: Some(
|
||||
Validator::Enumeration(
|
||||
vec![
|
||||
"8k".to_string(),
|
||||
"16k".to_string(),
|
||||
"32k".to_string(),
|
||||
"64k".to_string(),
|
||||
],
|
||||
),
|
||||
),
|
||||
stability: Stability::Stable("xxxx".to_string()),
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
ConfigOption {
|
||||
name: "esp_idf_version".to_string(),
|
||||
description: "ESP-IDF version used in the application descriptor. Currently it's not checked by the bootloader.".to_string(),
|
||||
default_value: Value::String("esp32c6".to_string()),
|
||||
constraint: None,
|
||||
stability: Stability::Unstable,
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
ConfigOption {
|
||||
name: "partition-table-offset".to_string(),
|
||||
description: "The address of partition table (by default 0x8000). Allows you to move the partition table, it gives more space for the bootloader. Note that the bootloader and app will both need to be compiled with the same PARTITION_TABLE_OFFSET value.".to_string(),
|
||||
default_value: Value::Integer(32768),
|
||||
constraint: None,
|
||||
stability: Stability::Unstable,
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
],
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization_fallback_default() {
|
||||
let yml = r#"
|
||||
crate: esp-bootloader-esp-idf
|
||||
|
||||
options:
|
||||
- name: esp_idf_version
|
||||
description: ESP-IDF version used in the application descriptor. Currently it's not checked by the bootloader.
|
||||
default:
|
||||
- if: 'chip == "esp32c6"'
|
||||
value: '"esp32c6"'
|
||||
- if: 'chip == "esp32"'
|
||||
value: '"other"'
|
||||
- value: '"default"'
|
||||
active: true
|
||||
"#;
|
||||
|
||||
let (cfg, options) = evaluate_yaml_config(
|
||||
yml,
|
||||
Some(esp_metadata::Config::for_chip(&esp_metadata::Chip::Esp32c3).clone()),
|
||||
vec![],
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!("esp-bootloader-esp-idf", cfg.krate);
|
||||
|
||||
assert_eq!(
|
||||
vec![
|
||||
ConfigOption {
|
||||
name: "esp_idf_version".to_string(),
|
||||
description: "ESP-IDF version used in the application descriptor. Currently it's not checked by the bootloader.".to_string(),
|
||||
default_value: Value::String("default".to_string()),
|
||||
constraint: None,
|
||||
stability: Stability::Unstable,
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
],
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization_fallback_contraint() {
|
||||
let yml = r#"
|
||||
crate: esp-bootloader-esp-idf
|
||||
|
||||
options:
|
||||
- name: option
|
||||
description: Desc
|
||||
default:
|
||||
- value: 100
|
||||
constraints:
|
||||
- if: 'chip == "esp32c6"'
|
||||
type:
|
||||
validator: integer_in_range
|
||||
value:
|
||||
start: 0
|
||||
end: 100
|
||||
- if: true
|
||||
type:
|
||||
validator: integer_in_range
|
||||
value:
|
||||
start: 0
|
||||
end: 50
|
||||
active: true
|
||||
"#;
|
||||
|
||||
let (cfg, options) = evaluate_yaml_config(
|
||||
yml,
|
||||
Some(esp_metadata::Config::for_chip(&esp_metadata::Chip::Esp32).clone()),
|
||||
vec![],
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!("esp-bootloader-esp-idf", cfg.krate);
|
||||
|
||||
assert_eq!(
|
||||
vec![ConfigOption {
|
||||
name: "option".to_string(),
|
||||
description: "Desc".to_string(),
|
||||
default_value: Value::Integer(100),
|
||||
constraint: Some(Validator::IntegerInRange(0..50)),
|
||||
stability: Stability::Unstable,
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
ConfigOption::new("number", "NA", 999)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ConfigOption {
|
||||
name: String::from("string"),
|
||||
description: String::from("descr"),
|
||||
default_value: Value::String("some string".to_string()),
|
||||
constraint: None,
|
||||
stability: Stability::Stable("1.0.0".to_string()),
|
||||
active: true,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
ConfigOption::new("string", "descr", "some string").stable("1.0.0")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ConfigOption {
|
||||
name: String::from("number"),
|
||||
description: String::from("NA"),
|
||||
default_value: Value::Integer(999),
|
||||
constraint: Some(Validator::PositiveInteger),
|
||||
stability: Stability::Unstable,
|
||||
active: false,
|
||||
display_hint: DisplayHint::None,
|
||||
},
|
||||
ConfigOption::new("number", "NA", 999)
|
||||
.active(false)
|
||||
.constraint(Validator::PositiveInteger)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ConfigOption {
|
||||
name: String::from("number"),
|
||||
description: String::from("NA"),
|
||||
default_value: Value::Integer(999),
|
||||
constraint: Some(Validator::PositiveInteger),
|
||||
stability: Stability::Unstable,
|
||||
active: true,
|
||||
display_hint: DisplayHint::Hex,
|
||||
},
|
||||
ConfigOption::new("number", "NA", 999)
|
||||
.constraint_by(Some(Validator::PositiveInteger))
|
||||
.display_hint(DisplayHint::Hex)
|
||||
},],
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use super::{Error, snake_case, value::Value};
|
||||
|
||||
/// Configuration value validation functions.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(tag = "validator", content = "value", rename_all = "snake_case")]
|
||||
pub enum Validator {
|
||||
/// Only allow negative integers, i.e. any values less than 0.
|
||||
NegativeInteger,
|
||||
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
use super::Error;
|
||||
|
||||
/// Supported configuration value types.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
/// Booleans.
|
||||
Bool(bool),
|
||||
@ -15,6 +15,70 @@ pub enum Value {
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
Value::String(s) => serializer.serialize_str(&format!("\"{s}\"")),
|
||||
Value::Integer(n) => serializer.serialize_str(&format!("{n}")),
|
||||
Value::Bool(b) => serializer.serialize_str(&format!("{b}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Value {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct ValueVisitor;
|
||||
|
||||
impl serde::de::Visitor<'_> for ValueVisitor {
|
||||
type Value = String;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
formatter.write_str("a String representing the Value")
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(v.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
let str_repr = deserializer.deserialize_string(ValueVisitor)?;
|
||||
let str_repr = str_repr.as_str();
|
||||
|
||||
if let Some(remaining) = str_repr.strip_prefix("\"") {
|
||||
let s = &remaining[..remaining.len() - 1];
|
||||
return Ok(Value::String(s.to_string()));
|
||||
}
|
||||
|
||||
if str_repr == "true" {
|
||||
return Ok(Value::Bool(true));
|
||||
}
|
||||
|
||||
if str_repr == "false" {
|
||||
return Ok(Value::Bool(false));
|
||||
}
|
||||
|
||||
Ok(Value::Integer(str_repr.parse().map_err(
|
||||
|e: core::num::ParseIntError| serde::de::Error::custom(e.to_string()),
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do we want to handle negative values for non-decimal values?
|
||||
impl Value {
|
||||
pub(crate) fn parse_in_place(&mut self, s: &str) -> Result<(), Error> {
|
||||
@ -118,3 +182,31 @@ impl From<String> for Value {
|
||||
Value::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn deserialization_number() {
|
||||
assert_eq!(
|
||||
serde_yaml::from_str::<Value>("128").unwrap(),
|
||||
Value::Integer(128)
|
||||
);
|
||||
assert_eq!(
|
||||
serde_yaml::from_str::<Value>(&format!("{}", i128::MAX)).unwrap(),
|
||||
Value::Integer(i128::MAX)
|
||||
);
|
||||
assert_eq!(
|
||||
serde_yaml::from_str::<Value>(&format!("{}", i128::MIN)).unwrap(),
|
||||
Value::Integer(i128::MIN)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization_string() {
|
||||
let yml = "'\"Hello\"'";
|
||||
let value: Value = serde_yaml::from_str(yml).unwrap();
|
||||
assert_eq!(value, Value::String("Hello".to_string()));
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ pub use generate::{
|
||||
Error,
|
||||
Stability,
|
||||
generate_config,
|
||||
generate_config_from_yaml_definition,
|
||||
validator::Validator,
|
||||
value::Value,
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use esp_config::{ConfigOption, Validator, Value, generate_config};
|
||||
use esp_config::{Value, generate_config_from_yaml_definition};
|
||||
use esp_metadata::{Chip, Config};
|
||||
|
||||
fn main() -> Result<(), Box<dyn StdError>> {
|
||||
@ -12,51 +12,11 @@ fn main() -> Result<(), Box<dyn StdError>> {
|
||||
config.define_symbols();
|
||||
|
||||
// emit config
|
||||
let crate_config = generate_config(
|
||||
"esp_hal_embassy",
|
||||
&[
|
||||
ConfigOption::new(
|
||||
"low-power-wait",
|
||||
"Enables the lower-power wait if no tasks are ready to run on the \
|
||||
thread-mode executor. This allows the MCU to use less power if the workload allows. \
|
||||
Recommended for battery-powered systems. May impact analog performance.",
|
||||
true,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"timer-queue",
|
||||
"The flavour of the timer queue provided by this crate. Integrated \
|
||||
queues require the `executors` feature to be enabled.</p><p>If you use \
|
||||
embassy-executor, the `single-integrated` queue is recommended for ease of use, \
|
||||
while the `multiple-integrated` queue is recommended for performance. The \
|
||||
`multiple-integrated` option needs one timer per executor.</p><p>The `generic` \
|
||||
queue allows using embassy-time without the embassy executors.",
|
||||
if cfg!(feature = "executors") {
|
||||
"single-integrated"
|
||||
} else {
|
||||
"generic"
|
||||
},
|
||||
)
|
||||
.constraint(if cfg!(feature = "executors") {
|
||||
Validator::Enumeration(vec![
|
||||
String::from("generic"),
|
||||
String::from("single-integrated"),
|
||||
String::from("multiple-integrated"),
|
||||
])
|
||||
} else {
|
||||
Validator::Enumeration(vec![String::from("generic")])
|
||||
})
|
||||
.active(cfg!(feature = "executors")),
|
||||
ConfigOption::new(
|
||||
"generic-queue-size",
|
||||
"The capacity of the queue when the `generic` timer \
|
||||
queue flavour is selected.",
|
||||
64,
|
||||
)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
],
|
||||
true,
|
||||
true,
|
||||
);
|
||||
println!("cargo:rerun-if-changed=./esp_config.yml");
|
||||
let cfg_yaml = std::fs::read_to_string("./esp_config.yml")
|
||||
.expect("Failed to read esp_config.yml for esp-hal-embassy");
|
||||
let crate_config =
|
||||
generate_config_from_yaml_definition(&cfg_yaml, true, true, Some(config.clone())).unwrap();
|
||||
|
||||
println!("cargo:rustc-check-cfg=cfg(integrated_timers)");
|
||||
println!("cargo:rustc-check-cfg=cfg(single_queue)");
|
||||
|
47
esp-hal-embassy/esp_config.yml
Normal file
47
esp-hal-embassy/esp_config.yml
Normal file
@ -0,0 +1,47 @@
|
||||
crate: esp-hal-embassy
|
||||
|
||||
options:
|
||||
- name: low-power-wait
|
||||
description: "Enables the lower-power wait if no tasks are ready to run on the
|
||||
thread-mode executor. This allows the MCU to use less power if the workload allows.
|
||||
Recommended for battery-powered systems. May impact analog performance."
|
||||
default:
|
||||
- value: true
|
||||
|
||||
- name: timer-queue
|
||||
description: "The flavour of the timer queue provided by this crate. Integrated
|
||||
queues require the `executors` feature to be enabled.</p><p>If you use
|
||||
embassy-executor, the `single-integrated` queue is recommended for ease of use,
|
||||
while the `multiple-integrated` queue is recommended for performance. The
|
||||
`multiple-integrated` option needs one timer per executor.</p><p>The `generic`
|
||||
queue allows using embassy-time without the embassy executors."
|
||||
default:
|
||||
- if: 'ignore_feature_gates()'
|
||||
value: '"single-integrated"'
|
||||
- if: 'cargo_feature("executors")'
|
||||
value: '"single-integrated"'
|
||||
- if: 'true'
|
||||
value: '"generic"'
|
||||
constraints:
|
||||
- if: 'cargo_feature("executors") || ignore_feature_gates()'
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 'generic'
|
||||
- 'single-integrated'
|
||||
- 'multiple-integrated'
|
||||
- if: 'true'
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 'generic'
|
||||
stability: Unstable
|
||||
active: 'cargo_feature("executors") || ignore_feature_gates()'
|
||||
|
||||
- name: generic-queue-size
|
||||
description: The capacity of the queue when the `generic` timer queue flavour is selected.
|
||||
default:
|
||||
- value: 64
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
117
esp-hal/build.rs
117
esp-hal/build.rs
@ -8,7 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use esp_build::assert_unique_features;
|
||||
use esp_config::{ConfigOption, DisplayHint, Validator, Value, generate_config};
|
||||
use esp_config::{Value, generate_config_from_yaml_definition};
|
||||
use esp_metadata::{Chip, Config};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
@ -49,116 +49,11 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// emit config
|
||||
let cfg = generate_config(
|
||||
"esp_hal",
|
||||
&[
|
||||
ConfigOption::new(
|
||||
"place-spi-master-driver-in-ram",
|
||||
"Places the SPI master driver in RAM for better performance",
|
||||
false,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"place-switch-tables-in-ram",
|
||||
"Places switch-tables, some lookup tables and constants related to \
|
||||
interrupt handling into RAM - resulting in better performance but slightly more \
|
||||
RAM consumption.",
|
||||
true,
|
||||
)
|
||||
.stable("1.0.0-beta.0"),
|
||||
ConfigOption::new(
|
||||
"place-anon-in-ram",
|
||||
"Places anonymous symbols into RAM - resulting in better performance \
|
||||
at the cost of significant more RAM consumption. Best to be combined with \
|
||||
`place-switch-tables-in-ram`.",
|
||||
false,
|
||||
)
|
||||
.stable("1.0.0-beta.0"),
|
||||
// Ideally, we should be able to set any clock frequency for any chip. However,
|
||||
// currently only the 32 and C2 implements any sort of configurability, and
|
||||
// the rest have a fixed clock frequeny.
|
||||
ConfigOption::new(
|
||||
"xtal-frequency",
|
||||
"The frequency of the crystal oscillator, in MHz. Set to `auto` to \
|
||||
automatically detect the frequency. `auto` may not be able to identify the clock \
|
||||
frequency in some cases. Also, configuring a specific frequency may increase \
|
||||
performance slightly.",
|
||||
match chip {
|
||||
Chip::Esp32 | Chip::Esp32c2 => "auto",
|
||||
// The rest has only one option
|
||||
Chip::Esp32c3 | Chip::Esp32c6 | Chip::Esp32s2 | Chip::Esp32s3 => "40",
|
||||
Chip::Esp32h2 => "32",
|
||||
},
|
||||
)
|
||||
.constraint_by(match chip {
|
||||
Chip::Esp32 | Chip::Esp32c2 => Some(Validator::Enumeration(vec![
|
||||
String::from("auto"),
|
||||
String::from("26"),
|
||||
String::from("40"),
|
||||
])),
|
||||
// The rest has only one option
|
||||
_ => None,
|
||||
})
|
||||
.active([Chip::Esp32, Chip::Esp32c2].contains(&chip)),
|
||||
ConfigOption::new(
|
||||
"spi-address-workaround",
|
||||
"Enables a workaround for the issue where SPI in \
|
||||
half-duplex mode incorrectly transmits the address on a single line if the \
|
||||
data buffer is empty.",
|
||||
true,
|
||||
)
|
||||
.active(chip == Chip::Esp32),
|
||||
ConfigOption::new(
|
||||
"flip-link",
|
||||
"Move the stack to start of RAM to get zero-cost stack overflow protection.",
|
||||
false,
|
||||
)
|
||||
.active([Chip::Esp32c6, Chip::Esp32h2].contains(&chip)),
|
||||
// TODO: automate "enum of single choice" handling - they don't need
|
||||
// to be presented to the user
|
||||
ConfigOption::new("psram-mode", "SPIRAM chip mode", "quad")
|
||||
.constraint(Validator::Enumeration(
|
||||
if config
|
||||
.symbols()
|
||||
.iter()
|
||||
.any(|s| s.eq_ignore_ascii_case("octal_psram"))
|
||||
{
|
||||
vec![String::from("quad"), String::from("octal")]
|
||||
} else {
|
||||
vec![String::from("quad")]
|
||||
},
|
||||
))
|
||||
.active(
|
||||
config
|
||||
.symbols()
|
||||
.iter()
|
||||
.any(|s| s.eq_ignore_ascii_case("psram")),
|
||||
),
|
||||
// Rust's stack smashing protection configuration
|
||||
ConfigOption::new(
|
||||
"stack-guard-offset",
|
||||
"The stack guard variable will be placed this many bytes from \
|
||||
the stack's end.",
|
||||
4096,
|
||||
)
|
||||
.stable("1.0.0-beta.0"),
|
||||
ConfigOption::new(
|
||||
"stack-guard-value",
|
||||
"The value to be written to the stack guard variable.",
|
||||
0xDEED_BAAD,
|
||||
)
|
||||
.stable("1.0.0-beta.0")
|
||||
.display_hint(DisplayHint::Hex),
|
||||
ConfigOption::new(
|
||||
"impl-critical-section",
|
||||
"Provide a `critical-section` implementation. Note that if disabled, \
|
||||
you will need to provide a `critical-section` implementation which is \
|
||||
using `restore-state-u32`.",
|
||||
true,
|
||||
),
|
||||
],
|
||||
cfg!(feature = "unstable"),
|
||||
true,
|
||||
);
|
||||
println!("cargo:rerun-if-changed=./esp_config.yml");
|
||||
let cfg_yaml = std::fs::read_to_string("./esp_config.yml")
|
||||
.expect("Failed to read esp_config.yml for esp-hal");
|
||||
let cfg =
|
||||
generate_config_from_yaml_definition(&cfg_yaml, true, true, Some(config.clone())).unwrap();
|
||||
|
||||
// RISC-V and Xtensa devices each require some special handling and processing
|
||||
// of linker scripts:
|
||||
|
110
esp-hal/esp_config.yml
Normal file
110
esp-hal/esp_config.yml
Normal file
@ -0,0 +1,110 @@
|
||||
crate: esp-hal
|
||||
|
||||
options:
|
||||
- name: place-spi-master-driver-in-ram
|
||||
description: Places the SPI master driver in RAM for better performance
|
||||
default:
|
||||
- value: false
|
||||
|
||||
- name: place-switch-tables-in-ram
|
||||
description: "Places switch-tables, some lookup tables and constants related to
|
||||
interrupt handling into RAM - resulting in better performance but slightly more
|
||||
RAM consumption."
|
||||
default:
|
||||
- value: true
|
||||
stability: !Stable '1.0.0-beta.0'
|
||||
|
||||
- name: place-anon-in-ram
|
||||
description: "Places anonymous symbols into RAM - resulting in better performance
|
||||
at the cost of significant more RAM consumption. Best to be combined with
|
||||
`place-switch-tables-in-ram`."
|
||||
default:
|
||||
- value: false
|
||||
stability: !Stable '1.0.0-beta.0'
|
||||
|
||||
# Ideally, we should be able to set any clock frequency for any chip. However,
|
||||
# currently only the 32 and C2 implements any sort of configurability, and
|
||||
# the rest have a fixed clock frequency.
|
||||
- name: xtal-frequency
|
||||
description: "The frequency of the crystal oscillator, in MHz. Set to `auto` to
|
||||
automatically detect the frequency. `auto` may not be able to identify the clock
|
||||
frequency in some cases. Also, configuring a specific frequency may increase
|
||||
performance slightly."
|
||||
default:
|
||||
- if: 'chip == "esp32" || chip == "esp32c2"'
|
||||
value: '"auto"'
|
||||
- if: 'chip == "esp32c3" || chip == "esp32c6" || chip == "esp32s2" || chip == "esp32s3"'
|
||||
value: '"40"'
|
||||
- if: 'chip == "esp32h2"'
|
||||
value: '"32"'
|
||||
constraints:
|
||||
- if: 'chip == "esp32" || chip == "esp32c2"'
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 'auto'
|
||||
- '26'
|
||||
- '40'
|
||||
- if: 'chip == "esp32c3" || chip == "esp32c6" || chip == "esp32s2" || chip == "esp32s3"'
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- '40'
|
||||
- if: 'chip == "esp32h2"'
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- '32'
|
||||
|
||||
- name: spi-address-workaround
|
||||
description: "Enables a workaround for the issue where SPI in
|
||||
half-duplex mode incorrectly transmits the address on a single line if the
|
||||
data buffer is empty."
|
||||
default:
|
||||
- value: true
|
||||
active: 'chip == "esp32"'
|
||||
|
||||
- name: flip-link
|
||||
description: Move the stack to start of RAM to get zero-cost stack overflow protection.
|
||||
default:
|
||||
- value: false
|
||||
active: 'chip == "esp32c6" || chip == "esp32h2"'
|
||||
|
||||
- name: psram-mode
|
||||
description: SPIRAM chip mode
|
||||
default:
|
||||
- value: '"quad"'
|
||||
constraints:
|
||||
- if: 'feature("octal_psram")'
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 'quad'
|
||||
- 'octal'
|
||||
- if: '!feature("octal_psram")'
|
||||
type:
|
||||
validator: enumeration
|
||||
value:
|
||||
- 'quad'
|
||||
active: 'feature("psram")'
|
||||
|
||||
- name: stack-guard-offset
|
||||
description: The stack guard variable will be placed this many bytes from the stack's end.
|
||||
default:
|
||||
- value: 4096
|
||||
stability: !Stable '1.0.0-beta.0'
|
||||
active: 'true'
|
||||
|
||||
- name: stack-guard-value
|
||||
description: The value to be written to the stack guard variable.
|
||||
default:
|
||||
- value: 3740121773
|
||||
stability: !Stable '1.0.0-beta.0'
|
||||
display_hint: Hex
|
||||
|
||||
- name: impl-critical-section
|
||||
description: "Provide a `critical-section` implementation. Note that if disabled,
|
||||
you will need to provide a `critical-section` implementation which is
|
||||
using `restore-state-u32`."
|
||||
default:
|
||||
- value: true
|
@ -1,19 +1,14 @@
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
use esp_config::{ConfigOption, Validator, generate_config};
|
||||
use esp_config::generate_config_from_yaml_definition;
|
||||
|
||||
fn main() {
|
||||
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// emit config
|
||||
generate_config(
|
||||
"esp_ieee802154",
|
||||
&[
|
||||
ConfigOption::new("rx_queue_size", "Size of the RX queue in frames", 50)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
],
|
||||
true,
|
||||
true,
|
||||
);
|
||||
println!("cargo:rerun-if-changed=./esp_config.yml");
|
||||
let cfg_yaml = std::fs::read_to_string("./esp_config.yml")
|
||||
.expect("Failed to read esp_config.yml for esp-ieee802154");
|
||||
generate_config_from_yaml_definition(&cfg_yaml, true, true, None).unwrap();
|
||||
}
|
||||
|
10
esp-ieee802154/esp_config.yml
Normal file
10
esp-ieee802154/esp_config.yml
Normal file
@ -0,0 +1,10 @@
|
||||
crate: esp-ieee802154
|
||||
|
||||
options:
|
||||
- name: rx_queue_size
|
||||
description: Size of the RX queue in frames
|
||||
default:
|
||||
- value: 10
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
@ -1,6 +1,6 @@
|
||||
use std::error::Error;
|
||||
|
||||
use esp_config::{ConfigOption, Validator, generate_config};
|
||||
use esp_config::generate_config_from_yaml_definition;
|
||||
use esp_metadata::{Chip, Config};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
@ -64,169 +64,10 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
//
|
||||
// keep the defaults aligned with `esp_wifi_sys::include::*` e.g.
|
||||
// `esp_wifi_sys::include::CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM`
|
||||
generate_config(
|
||||
"esp_wifi",
|
||||
&[
|
||||
ConfigOption::new("rx_queue_size", "Size of the RX queue in frames", 5)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
ConfigOption::new("tx_queue_size", "Size of the TX queue in frames", 3)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
ConfigOption::new(
|
||||
"static_rx_buf_num",
|
||||
"WiFi static RX buffer number. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
10,
|
||||
)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
ConfigOption::new(
|
||||
"dynamic_rx_buf_num",
|
||||
"WiFi dynamic RX buffer number. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
32,
|
||||
)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
ConfigOption::new(
|
||||
"static_tx_buf_num",
|
||||
"WiFi static TX buffer number. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
0,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"dynamic_tx_buf_num",
|
||||
"WiFi dynamic TX buffer number. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
32,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"ampdu_rx_enable",
|
||||
"WiFi AMPDU RX feature enable flag. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
true,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"ampdu_tx_enable",
|
||||
"WiFi AMPDU TX feature enable flag. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
true,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"amsdu_tx_enable",
|
||||
"WiFi AMSDU TX feature enable flag. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
false,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"rx_ba_win",
|
||||
"WiFi Block Ack RX window size. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/\
|
||||
network/esp_wifi.html#_CPPv418wifi_init_config_t)",
|
||||
6,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"max_burst_size",
|
||||
"See [smoltcp's documentation]\
|
||||
(https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html\
|
||||
#structfield.max_burst_size)",
|
||||
1,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"country_code",
|
||||
"Country code. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/\
|
||||
wifi.html#wi-fi-country-code)",
|
||||
"CN",
|
||||
),
|
||||
ConfigOption::new(
|
||||
"country_code_operating_class",
|
||||
"If not 0: Operating Class table number. See [ESP-IDF Programming Guide]\
|
||||
(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/\
|
||||
wifi.html#wi-fi-country-code)",
|
||||
0,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"mtu",
|
||||
"MTU, see [smoltcp's documentation]\
|
||||
(https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html\
|
||||
#structfield.max_transmission_unit)",
|
||||
1492,
|
||||
)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
ConfigOption::new(
|
||||
"tick_rate_hz",
|
||||
"Tick rate of the internal task scheduler in hertz",
|
||||
100,
|
||||
)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
ConfigOption::new(
|
||||
"listen_interval",
|
||||
"Interval for station to listen to beacon from AP.
|
||||
The unit of listen interval is one beacon interval.
|
||||
For example, if beacon interval is 100 ms and listen interval is 3,
|
||||
the interval for station to listen to beacon is 300 ms",
|
||||
3,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"beacon_timeout",
|
||||
"For Station, If the station does not receive a beacon frame
|
||||
from the connected SoftAP during the inactive time, disconnect from SoftAP.
|
||||
Default 6s. Range 6-30",
|
||||
6,
|
||||
)
|
||||
.constraint(Validator::IntegerInRange(6..30)),
|
||||
ConfigOption::new(
|
||||
"ap_beacon_timeout",
|
||||
"For SoftAP, If the SoftAP doesn't receive any data from the connected STA
|
||||
during inactive time, the SoftAP will force deauth the STA. Default is 300s",
|
||||
300,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"failure_retry_cnt",
|
||||
"Number of connection retries station will do before moving to next AP.
|
||||
scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config.
|
||||
Note: Enabling this may cause connection time to increase incase best AP
|
||||
doesn't behave properly. Defaults to 1",
|
||||
1,
|
||||
)
|
||||
.constraint(Validator::PositiveInteger),
|
||||
ConfigOption::new(
|
||||
"scan_method",
|
||||
"0 = WIFI_FAST_SCAN, 1 = WIFI_ALL_CHANNEL_SCAN, defaults to 0",
|
||||
0,
|
||||
)
|
||||
.constraint(Validator::IntegerInRange(0..2)),
|
||||
ConfigOption::new(
|
||||
"dump_packets",
|
||||
"Dump packets via an info log statement",
|
||||
false,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"phy_enable_usb",
|
||||
"Keeps USB running when using WiFi.
|
||||
This allows debugging and log messages via USB Serial JTAG.
|
||||
Turn off for best WiFi performance.",
|
||||
true,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"phy_skip_calibration_after_deep_sleep",
|
||||
"Use PHY_RF_CAL_NONE after deep sleep.",
|
||||
false,
|
||||
),
|
||||
ConfigOption::new(
|
||||
"phy_full_calibration",
|
||||
"Use PHY_RF_CAL_FULL instead of PHY_RF_CAL_PARTIAL.",
|
||||
true,
|
||||
),
|
||||
],
|
||||
true,
|
||||
true,
|
||||
);
|
||||
println!("cargo:rerun-if-changed=./esp_config.yml");
|
||||
let cfg_yaml = std::fs::read_to_string("./esp_config.yml")
|
||||
.expect("Failed to read esp_config.yml for esp-wifi");
|
||||
generate_config_from_yaml_definition(&cfg_yaml, true, true, Some(config.clone())).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
180
esp-wifi/esp_config.yml
Normal file
180
esp-wifi/esp_config.yml
Normal file
@ -0,0 +1,180 @@
|
||||
crate: esp-wifi
|
||||
|
||||
options:
|
||||
- name: rx_queue_size
|
||||
description: Size of the RX queue in frames
|
||||
default:
|
||||
- value: 5
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: tx_queue_size
|
||||
description: Size of the TX queue in frames
|
||||
default:
|
||||
- value: 3
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: static_rx_buf_num
|
||||
description: "WiFi static RX buffer number. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: 10
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: dynamic_rx_buf_num
|
||||
description: "WiFi dynamic RX buffer number. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: 32
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: static_tx_buf_num
|
||||
description: "WiFi static TX buffer number. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: 0
|
||||
|
||||
- name: dynamic_tx_buf_num
|
||||
description: "WiFi dynamic TX buffer number. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: 32
|
||||
|
||||
- name: ampdu_rx_enable
|
||||
description: "WiFi AMPDU RX feature enable flag.
|
||||
See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: true
|
||||
|
||||
- name: ampdu_tx_enable
|
||||
description: "WiFi AMPDU TX feature enable flag. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: true
|
||||
|
||||
- name: amsdu_tx_enable
|
||||
description: "WiFi AMSDU TX feature enable flag. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: false
|
||||
|
||||
- name: rx_ba_win
|
||||
description: "WiFi Block Ack RX window size. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"
|
||||
default:
|
||||
- value: 6
|
||||
|
||||
- name: max_burst_size
|
||||
description: See [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_burst_size)
|
||||
default:
|
||||
- value: 1
|
||||
|
||||
- name: country_code
|
||||
description: "Country code. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)"
|
||||
default:
|
||||
- value: '"CN"'
|
||||
|
||||
- name: country_code_operating_class
|
||||
description: 'If not 0: Operating Class table number. See
|
||||
[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)'
|
||||
default:
|
||||
- value: 0
|
||||
|
||||
- name: mtu
|
||||
description: "MTU, see [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_transmission_unit)"
|
||||
default:
|
||||
- value: 1492
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: tick_rate_hz
|
||||
description: 'Tick rate of the internal task scheduler in hertz'
|
||||
default:
|
||||
- value: 100
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: listen_interval
|
||||
description: 'Interval for station to listen to beacon from AP.
|
||||
The unit of listen interval is one beacon interval.
|
||||
For example, if beacon interval is 100 ms and listen interval is 3,
|
||||
the interval for station to listen to beacon is 300 ms'
|
||||
default:
|
||||
- value: 3
|
||||
|
||||
- name: beacon_timeout
|
||||
description: 'For Station, If the station does not receive a beacon frame
|
||||
from the connected SoftAP during the inactive time, disconnect from SoftAP.
|
||||
Default 6s. Range 6-30'
|
||||
default:
|
||||
- value: 6
|
||||
constraints:
|
||||
- type:
|
||||
validator: integer_in_range
|
||||
value:
|
||||
start: 6
|
||||
end: 31
|
||||
|
||||
- name: ap_beacon_timeout
|
||||
description: "For SoftAP, If the SoftAP doesn't receive any data from the connected STA
|
||||
during inactive time, the SoftAP will force deauth the STA. Default is 300s"
|
||||
default:
|
||||
- value: 300
|
||||
|
||||
- name: failure_retry_cnt
|
||||
description: "Number of connection retries station will do before moving to next AP.
|
||||
scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config.
|
||||
Note: Enabling this may cause connection time to increase incase best AP
|
||||
doesn't behave properly. Defaults to 1"
|
||||
default:
|
||||
- value: 1
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: scan_method
|
||||
description: "0 = WIFI_FAST_SCAN, 1 = WIFI_ALL_CHANNEL_SCAN, defaults to 0"
|
||||
default:
|
||||
- value: 0
|
||||
constraints:
|
||||
- type:
|
||||
validator: integer_in_range
|
||||
value:
|
||||
start: 0
|
||||
end: 2
|
||||
|
||||
- name: dump_packets
|
||||
description: "Dump packets via an info log statement"
|
||||
default:
|
||||
- value: false
|
||||
|
||||
- name: phy_enable_usb
|
||||
description: "Keeps USB running when using WiFi.
|
||||
This allows debugging and log messages via USB Serial JTAG.
|
||||
Turn off for best WiFi performance."
|
||||
default:
|
||||
- value: true
|
||||
|
||||
- name: phy_skip_calibration_after_deep_sleep
|
||||
description: "Use PHY_RF_CAL_NONE after deep sleep."
|
||||
default:
|
||||
- value: false
|
||||
|
||||
- name: phy_full_calibration
|
||||
description: "Use PHY_RF_CAL_FULL instead of PHY_RF_CAL_PARTIAL."
|
||||
default:
|
||||
- value: true
|
||||
|
||||
checks:
|
||||
- 'ESP_WIFI_CONFIG_RX_BA_WIN < ESP_WIFI_CONFIG_DYNAMIC_RX_BUF_NUM'
|
||||
- 'ESP_WIFI_CONFIG_RX_BA_WIN < (ESP_WIFI_CONFIG_STATIC_RX_BUF_NUM * 2)'
|
@ -227,21 +227,6 @@ pub(crate) const CONFIG: config::EspWifiConfig = config::EspWifiConfig {
|
||||
scan_method: esp_config_int!(u32, "ESP_WIFI_CONFIG_SCAN_METHOD"),
|
||||
};
|
||||
|
||||
// Validate the configuration at compile time
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
const _: () = {
|
||||
// We explicitely use `core` assert here because this evaluation happens at
|
||||
// compile time and won't bloat the binary
|
||||
core::assert!(
|
||||
CONFIG.rx_ba_win < CONFIG.dynamic_rx_buf_num,
|
||||
"WiFi configuration check: rx_ba_win should not be larger than dynamic_rx_buf_num!"
|
||||
);
|
||||
core::assert!(
|
||||
CONFIG.rx_ba_win < (CONFIG.static_rx_buf_num * 2),
|
||||
"WiFi configuration check: rx_ba_win should not be larger than double of the static_rx_buf_num!"
|
||||
);
|
||||
};
|
||||
|
||||
type TimeBase = PeriodicTimer<'static, Blocking>;
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user