diff options
| author | Côme ALLART <[email protected]> | 2021-08-27 11:09:27 +0200 |
|---|---|---|
| committer | Côme ALLART <[email protected]> | 2021-08-27 11:09:27 +0200 |
| commit | 022b8092485c39cd68ad4e259ced5253b8a59460 (patch) | |
| tree | 5f46664ba3947af8513e6ef63ca99a99db81c7b8 /stm32-gen-features/src | |
| parent | 1e1cd0506aa655456d7cbf7d6915b46abf7829e5 (diff) | |
refactor(gen_features): use Rust instead of Python
Done for /embassy-stm32 only
The new generator is in /stm32-gen-features
/stm32-metapac could/should be added too
A CI check "generated features up to date" could/should be performed
Diffstat (limited to 'stm32-gen-features/src')
| -rw-r--r-- | stm32-gen-features/src/lib.rs | 177 | ||||
| -rw-r--r-- | stm32-gen-features/src/main.rs | 18 |
2 files changed, 195 insertions, 0 deletions
diff --git a/stm32-gen-features/src/lib.rs b/stm32-gen-features/src/lib.rs new file mode 100644 index 000000000..b20a1ba4a --- /dev/null +++ b/stm32-gen-features/src/lib.rs | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | use std::{ | ||
| 2 | collections::HashMap, | ||
| 3 | path::{Path, PathBuf}, | ||
| 4 | }; | ||
| 5 | |||
| 6 | const SUPPORTED_FAMILIES: [&str; 8] = [ | ||
| 7 | "STM32F0", | ||
| 8 | "STM32F4", | ||
| 9 | "STM32G0", | ||
| 10 | "STM32L0", | ||
| 11 | "STM32L4", | ||
| 12 | "STM32H7", | ||
| 13 | "STM32WB55", | ||
| 14 | "STM32WL55", | ||
| 15 | ]; | ||
| 16 | |||
| 17 | const SEPARATOR_START: &str = "# BEGIN GENERATED FEATURES\n"; | ||
| 18 | const SEPARATOR_END: &str = "# END GENERATED FEATURES\n"; | ||
| 19 | const HELP: &str = "# Generated by stm32-gen-features. DO NOT EDIT.\n"; | ||
| 20 | |||
| 21 | /// True if the chip named `name` is supported else false | ||
| 22 | fn is_supported(name: &str) -> bool { | ||
| 23 | SUPPORTED_FAMILIES | ||
| 24 | .iter() | ||
| 25 | .any(|family| name.starts_with(family)) | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Get the yaml file names and the associated chip names for supported chips | ||
| 29 | /// | ||
| 30 | /// Print errors to `stderr` when something is returned by the glob but is not in the returned | ||
| 31 | /// [`Vec`] | ||
| 32 | fn supported_chip_yaml_files_with_names() -> Vec<(PathBuf, String)> { | ||
| 33 | glob::glob("../stm32-data/data/chips/*.yaml") | ||
| 34 | .expect("bad glob pattern") | ||
| 35 | .filter_map(|entry| entry.map_err(|e| eprintln!("{:?}", e)).ok()) | ||
| 36 | .filter_map(|entry| { | ||
| 37 | if let Some(name) = entry.file_stem().and_then(|stem| stem.to_str()) { | ||
| 38 | if is_supported(name) { | ||
| 39 | let owned_name = name.to_lowercase(); | ||
| 40 | Some((entry, owned_name)) | ||
| 41 | } else { | ||
| 42 | eprintln!("{} is not supported", name); | ||
| 43 | None | ||
| 44 | } | ||
| 45 | } else { | ||
| 46 | eprintln!("{:?} is not a regural file", entry); | ||
| 47 | None | ||
| 48 | } | ||
| 49 | }) | ||
| 50 | .collect() | ||
| 51 | } | ||
| 52 | |||
| 53 | /// Get the list of the cores of a chip by its associated file | ||
| 54 | /// | ||
| 55 | /// # Panic | ||
| 56 | /// Panics if the file does not exist or if it contains yaml syntax errors | ||
| 57 | /// | ||
| 58 | /// # None | ||
| 59 | /// Returns none if "cores" is not an array | ||
| 60 | fn chip_cores(path: &Path) -> Option<Vec<yaml_rust::Yaml>> { | ||
| 61 | let file_contents = std::fs::read_to_string(path).unwrap(); | ||
| 62 | let doc = &yaml_rust::YamlLoader::load_from_str(&file_contents).unwrap()[0]; | ||
| 63 | doc["cores"].as_vec().cloned() | ||
| 64 | } | ||
| 65 | |||
| 66 | /// Load the list of chips | ||
| 67 | /// | ||
| 68 | /// # Panic | ||
| 69 | /// Panics if a file contains yaml syntax errors or if a value does not have a consistent type | ||
| 70 | pub fn load_chip_list() -> HashMap<String, Vec<String>> { | ||
| 71 | let mut result = HashMap::new(); | ||
| 72 | for (path, name) in supported_chip_yaml_files_with_names() { | ||
| 73 | let cores = chip_cores(&path).unwrap_or_else(|| panic!("{}[cores] is not an array", name)); | ||
| 74 | if cores.len() > 1 { | ||
| 75 | for (i, core) in cores.into_iter().enumerate() { | ||
| 76 | let core_name = core["name"] | ||
| 77 | .as_str() | ||
| 78 | .unwrap_or_else(|| panic!("{}[cores][{}][name] is not a string", name, i)); | ||
| 79 | let key = format!("{}_{}", name, core_name); | ||
| 80 | let value = vec![format!("stm32-metapac/{}_{}", name, core_name)]; | ||
| 81 | result.insert(key, value); | ||
| 82 | } | ||
| 83 | } else { | ||
| 84 | let value = vec![format!("stm32-metapac/{}", &name)]; | ||
| 85 | result.insert(name, value); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | result | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Get contents before and after generated contents | ||
| 92 | /// | ||
| 93 | /// # Panic | ||
| 94 | /// Panics when a separator cound not be not found | ||
| 95 | fn split_cargo_toml_contents(contents: &str) -> (&str, &str) { | ||
| 96 | let (before, remainder) = contents | ||
| 97 | .split_once(SEPARATOR_START) | ||
| 98 | .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_START)); | ||
| 99 | let (_, after) = remainder | ||
| 100 | .split_once(SEPARATOR_END) | ||
| 101 | .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_END)); | ||
| 102 | |||
| 103 | (before, after) | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Generates new contents for Cargo.toml | ||
| 107 | /// | ||
| 108 | /// # Panic | ||
| 109 | /// Panics when a separator cound not be not found | ||
| 110 | pub fn generate_cargo_toml_file( | ||
| 111 | previous_text: &str, | ||
| 112 | new_contents: &HashMap<String, Vec<String>>, | ||
| 113 | ) -> String { | ||
| 114 | let (before, after) = split_cargo_toml_contents(previous_text); | ||
| 115 | let generated_content = toml::to_string(new_contents).unwrap(); | ||
| 116 | before.to_owned() + SEPARATOR_START + HELP + &generated_content + SEPARATOR_END + after | ||
| 117 | } | ||
| 118 | |||
| 119 | #[cfg(test)] | ||
| 120 | mod tests { | ||
| 121 | use super::*; | ||
| 122 | |||
| 123 | #[test] | ||
| 124 | fn stm32f407vg_is_supported() { | ||
| 125 | assert!(is_supported("STM32F407VG")) | ||
| 126 | } | ||
| 127 | |||
| 128 | #[test] | ||
| 129 | fn abcdef_is_not_supported() { | ||
| 130 | assert!(!is_supported("ABCDEF")) | ||
| 131 | } | ||
| 132 | |||
| 133 | #[test] | ||
| 134 | fn stm32f407vg_yaml_file_exists() { | ||
| 135 | assert!(supported_chip_yaml_files_with_names() | ||
| 136 | .into_iter() | ||
| 137 | .any(|(path, name)| { | ||
| 138 | name == "stm32f407vg" | ||
| 139 | && path.to_str() == Some("../stm32-data/data/chips/STM32F407VG.yaml") | ||
| 140 | })) | ||
| 141 | } | ||
| 142 | |||
| 143 | #[test] | ||
| 144 | fn keeps_text_around_separators() { | ||
| 145 | let initial = "\ | ||
| 146 | before | ||
| 147 | # BEGIN GENERATED FEATURES | ||
| 148 | # END GENERATED FEATURES | ||
| 149 | after | ||
| 150 | "; | ||
| 151 | |||
| 152 | let expected = "\ | ||
| 153 | before | ||
| 154 | # BEGIN GENERATED FEATURES | ||
| 155 | # Generated by stm32-gen-features. DO NOT EDIT. | ||
| 156 | a = [\"b\"] | ||
| 157 | # END GENERATED FEATURES | ||
| 158 | after | ||
| 159 | "; | ||
| 160 | |||
| 161 | let map = HashMap::from([(String::from("a"), vec![String::from("b")])]); | ||
| 162 | assert_eq!(generate_cargo_toml_file(initial, &map), expected); | ||
| 163 | } | ||
| 164 | |||
| 165 | #[test] | ||
| 166 | #[should_panic] | ||
| 167 | fn does_not_generate_if_separators_are_missing() { | ||
| 168 | let initial = "\ | ||
| 169 | before | ||
| 170 | # END GENERATED FEATURES | ||
| 171 | after | ||
| 172 | "; | ||
| 173 | |||
| 174 | let map = HashMap::from([(String::from("a"), vec![String::from("b")])]); | ||
| 175 | generate_cargo_toml_file(initial, &map); | ||
| 176 | } | ||
| 177 | } | ||
diff --git a/stm32-gen-features/src/main.rs b/stm32-gen-features/src/main.rs new file mode 100644 index 000000000..9f1d8ef34 --- /dev/null +++ b/stm32-gen-features/src/main.rs | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | use std::collections::HashMap; | ||
| 2 | |||
| 3 | use gen_features::{generate_cargo_toml_file, load_chip_list}; | ||
| 4 | |||
| 5 | fn main() { | ||
| 6 | let chip_list = load_chip_list(); | ||
| 7 | update_cargo_file("../embassy-stm32/Cargo.toml", &chip_list); | ||
| 8 | } | ||
| 9 | |||
| 10 | /// Update a Cargo.toml file | ||
| 11 | /// | ||
| 12 | /// Update the content between "# BEGIN GENERATED FEATURES" and "# END GENERATED FEATURES" | ||
| 13 | /// with the given content | ||
| 14 | fn update_cargo_file(path: &str, new_contents: &HashMap<String, Vec<String>>) { | ||
| 15 | let previous_text = std::fs::read_to_string(path).unwrap(); | ||
| 16 | let new_text = generate_cargo_toml_file(&previous_text, new_contents); | ||
| 17 | std::fs::write(path, new_text).unwrap(); | ||
| 18 | } | ||
