Skip to content

Commit 471cb3d

Browse files
committed
fix(cargo-codspeed): handle bench build per process to fix working directory issues
1 parent 2e7a495 commit 471cb3d

File tree

5 files changed

+146
-87
lines changed

5 files changed

+146
-87
lines changed

crates/cargo-codspeed/src/build.rs

Lines changed: 77 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use crate::{
2-
helpers::{clear_dir, get_codspeed_target_dir},
2+
helpers::{clear_dir, get_codspeed_target_dir, get_target_packages},
33
prelude::*,
44
};
55

66
use std::{collections::BTreeSet, fs::create_dir_all, rc::Rc};
77

88
use cargo::{
9-
core::{FeatureValue, Package, Verbosity, Workspace},
9+
core::{FeatureValue, Package, Target, Verbosity, Workspace},
1010
ops::{CompileFilter, CompileOptions, Packages},
1111
util::{command_prelude::CompileMode, interning::InternedString},
1212
Config,
@@ -50,46 +50,56 @@ fn get_compile_options(
5050
Ok(compile_opts)
5151
}
5252

53+
struct BenchToBuild<'a> {
54+
package: &'a Package,
55+
target: &'a Target,
56+
}
57+
5358
pub fn build_benches(
5459
ws: &Workspace,
5560
selected_benches: Option<Vec<String>>,
5661
package_name: Option<String>,
5762
features: Option<Vec<String>>,
5863
) -> Result<()> {
59-
let is_root_package = package_name.is_none();
60-
let package = match package_name.as_ref() {
61-
Some(package_name) => ws.members()
62-
.find(|p| p.name().to_string() == *package_name)
63-
.ok_or_else(|| anyhow!("Package {} not found", package_name))?
64-
65-
,
66-
None => ws.current().map_err(|_| anyhow!("No package found. If working with a workspace please use the -p option to specify a member."))?
67-
};
68-
let all_benches = package
69-
.targets()
70-
.iter()
71-
.filter(|t| t.is_bench())
72-
.collect_vec();
64+
let packages_to_build = get_target_packages(&package_name, ws)?;
65+
let mut benches_to_build = vec![];
66+
for package in packages_to_build.iter() {
67+
let benches = package
68+
.targets()
69+
.iter()
70+
.filter(|t| t.is_bench())
71+
.collect_vec();
72+
benches_to_build.extend(
73+
benches
74+
.into_iter()
75+
.map(|t| BenchToBuild { package, target: t }),
76+
);
77+
}
7378

74-
let all_benches_count = all_benches.len();
79+
let all_benches_count = benches_to_build.len();
7580

76-
let benches = if let Some(selected_benches) = selected_benches {
77-
all_benches
81+
let benches_to_build = if let Some(selected_benches) = selected_benches {
82+
benches_to_build
7883
.into_iter()
79-
.filter(|t| selected_benches.contains(&t.name().to_string()))
84+
.filter(|t| selected_benches.contains(&t.target.name().to_string()))
8085
.collect_vec()
8186
} else {
82-
all_benches
87+
benches_to_build
8388
};
8489

90+
let actual_benches_count = benches_to_build.len();
91+
8592
ws.config().shell().set_verbosity(Verbosity::Normal);
8693
ws.config().shell().status_with_color(
8794
"Collected",
8895
format!(
8996
"{} benchmark suite(s) to build{}",
90-
benches.len(),
91-
if all_benches_count > benches.len() {
92-
format!(" ({} filtered out)", all_benches_count - benches.len())
97+
benches_to_build.len(),
98+
if all_benches_count > actual_benches_count {
99+
format!(
100+
" ({} filtered out)",
101+
all_benches_count - actual_benches_count
102+
)
93103
} else {
94104
"".to_string()
95105
}
@@ -98,51 +108,57 @@ pub fn build_benches(
98108
)?;
99109

100110
let config = ws.config();
111+
let codspeed_root_target_dir = get_codspeed_target_dir(ws);
112+
// Create and clear packages target directories
113+
for package in packages_to_build.iter() {
114+
let package_target_dir = codspeed_root_target_dir.join(package.name());
115+
create_dir_all(&package_target_dir)?;
116+
clear_dir(&package_target_dir)?;
117+
}
118+
let mut built_benches = 0;
119+
for bench in benches_to_build.iter() {
120+
ws.config().shell().status_with_color(
121+
"Building",
122+
format!("{} {}", bench.package.name(), bench.target.name()),
123+
Color::Yellow,
124+
)?;
125+
let is_root_package = ws.current_opt().map_or(false, |p| p == bench.package);
126+
let benches_names = vec![bench.target.name()];
127+
let compile_opts = get_compile_options(
128+
config,
129+
&features,
130+
bench.package,
131+
benches_names,
132+
is_root_package,
133+
)?;
134+
let result = cargo::ops::compile(ws, &compile_opts)?;
135+
let built_units = result
136+
.tests
137+
.into_iter()
138+
.filter(|u| u.unit.target.is_bench())
139+
.collect_vec();
140+
if built_units.is_empty() {
141+
continue;
142+
}
143+
built_benches += 1;
144+
let codspeed_target_dir = codspeed_root_target_dir.join(bench.package.name());
145+
for built_unit in built_units.iter() {
146+
let bench_dest = codspeed_target_dir
147+
.clone()
148+
.join(built_unit.unit.target.name());
149+
std::fs::copy(built_unit.path.clone(), bench_dest)?;
150+
}
151+
}
101152

102-
let benches_names = benches.iter().map(|t| t.name()).collect_vec();
103-
let benches_names_str = benches_names.join(", ");
104-
105-
ws.config()
106-
.shell()
107-
.status_with_color("Building", benches_names_str.clone(), Color::Yellow)?;
108-
let compile_opts =
109-
get_compile_options(config, &features, package, benches_names, is_root_package)?;
110-
let result = cargo::ops::compile(ws, &compile_opts)?;
111-
let built_benches = result
112-
.tests
113-
.into_iter()
114-
.filter(|u| u.unit.target.is_bench())
115-
.collect_vec();
116-
117-
if built_benches.is_empty() {
153+
if built_benches == 0 {
118154
bail!(
119155
"No benchmark target found. \
120156
Please add a benchmark target to your Cargo.toml"
121157
);
122158
}
123-
124-
ws.config()
125-
.shell()
126-
.status_with_color("Built", benches_names_str, Color::Green)?;
127-
128-
let mut codspeed_target_dir = get_codspeed_target_dir(ws);
129-
create_dir_all(&codspeed_target_dir)?;
130-
if let Some(name) = package_name.as_ref() {
131-
codspeed_target_dir = codspeed_target_dir.join(name);
132-
create_dir_all(&codspeed_target_dir)?;
133-
}
134-
clear_dir(&codspeed_target_dir)?;
135-
136-
for built_bench in built_benches.iter() {
137-
let bench_dest = codspeed_target_dir
138-
.clone()
139-
.join(built_bench.unit.target.name());
140-
std::fs::copy(built_bench.path.clone(), bench_dest)?;
141-
}
142-
143159
ws.config().shell().status_with_color(
144160
"Finished",
145-
format!("built {} benchmark suite(s)", benches.len()),
161+
format!("built {} benchmark suite(s)", built_benches),
146162
Color::Green,
147163
)?;
148164

crates/cargo-codspeed/src/helpers.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::prelude::*;
2+
use cargo::CargoResult;
23
use std::path::{Path, PathBuf};
34

45
pub fn get_codspeed_target_dir(ws: &Workspace) -> PathBuf {
@@ -8,6 +9,29 @@ pub fn get_codspeed_target_dir(ws: &Workspace) -> PathBuf {
89
.join("codspeed")
910
}
1011

12+
/// Get the packages to run benchmarks for
13+
/// If a package name is provided, only that package is a target
14+
/// If no package name is provided,
15+
/// and the current directory is a package then only that package is a target
16+
/// Otherwise all packages in the workspace are targets
17+
pub fn get_target_packages<'a>(
18+
package_name: &Option<String>,
19+
ws: &'a Workspace<'_>,
20+
) -> Result<Vec<&'a cargo::core::Package>> {
21+
let packages_to_run = if let Some(package) = package_name.as_ref() {
22+
let p = ws
23+
.members()
24+
.find(|m| m.manifest().name().to_string().as_str() == package)
25+
.ok_or(anyhow!("Package {} not found", package))?;
26+
vec![p]
27+
} else if let CargoResult::Ok(p) = ws.current() {
28+
vec![p]
29+
} else {
30+
ws.members().collect::<Vec<_>>()
31+
};
32+
Ok(packages_to_run)
33+
}
34+
1135
pub fn clear_dir<P>(dir: P) -> Result<()>
1236
where
1337
P: AsRef<Path>,

crates/cargo-codspeed/src/run.rs

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use std::{io, path::PathBuf};
33
use anyhow::anyhow;
44
use termcolor::Color;
55

6-
use crate::{helpers::get_codspeed_target_dir, prelude::*};
6+
use crate::{
7+
helpers::{get_codspeed_target_dir, get_target_packages},
8+
prelude::*,
9+
};
710

811
struct BenchToRun {
912
bench_path: PathBuf,
@@ -18,28 +21,11 @@ pub fn run_benches(
1821
package: Option<String>,
1922
) -> Result<()> {
2023
let codspeed_target_dir = get_codspeed_target_dir(ws);
21-
22-
let packages_to_run = if let Some(package) = package.as_ref() {
23-
let p = ws
24-
.members()
25-
.find(|m| m.manifest().name().to_string().as_str() == package);
26-
if let Some(p) = p {
27-
vec![p]
28-
} else {
29-
bail!("Package {} not found", package);
30-
}
31-
} else {
32-
ws.members().collect::<Vec<_>>()
33-
};
24+
let packages_to_run = get_target_packages(&package, ws)?;
3425
let mut benches: Vec<BenchToRun> = vec![];
3526
for p in packages_to_run {
3627
let package_name = p.manifest().name().to_string();
37-
let is_root_package = p.root() == ws.root();
38-
let package_target_dir = if is_root_package {
39-
codspeed_target_dir.clone()
40-
} else {
41-
codspeed_target_dir.join(&package_name)
42-
};
28+
let package_target_dir = codspeed_target_dir.join(&package_name);
4329
let working_directory = p.root().to_path_buf();
4430
if let io::Result::Ok(read_dir) = std::fs::read_dir(&package_target_dir) {
4531
for entry in read_dir {

crates/cargo-codspeed/tests/crates_working_directory.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use predicates::str::contains;
2+
use std::path::PathBuf;
23

34
mod helpers;
45
use helpers::*;
@@ -34,3 +35,30 @@ fn test_crates_working_directory_build_and_run_implicit() {
3435
.stderr(contains("Finished running 1 benchmark suite(s)"));
3536
teardown(dir);
3637
}
38+
39+
#[test]
40+
fn test_crates_working_directory_build_in_subfolder_and_run() {
41+
let dir = setup(DIR, Project::CratesWorkingDirectory);
42+
cargo_codspeed(&dir)
43+
.current_dir(PathBuf::from(&dir).join("the_crate"))
44+
.args(["build"])
45+
.assert()
46+
.success();
47+
cargo_codspeed(&dir)
48+
.arg("run")
49+
.current_dir(PathBuf::from(&dir).join("the_crate"))
50+
.assert()
51+
.success()
52+
.stderr(contains("Finished running 1 benchmark suite(s)"));
53+
cargo_codspeed(&dir)
54+
.arg("run")
55+
.assert()
56+
.success()
57+
.stderr(contains("Finished running 1 benchmark suite(s)"));
58+
cargo_codspeed(&dir)
59+
.args(["run", "-p", "the_crate"])
60+
.assert()
61+
.success()
62+
.stderr(contains("Finished running 1 benchmark suite(s)"));
63+
teardown(dir);
64+
}

crates/cargo-codspeed/tests/workspace.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ fn test_workspace_build_without_package_spec() {
2222
cargo_codspeed(&dir)
2323
.arg("build")
2424
.assert()
25-
.failure()
26-
.stderr(contains(
27-
"Error No package found. If working with a workspace please use the -p option to specify a member.",
28-
));
25+
.success()
26+
.stderr(contains("Finished built 3 benchmark suite(s)"));
27+
cargo_codspeed(&dir)
28+
.arg("run")
29+
.assert()
30+
.success()
31+
.stderr(contains("Finished running 3 benchmark suite(s)"));
2932
teardown(dir);
3033
}
3134

@@ -91,12 +94,14 @@ fn test_workspace_build_both_and_run_all() {
9194
cargo_codspeed(&dir)
9295
.arg("build")
9396
.args(["--package", "package-a"])
94-
.assert();
97+
.assert()
98+
.success();
9599

96100
cargo_codspeed(&dir)
97101
.arg("build")
98102
.args(["--package", "package-b"])
99-
.assert();
103+
.assert()
104+
.success();
100105

101106
cargo_codspeed(&dir)
102107
.arg("run")

0 commit comments

Comments
 (0)