1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
* Mitts Build — Rust-first build and package tooling with shared artifact cache
* Copyright (c) 2026 Alexander Panasenko
*
* Contact: alex@prod.codes
* Author: https://prod.codes/about/
* Project: https://github.com/alex09x/mitts
* SPDX-License-Identifier: MIT OR Apache-2.0
*/
//! `mitts bench`: compile the benchmark targets of the selected packages in the `bench` profile
//! (the release profile plus `[profile.bench]`) and run them with `--bench`, the argument Cargo
//! gives a benchmark binary to measure instead of test. It reuses the test pipeline; the
//! differences are the selection (benchmark targets only), the profile, the argument and that
//! no documentation tests run.
use crate::cli::test::{handle_test, TestArgs};
use clap::Args;
use std::path::PathBuf;
#[derive(Args, Debug, Clone, Default)]
pub struct BenchArgs {
/// Path to Cargo.toml
#[arg(long)]
pub manifest_path: Option<PathBuf>,
/// Limit concurrent native build units (same as MITTS_JOBS; the flag wins)
#[arg(short = 'j', long, value_name = "N", value_parser = clap::value_parser!(u64).range(1..))]
pub jobs: Option<u64>,
/// Packages to benchmark (repeat for multiple packages)
#[arg(short = 'p', long = "package", action = clap::ArgAction::Append)]
pub packages: Vec<String>,
/// Benchmark all packages in the workspace
#[arg(long, visible_alias = "all")]
pub workspace: bool,
/// Exclude packages from being benchmarked
#[arg(long, value_name = "SPEC", action = clap::ArgAction::Append)]
pub exclude: Vec<String>,
/// Benchmark only the named benchmark target (repeatable); default is every benchmark target
#[arg(long = "bench", value_name = "NAME", action = clap::ArgAction::Append)]
pub bench: Vec<String>,
/// Directory for all generated artifacts
#[arg(long)]
pub target_dir: Option<PathBuf>,
/// Space or comma separated list of features to activate
#[arg(short = 'F', long, value_delimiter = ',', num_args = 1..)]
pub features: Vec<String>,
/// Do not activate the `default` feature
#[arg(long)]
pub no_default_features: bool,
/// Activate all available features
#[arg(long)]
pub all_features: bool,
/// Do not read from or write to the shared action cache
#[arg(long)]
pub no_cache: bool,
/// Directory for the shared artifact and action cache
#[arg(long)]
pub cache_dir: Option<PathBuf>,
/// Select target triple for cross-compilation
#[arg(long)]
pub target: Option<String>,
/// Require an existing compatible Cargo.lock; do not resolve or write one.
#[arg(long)]
pub locked: bool,
/// Do not use the network to resolve dependencies; use only local sources.
#[arg(long)]
pub offline: bool,
/// Require a compatible Cargo.lock and use only locally available sources.
#[arg(long)]
pub frozen: bool,
/// Compile the benchmarks, but don't run them
#[arg(long)]
pub no_run: bool,
/// Run all benchmark targets regardless of failure (default is fail-fast)
#[arg(long)]
pub no_fail_fast: bool,
/// Use verbose output (-v, -vv for very verbose)
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
/// Benchmark name filter, passed to the benchmark binaries
#[arg(value_name = "BENCHNAME")]
pub filter: Option<String>,
/// Arguments passed to the benchmark binaries after `--`
#[arg(last = true)]
pub bench_args: Vec<String>,
}
pub fn handle_bench(args: &BenchArgs) -> i32 {
handle_test(&test_args(args))
}
/// The `mitts test` invocation that `mitts bench` stands for.
fn test_args(args: &BenchArgs) -> TestArgs {
let mut passed = vec!["--bench".to_string()];
passed.extend(args.bench_args.iter().cloned());
TestArgs {
manifest_path: args.manifest_path.clone(),
jobs: args.jobs,
packages: args.packages.clone(),
workspace: args.workspace,
exclude: args.exclude.clone(),
release: true,
target_dir: args.target_dir.clone(),
features: args.features.clone(),
no_default_features: args.no_default_features,
all_features: args.all_features,
no_cache: args.no_cache,
cache_dir: args.cache_dir.clone(),
target: args.target.clone(),
locked: args.locked,
offline: args.offline,
frozen: args.frozen,
no_run: args.no_run,
no_fail_fast: args.no_fail_fast,
verbose: args.verbose,
filter: args.filter.clone(),
// Every benchmark target unless some are named.
benches: args.bench.is_empty(),
bench_names: args.bench.clone(),
bench_mode: true,
test_args: passed,
..Default::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bench_runs_the_benchmark_targets_in_release_with_the_bench_argument() {
let args = BenchArgs {
bench_args: vec!["--save-baseline".into(), "main".into()],
filter: Some("fast".into()),
..Default::default()
};
let test = test_args(&args);
assert!(test.release && test.benches && test.bench_mode);
assert!(test.bench_names.is_empty());
assert_eq!(test.test_args, ["--bench", "--save-baseline", "main"]);
assert_eq!(test.filter.as_deref(), Some("fast"));
}
#[test]
fn named_benchmarks_replace_the_all_benchmarks_selection() {
let args = BenchArgs {
bench: vec!["throughput".into()],
..Default::default()
};
let test = test_args(&args);
assert!(!test.benches);
assert_eq!(test.bench_names, ["throughput"]);
}
}