From dde8655c234b70eed2656c9c22c1cc444c95ccfb Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Thu, 16 Oct 2025 12:16:13 +0200 Subject: [PATCH 01/11] do you work? --- .github/workflows/ci.yml | 9 ++- xtask/src/main.rs | 154 ++++++++++++++++++++++++++------------- 2 files changed, 111 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf155e7..2c232a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: verify: name: "Check ${{ matrix.chip }}" - runs-on: ubuntu-latest + runs-on: macos-m1-self-hosted strategy: fail-fast: false @@ -72,6 +72,13 @@ jobs: target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf components: clippy,rustfmt,rust-src + # Prepare cargo-batch + - name: Setup cargo-batch + run: | + if ! command -v cargo-batch &> /dev/null; then + cargo install --git https://github.com/embassy-rs/cargo-batch cargo --bin cargo-batch --locked --force + fi + # //Define a new environment variable called toolchain - if: ${{ contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} run: echo "TOOLCHAIN=+esp" >> $GITHUB_ENV diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 904a0ef..28146d7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -11,7 +11,7 @@ use esp_generate::{ template::{GeneratorOptionCategory, GeneratorOptionItem, Template}, }; use esp_metadata::Chip; -use log::info; +use log::{info, warn}; // Unfortunate hard-coded list of non-codegen options const IGNORED_CATEGORIES: &[&str] = &["editor", "optional"]; @@ -78,78 +78,47 @@ fn check( log::info!("CHECK: {chip}"); } - info!("Going to check"); + info!("Collecting configurations to check..."); let to_check = options_for_chip(chip, all_combinations)?; for check in &to_check { - info!("\"{}\"", check.join(", ")); + info!("→ Combination: [{}]", check.join(", ")); } if dry_run { + info!("Dry run — no commands executed."); return Ok(()); } const PROJECT_NAME: &str = "test"; + for options in to_check { - log::info!("WITH OPTIONS: {options:?}"); + log::info!("=== Checking configuration: {options:?}"); - // We will generate the project in a temporary directory, to avoid - // making a mess when this subcommand is executed locally: + // Temporary directory for generated project let project_dir = tempfile::tempdir()?; let project_path = project_dir.path(); log::info!("PROJECT PATH: {project_path:?}"); - // Generate a project targeting the specified chip and using the - // specified generation options: + // Generate project generate(workspace, &project_path, PROJECT_NAME, chip, &options)?; - // Ensure that the generated project builds without errors: - let output = Command::new("cargo") - .args([if build { "build" } else { "check" }]) - .env_remove("RUSTUP_TOOLCHAIN") - .current_dir(project_path.join(PROJECT_NAME)) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; - if !output.status.success() { - bail!("Failed to execute cargo check subcommand") - } + let project_root = project_path.join(PROJECT_NAME); + let mut batch = CargoBatch::new(&project_root, dry_run); + + // Add commands to the batch + batch.check_or_build(build); - // Ensure that the generated test project builds also: if options.iter().any(|o| o == "embedded-test") { - let output = Command::new("cargo") - .args(["test", "--no-run"]) - .env_remove("RUSTUP_TOOLCHAIN") - .current_dir(project_path.join(PROJECT_NAME)) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; - if !output.status.success() { - bail!("Failed to execute cargo test subcommand") - } + batch.test(); } - // Run clippy against the generated project to check for lint errors: - let output = Command::new("cargo") - .args(["clippy", "--no-deps", "--", "-Dwarnings"]) - .env_remove("RUSTUP_TOOLCHAIN") - .current_dir(project_path.join(PROJECT_NAME)) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; - if !output.status.success() { - bail!("Failed to execute cargo clippy subcommand") - } + batch.clippy(); + batch.fmt_check(); - // Ensure that the generated project is correctly formatted: - let output = Command::new("cargo") - .args(["fmt", "--", "--check"]) - .env_remove("RUSTUP_TOOLCHAIN") - .current_dir(project_path.join(PROJECT_NAME)) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; - if !output.status.success() { - bail!("Failed to execute cargo fmt subcommand") + // Run all cargo commands in sequence + if let Err(e) = batch.run() { + warn!("Build failed for options {options:?}: {e}"); + return Err(e); } } @@ -338,3 +307,86 @@ fn generate( Ok(()) } + +/// Represents a single cargo command to be executed. +pub struct CargoCommand { + pub args: Vec, + pub description: String, +} + +impl CargoCommand { + pub fn new(description: impl Into, args: impl IntoIterator>) -> Self { + Self { + description: description.into(), + args: args.into_iter().map(|a| a.into()).collect(), + } + } +} + +/// Helper to batch multiple cargo commands for a project directory. +pub struct CargoBatch<'a> { + project_dir: &'a Path, + commands: Vec, + dry_run: bool, +} + +impl<'a> CargoBatch<'a> { + pub fn new(project_dir: &'a Path, dry_run: bool) -> Self { + Self { + project_dir, + commands: vec![], + dry_run, + } + } + + /// Add a cargo command to the batch. + pub fn add(&mut self, cmd: CargoCommand) { + self.commands.push(cmd); + } + + /// Convenience: cargo check or build. + pub fn check_or_build(&mut self, build: bool) { + let verb = if build { "build" } else { "check" }; + self.add(CargoCommand::new(format!("cargo {verb}"), [verb])); + } + + /// Convenience: cargo test --no-run + pub fn test(&mut self) { + self.add(CargoCommand::new("cargo test", ["test", "--no-run"])); + } + + /// Convenience: cargo clippy -- -D warnings + pub fn clippy(&mut self) { + self.add(CargoCommand::new("cargo clippy", ["clippy", "--no-deps", "--", "-Dwarnings"])); + } + + /// Convenience: cargo fmt --check + pub fn fmt_check(&mut self) { + self.add(CargoCommand::new("cargo fmt", ["fmt", "--", "--check"])); + } + + /// Executes all queued commands in sequence. + pub fn run(&self) -> Result<()> { + for cmd in &self.commands { + info!("→ Running: {}", cmd.description); + + if self.dry_run { + continue; + } + + let output = Command::new("cargo") + .args(&cmd.args) + .env_remove("RUSTUP_TOOLCHAIN") + .current_dir(self.project_dir) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .output()?; + + if !output.status.success() { + bail!("Failed to execute {}", cmd.description); + } + } + + Ok(()) + } +} \ No newline at end of file From 6254f9ef31518c0784cc1b52196c637db2463aec Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Thu, 16 Oct 2025 12:26:27 +0200 Subject: [PATCH 02/11] no mac for peasants comments --- .github/workflows/ci.yml | 2 +- xtask/src/main.rs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c232a8..5a39315 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: verify: name: "Check ${{ matrix.chip }}" - runs-on: macos-m1-self-hosted + runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 28146d7..a2ce984 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -78,10 +78,10 @@ fn check( log::info!("CHECK: {chip}"); } - info!("Collecting configurations to check..."); + info!("Going to check"); let to_check = options_for_chip(chip, all_combinations)?; for check in &to_check { - info!("→ Combination: [{}]", check.join(", ")); + info!("\"{}\"", check.join(", ")); } if dry_run { @@ -92,27 +92,33 @@ fn check( const PROJECT_NAME: &str = "test"; for options in to_check { - log::info!("=== Checking configuration: {options:?}"); + log::info!("WITH OPTIONS: {options:?}"); - // Temporary directory for generated project + // We will generate the project in a temporary directory, to avoid + // making a mess when this subcommand is executed locally: let project_dir = tempfile::tempdir()?; let project_path = project_dir.path(); log::info!("PROJECT PATH: {project_path:?}"); - // Generate project + // Generate a project targeting the specified chip and using the + // specified generation options: generate(workspace, &project_path, PROJECT_NAME, chip, &options)?; let project_root = project_path.join(PROJECT_NAME); let mut batch = CargoBatch::new(&project_root, dry_run); // Add commands to the batch + // Ensure that the generated project builds without errors: batch.check_or_build(build); + // Ensure that the generated test project builds also: if options.iter().any(|o| o == "embedded-test") { batch.test(); } + // Run clippy against the generated project to check for lint errors: batch.clippy(); + // Ensure that the generated project is correctly formatted: batch.fmt_check(); // Run all cargo commands in sequence From 886d8bf86c9c2ec4d7de4487cbcd6d8d431a5427 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Thu, 16 Oct 2025 13:13:18 +0200 Subject: [PATCH 03/11] experimental: always build with `alloc` --- template/.cargo/config.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/template/.cargo/config.toml b/template/.cargo/config.toml index 2adc905..a7c784a 100644 --- a/template/.cargo/config.toml +++ b/template/.cargo/config.toml @@ -36,8 +36,4 @@ rustflags = [ target = "riscv32imac-unknown-none-elf" [unstable] -#IF option("alloc") -build-std = ["alloc", "core"] -#ELSE -#+build-std = ["core"] -#ENDIF +build-std = ["alloc", "core"] # TODO: experimental for `cargo-batch` From 1786c54b1d1e1003776db69879cfd217e1d6ca35 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Fri, 17 Oct 2025 19:12:02 +0200 Subject: [PATCH 04/11] this is terrible, don't look here, I just want to test it now --- template/.cargo/config.toml | 2 +- xtask/src/main.rs | 523 +++++++++++++++++++++++++++++++----- 2 files changed, 452 insertions(+), 73 deletions(-) diff --git a/template/.cargo/config.toml b/template/.cargo/config.toml index a7c784a..4861d49 100644 --- a/template/.cargo/config.toml +++ b/template/.cargo/config.toml @@ -36,4 +36,4 @@ rustflags = [ target = "riscv32imac-unknown-none-elf" [unstable] -build-std = ["alloc", "core"] # TODO: experimental for `cargo-batch` +build-std = ["alloc", "core"] diff --git a/xtask/src/main.rs b/xtask/src/main.rs index a2ce984..2c87bb1 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,10 +1,12 @@ use std::{ - collections::HashSet, + collections::{HashSet, HashMap}, path::{Path, PathBuf}, process::{Command, Stdio}, }; -use anyhow::{bail, Result}; +use std::ffi::OsStr; + +use anyhow::{bail, Result, Context}; use clap::{Parser, Subcommand}; use esp_generate::{ config::{find_option, ActiveConfiguration}, @@ -42,6 +44,184 @@ enum Commands { }, } +/// A builder for constructing cargo command line arguments. +#[derive(Clone, Debug, Default)] +pub struct CargoArgsBuilder { + pub(crate) artifact_name: String, + pub(crate) config_path: Option, + pub(crate) manifest_path: Option, + pub(crate) toolchain: Option, + pub(crate) subcommand: String, + pub(crate) target: Option, + pub(crate) features: Vec, + pub(crate) args: Vec, + pub(crate) configs: Vec, + pub(crate) env_vars: HashMap, + +} + +impl CargoArgsBuilder { + pub fn new(artifact_name: String) -> Self { + Self { + subcommand: artifact_name.clone(), + artifact_name, + ..Default::default() + } +} + + /// Set the path to the Cargo manifest file (Cargo.toml) + #[must_use] + pub fn manifest_path(mut self, path: PathBuf) -> Self { + self.manifest_path = Some(path); + self + } + + /// Set the path to the Cargo configuration file (.cargo/config.toml) + #[must_use] + pub fn config_path(mut self, path: PathBuf) -> Self { + self.config_path = Some(path); + self + } + + /// Set the Rust toolchain to use. + #[must_use] + pub fn toolchain(mut self, toolchain: S) -> Self + where + S: Into, + { + self.toolchain = Some(toolchain.into()); + self + } + + /// Set the cargo subcommand to use. + #[must_use] + pub fn subcommand(mut self, subcommand: S) -> Self + where + S: Into, + { + self.subcommand = subcommand.into(); + self + } + + /// Set the compilation target to use. + #[must_use] + pub fn target(mut self, target: S) -> Self + where + S: Into, + { + self.target = Some(target.into()); + self + } + + /// Set the cargo features to use. + #[must_use] + pub fn features(mut self, features: &[String]) -> Self { + self.features = features.to_vec(); + self + } + + /// Add a single argument to the cargo command line. + #[must_use] + pub fn arg(mut self, arg: S) -> Self + where + S: Into, + { + self.args.push(arg.into()); + self + } + + /// Add multiple arguments to the cargo command line. + #[must_use] + pub fn args(mut self, args: &[S]) -> Self + where + S: Clone + Into, + { + for arg in args { + self.args.push(arg.clone().into()); + } + self + } + + /// Add a single argument to the cargo command line. + pub fn add_arg(&mut self, arg: S) -> &mut Self + where + S: Into, + { + self.args.push(arg.into()); + self + } + + /// Adds a raw configuration argument (--config, -Z, ...) + #[must_use] + pub fn config(mut self, arg: S) -> Self + where + S: Into, + { + self.add_config(arg); + self + } + + /// Adds a raw configuration argument (--config, -Z, ...) + pub fn add_config(&mut self, arg: S) -> &mut Self + where + S: Into, + { + self.configs.push(arg.into()); + self + } + + /// Adds an environment variable + pub fn add_env_var(&mut self, key: S, value: S) -> &mut Self + where + S: Into, + { + self.env_vars.insert(key.into(), value.into()); + self + } + + /// Build the final list of cargo command line arguments. + #[must_use] + pub fn build(&self) -> Vec { + let mut args = vec![]; + + if let Some(ref toolchain) = self.toolchain { + args.push(format!("+{toolchain}")); + } + + args.push(self.subcommand.clone()); + + if let Some(manifest_path) = &self.manifest_path { + args.push("--manifest-path".to_string()); + args.push(manifest_path.display().to_string()); + } + + if let Some(config_path) = &self.config_path { + args.push("--config".to_string()); + args.push(config_path.display().to_string()); + } + + if let Some(ref target) = self.target { + args.push(format!("--target={target}")); + } + + for config in self.configs.iter() { + args.push(config.clone()); + } + + if !self.features.is_empty() { + args.push(format!("--features={}", self.features.join(","))); + } + + for arg in self.args.iter() { + args.push(arg.clone()); + } + + log::debug!("Built cargo args: {:?}", args); + args + } +} + + fn main() -> Result<()> { env_logger::Builder::new() .filter_module("xtask", log::LevelFilter::Info) @@ -62,9 +242,6 @@ fn main() -> Result<()> { } } -// ---------------------------------------------------------------------------- -// CHECK - fn check( workspace: &Path, chip: Chip, @@ -85,12 +262,10 @@ fn check( } if dry_run { - info!("Dry run — no commands executed."); return Ok(()); } const PROJECT_NAME: &str = "test"; - for options in to_check { log::info!("WITH OPTIONS: {options:?}"); @@ -104,27 +279,40 @@ fn check( // specified generation options: generate(workspace, &project_path, PROJECT_NAME, chip, &options)?; - let project_root = project_path.join(PROJECT_NAME); - let mut batch = CargoBatch::new(&project_root, dry_run); + let current_dir = project_path.join(PROJECT_NAME); + + // batcher **per project** + let mut commands = CargoCommandBatcher::new(); - // Add commands to the batch // Ensure that the generated project builds without errors: - batch.check_or_build(build); + commands.push( + CargoArgsBuilder::new(if build { "build".to_string() } else { "check".to_string() }) + .target(chip.target()), + ); // Ensure that the generated test project builds also: if options.iter().any(|o| o == "embedded-test") { - batch.test(); + commands.push( + CargoArgsBuilder::new("test".to_string()) + .args(&["--no-run".to_string()]) + .target(chip.target()), + ); } // Run clippy against the generated project to check for lint errors: - batch.clippy(); - // Ensure that the generated project is correctly formatted: - batch.fmt_check(); - - // Run all cargo commands in sequence - if let Err(e) = batch.run() { - warn!("Build failed for options {options:?}: {e}"); - return Err(e); + commands.push( + CargoArgsBuilder::new("clippy".to_string()) + .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) + .target(chip.target()), + ); + + // TODO get me back + // commands.push(CargoArgsBuilder::new("fmt".to_string()) + // .args(&["--".to_string(), "--check".to_string()])); + + for c in commands.build(false) { + println!("Command: cargo {}", c.command.join(" ").replace("---", "\n ---")); + c.run(false, ¤t_dir)?; } } @@ -314,85 +502,276 @@ fn generate( Ok(()) } -/// Represents a single cargo command to be executed. -pub struct CargoCommand { - pub args: Vec, - pub description: String, + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +struct BatchKey { + config_file: String, + toolchain: Option, + config: Vec, + env_vars: Vec<(String, String)>, } -impl CargoCommand { - pub fn new(description: impl Into, args: impl IntoIterator>) -> Self { +impl BatchKey { + fn from_command(command: &CargoArgsBuilder) -> Self { + let config_file = if let Some(config_path) = &command.config_path { + std::fs::read_to_string(config_path).unwrap_or_default() + } else { + String::new() + }; + Self { - description: description.into(), - args: args.into_iter().map(|a| a.into()).collect(), + toolchain: command.toolchain.clone(), + config: command.configs.clone(), + config_file, + env_vars: { + let mut env_vars = command + .env_vars + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>(); + + env_vars.sort(); + env_vars + }, } } } -/// Helper to batch multiple cargo commands for a project directory. -pub struct CargoBatch<'a> { - project_dir: &'a Path, - commands: Vec, - dry_run: bool, +#[derive(Debug)] +pub struct CargoCommandBatcher { + commands: HashMap>, } -impl<'a> CargoBatch<'a> { - pub fn new(project_dir: &'a Path, dry_run: bool) -> Self { - Self { - project_dir, - commands: vec![], - dry_run, - } +#[derive(Debug, Clone)] +pub struct BuiltCommand { + pub artifact_name: String, + pub command: Vec, + pub env_vars: Vec<(String, String)>, +} + +impl BuiltCommand { + pub fn run(&self, capture: bool, dir: &PathBuf) -> Result { + run_with_env(&self.command, &dir, self.env_vars.clone(), capture) } +} - /// Add a cargo command to the batch. - pub fn add(&mut self, cmd: CargoCommand) { - self.commands.push(cmd); +fn run_with_env(args: &[String], cwd: &Path, envs: I, capture: bool) -> Result +where + I: IntoIterator + core::fmt::Debug, + K: AsRef, + V: AsRef, +{ + if !cwd.is_dir() { + bail!("The `cwd` argument MUST be a directory"); } - /// Convenience: cargo check or build. - pub fn check_or_build(&mut self, build: bool) { - let verb = if build { "build" } else { "check" }; - self.add(CargoCommand::new(format!("cargo {verb}"), [verb])); + #[cfg(target_os = "windows")] + fn windows_safe_path(p: &Path) -> &Path { + if let Ok(stripped) = p.strip_prefix(r"\\?\") { + stripped + } else { + p + } } + #[cfg(not(target_os = "windows"))] + fn windows_safe_path(p: &Path) -> &Path { + p + } + + let cwd = windows_safe_path(cwd); - /// Convenience: cargo test --no-run - pub fn test(&mut self) { - self.add(CargoCommand::new("cargo test", ["test", "--no-run"])); + log::debug!( + "Running `cargo {}` in {:?} - Environment {:?}", + args.join(" "), + cwd, + envs + ); + + let mut command = Command::new("cargo"); + command + .args(args) + .current_dir(cwd) + .env_remove("RUSTUP_TOOLCHAIN") + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + + if args.iter().any(|a| a.starts_with('+')) { + command.env_remove("CARGO"); } - /// Convenience: cargo clippy -- -D warnings - pub fn clippy(&mut self) { - self.add(CargoCommand::new("cargo clippy", ["clippy", "--no-deps", "--", "-Dwarnings"])); + let output = command + .stdin(Stdio::inherit()) + .output() + .with_context(|| format!("Couldn't get output for command {command:?}"))?; + + if output.status.success() { + Ok(String::from_utf8_lossy(&output.stdout).to_string()) + } else { + bail!( + "Failed to execute cargo subcommand `cargo {}`", + args.join(" "), + ) } +} - /// Convenience: cargo fmt --check - pub fn fmt_check(&mut self) { - self.add(CargoCommand::new("cargo fmt", ["fmt", "--", "--check"])); +impl CargoCommandBatcher { + pub fn new() -> Self { + Self { + commands: HashMap::new(), + } } - /// Executes all queued commands in sequence. - pub fn run(&self) -> Result<()> { - for cmd in &self.commands { - info!("→ Running: {}", cmd.description); + pub fn push(&mut self, command: CargoArgsBuilder) { + let key = BatchKey::from_command(&command); + self.commands.entry(key).or_default().push(command); + } + + fn build_for_cargo_batch(&self) -> Vec { + let mut all = Vec::new(); - if self.dry_run { + for (key, group) in self.commands.iter() { + if group.len() == 1 { + all.push(Self::build_one_for_cargo(&group[0])); continue; } - let output = Command::new("cargo") - .args(&cmd.args) - .env_remove("RUSTUP_TOOLCHAIN") - .current_dir(self.project_dir) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; + let mut command = Vec::new(); + let mut batch_len = 0; + let mut commands_in_batch = 0; + + // Windows be Windows, it has a command length limit. + let limit = if cfg!(target_os = "windows") { + Some(8191) + } else { + None + }; + + for item in group.iter() { + // Only some commands can be batched + let batchable = ["build", "doc", "check"]; + if !batchable + .iter() + .any(|&subcommand| subcommand == item.subcommand) + { + all.push(Self::build_one_for_cargo(item)); + continue; + } + + let mut c = item.clone(); + + c.toolchain = None; + c.configs = Vec::new(); + c.config_path = None; + + let args = c.build(); - if !output.status.success() { - bail!("Failed to execute {}", cmd.description); + let command_chars = 4 + args.iter().map(|arg| arg.len() + 1).sum::(); + + if !command.is_empty() + && let Some(limit) = limit + && batch_len + command_chars > limit + { + all.push(BuiltCommand { + artifact_name: String::from("batch"), + command: std::mem::take(&mut command), + env_vars: key.env_vars.clone(), + }); + } + + if command.is_empty() { + if let Some(tc) = key.toolchain.as_ref() { + command.push(format!("+{tc}")); + } + + command.push("batch".to_string()); + if !key.config_file.is_empty() + && let Some(config_path) = &group[0].config_path + { + command.push("--config".to_string()); + command.push(config_path.display().to_string()); + } + command.extend_from_slice(&key.config); + + commands_in_batch = 0; + batch_len = command.iter().map(|s| s.len() + 1).sum::() - 1; + } + + command.push("---".to_string()); + command.extend_from_slice(&args); + + commands_in_batch += 1; + batch_len += command_chars; + } + + if commands_in_batch > 0 { + all.push(BuiltCommand { + artifact_name: String::from("batch"), + command, + env_vars: key.env_vars.clone(), + }); + } + } + + all + } + + fn build_for_cargo(&self) -> Vec { + let mut all = Vec::new(); + + for group in self.commands.values() { + for item in group.iter() { + all.push(Self::build_one_for_cargo(item)); } } - Ok(()) + all + } + + pub fn build_one_for_cargo(item: &CargoArgsBuilder) -> BuiltCommand { + BuiltCommand { + artifact_name: item.artifact_name.clone(), + command: { + let mut args = item.build(); + + if item.args.iter().any(|arg| arg == "--artifact-dir") { + args.push("-Zunstable-options".to_string()); + } + + args + }, + env_vars: item + .env_vars + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(), + } + } + + pub fn build(&self, no_batch: bool) -> Vec { + let cargo_batch_available = Command::new("cargo") + .arg("batch") + .arg("-h") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .map(|s| s.success()) + .unwrap_or(false); + + if cargo_batch_available && !no_batch { + self.build_for_cargo_batch() + } else { + if !no_batch { + log::warn!("You don't have cargo batch installed. Falling back to cargo."); + log::warn!("You should really install cargo-batch."); + log::warn!( + "cargo install --git https://github.com/embassy-rs/cargo-batch cargo --bin cargo-batch --locked" + ); + } + self.build_for_cargo() + } } -} \ No newline at end of file +} + +impl Drop for CargoCommandBatcher { + fn drop(&mut self) {} +} From 92be58c3ebb3b05309db6fa78deafdc0e34e6d20 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Fri, 17 Oct 2025 19:49:40 +0200 Subject: [PATCH 05/11] go nuts --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a39315..e946f39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: verify: name: "Check ${{ matrix.chip }}" - runs-on: ubuntu-latest + runs-on: macos-m1-self-hosted strategy: fail-fast: false @@ -86,7 +86,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Generate and check project - run: cargo ${{ env.TOOLCHAIN }} xtask check ${{ matrix.chip }} ${{ fromJSON('["", "--all-combinations"]')[inputs.all_combinations || github.event_name == 'schedule'] }} ${{ fromJSON('["", "--build"]')[inputs.build || github.event_name == 'schedule'] }} + run: cargo ${{ env.TOOLCHAIN }} xtask check ${{ matrix.chip }} --all-combinations --build - if: github.event_name == 'schedule' name: Run cargo-package From c56ed459cc44e4430f64d0db1fb75c0a1ba7a99b Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Mon, 20 Oct 2025 11:00:21 +0200 Subject: [PATCH 06/11] single batch --- .github/workflows/ci.yml | 64 +++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e946f39..5224089 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,38 +39,34 @@ jobs: # Verify verify: - name: "Check ${{ matrix.chip }}" runs-on: macos-m1-self-hosted - strategy: - fail-fast: false - matrix: - chip: [esp32, esp32c2, esp32c3, esp32c6, esp32h2, esp32s2, esp32s3] + # chip: [esp32, esp32c2, esp32c3, esp32c6, esp32h2, esp32s2, esp32s3] steps: - uses: actions/checkout@v4 - # Rust toolchain for Xtensa: - - if: ${{ contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} - uses: esp-rs/xtensa-toolchain@v1.5 - with: - default: true - buildtargets: ${{ matrix.chip }} - ldproxy: false - - # Rust toolchain for RISC-V: - - if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} - uses: dtolnay/rust-toolchain@stable - with: - target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf - components: clippy,rustfmt,rust-src - - # Rust toolchain for RISC-V: - - if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} - uses: dtolnay/rust-toolchain@nightly - with: - target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf - components: clippy,rustfmt,rust-src + # # Rust toolchain for Xtensa: + # - if: ${{ contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} + # uses: esp-rs/xtensa-toolchain@v1.5 + # with: + # default: true + # buildtargets: ${{ matrix.chip }} + # ldproxy: false + + # # Rust toolchain for RISC-V: + # - if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} + # uses: dtolnay/rust-toolchain@stable + # with: + # target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf + # components: clippy,rustfmt,rust-src + + # # Rust toolchain for RISC-V: + # - if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} + # uses: dtolnay/rust-toolchain@nightly + # with: + # target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf + # components: clippy,rustfmt,rust-src # Prepare cargo-batch - name: Setup cargo-batch @@ -79,14 +75,22 @@ jobs: cargo install --git https://github.com/embassy-rs/cargo-batch cargo --bin cargo-batch --locked --force fi - # //Define a new environment variable called toolchain - - if: ${{ contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} - run: echo "TOOLCHAIN=+esp" >> $GITHUB_ENV + # # //Define a new environment variable called toolchain + # - if: ${{ contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.chip) }} + # run: echo "TOOLCHAIN=+esp" >> $GITHUB_ENV - uses: Swatinem/rust-cache@v2 - name: Generate and check project - run: cargo ${{ env.TOOLCHAIN }} xtask check ${{ matrix.chip }} --all-combinations --build + run: | + cargo +esp xtask check esp32 --all-combinations --build + cargo +esp xtask check esp32s2 --all-combinations --build + cargo +esp xtask check esp32s3 --all-combinations --build + cargo +stable xtask check esp32c2 --all-combinations --build + cargo +stable xtask check esp32c3 --all-combinations --build + cargo +stable xtask check esp32c6 --all-combinations --build + cargo +stable xtask check esp32h2 --all-combinations --build + # run: cargo ${{ env.TOOLCHAIN }} xtask check ${{ matrix.chip }} --all-combinations --build - if: github.event_name == 'schedule' name: Run cargo-package From 742171abdd8f6f1dc3d3a1d2495cd068ac28df30 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Mon, 20 Oct 2025 12:12:51 +0200 Subject: [PATCH 07/11] sure --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5224089..da37a24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,18 @@ jobs: # target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf # components: clippy,rustfmt,rust-src + # Install the Rust toolchain for Xtensa devices: + - uses: esp-rs/xtensa-toolchain@v1.6 + with: + version: 1.90.0.0 + + # Install the Rust stable toolchain for RISC-V devices: + - uses: dtolnay/rust-toolchain@v1 + with: + target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf + toolchain: stable + components: rust-src + # Prepare cargo-batch - name: Setup cargo-batch run: | From 968e7f344c523c8ed02614d7a25a3254400a2093 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Mon, 20 Oct 2025 15:13:32 +0200 Subject: [PATCH 08/11] now what? fix --- .github/workflows/ci.yml | 9 +-- xtask/Cargo.toml | 1 + xtask/src/main.rs | 139 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 131 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da37a24..1f8e663 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,14 +94,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Generate and check project - run: | - cargo +esp xtask check esp32 --all-combinations --build - cargo +esp xtask check esp32s2 --all-combinations --build - cargo +esp xtask check esp32s3 --all-combinations --build - cargo +stable xtask check esp32c2 --all-combinations --build - cargo +stable xtask check esp32c3 --all-combinations --build - cargo +stable xtask check esp32c6 --all-combinations --build - cargo +stable xtask check esp32h2 --all-combinations --build + run: cargo xtask check-all # run: cargo ${{ env.TOOLCHAIN }} xtask check ${{ matrix.chip }} --all-combinations --build - if: github.event_name == 'schedule' diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 5ee1752..04cdb07 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -13,3 +13,4 @@ esp-generate = { path = "..", default-features = false } log = "0.4.28" tempfile = "3.23.0" serde_yaml = "0.9.33" +strum = { version = "0.27.1", features = ["derive"] } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 2c87bb1..382f1ca 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -13,7 +13,9 @@ use esp_generate::{ template::{GeneratorOptionCategory, GeneratorOptionItem, Template}, }; use esp_metadata::Chip; -use log::{info, warn}; +use log::info; + +use strum::IntoEnumIterator as _; // Unfortunate hard-coded list of non-codegen options const IGNORED_CATEGORIES: &[&str] = &["editor", "optional"]; @@ -42,6 +44,13 @@ enum Commands { #[arg(short, long)] dry_run: bool, }, + /// Technically equal to `Check` command executed with `--all-combinations --build` options for every chip. + /// Intented to be used for global testing using `cargo-batch` + CheckAll { + /// Just print what would be tested + #[arg(short, long)] + dry_run: bool, + }, } /// A builder for constructing cargo command line arguments. @@ -57,7 +66,7 @@ pub struct CargoArgsBuilder { pub(crate) args: Vec, pub(crate) configs: Vec, pub(crate) env_vars: HashMap, - + pub(crate) current_dir: Option, } impl CargoArgsBuilder { @@ -76,6 +85,12 @@ impl CargoArgsBuilder { self } + #[must_use] + pub fn current_dir>(mut self, dir: P) -> Self { + self.current_dir = Some(dir.into()); + self + } + /// Set the path to the Cargo configuration file (.cargo/config.toml) #[must_use] pub fn config_path(mut self, path: PathBuf) -> Self { @@ -239,6 +254,10 @@ fn main() -> Result<()> { build, dry_run, } => check(&workspace, chip, all_combinations, build, dry_run), + + Commands::CheckAll { + dry_run, + } => check_all(&workspace, dry_run), } } @@ -287,6 +306,7 @@ fn check( // Ensure that the generated project builds without errors: commands.push( CargoArgsBuilder::new(if build { "build".to_string() } else { "check".to_string() }) + .current_dir(¤t_dir) .target(chip.target()), ); @@ -295,6 +315,7 @@ fn check( commands.push( CargoArgsBuilder::new("test".to_string()) .args(&["--no-run".to_string()]) + .current_dir(¤t_dir) .target(chip.target()), ); } @@ -303,6 +324,7 @@ fn check( commands.push( CargoArgsBuilder::new("clippy".to_string()) .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) + .current_dir(¤t_dir) .target(chip.target()), ); @@ -312,10 +334,100 @@ fn check( for c in commands.build(false) { println!("Command: cargo {}", c.command.join(" ").replace("---", "\n ---")); - c.run(false, ¤t_dir)?; + c.run()?; + } + } + + Ok(()) +} + + +// Run a full check over every chip in one cargo-batch. +fn check_all( + workspace: &Path, + dry_run: bool, +) -> Result<()> { + if dry_run { + info!("Dry run — no commands executed."); + return Ok(()); + } + + let mut batch = CargoCommandBatcher::new(); + // Keep tempdirs alive until after the batch executes + let mut _tempdirs: Vec = Vec::new(); + + const PROJECT_NAME: &str = "test"; + + // let chip = Chip::Esp32; + + for chip in Chip::iter().collect::>() { + log::info!("BUILD: {chip}"); + + info!("Going to check"); + let to_check = options_for_chip(chip, true)?; + for check in &to_check { + info!("\"{}\"", check.join(", ")); + } + + for options in to_check { + log::info!("WITH OPTIONS: {options:?}"); + + // We will generate the project in a temporary directory, to avoid + // making a mess when this subcommand is executed locally: + let project_dir = tempfile::tempdir()?; + let project_path = project_dir.path().to_path_buf(); + log::info!("PROJECT PATH: {project_path:?}"); + + // Generate a project targeting the specified chip and using the + // specified generation options: + generate(workspace, &project_path, PROJECT_NAME, chip, &options)?; + + // Hold the tempdir so it isn’t deleted before we run the batch + _tempdirs.push(project_dir); + + let current_dir = project_path.join(PROJECT_NAME); + + // let manifest_path = project_path.join(PROJECT_NAME).join("Cargo.toml"); + + // Ensure that the generated project builds without errors: + batch.push( + CargoArgsBuilder::new("build".to_string()) + .current_dir(¤t_dir) + .target(chip.target()), + ); + + // Ensure that the generated test project builds also: + if options.iter().any(|o| o == "embedded-test") { + batch.push( + CargoArgsBuilder::new("test".to_string()) + .args(&["--no-run".to_string()]) + .current_dir(¤t_dir) + .target(chip.target()), + ); + } + + // Run clippy against the generated project to check for lint errors: + batch.push( + CargoArgsBuilder::new("clippy".to_string()) + .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) + .current_dir(¤t_dir) + .target(chip.target()), + ); + + // Ensure that the generated project is correctly formatted: + // batch.push( + // CargoArgsBuilder::new("fmt".to_string()) + // .args(&["--".to_string(), "--check".to_string()]) + // .manifest_path(manifest_path.clone()) + // ); } } + for c in batch.build(false) { + println!("Command: cargo {}", c.command.join(" ").replace("---", "\n ---")); + c.run()?; + } + Ok(()) } @@ -547,15 +659,16 @@ pub struct BuiltCommand { pub artifact_name: String, pub command: Vec, pub env_vars: Vec<(String, String)>, + pub cwd: PathBuf, } impl BuiltCommand { - pub fn run(&self, capture: bool, dir: &PathBuf) -> Result { - run_with_env(&self.command, &dir, self.env_vars.clone(), capture) + pub fn run(&self) -> Result { + run_with_env(&self.command, &self.cwd, self.env_vars.clone()) } } -fn run_with_env(args: &[String], cwd: &Path, envs: I, capture: bool) -> Result +fn run_with_env(args: &[String], cwd: &Path, envs: I) -> Result where I: IntoIterator + core::fmt::Debug, K: AsRef, @@ -635,6 +748,11 @@ impl CargoCommandBatcher { continue; } + let batch_cwd = group[0] + .current_dir + .clone() + .expect("current_dir must be set for batched commands"); + let mut command = Vec::new(); let mut batch_len = 0; let mut commands_in_batch = 0; @@ -658,13 +776,11 @@ impl CargoCommandBatcher { } let mut c = item.clone(); - c.toolchain = None; c.configs = Vec::new(); c.config_path = None; let args = c.build(); - let command_chars = 4 + args.iter().map(|arg| arg.len() + 1).sum::(); if !command.is_empty() @@ -675,6 +791,7 @@ impl CargoCommandBatcher { artifact_name: String::from("batch"), command: std::mem::take(&mut command), env_vars: key.env_vars.clone(), + cwd: batch_cwd.clone(), }); } @@ -682,8 +799,7 @@ impl CargoCommandBatcher { if let Some(tc) = key.toolchain.as_ref() { command.push(format!("+{tc}")); } - - command.push("batch".to_string()); + command.push(String::from("batch")); if !key.config_file.is_empty() && let Some(config_path) = &group[0].config_path { @@ -708,6 +824,7 @@ impl CargoCommandBatcher { artifact_name: String::from("batch"), command, env_vars: key.env_vars.clone(), + cwd: batch_cwd, }); } } @@ -715,6 +832,7 @@ impl CargoCommandBatcher { all } + fn build_for_cargo(&self) -> Vec { let mut all = Vec::new(); @@ -744,6 +862,7 @@ impl CargoCommandBatcher { .iter() .map(|(k, v)| (k.clone(), v.clone())) .collect(), + cwd: item.current_dir.clone().expect("current_dir must be set"), } } From be1239ed7ec5feb1c4ca24fbfba3d22a4655fb3a Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Mon, 20 Oct 2025 16:21:01 +0200 Subject: [PATCH 09/11] skip clippy build --- xtask/src/main.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 382f1ca..a5f0887 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -407,12 +407,12 @@ fn check_all( } // Run clippy against the generated project to check for lint errors: - batch.push( - CargoArgsBuilder::new("clippy".to_string()) - .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) - .current_dir(¤t_dir) - .target(chip.target()), - ); + // batch.push( + // CargoArgsBuilder::new("clippy".to_string()) + // .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) + // .current_dir(¤t_dir) + // .target(chip.target()), + // ); // Ensure that the generated project is correctly formatted: // batch.push( From 1dcb00056362ff3473df8e8dac026efcac96280b Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Mon, 20 Oct 2025 17:35:37 +0200 Subject: [PATCH 10/11] will work now --- template/.cargo/config.toml | 4 +++ xtask/src/main.rs | 58 +++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/template/.cargo/config.toml b/template/.cargo/config.toml index 4861d49..2adc905 100644 --- a/template/.cargo/config.toml +++ b/template/.cargo/config.toml @@ -36,4 +36,8 @@ rustflags = [ target = "riscv32imac-unknown-none-elf" [unstable] +#IF option("alloc") build-std = ["alloc", "core"] +#ELSE +#+build-std = ["core"] +#ENDIF diff --git a/xtask/src/main.rs b/xtask/src/main.rs index a5f0887..4ff9792 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -321,12 +321,12 @@ fn check( } // Run clippy against the generated project to check for lint errors: - commands.push( - CargoArgsBuilder::new("clippy".to_string()) - .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) - .current_dir(¤t_dir) - .target(chip.target()), - ); + // commands.push( + // CargoArgsBuilder::new("clippy".to_string()) + // .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) + // .current_dir(¤t_dir) + // .target(chip.target()), + // ); // TODO get me back // commands.push(CargoArgsBuilder::new("fmt".to_string()) @@ -382,17 +382,21 @@ fn check_all( // specified generation options: generate(workspace, &project_path, PROJECT_NAME, chip, &options)?; + let project_root = project_path.join(PROJECT_NAME); + let manifest_path = project_root.join("Cargo.toml"); + let config_path = project_root.join(".cargo").join("config.toml"); + // Hold the tempdir so it isn’t deleted before we run the batch _tempdirs.push(project_dir); - let current_dir = project_path.join(PROJECT_NAME); - // let manifest_path = project_path.join(PROJECT_NAME).join("Cargo.toml"); // Ensure that the generated project builds without errors: batch.push( CargoArgsBuilder::new("build".to_string()) - .current_dir(¤t_dir) + .manifest_path(manifest_path.clone()) + .config_path(config_path.clone()) + // .current_dir(¤t_dir) .target(chip.target()), ); @@ -401,7 +405,8 @@ fn check_all( batch.push( CargoArgsBuilder::new("test".to_string()) .args(&["--no-run".to_string()]) - .current_dir(¤t_dir) + .manifest_path(manifest_path.clone()) + .config_path(config_path.clone()) .target(chip.target()), ); } @@ -410,7 +415,8 @@ fn check_all( // batch.push( // CargoArgsBuilder::new("clippy".to_string()) // .args(&["--no-deps".to_string(), "--".to_string(), "-Dwarnings".to_string()]) - // .current_dir(¤t_dir) + // .manifest_path(manifest_path.clone()) + // .config_path(config_path.clone()) // .target(chip.target()), // ); @@ -419,6 +425,7 @@ fn check_all( // CargoArgsBuilder::new("fmt".to_string()) // .args(&["--".to_string(), "--check".to_string()]) // .manifest_path(manifest_path.clone()) + // .config_path(config_path.clone()) // ); } } @@ -659,12 +666,23 @@ pub struct BuiltCommand { pub artifact_name: String, pub command: Vec, pub env_vars: Vec<(String, String)>, - pub cwd: PathBuf, + // pub cwd: PathBuf, } impl BuiltCommand { pub fn run(&self) -> Result { - run_with_env(&self.command, &self.cwd, self.env_vars.clone()) + let cwd = self + .command + .windows(2) + .find_map(|w| { + if w[0] == "--manifest-path" { + Path::new(&w[1]).parent().map(|p| p.to_path_buf()) + } else { + None + } + }).unwrap(); + + run_with_env(&self.command, &cwd, self.env_vars.clone()) } } @@ -748,11 +766,6 @@ impl CargoCommandBatcher { continue; } - let batch_cwd = group[0] - .current_dir - .clone() - .expect("current_dir must be set for batched commands"); - let mut command = Vec::new(); let mut batch_len = 0; let mut commands_in_batch = 0; @@ -767,10 +780,7 @@ impl CargoCommandBatcher { for item in group.iter() { // Only some commands can be batched let batchable = ["build", "doc", "check"]; - if !batchable - .iter() - .any(|&subcommand| subcommand == item.subcommand) - { + if !batchable.iter().any(|&sub| sub == item.subcommand) { all.push(Self::build_one_for_cargo(item)); continue; } @@ -791,7 +801,6 @@ impl CargoCommandBatcher { artifact_name: String::from("batch"), command: std::mem::take(&mut command), env_vars: key.env_vars.clone(), - cwd: batch_cwd.clone(), }); } @@ -824,7 +833,6 @@ impl CargoCommandBatcher { artifact_name: String::from("batch"), command, env_vars: key.env_vars.clone(), - cwd: batch_cwd, }); } } @@ -862,7 +870,7 @@ impl CargoCommandBatcher { .iter() .map(|(k, v)| (k.clone(), v.clone())) .collect(), - cwd: item.current_dir.clone().expect("current_dir must be set"), + // cwd: item.current_dir.clone().expect("current_dir must be set"), } } From 745dc94fce9f584c0b6de8fb030fbc3be92e77fb Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Tue, 21 Oct 2025 13:18:17 +0200 Subject: [PATCH 11/11] linker args --- template/.cargo/config.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/template/.cargo/config.toml b/template/.cargo/config.toml index 2adc905..e218691 100644 --- a/template/.cargo/config.toml +++ b/template/.cargo/config.toml @@ -30,6 +30,13 @@ rustflags = [ #IF option("stack-smashing-protection") "-Z", "stack-protector=all", #ENDIF +#IF option("defmt") + "-C", "link-arg=-Tdefmt.x", +#ENDIF +#IF option("embedded-test") + "-C", "link-arg=-Tembedded-test.x", +#ENDIF + "-C", "link-arg=-Tlinkall.x", ] #REPLACE riscv32imac-unknown-none-elf rust_target