diff --git a/docs/bootload.md b/docs/bootload.md index 692f78e..94d67b7 100644 --- a/docs/bootload.md +++ b/docs/bootload.md @@ -111,15 +111,22 @@ in the release or ZIP file. Since the bin files do not contain where they should be flashed you can either set the target on the commandline or leave it blank to select. -The following command will promt you to select what target to flash the firmware +The following command will prompt you to select what target to flash the firmware bin to: ```text cfcli bootload flash --bin firmware.bin ``` +If you want to flash one binary to one target without an interactive prompt, +select that target with `--targets`: + +```text +cfcli bootload flash --bin lighthouse.bin --targets bcLighthouse4-fw +``` + It's also possible to set the target directly and also to flash multiple bins. The command -below will promt you for where you want to flash ```color-led-firmware.bin``` and will +below will prompt you for where you want to flash ```color-led-firmware.bin``` and will then flash it to the selected target as well as flashing the ```nrf51-firmware.bin``` to the nRF51. diff --git a/src/main.rs b/src/main.rs index 15f2f27..0fc7415 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,88 @@ use modules::settings; include!("cli.rs"); +fn single_explicit_target_for_bare_bin( + bin: &Option>>, + targets: &Option>, +) -> Option { + let bin_map = bin.as_ref()?; + if bin_map.len() != 1 { + return None; + } + + let (_bin_path, selected_target) = bin_map.iter().next()?; + if selected_target.is_some() { + return None; + } + + let target_arg = match targets { + Some(Some(target_arg)) => target_arg, + _ => return None, + }; + + let mut target_names = target_arg + .split(',') + .map(str::trim) + .filter(|target| !target.is_empty()); + let target = target_names.next()?; + if target_names.next().is_some() { + return None; + } + + Some(target.to_string()) +} + +fn unsupported_flash_targets(selected: &[String]) -> Vec { + let supported = bootloader::get_hardcoded_list_of_targets(); + + selected + .iter() + .filter(|target| !supported.contains(&target.as_str())) + .cloned() + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn single_bare_bin_uses_single_explicit_target() { + let mut bin = HashMap::new(); + bin.insert("lighthouse.bin".to_string(), None); + let targets = Some(Some("bcLighthouse4-fw".to_string())); + + assert_eq!( + single_explicit_target_for_bare_bin(&Some(bin), &targets), + Some("bcLighthouse4-fw".to_string()) + ); + } + + #[test] + fn single_bare_bin_does_not_use_multiple_explicit_targets() { + let mut bin = HashMap::new(); + bin.insert("lighthouse.bin".to_string(), None); + let targets = Some(Some("bcLighthouse4-fw,stm32-fw".to_string())); + + assert_eq!(single_explicit_target_for_bare_bin(&Some(bin), &targets), None); + } + + #[test] + fn keyed_bin_does_not_get_rewritten_from_targets() { + let mut bin = HashMap::new(); + bin.insert("bcLighthouse4-fw".to_string(), Some("lighthouse.bin".to_string())); + let targets = Some(Some("bcLighthouse4-fw".to_string())); + + assert_eq!(single_explicit_target_for_bare_bin(&Some(bin), &targets), None); + } + + #[test] + fn unsupported_flash_targets_reports_unknown_targets() { + let selected = vec!["stm32ohnooo-fw".to_string(), "stm32-fw".to_string()]; + + assert_eq!(unsupported_flash_targets(&selected), vec!["stm32ohnooo-fw".to_string()]); + } +} impl MemoryTypeArg { /// Convert to the `crazyflie_lib` memory type. Defined here (not on the @@ -1367,19 +1449,25 @@ async fn run() -> Result<()> { // until we reach the deck flashing stage. let bin_with_selections = { let mut result = HashMap::new(); + let target_from_single_bare_bin = + single_explicit_target_for_bare_bin(¶ms.bin, ¶ms.targets); if let Some(bin_map) = ¶ms.bin { for (key, value_opt) in bin_map.iter() { let (k,v) = match (key, value_opt) { (k, Some(v)) => (k.clone(), v.clone()), (k, None) => { - require_arg(non_interactive, "--bin target=file")?; - let selected_target = Select::new( - &format!("Select target for [{}]:", k), - bootloader::get_hardcoded_list_of_targets() - ) - .prompt() - .map_err(|_| anyhow::anyhow!("No binary selected"))?; - (selected_target.to_string(), k.to_string()) + if let Some(selected_target) = &target_from_single_bare_bin { + (selected_target.clone(), k.to_string()) + } else { + require_arg(non_interactive, "--bin target=file (or: --targets for a single bare --bin)")?; + let selected_target = Select::new( + &format!("Select target for [{}]:", k), + bootloader::get_hardcoded_list_of_targets() + ) + .prompt() + .map_err(|_| anyhow::anyhow!("No binary selected"))?; + (selected_target.to_string(), k.to_string()) + } } }; result.insert(k, v); @@ -1444,6 +1532,17 @@ async fn run() -> Result<()> { None => upgrade.get_target_and_types(), }; + if matches!(¶ms.targets, Some(Some(_))) { + let unsupported_targets = unsupported_flash_targets(&selected_target_and_types); + if !unsupported_targets.is_empty() { + bail!(CliError::InvalidValue(format!( + "unknown flash target(s): {}. Valid targets: {}", + unsupported_targets.join(", "), + bootloader::get_hardcoded_list_of_targets().join(", ") + ))); + } + } + upgrade.filter_targets(&selected_target_and_types); if upgrade.get_target_and_types().is_empty() {