Skip to content

Commit 647f3e4

Browse files
feat(cargo-codspeed): throw an error when building a bench target with default harness
1 parent d332b91 commit 647f3e4

File tree

6 files changed

+112
-8
lines changed

6 files changed

+112
-8
lines changed

Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cargo-codspeed/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ itertools = { workspace = true }
2727
anstyle = "1.0.8"
2828
serde = { workspace = true }
2929
serde_json = { workspace = true }
30+
toml = "0.8"
3031
codspeed = { path = "../codspeed", version = "=4.0.5" }
3132

3233
[dev-dependencies]

crates/cargo-codspeed/src/build.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use crate::{
44
measurement_mode::MeasurementMode,
55
prelude::*,
66
};
7+
use anyhow::Context;
78
use cargo_metadata::{camino::Utf8PathBuf, Message, Metadata, TargetKind};
9+
use std::collections::HashMap;
810
use std::process::{exit, Command, Stdio};
911

1012
struct BuildOptions<'a> {
@@ -31,6 +33,50 @@ pub struct BuildConfig {
3133
pub passthrough_flags: Vec<String>,
3234
}
3335

36+
fn get_bench_harness_value(
37+
manifest_path: &Utf8PathBuf,
38+
bench_name: &str,
39+
cache: &mut HashMap<Utf8PathBuf, toml::Table>,
40+
) -> Result<bool> {
41+
let manifest_table = if let Some(table) = cache.get(manifest_path) {
42+
table
43+
} else {
44+
// Read and parse the Cargo.toml file
45+
let manifest_content = std::fs::read_to_string(manifest_path)
46+
.with_context(|| format!("Failed to read manifest at {manifest_path}"))?;
47+
let table: toml::Table = toml::from_str(&manifest_content)
48+
.with_context(|| format!("Failed to parse TOML in {manifest_path}"))?;
49+
cache.insert(manifest_path.clone(), table);
50+
cache.get(manifest_path).unwrap()
51+
};
52+
53+
// Look for [[bench]] sections
54+
let Some(benches) = manifest_table.get("bench").and_then(|v| v.as_array()) else {
55+
// If no [[bench]] sections, it's not an error, benches present in <root>/benches/<name>.rs
56+
// are still collected with harness = true
57+
return Ok(true);
58+
};
59+
60+
// Find the bench entry with matching name
61+
let matching_bench = benches
62+
.iter()
63+
.filter_map(|bench| bench.as_table())
64+
.find(|bench_table| {
65+
bench_table
66+
.get("name")
67+
.and_then(|v| v.as_str())
68+
.is_some_and(|name| name == bench_name)
69+
});
70+
71+
// Check if harness is enabled (defaults to true)
72+
let harness_enabled = matching_bench
73+
.and_then(|bench_table| bench_table.get("harness"))
74+
.and_then(|v| v.as_bool())
75+
.unwrap_or(true);
76+
77+
Ok(harness_enabled)
78+
}
79+
3480
impl BuildOptions<'_> {
3581
/// Builds the benchmarks by invoking cargo
3682
/// Returns a list of built benchmarks, with path to associated executables
@@ -60,6 +106,8 @@ impl BuildOptions<'_> {
60106
);
61107

62108
let mut built_benches = Vec::new();
109+
let mut bench_targets_with_default_harness = Vec::new();
110+
let mut manifest_cache = HashMap::new();
63111

64112
let package_names = self
65113
.package_filters
@@ -95,6 +143,15 @@ impl BuildOptions<'_> {
95143
let add_bench_to_codspeed_dir = package_names.iter().contains(&package.name);
96144

97145
if add_bench_to_codspeed_dir {
146+
if get_bench_harness_value(
147+
&package.manifest_path,
148+
&bench_target_name,
149+
&mut manifest_cache,
150+
)? {
151+
bench_targets_with_default_harness
152+
.push((package.name.to_string(), bench_target_name.clone()));
153+
}
154+
98155
built_benches.push(BuiltBench {
99156
package: package.name.to_string(),
100157
bench: bench_target_name,
@@ -114,6 +171,25 @@ impl BuildOptions<'_> {
114171
exit(status.code().expect("Could not get exit code"));
115172
}
116173

174+
if !bench_targets_with_default_harness.is_empty() {
175+
let targets_list = bench_targets_with_default_harness
176+
.into_iter()
177+
.map(|(package, bench)| format!(" - `{bench}` in package `{package}`"))
178+
.join("\n");
179+
180+
bail!("\
181+
CodSpeed will not work with the following benchmark targets:
182+
{targets_list}
183+
184+
CodSpeed requires benchmark targets to disable the default test harness because benchmark frameworks handle harnessing themselves.
185+
186+
Either disable the default harness by adding `harness = false` to the corresponding \
187+
`[[bench]]` section in the Cargo.toml, or specify which targets to build by using \
188+
`cargo codspeed build -p package_name --bench first_target --bench second_target`.
189+
190+
See `cargo codspeed build --help` for more information.");
191+
}
192+
117193
for built_bench in &built_benches {
118194
eprintln!(
119195
"Built benchmark `{}` in package `{}`",

crates/cargo-codspeed/src/run.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ pub fn run_benches(
134134

135135
command
136136
.status()
137-
.map_err(|e| anyhow!("failed to execute the benchmark process: {}", e))
137+
.map_err(|e| anyhow!("failed to execute the benchmark process: {e}"))
138138
.and_then(|status| {
139139
if status.success() {
140140
Ok(())

crates/criterion_compat/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ repository = "https://github.com/CodSpeedHQ/codspeed-rust"
1111
homepage = "https://codspeed.io"
1212
license = "MIT OR Apache-2.0"
1313
categories = [
14-
"development-tools",
15-
"development-tools::profiling",
16-
"development-tools::testing",
14+
"development-tools",
15+
"development-tools::profiling",
16+
"development-tools::testing",
1717
]
1818
keywords = ["codspeed", "benchmark", "criterion"]
1919
[dependencies]
@@ -26,7 +26,7 @@ regex = { version = "1.5", default-features = false, features = ["std"] }
2626
futures = { version = "0.3", default-features = false, optional = true }
2727
smol = { version = "2.0", default-features = false, optional = true }
2828
tokio = { version = "1.39", default-features = false, features = [
29-
"rt",
29+
"rt",
3030
], optional = true }
3131
async-std = { version = "1.12", optional = true }
3232

crates/divan_compat/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ repository = "https://github.com/CodSpeedHQ/codspeed-rust"
1111
homepage = "https://codspeed.io"
1212
license = "MIT OR Apache-2.0"
1313
categories = [
14-
"development-tools",
15-
"development-tools::profiling",
16-
"development-tools::testing",
14+
"development-tools",
15+
"development-tools::profiling",
16+
"development-tools::testing",
1717
]
1818
keywords = ["codspeed", "benchmark", "divan"]
1919

0 commit comments

Comments
 (0)