Skip to content

Commit f247b40

Browse files
authored
Remove flip-link feature, replace by flip-link option (#3001)
* Fix renamed config options * Make `flip-link` feature into a config-option * Remove `psram-quad`/`psram-octal` - replaced by `psram` * Changelogs * Fix * Add `esp_config::Validator::Enumeration` * Fix HIL-tests * HIL: use octal psram for the S3 * Fix xtask * xtask * examples * Make `spi-address-workaround` only available on ESP32 * Always allow `cfg(spi_address_workaround)` * Make config-options valid for all targets
1 parent e842ec4 commit f247b40

File tree

30 files changed

+290
-181
lines changed

30 files changed

+290
-181
lines changed

.github/workflows/hil.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,13 @@ jobs:
118118
ldproxy: false
119119
version: 1.84.0.0
120120

121+
# remove once we have a proper solution for target specific configs
122+
- name: Build tests ESP32S3
123+
if: ${{ matrix.target.soc == 'esp32s3'}}
124+
run: ESP_HAL_CONFIG_PSRAM_MODE="octal" cargo xtask build-tests ${{ matrix.target.soc }}
125+
121126
- name: Build tests
127+
if: ${{ matrix.target.soc != 'esp32s3'}}
122128
run: cargo xtask build-tests ${{ matrix.target.soc }}
123129

124130
- uses: actions/upload-artifact@v4

esp-config/src/generate.rs

Lines changed: 148 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ use std::{
33
env,
44
fmt::{self, Write as _},
55
fs,
6+
io::Write,
67
ops::Range,
78
path::PathBuf,
89
};
910

1011
const DOC_TABLE_HEADER: &str = r#"
11-
| Name | Description | Default value |
12-
|------|-------------|---------------|
12+
| Name | Description | Default value | Allowed value |
13+
|------|-------------|---------------|---------------|
1314
"#;
1415

1516
const SELECTED_TABLE_HEADER: &str = r#"
@@ -155,6 +156,8 @@ pub enum Validator {
155156
PositiveInteger,
156157
/// Ensure that an integer value falls within the specified range.
157158
IntegerInRange(Range<i128>),
159+
/// String-Enumeration. Only allows one of the given Strings.
160+
Enumeration(Vec<String>),
158161
/// A custom validation function to run against any supported value type.
159162
Custom(Box<dyn Fn(&Value) -> Result<(), Error>>),
160163
}
@@ -166,11 +169,59 @@ impl Validator {
166169
Validator::NonNegativeInteger => non_negative_integer(value)?,
167170
Validator::PositiveInteger => positive_integer(value)?,
168171
Validator::IntegerInRange(range) => integer_in_range(range, value)?,
172+
Validator::Enumeration(values) => enumeration(values, value)?,
169173
Validator::Custom(validator_fn) => validator_fn(value)?,
170174
}
171175

172176
Ok(())
173177
}
178+
179+
fn description(&self) -> Option<String> {
180+
match self {
181+
Validator::NegativeInteger => Some(String::from("Negative integer")),
182+
Validator::NonNegativeInteger => Some(String::from("Positive integer or 0")),
183+
Validator::PositiveInteger => Some(String::from("Positive integer")),
184+
Validator::IntegerInRange(range) => {
185+
Some(format!("Integer in range {}..{}", range.start, range.end))
186+
}
187+
Validator::Enumeration(values) => Some(format!("Any of {:?}", values)),
188+
Validator::Custom(_) => None,
189+
}
190+
}
191+
192+
fn emit_cargo_extras<W: Write>(&self, stdout: &mut W, config_key: &str) {
193+
match self {
194+
Validator::Enumeration(values) => {
195+
let config_key = snake_case(config_key);
196+
for value in values {
197+
writeln!(
198+
stdout,
199+
"cargo:rustc-check-cfg=cfg({config_key}_{})",
200+
snake_case(value)
201+
)
202+
.ok();
203+
}
204+
}
205+
_ => (),
206+
}
207+
}
208+
}
209+
210+
fn enumeration(values: &Vec<String>, value: &Value) -> Result<(), Error> {
211+
if let Value::String(value) = value {
212+
if !values.contains(value) {
213+
return Err(Error::validation(format!(
214+
"Expected one of {:?}, found '{}'",
215+
values, value
216+
)));
217+
}
218+
219+
Ok(())
220+
} else {
221+
return Err(Error::parse(
222+
"Validator::Enumeration can only be used with string values",
223+
));
224+
}
174225
}
175226

