refactor(gen-features): use Rust instead of Python

Added support for /stm32-metapac
This commit is contained in:
Côme ALLART 2021-09-05 20:03:52 +02:00
parent 022b809248
commit addee8778d
4 changed files with 1337 additions and 1329 deletions

View file

@ -1,17 +1,16 @@
use std::{ //! FIXME discuss about which errors to print and when to panic
collections::HashMap,
path::{Path, PathBuf}, use std::{collections::HashMap, iter::FilterMap, path::Path, slice::Iter};
};
const SUPPORTED_FAMILIES: [&str; 8] = [ const SUPPORTED_FAMILIES: [&str; 8] = [
"STM32F0", "stm32f0",
"STM32F4", "stm32f4",
"STM32G0", "stm32g0",
"STM32L0", "stm32l0",
"STM32L4", "stm32l4",
"STM32H7", "stm32h7",
"STM32WB55", "stm32wb55",
"STM32WL55", "stm32wl55",
]; ];
const SEPARATOR_START: &str = "# BEGIN GENERATED FEATURES\n"; const SEPARATOR_START: &str = "# BEGIN GENERATED FEATURES\n";
@ -25,25 +24,36 @@ fn is_supported(name: &str) -> bool {
.any(|family| name.starts_with(family)) .any(|family| name.starts_with(family))
} }
/// Get the yaml file names and the associated chip names for supported chips type SupportedIter<'a> = FilterMap<
Iter<'a, (String, Vec<String>)>,
fn(&(String, Vec<String>)) -> Option<(&String, &Vec<String>)>,
>;
trait FilterSupported {
fn supported(&self) -> SupportedIter;
}
impl FilterSupported for &[(String, Vec<String>)] {
/// Get a new Vec with only the supported chips
fn supported(&self) -> SupportedIter {
self.iter()
.filter_map(|(name, cores)| is_supported(name).then(|| (name, cores)))
}
}
/// Get the list of all the chips and their supported cores
/// ///
/// Print errors to `stderr` when something is returned by the glob but is not in the returned /// Print errors to `stderr` when something is returned by the glob but is not in the returned
/// [`Vec`] /// [`Vec`]
fn supported_chip_yaml_files_with_names() -> Vec<(PathBuf, String)> { ///
/// This function is slow because all the yaml files are parsed.
pub fn chip_names_and_cores() -> Vec<(String, Vec<String>)> {
glob::glob("../stm32-data/data/chips/*.yaml") glob::glob("../stm32-data/data/chips/*.yaml")
.expect("bad glob pattern") .unwrap()
.filter_map(|entry| entry.map_err(|e| eprintln!("{:?}", e)).ok()) .filter_map(|entry| entry.map_err(|e| eprintln!("{:?}", e)).ok())
.filter_map(|entry| { .filter_map(|entry| {
if let Some(name) = entry.file_stem().and_then(|stem| stem.to_str()) { if let Some(name) = entry.file_stem().and_then(|stem| stem.to_str()) {
if is_supported(name) { Some((name.to_lowercase(), chip_cores(&entry)))
let owned_name = name.to_lowercase();
Some((entry, owned_name))
} else {
eprintln!("{} is not supported", name);
None
}
} else { } else {
eprintln!("{:?} is not a regural file", entry); eprintln!("{:?} is not a regular file", entry);
None None
} }
}) })
@ -53,36 +63,71 @@ fn supported_chip_yaml_files_with_names() -> Vec<(PathBuf, String)> {
/// Get the list of the cores of a chip by its associated file /// Get the list of the cores of a chip by its associated file
/// ///
/// # Panic /// # Panic
/// Panics if the file does not exist or if it contains yaml syntax errors /// Panics if the file does not exist or if it contains yaml syntax errors.
/// /// Panics if "cores" is not an array.
/// # None fn chip_cores(path: &Path) -> Vec<String> {
/// 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 file_contents = std::fs::read_to_string(path).unwrap();
let doc = &yaml_rust::YamlLoader::load_from_str(&file_contents).unwrap()[0]; let doc = &yaml_rust::YamlLoader::load_from_str(&file_contents).unwrap()[0];
doc["cores"].as_vec().cloned() doc["cores"]
.as_vec()
.unwrap_or_else(|| panic!("{:?}:[cores] is not an array", path))
.iter()
.enumerate()
.map(|(i, core)| {
core["name"]
.as_str()
.unwrap_or_else(|| panic!("{:?}:[cores][{}][name] is not a string", path, i))
.to_owned()
})
.collect()
} }
/// Load the list of chips /// Generate data needed in `../embassy-stm32/Cargo.toml`
///
/// Print errors to `stderr` when something is returned by the glob but is not in the returned
/// [`Vec`]
/// ///
/// # Panic /// # Panic
/// Panics if a file contains yaml syntax errors or if a value does not have a consistent type /// 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>> { pub fn embassy_stm32_needed_data(
names_and_cores: &[(String, Vec<String>)],
) -> HashMap<String, Vec<String>> {
let mut result = HashMap::new(); let mut result = HashMap::new();
for (path, name) in supported_chip_yaml_files_with_names() { for (chip_name, cores) in names_and_cores.supported() {
let cores = chip_cores(&path).unwrap_or_else(|| panic!("{}[cores] is not an array", name));
if cores.len() > 1 { if cores.len() > 1 {
for (i, core) in cores.into_iter().enumerate() { for core_name in cores.iter() {
let core_name = core["name"] let key = format!("{}_{}", chip_name, core_name);
.as_str() let value = vec![format!("stm32-metapac/{}_{}", chip_name, core_name)];
.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); result.insert(key, value);
} }
} else { } else {
let value = vec![format!("stm32-metapac/{}", &name)]; let key = chip_name.to_string();
result.insert(name, value); let value = vec![format!("stm32-metapac/{}", chip_name)];
result.insert(key, value);
}
}
result
}
/// Generate data needed in `../stm32-metapac/Cargo.toml`
///
/// Print errors to `stderr` when something is returned by the glob but is not in the returned
/// [`Vec`]
///
/// # Panic
/// Panics if a file contains yaml syntax errors or if a value does not have a consistent type
pub fn stm32_metapac_needed_data(
names_and_cores: &[(String, Vec<String>)],
) -> HashMap<String, Vec<String>> {
let mut result = HashMap::new();
for (chip_name, cores) in names_and_cores {
if cores.len() > 1 {
for core_name in cores {
let key = format!("{}_{}", chip_name, core_name);
result.insert(key, vec![]);
}
} else {
result.insert(chip_name.clone(), vec![]);
} }
} }
result result
@ -122,22 +167,22 @@ mod tests {
#[test] #[test]
fn stm32f407vg_is_supported() { fn stm32f407vg_is_supported() {
assert!(is_supported("STM32F407VG")) assert!(is_supported("stm32f407vg"))
} }
#[test] #[test]
fn abcdef_is_not_supported() { fn abcdef_is_not_supported() {
assert!(!is_supported("ABCDEF")) assert!(!is_supported("abcdef"))
} }
#[test] #[test]
fn stm32f407vg_yaml_file_exists() { #[ignore]
assert!(supported_chip_yaml_files_with_names() fn stm32f407vg_yaml_file_exists_and_is_supported() {
assert!(chip_names_and_cores()
.as_slice()
.supported()
.into_iter() .into_iter()
.any(|(path, name)| { .any(|(name, _)| { name == "stm32f407vg" }))
name == "stm32f407vg"
&& path.to_str() == Some("../stm32-data/data/chips/STM32F407VG.yaml")
}))
} }
#[test] #[test]

View file

@ -1,10 +1,20 @@
use std::collections::HashMap; use std::collections::HashMap;
use gen_features::{generate_cargo_toml_file, load_chip_list}; use gen_features::{
chip_names_and_cores, embassy_stm32_needed_data, generate_cargo_toml_file,
stm32_metapac_needed_data,
};
fn main() { fn main() {
let chip_list = load_chip_list(); let names_and_cores = chip_names_and_cores();
update_cargo_file("../embassy-stm32/Cargo.toml", &chip_list); update_cargo_file(
"../embassy-stm32/Cargo.toml",
&embassy_stm32_needed_data(&names_and_cores),
);
update_cargo_file(
"../stm32-metapac/Cargo.toml",
&stm32_metapac_needed_data(&names_and_cores),
);
} }
/// Update a Cargo.toml file /// Update a Cargo.toml file

File diff suppressed because it is too large Load diff

View file

@ -1,47 +0,0 @@
import xmltodict
import yaml
import re
import json
import os
import re
import toml
from collections import OrderedDict
from glob import glob
try:
from yaml import CSafeLoader as SafeLoader
except ImportError:
from yaml import SafeLoader
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
# ======= load chip list
features = {}
for f in sorted(glob('../stm32-data/data/chips/*.yaml')):
# Use the filename to get the chip name. Ultra fast, we don't have to read YAML!
name = os.path.splitext(os.path.basename(f))[0].lower()
with open(f, 'r') as f:
chip = yaml.load(f, Loader=SafeLoader)
if len(chip['cores']) > 1:
for core in chip['cores']:
features[name + "_" + core['name']] = []
else:
features[name] = []
# ========= Update Cargo features
SEPARATOR_START = '# BEGIN GENERATED FEATURES\n'
SEPARATOR_END = '# END GENERATED FEATURES\n'
HELP = '# Generated by gen_features.py. DO NOT EDIT.\n'
with open('Cargo.toml', 'r') as f:
cargo = f.read()
before, cargo = cargo.split(SEPARATOR_START, maxsplit=1)
_, after = cargo.split(SEPARATOR_END, maxsplit=1)
cargo = before + SEPARATOR_START + HELP + \
toml.dumps(features) + SEPARATOR_END + after
with open('Cargo.toml', 'w') as f:
f.write(cargo)