summaryrefslogtreecommitdiff
path: root/stm32-gen-features
diff options
context:
space:
mode:
authorCôme ALLART <come.allart@etu.emse.fr>2021-08-27 11:09:27 +0200
committerCôme ALLART <come.allart@etu.emse.fr>2021-08-27 11:09:27 +0200
commit022b8092485c39cd68ad4e259ced5253b8a59460 (patch)
tree5f46664ba3947af8513e6ef63ca99a99db81c7b8 /stm32-gen-features
parent1e1cd0506aa655456d7cbf7d6915b46abf7829e5 (diff)
downloadembassy-022b8092485c39cd68ad4e259ced5253b8a59460.zip
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')
-rw-r--r--stm32-gen-features/.cargo/config.toml3
-rw-r--r--stm32-gen-features/.gitignore1
-rw-r--r--stm32-gen-features/Cargo.toml12
-rw-r--r--stm32-gen-features/src/lib.rs177
-rw-r--r--stm32-gen-features/src/main.rs18
5 files changed, 211 insertions, 0 deletions
diff --git a/stm32-gen-features/.cargo/config.toml b/stm32-gen-features/.cargo/config.toml
new file mode 100644
index 00000000..17d81c14
--- /dev/null
+++ b/stm32-gen-features/.cargo/config.toml
@@ -0,0 +1,3 @@
+[profile.dev]
+opt-level = 3
+lto = false
diff --git a/stm32-gen-features/.gitignore b/stm32-gen-features/.gitignore
new file mode 100644
index 00000000..ea8c4bf7
--- /dev/null
+++ b/stm32-gen-features/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/stm32-gen-features/Cargo.toml b/stm32-gen-features/Cargo.toml
new file mode 100644
index 00000000..1b8f7951
--- /dev/null
+++ b/stm32-gen-features/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "gen_features"
+version = "0.1.0"
+authors = ["Côme ALLART <come.allart@netc.fr>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+glob = "0.3.0"
+yaml-rust = "0.4.5"
+toml = "0.5.8"
diff --git a/stm32-gen-features/src/lib.rs b/stm32-gen-features/src/lib.rs
new file mode 100644
index 00000000..b20a1ba4
--- /dev/null
+++ b/stm32-gen-features/src/lib.rs
@@ -0,0 +1,177 @@
+use std::{
+ collections::HashMap,
+ path::{Path, PathBuf},
+};
+
+const SUPPORTED_FAMILIES: [&str; 8] = [
+ "STM32F0",
+ "STM32F4",
+ "STM32G0",
+ "STM32L0",
+ "STM32L4",
+ "STM32H7",
+ "STM32WB55",
+ "STM32WL55",
+];
+
+const SEPARATOR_START: &str = "# BEGIN GENERATED FEATURES\n";
+const SEPARATOR_END: &str = "# END GENERATED FEATURES\n";
+const HELP: &str = "# Generated by stm32-gen-features. DO NOT EDIT.\n";
+
+/// True if the chip named `name` is supported else false
+fn is_supported(name: &str) -> bool {
+ SUPPORTED_FAMILIES
+ .iter()
+ .any(|family| name.starts_with(family))
+}
+
+/// Get the yaml file names and the associated chip names for supported chips
+///
+/// Print errors to `stderr` when something is returned by the glob but is not in the returned
+/// [`Vec`]
+fn supported_chip_yaml_files_with_names() -> Vec<(PathBuf, String)> {
+ glob::glob("../stm32-data/data/chips/*.yaml")
+ .expect("bad glob pattern")
+ .filter_map(|entry| entry.map_err(|e| eprintln!("{:?}", e)).ok())
+ .filter_map(|entry| {
+ if let Some(name) = entry.file_stem().and_then(|stem| stem.to_str()) {
+ if is_supported(name) {
+ let owned_name = name.to_lowercase();
+ Some((entry, owned_name))
+ } else {
+ eprintln!("{} is not supported", name);
+ None
+ }
+ } else {
+ eprintln!("{:?} is not a regural file", entry);
+ None
+ }
+ })
+ .collect()
+}
+
+/// Get the list of the cores of a chip by its associated file
+///
+/// # Panic
+/// Panics if the file does not exist or if it contains yaml syntax errors
+///
+/// # None
+/// Returns none if "cores" is not an array
+fn chip_cores(path: &Path) -> Option<Vec<yaml_rust::Yaml>> {
+ let file_contents = std::fs::read_to_string(path).unwrap();
+ let doc = &yaml_rust::YamlLoader::load_from_str(&file_contents).unwrap()[0];
+ doc["cores"].as_vec().cloned()
+}
+
+/// Load the list of chips
+///
+/// # Panic
+/// Panics if a file contains yaml syntax errors or if a value does not have a consistent type
+pub fn load_chip_list() -> HashMap<String, Vec<String>> {
+ let mut result = HashMap::new();
+ for (path, name) in supported_chip_yaml_files_with_names() {
+ let cores = chip_cores(&path).unwrap_or_else(|| panic!("{}[cores] is not an array", name));
+ if cores.len() > 1 {
+ for (i, core) in cores.into_iter().enumerate() {
+ let core_name = core["name"]
+ .as_str()
+ .unwrap_or_else(|| panic!("{}[cores][{}][name] is not a string", name, i));
+ let key = format!("{}_{}", name, core_name);
+ let value = vec![format!("stm32-metapac/{}_{}", name, core_name)];
+ result.insert(key, value);
+ }
+ } else {
+ let value = vec![format!("stm32-metapac/{}", &name)];
+ result.insert(name, value);
+ }
+ }
+ result
+}
+
+/// Get contents before and after generated contents
+///
+/// # Panic
+/// Panics when a separator cound not be not found
+fn split_cargo_toml_contents(contents: &str) -> (&str, &str) {
+ let (before, remainder) = contents
+ .split_once(SEPARATOR_START)
+ .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_START));
+ let (_, after) = remainder
+ .split_once(SEPARATOR_END)
+ .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_END));
+
+ (before, after)
+}
+
+/// Generates new contents for Cargo.toml
+///
+/// # Panic
+/// Panics when a separator cound not be not found
+pub fn generate_cargo_toml_file(
+ previous_text: &str,
+ new_contents: &HashMap<String, Vec<String>>,
+) -> String {
+ let (before, after) = split_cargo_toml_contents(previous_text);
+ let generated_content = toml::to_string(new_contents).unwrap();
+ before.to_owned() + SEPARATOR_START + HELP + &generated_content + SEPARATOR_END + after
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn stm32f407vg_is_supported() {
+ assert!(is_supported("STM32F407VG"))
+ }
+
+ #[test]
+ fn abcdef_is_not_supported() {
+ assert!(!is_supported("ABCDEF"))
+ }
+
+ #[test]
+ fn stm32f407vg_yaml_file_exists() {
+ assert!(supported_chip_yaml_files_with_names()
+ .into_iter()
+ .any(|(path, name)| {
+ name == "stm32f407vg"
+ && path.to_str() == Some("../stm32-data/data/chips/STM32F407VG.yaml")
+ }))
+ }
+
+ #[test]
+ fn keeps_text_around_separators() {
+ let initial = "\
+before
+# BEGIN GENERATED FEATURES
+# END GENERATED FEATURES
+after
+";
+
+ let expected = "\
+before
+# BEGIN GENERATED FEATURES
+# Generated by stm32-gen-features. DO NOT EDIT.
+a = [\"b\"]
+# END GENERATED FEATURES
+after
+";
+
+ let map = HashMap::from([(String::from("a"), vec![String::from("b")])]);
+ assert_eq!(generate_cargo_toml_file(initial, &map), expected);
+ }
+
+ #[test]
+ #[should_panic]
+ fn does_not_generate_if_separators_are_missing() {
+ let initial = "\
+before
+# END GENERATED FEATURES
+after
+";
+
+ let map = HashMap::from([(String::from("a"), vec![String::from("b")])]);
+ generate_cargo_toml_file(initial, &map);
+ }
+}
diff --git a/stm32-gen-features/src/main.rs b/stm32-gen-features/src/main.rs
new file mode 100644
index 00000000..9f1d8ef3
--- /dev/null
+++ b/stm32-gen-features/src/main.rs
@@ -0,0 +1,18 @@
+use std::collections::HashMap;
+
+use gen_features::{generate_cargo_toml_file, load_chip_list};
+
+fn main() {
+ let chip_list = load_chip_list();
+ update_cargo_file("../embassy-stm32/Cargo.toml", &chip_list);
+}
+
+/// Update a Cargo.toml file
+///
+/// Update the content between "# BEGIN GENERATED FEATURES" and "# END GENERATED FEATURES"
+/// with the given content
+fn update_cargo_file(path: &str, new_contents: &HashMap<String, Vec<String>>) {
+ let previous_text = std::fs::read_to_string(path).unwrap();
+ let new_text = generate_cargo_toml_file(&previous_text, new_contents);
+ std::fs::write(path, new_text).unwrap();
+}