176227
fn negative_integer(value: &Value) -> Result<(), Error> {
@@ -252,13 +303,22 @@ pub fn generate_config(
252303
crate_name: &str,
253304
config: &[(&str, &str, Value, Option<Validator>)],
254305
emit_md_tables: bool,
306+
) -> HashMap<String, Value> {
307+
generate_config_internal(&mut std::io::stdout(), crate_name, config, emit_md_tables)
308+
}
309+
310+
pub fn generate_config_internal<W: Write>(
311+
stdout: &mut W,
312+
crate_name: &str,
313+
config: &[(&str, &str, Value, Option<Validator>)],
314+
emit_md_tables: bool,
255315
) -> HashMap<String, Value> {
256316
// Only rebuild if `build.rs` changed. Otherwise, Cargo will rebuild if any
257317
// other file changed.
258-
println!("cargo:rerun-if-changed=build.rs");
318+
writeln!(stdout, "cargo:rerun-if-changed=build.rs").ok();
259319

260320
#[cfg(not(test))]
261-
env_change_work_around();
321+
env_change_work_around(stdout);
262322

263323
let mut doc_table = String::from(DOC_TABLE_HEADER);
264324
let mut selected_config = String::from(SELECTED_TABLE_HEADER);
@@ -281,7 +341,7 @@ pub fn generate_config(
281341
})
282342
.collect::<HashMap<_, _>>();
283343

284-
let mut configs = create_config(&prefix, config, &mut doc_table);
344+
let mut configs = create_config(stdout, &prefix, config, &mut doc_table);
285345
capture_from_env(&prefix, &mut configs);
286346

287347
for (name, value) in configs.iter() {
@@ -290,7 +350,18 @@ pub fn generate_config(
290350
}
291351
}
292352

293-
emit_configuration(&prefix, &configs, &mut selected_config);
353+
let validators: HashMap<String, &Validator> = config
354+
.iter()
355+
.filter_map(|(name, _, _, validator)| {
356+
if validator.is_some() {
357+
Some((snake_case(name), validator.as_ref().unwrap()))
358+
} else {
359+
None
360+
}
361+
})
362+
.collect();
363+
364+
emit_configuration(stdout, &prefix, &configs, &validators, &mut selected_config);
294365

295366
if emit_md_tables {
296367
let file_name = snake_case(crate_name);
@@ -304,7 +375,7 @@ pub fn generate_config(
304375
// This can be removed when https://github.com/rust-lang/cargo/pull/14058 is merged.
305376
// Unlikely to work on projects in workspaces
306377
#[cfg(not(test))]
307-
fn env_change_work_around() {
378+
fn env_change_work_around<W: Write>(stdout: &mut W) {
308379
let mut out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
309380

310381
// We clean out_dir by removing all trailing directories, until it ends with
@@ -319,37 +390,53 @@ fn env_change_work_around() {
319390
let dotcargo = out_dir.join(".cargo/");
320391
if dotcargo.exists() {
321392
if dotcargo.join("config.toml").exists() {
322-
println!(
393+
writeln!(
394+
stdout,
323395
"cargo:rerun-if-changed={}",
324396
dotcargo.join("config.toml").display()
325-
);
397+
)
398+
.ok();
326399
}
327400
if dotcargo.join("config").exists() {
328-
println!(
401+
writeln!(
402+
stdout,
329403
"cargo:rerun-if-changed={}",
330404
dotcargo.join("config").display()
331-
);
405+
)
406+
.ok();
332407
}
333408
}
334409
}
335410

336-
fn create_config(
411+
fn create_config<W: Write>(
412+
stdout: &mut W,
337413
prefix: &str,
338414
config: &[(&str, &str, Value, Option<Validator>)],
339415
doc_table: &mut String,
340416
) -> HashMap<String, Value> {
341417
let mut configs = HashMap::new();
342418

343-
for (name, description, default, _validator) in config {
419+
for (name, description, default, validator) in config {
344420
let name = format!("{prefix}{}", screaming_snake_case(name));
421+
let allowed_values = if let Some(validator) = validator {
422+
validator.description()
423+
} else {
424+
None
425+
}
426+
.unwrap_or(String::from("-"));
427+
345428
configs.insert(name.clone(), default.clone());
346429

347430
// Write documentation table line:
348431
let default = default.to_string();
349-
writeln!(doc_table, "|**{name}**|{description}|{default}|").unwrap();
432+
writeln!(
433+
doc_table,
434+
"|**{name}**|{description}|{default}|{allowed_values}"
435+
)
436+
.unwrap();
350437

351438
// Rebuild if config environment variable changed:
352-
println!("cargo:rerun-if-env-changed={name}");
439+
writeln!(stdout, "cargo:rerun-if-env-changed={name}").ok();
353440
}
354441

355442
configs
@@ -382,25 +469,38 @@ fn capture_from_env(prefix: &str, configs: &mut HashMap<String, Value>) {
382469
}
383470
}
384471

385-
fn emit_configuration(
472+
fn emit_configuration<W: Write>(
473+
stdout: &mut W,
386474
prefix: &str,
387475
configs: &HashMap<String, Value>,
476+
validators: &HashMap<String, &Validator>,
388477
selected_config: &mut String,
389478
) {
390479
for (name, value) in configs.iter() {
391480
let cfg_name = snake_case(name.trim_start_matches(prefix));
392-
println!("cargo:rustc-check-cfg=cfg({cfg_name})");
481+
writeln!(stdout, "cargo:rustc-check-cfg=cfg({cfg_name})").ok();
393482

394483
if let Value::Bool(true) = value {
395-
println!("cargo:rustc-cfg={cfg_name}");
484+
writeln!(stdout, "cargo:rustc-cfg={cfg_name}").ok();
485+
}
486+
487+
if let Value::String(value) = value {
488+
if let Some(Validator::Enumeration(_)) = validators.get(&cfg_name) {
489+
let value = format!("{}_{}", cfg_name, snake_case(value));
490+
writeln!(stdout, "cargo:rustc-cfg={value}").ok();
491+
}
396492
}
397493

398494
let value = value.to_string();
399495

400496
// Values that haven't been seen will be output here with the default value:
401-
println!("cargo:rustc-env={}={}", name, value);
497+
writeln!(stdout, "cargo:rustc-env={}={}", name, value).ok();
402498
writeln!(selected_config, "|**{name}**|{value}|").unwrap();
403499
}
500+
501+
for (name, validator) in validators {
502+
validator.emit_cargo_extras(stdout, &name);
503+
}
404504
}
405505

406506
fn write_config_tables(prefix: &str, doc_table: String, selected_config: String) {
@@ -696,4 +796,33 @@ mod test {
696796
},
697797
);
698798
}
799+
800+
#[test]
801+
fn enumeration_validator() {
802+
let mut stdout = Vec::new();
803+
temp_env::with_vars([("ESP_TEST_CONFIG_SOME_KEY", Some("variant-0"))], || {
804+
generate_config_internal(
805+
&mut stdout,
806+
"esp-test",
807+
&[(
808+
"some-key",
809+
"NA",
810+
Value::String("variant-0".to_string()),
811+
Some(Validator::Enumeration(vec![
812+
"variant-0".to_string(),
813+
"variant-1".to_string(),
814+
])),
815+
)],
816+
false,
817+
);
818+
});
819+
820+
let cargo_lines: Vec<&str> = std::str::from_utf8(&stdout).unwrap().lines().collect();
821+
println!("{:#?}", cargo_lines);
822+
assert!(cargo_lines.contains(&"cargo:rustc-check-cfg=cfg(some_key)"));
823+
assert!(cargo_lines.contains(&"cargo:rustc-env=ESP_TEST_CONFIG_SOME_KEY=variant-0"));
824+
assert!(cargo_lines.contains(&"cargo:rustc-check-cfg=cfg(some_key_variant_0)"));
825+
assert!(cargo_lines.contains(&"cargo:rustc-check-cfg=cfg(some_key_variant_1)"));
826+
assert!(cargo_lines.contains(&"cargo:rustc-cfg=some_key_variant_0"));
827+
}
699828
}

esp-hal/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828

2929
- `Async` drivers are no longer `Send` (#2980)
3030
- GPIO drivers now take configuration structs, and their constructors are fallible (#2990)
31+
- `flip-link` feature is now a config option
32+
- `flip-link` feature is now a config option (`ESP_HAL_CONFIG_FLIP_LINK`)
33+
34+
- `flip-link` feature is now a config option (`ESP_HAL_CONFIG_FLIP_LINK`) (#3001)
35+
36+
- Removed features `psram-quad` and `psram-octal` - replaced by `psram` and the `ESP_HAL_CONFIG_PSRAM_MODE` (`quad`/`octal`) (#3001)
3137

3238
### Fixed
3339

esp-hal/Cargo.toml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,6 @@ esp32s2 = ["dep:esp32s2", "portable-atomic/critical-section", "procmacros/has-ul
120120
# Target the ESP32-S3.
121121
esp32s3 = ["dep:esp32s3", "procmacros/has-ulp-core", "procmacros/rtc-slow", "usb-otg", "xtensa-lx-rt/esp32s3"]
122122

123-
#! ### RISC-V Exclusive Feature Flags
124-
## Move the stack to start of RAM to get zero-cost stack overflow protection
125-
## (ESP32-C6 and ESPS32-H2 only!).
126-
flip-link = ["esp-riscv-rt/fix-sp"]
127-
128123
#! ### Trait Implementation Feature Flags
129124
## Implement `defmt::Format` on certain types.
130125
defmt = [
@@ -145,11 +140,8 @@ defmt = [
145140
]
146141

147142
#! ### PSRAM Feature Flags
148-
## Use externally connected Quad PSRAM
149-
quad-psram = []
150-
151-
## Use externally connected Octal RAM
152-
octal-psram = []
143+
## Use externally connected PSRAM (`quad` by default, can be configured to `octal` via ESP_HAL_CONFIG_PSRAM_MODE)
144+
psram = []
153145

154146
# This feature is intended for testing; you probably don't want to enable it:
155147
ci = ["defmt", "bluetooth"]

esp-hal/MIGRATING-0.23.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,41 @@ Use `DataMode::SingleTwoDataLines` to get the previous behavior.
128128

129129
`Spi` now offers both, `with_mosi` and `with_sio0`. Consider using `with_sio` for half-duplex SPI except for [DataMode::SingleTwoDataLines] or for a mixed-bus.
130130

131+
## Removed `flip-link` Feature
132+
133+
The `flip-link` feature is removed and replaced by the `ESP_HAL_CONFIG_FLIP_LINK` option.
134+
135+
Cargo.toml
136+
```diff
137+
- esp-hal = { version = "0.23.0", features = ["flip-link"]}
138+
+ esp-hal = "0.23.0"
139+
```
140+
141+
config/config.toml
142+
```diff
143+
[env]
144+
+ ESP_HAL_CONFIG_FLIP_LINK = "true"
145+
```
146+
147+
## Removed `psram-quad`/`prsram-octal` Feature
148+
149+
The features `psram-quad`/`prsram-octal` are replaced by a single `psram` feature and an additional config option (`ESP_HAL_CONFIG_PSRAM_MODE`).
150+
151+
`ESP_HAL_CONFIG_PSRAM_MODE` defaults to `quad` and (for ESP32-S3) also allows `octal`.
152+
153+
Cargo.toml
154+
```diff
155+
- esp-hal = { version = "0.23.0", features = ["psram-octal"]}
156+
+ esp-hal = { version = "0.23.0", features = ["psram"]}
157+
```
158+
159+
config/config.toml
160+
```diff
161+
[env]
162+
+ ESP_HAL_CONFIG_PSRAM_MODE = "octal"
163+
```
164+
165+
131166
## UART halves have their configuration split too
132167

133168
`Uart::Config` structure now contains separate `RxConfig` and `TxConfig`:

0 commit comments

Comments
 (0)