From 61e6b52870072a95c2ec2b3701323fc493e0e8eb Mon Sep 17 00:00:00 2001 From: Bob McWhirter Date: Tue, 17 Aug 2021 12:15:24 -0400 Subject: [PATCH] xtask runner for CI types of things and other utilities. --- xtask/.cargo/config | 6 ++ xtask/Cargo.toml | 14 +++ xtask/src/main.rs | 225 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 xtask/.cargo/config create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/main.rs diff --git a/xtask/.cargo/config b/xtask/.cargo/config new file mode 100644 index 00000000..919ec05a --- /dev/null +++ b/xtask/.cargo/config @@ -0,0 +1,6 @@ +[alias] +xtask = "run --package xtask --" +ci = "run --package xtask -- ci" +core = "run --package xtask -- core" +examples = "run --package xtask -- examples" +fmt = "run --package xtask -- fmt" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..ffa06ea9 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = [ + "Ulf Lilleengen ", + "Bob McWhirter " +] +edition = "2018" +name = "xtask" +version = "0.1.0" + +[dependencies] +anyhow = "1.0" +xshell = "0.1" +yaml-rust = "0.4" +walkdir = "2.3.2" \ No newline at end of file diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..d8d5e07d --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,225 @@ +#![allow(dead_code)] +#![deny(unused_must_use)] + +use std::format; +use std::{env, fs, path::PathBuf}; + +use std::path::Path; +use walkdir::WalkDir; +use xshell::{cmd, Cmd}; +use yaml_rust::YamlLoader; + +extern crate yaml_rust; + +fn main() -> Result<(), anyhow::Error> { + let args = env::args().skip(1).collect::>(); + let args = args.iter().map(|s| &**s).collect::>(); + + match &args[..] { + ["ci"] => task_ci()?, + ["core"] => task_check(Realm::Core)?, + ["metapac"] => task_metapac_gen()?, + ["examples"] => task_check(Realm::Examples)?, + ["fmt-check"] => task_cargo_fmt_check()?, + ["fmt"] => task_cargo_fmt()?, + _ => { + println!(""); + println!("USAGE: cargo xtask [command]"); + println!(""); + println!("Commands:"); + println!(" ci :: Runs entire CI"); + println!(" core :: Builds the core"); + println!(" metapac :: Builds the metapac"); + println!(" examples :: Builds the examples"); + println!(" fmt-check :: Checks rustfmt"); + println!(" fmt :: Performs rustfmt"); + println!(""); + } + } + Ok(()) +} + +fn task_ci() -> Result<(), anyhow::Error> { + task_check(Realm::Core)?; + task_check(Realm::Examples)?; + task_metapac_gen()?; + task_cargo_fmt_check()?; + Ok(()) +} + +#[derive(Copy, Clone)] +enum Realm { + All, + Core, + Examples, +} + +impl Realm { + fn accepts(&self, package: &str) -> bool { + match self { + Realm::All => true, + Realm::Core => !package.contains("examples"), + Realm::Examples => package.contains("examples"), + } + } +} + +fn task_check(realm: Realm) -> Result<(), anyhow::Error> { + let _e = xshell::pushenv("CI", "true"); + + let matrix_yaml = root_dir() + .join(".github") + .join("workflows") + .join("rust.yml"); + + let matrix = YamlLoader::load_from_str(&*fs::read_to_string(matrix_yaml).unwrap()).unwrap(); + + let matrix = &matrix.get(0).unwrap()["jobs"]["ci"]["strategy"]["matrix"]["include"]; + + let entries = matrix.as_vec().unwrap(); + + for entry in entries { + let package = entry["package"].as_str().unwrap(); + if !realm.accepts(package) { + continue; + } + let target = entry["target"].as_str().unwrap(); + let features = entry["features"].as_str(); + let package_dir = root_dir().join(entry["package"].as_str().unwrap()); + let _p = xshell::pushd(package_dir)?; + banner(&*format!( + "Building {} [target={}] [features={}]", + package, + target, + features.unwrap_or("default-features") + )); + + let root_cargo_dir = root_dir().join(".cargo"); + fs::create_dir_all(root_cargo_dir.clone()).unwrap(); + fs::write( + root_cargo_dir.join("config"), + "[target.\"cfg(all())\"]\nrustflags = [\"-D\", \"warnings\"]", + ) + .unwrap(); + + let mut args = Vec::new(); + args.push("check"); + args.push("--target"); + args.push(target); + + if let Some(features) = features { + args.push("--features"); + args.push(features); + } + + let command = Cmd::new(PathBuf::from("cargo")); + let command = command.args(args); + let result = command.run(); + + fs::remove_file(root_cargo_dir.join("config")).unwrap(); + + result?; + } + + Ok(()) +} + +fn task_metapac_gen() -> Result<(), anyhow::Error> { + banner("Building metapac"); + let _p = xshell::pushd(root_dir().join("stm32-metapac-gen")); + cmd!("cargo run").run()?; + Ok(()) +} + +fn task_cargo_fmt() -> Result<(), anyhow::Error> { + for entry in WalkDir::new(root_dir()) + .follow_links(false) + .into_iter() + .filter_map(|e| e.ok()) + { + let f_name = entry.file_name().to_string_lossy(); + + if f_name.ends_with(".rs") { + if !is_primary_source(entry.path()) { + continue; + } + let mut args = Vec::new(); + args.push("--skip-children"); + args.push("--unstable-features"); + args.push("--edition=2018"); + args.push(&*entry.path().to_str().unwrap()); + let command = Cmd::new("rustfmt"); + command.args(args).run()?; + } + } + + Ok(()) +} + +fn task_cargo_fmt_check() -> Result<(), anyhow::Error> { + let mut actual_result = Ok(()); + for entry in WalkDir::new(root_dir()) + .follow_links(false) + .into_iter() + .filter_map(|e| e.ok()) + { + let f_name = entry.file_name().to_string_lossy(); + + if f_name.ends_with(".rs") { + if !is_primary_source(entry.path()) { + continue; + } + let mut args = Vec::new(); + args.push("--check"); + args.push("--skip-children"); + args.push("--unstable-features"); + args.push("--edition=2018"); + args.push(&*entry.path().to_str().unwrap()); + let command = Cmd::new("rustfmt"); + if let Err(result) = command.args(args).run() { + actual_result = Err(result.into()); + } + } + } + + actual_result +} + +fn root_dir() -> PathBuf { + let mut xtask_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + xtask_dir.pop(); + xtask_dir +} + +fn examples_dir() -> PathBuf { + root_dir().join("examples") +} + +fn is_primary_source(path: &Path) -> bool { + let mut current = path; + + loop { + let current_file_name = current.file_name().unwrap().to_str().unwrap(); + if current_file_name == "target" + || current_file_name == "stm32-metapac-gen" + || current_file_name == "stm32-data" + { + return false; + } + + if let Some(path) = current.parent() { + current = path.into(); + if current == root_dir() { + return true; + } + } else { + return false; + } + } +} + +fn banner(text: &str) { + println!("------------------------------------------------------------------------------------------------------------------------------------------------"); + println!("== {}", text); + println!("------------------------------------------------------------------------------------------------------------------------------------------------"); +}