From bfeb0be8ba290d3d267a0bf1db2850b672fba918 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Wed, 21 Jan 2026 17:33:32 +0100 Subject: [PATCH] feat(cargo-codspeed): allow build with multiple modes --- crates/cargo-codspeed/src/app.rs | 95 +++++++++++++------ crates/cargo-codspeed/src/build.rs | 7 +- crates/cargo-codspeed/src/measurement_mode.rs | 3 +- .../cargo-codspeed/tests/measurement_modes.rs | 30 ++++++ 4 files changed, 101 insertions(+), 34 deletions(-) create mode 100644 crates/cargo-codspeed/tests/measurement_modes.rs diff --git a/crates/cargo-codspeed/src/app.rs b/crates/cargo-codspeed/src/app.rs index 439bb97c..544ea7d8 100644 --- a/crates/cargo-codspeed/src/app.rs +++ b/crates/cargo-codspeed/src/app.rs @@ -1,6 +1,10 @@ -use crate::{measurement_mode::MeasurementMode, prelude::*, run::run_benches}; +use crate::{ + measurement_mode::{BuildMode, MeasurementMode}, + prelude::*, + run::run_benches, +}; use cargo_metadata::MetadataCommand; -use clap::{Args, Parser, Subcommand}; +use clap::{ArgAction, Args, Parser, Subcommand}; use std::{ffi::OsString, process::exit}; use crate::build::{build_benches, BuildConfig}; @@ -12,17 +16,12 @@ struct Cli { #[arg(short, long, global = true)] quiet: bool, - /// The measurement tool to use for measuring performance. - /// Automatically set to `walltime` on macro runners - #[arg(short, long, global = true, env = "CODSPEED_RUNNER_MODE")] - measurement_mode: Option, - #[command(subcommand)] command: Commands, } const PACKAGE_HELP: &str = "Package Selection"; -#[derive(Args)] +#[derive(Args, Clone)] pub(crate) struct PackageFilters { /// Select all packages in the workspace #[arg(long, help_heading = PACKAGE_HELP)] @@ -35,7 +34,7 @@ pub(crate) struct PackageFilters { pub(crate) package: Vec, } -#[derive(Args)] +#[derive(Args, Clone)] pub(crate) struct BenchTargetFilters { /// Select only the specified benchmark target (all benchmark targets by default) #[arg(long, help_heading = TARGET_HELP)] @@ -89,6 +88,18 @@ enum Commands { #[command(flatten)] bench_target_filters: BenchTargetFilters, + + /// The measurement tool(s) to use for measuring performance. + /// Can be specified multiple times or comma-separated. + #[arg( + short = 'm', + long = "measurement-mode", + value_delimiter = ',', + action = ArgAction::Append, + help_heading = COMPILATION_HELP, + env = "CODSPEED_RUNNER_MODE" + )] + measurement_mode: Vec, }, /// Run the previously built benchmarks Run { @@ -100,6 +111,11 @@ enum Commands { #[command(flatten)] bench_target_filters: BenchTargetFilters, + + /// The measurement tool to use for measuring performance. + /// Automatically set to `walltime` on macro runners + #[arg(short = 'm', long = "measurement-mode", env = "CODSPEED_RUNNER_MODE")] + measurement_mode: Option, }, } @@ -107,9 +123,6 @@ pub fn run(args: impl Iterator) -> Result<()> { let metadata = MetadataCommand::new().exec()?; let cli = Cli::try_parse_from(args)?; - let measurement_mode = cli.measurement_mode.unwrap_or_default(); - eprintln!("[cargo-codspeed] Measurement mode: {measurement_mode:?}\n"); - let res = match cli.command { Commands::Build { package_filters, @@ -122,6 +135,7 @@ pub fn run(args: impl Iterator) -> Result<()> { locked, offline, frozen, + measurement_mode, } => { let passthrough_flags = { let mut passthrough_flags = Vec::new(); @@ -147,30 +161,51 @@ pub fn run(args: impl Iterator) -> Result<()> { }; let features = features.map(|f| f.split([' ', ',']).map(|s| s.to_string()).collect_vec()); - build_benches( - &metadata, - BuildConfig { - package_filters, - bench_target_filters, - features, - profile, - quiet: cli.quiet, - measurement_mode, - passthrough_flags, - }, - ) + + let build_modes: Vec = measurement_mode + .into_iter() + .map(BuildMode::from) + .unique() + .collect(); + let build_modes = if build_modes.is_empty() { + vec![BuildMode::default()] + } else { + build_modes + }; + + for build_mode in build_modes { + eprintln!("[cargo-codspeed] Building with build mode: {build_mode}\n"); + build_benches( + &metadata, + BuildConfig { + package_filters: package_filters.clone(), + bench_target_filters: bench_target_filters.clone(), + features: features.clone(), + profile: profile.clone(), + quiet: cli.quiet, + build_mode, + passthrough_flags: passthrough_flags.clone(), + }, + )?; + } + Ok(()) } Commands::Run { benchname, package_filters, bench_target_filters, - } => run_benches( - &metadata, - benchname, - package_filters, - bench_target_filters, measurement_mode, - ), + } => { + let mode = measurement_mode.unwrap_or_default(); + eprintln!("[cargo-codspeed] Measurement mode: {mode:?}\n"); + run_benches( + &metadata, + benchname, + package_filters, + bench_target_filters, + mode, + ) + } }; if let Err(e) = res { diff --git a/crates/cargo-codspeed/src/build.rs b/crates/cargo-codspeed/src/build.rs index 7fe2f8b5..d8823c50 100644 --- a/crates/cargo-codspeed/src/build.rs +++ b/crates/cargo-codspeed/src/build.rs @@ -1,7 +1,7 @@ use crate::{ app::{BenchTargetFilters, PackageFilters}, helpers::{clear_dir, get_codspeed_target_dir}, - measurement_mode::{BuildMode, MeasurementMode}, + measurement_mode::BuildMode, prelude::*, }; use anyhow::Context; @@ -23,13 +23,14 @@ struct BuiltBench { executable_path: Utf8PathBuf, } +#[derive(Clone)] pub struct BuildConfig { pub package_filters: PackageFilters, pub bench_target_filters: BenchTargetFilters, pub features: Option>, pub profile: String, pub quiet: bool, - pub measurement_mode: MeasurementMode, + pub build_mode: BuildMode, pub passthrough_flags: Vec, } @@ -301,7 +302,7 @@ impl PackageFilters { } pub fn build_benches(metadata: &Metadata, config: BuildConfig) -> Result<()> { - let build_mode = config.measurement_mode.into(); + let build_mode = config.build_mode; let built_benches = BuildOptions { bench_target_filters: config.bench_target_filters, package_filters: config.package_filters, diff --git a/crates/cargo-codspeed/src/measurement_mode.rs b/crates/cargo-codspeed/src/measurement_mode.rs index f0beaa4b..f2635a0c 100644 --- a/crates/cargo-codspeed/src/measurement_mode.rs +++ b/crates/cargo-codspeed/src/measurement_mode.rs @@ -2,8 +2,9 @@ use clap::ValueEnum; use serde::Serialize; use std::fmt; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum BuildMode { + #[default] Analysis, Walltime, } diff --git a/crates/cargo-codspeed/tests/measurement_modes.rs b/crates/cargo-codspeed/tests/measurement_modes.rs new file mode 100644 index 00000000..7ffa1828 --- /dev/null +++ b/crates/cargo-codspeed/tests/measurement_modes.rs @@ -0,0 +1,30 @@ +use predicates::str::contains; + +mod helpers; +use helpers::*; + +const DIR: &str = "tests/simple-criterion.in"; + +#[test] +fn test_build_multiple_measurement_modes() { + let dir = setup(DIR, Project::Simple); + cargo_codspeed(&dir) + .args(["build", "-m", "simulation", "-m", "walltime"]) + .assert() + .success() + .stderr(contains("build mode: analysis")) + .stderr(contains("build mode: walltime")); + teardown(dir); +} + +#[test] +fn test_build_multiple_measurement_modes_comma_separated() { + let dir = setup(DIR, Project::Simple); + cargo_codspeed(&dir) + .args(["build", "-m", "simulation,walltime"]) + .assert() + .success() + .stderr(contains("build mode: analysis")) + .stderr(contains("build mode: walltime")); + teardown(dir); +}