Skip to content

Commit 08d8da1

Browse files
committed
Extract Cargo artifact iteration into a separate module
1 parent 22e7bfa commit 08d8da1

File tree

3 files changed

+133
-74
lines changed

3 files changed

+133
-74
lines changed

collector/src/cargo.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use anyhow::Context;
2+
use std::io::BufReader;
3+
use std::process::{Child, ChildStdout, Command, Stdio};
4+
5+
use cargo_metadata::{Message, MessageIter};
6+
7+
/// Iterator that returns built artifacts from a Cargo command invocation.
8+
/// It also prints any text lines or messages produced during compilation,
9+
/// and gathers the messages for better error messages.
10+
pub struct CargoArtifactIter {
11+
stream: MessageIter<BufReader<ChildStdout>>,
12+
cargo_process: Child,
13+
messages: Vec<String>,
14+
}
15+
16+
impl CargoArtifactIter {
17+
/// Adds arguments to the command required for JSON message parsing, and starts the Cargo
18+
/// invocation.
19+
pub fn from_cargo_cmd(mut cmd: Command) -> anyhow::Result<Self> {
20+
cmd.arg("--message-format")
21+
.arg("json-diagnostic-short")
22+
.stdin(Stdio::null())
23+
.stdout(Stdio::piped())
24+
.stderr(Stdio::null());
25+
let mut cargo_process = cmd.spawn()?;
26+
let stream = BufReader::new(cargo_process.stdout.take().unwrap());
27+
Ok(Self {
28+
stream: Message::parse_stream(stream),
29+
cargo_process,
30+
messages: Default::default(),
31+
})
32+
}
33+
34+
pub fn finish(mut self) -> anyhow::Result<()> {
35+
let output = self
36+
.cargo_process
37+
.wait()
38+
.context("Cargo did not exit successfully")?;
39+
if !output.success() {
40+
return Err(anyhow::anyhow!(
41+
"Failed to run cargo, exit code {}\n{}",
42+
output.code().unwrap_or(1),
43+
self.messages.join("")
44+
));
45+
}
46+
Ok(())
47+
}
48+
}
49+
50+
impl Drop for CargoArtifactIter {
51+
fn drop(&mut self) {
52+
self.cargo_process
53+
.wait()
54+
.expect("Cargo process did not exit successfully");
55+
}
56+
}
57+
58+
impl Iterator for CargoArtifactIter {
59+
type Item = anyhow::Result<cargo_metadata::Artifact>;
60+
61+
fn next(&mut self) -> Option<Self::Item> {
62+
loop {
63+
match self.stream.next()? {
64+
Ok(message) => match message {
65+
Message::CompilerArtifact(artifact) => {
66+
return Some(Ok(artifact));
67+
}
68+
Message::TextLine(line) => {
69+
println!("{line}")
70+
}
71+
Message::CompilerMessage(msg) => {
72+
let message = msg.message.rendered.unwrap_or(msg.message.message);
73+
print!("{message}");
74+
self.messages.push(message);
75+
}
76+
_ => {}
77+
},
78+
Err(error) => return Some(Err(error.into())),
79+
}
80+
}
81+
}
82+
}

collector/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::fmt;
66
use std::process::{self, Command};
77

88
pub mod api;
9+
pub mod cargo;
910
pub mod codegen;
1011
pub mod compile;
1112
pub mod runtime;

collector/src/runtime/benchmark.rs

Lines changed: 50 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
use crate::runtime_group_step_name;
2-
use crate::toolchain::Toolchain;
3-
use crate::utils::fs::EnsureImmutableFile;
4-
use anyhow::Context;
5-
use benchlib::benchmark::passes_filter;
6-
use cargo_metadata::Message;
71
use core::option::Option;
82
use core::option::Option::Some;
93
use core::result::Result::Ok;
104
use std::collections::HashMap;
11-
use std::io::BufReader;
125
use std::path::{Path, PathBuf};
13-
use std::process::{Child, Command, Stdio};
6+
use std::process::Command;
7+
8+
use anyhow::Context;
149
use tempfile::TempDir;
1510

11+
use benchlib::benchmark::passes_filter;
12+
13+
use crate::cargo::CargoArtifactIter;
14+
use crate::runtime_group_step_name;
15+
use crate::toolchain::Toolchain;
16+
use crate::utils::fs::EnsureImmutableFile;
17+
1618
/// Directory containing runtime benchmarks.
1719
/// We measure how long does it take to execute these crates, which is a proxy of the quality
1820
/// of code generated by rustc.
@@ -217,16 +219,16 @@ pub fn prepare_runtime_benchmark_suite(
217219
.with_context(|| {
218220
anyhow::anyhow!("Cannot start compilation of {}", benchmark_crate.name)
219221
})
220-
.and_then(|process| {
221-
parse_benchmark_group(process, &benchmark_crate.name).with_context(|| {
222+
.and_then(|iter| {
223+
parse_benchmark_group(iter, &benchmark_crate.name).with_context(|| {
222224
anyhow::anyhow!("Cannot compile runtime benchmark {}", benchmark_crate.name)
223225
})
224226
});
225227
match result {
226228
Ok(group) => groups.push(group),
227229
Err(error) => {
228230
log::error!(
229-
"Cannot compile runtime benchmark group `{}`",
231+
"Cannot compile runtime benchmark group `{}` {error:?}",
230232
benchmark_crate.name
231233
);
232234
failed_to_compile.insert(
@@ -276,66 +278,47 @@ fn check_duplicates(groups: &[BenchmarkGroup]) -> anyhow::Result<()> {
276278
/// Locates the benchmark binary of a runtime benchmark crate compiled by cargo, and then executes it
277279
/// to find out what benchmarks do they contain.
278280
fn parse_benchmark_group(
279-
mut cargo_process: Child,
281+
mut cargo_iter: CargoArtifactIter,
280282
group_name: &str,
281283
) -> anyhow::Result<BenchmarkGroup> {
282284
let mut group: Option<BenchmarkGroup> = None;
283285

284-
let stream = BufReader::new(cargo_process.stdout.take().unwrap());
285-
let mut messages = String::new();
286-
for message in Message::parse_stream(stream) {
287-
let message = message?;
288-
match message {
289-
Message::CompilerArtifact(artifact) => {
290-
if let Some(ref executable) = artifact.executable {
291-
// Found a binary compiled by a runtime benchmark crate.
292-
// Execute it so that we find all the benchmarks it contains.
293-
if artifact.target.kind.iter().any(|k| k == "bin") {
294-
if group.is_some() {
295-
return Err(anyhow::anyhow!("Runtime benchmark group `{group_name}` has produced multiple binaries"));
296-
}
297-
298-
let path = executable.as_std_path().to_path_buf();
299-
let benchmarks = gather_benchmarks(&path).map_err(|err| {
300-
anyhow::anyhow!(
301-
"Cannot gather benchmarks from `{}`: {err:?}",
302-
path.display()
303-
)
304-
})?;
305-
log::info!("Compiled {}", path.display());
306-
307-
group = Some(BenchmarkGroup {
308-
binary: path,
309-
name: group_name.to_string(),
310-
benchmark_names: benchmarks,
311-
});
312-
}
286+
for artifact in &mut cargo_iter {
287+
let artifact = artifact?;
288+
if let Some(ref executable) = artifact.executable {
289+
// Found a binary compiled by a runtime benchmark crate.
290+
// Execute it so that we find all the benchmarks it contains.
291+
if artifact.target.kind.iter().any(|k| k == "bin") {
292+
if group.is_some() {
293+
return Err(anyhow::anyhow!(
294+
"Runtime benchmark group `{group_name}` has produced multiple binaries"
295+
));
313296
}
297+
298+
let path = executable.as_std_path().to_path_buf();
299+
let benchmarks = gather_benchmarks(&path).map_err(|err| {
300+
anyhow::anyhow!(
301+
"Cannot gather benchmarks from `{}`: {err:?}",
302+
path.display()
303+
)
304+
})?;
305+
log::info!("Compiled {}", path.display());
306+
307+
group = Some(BenchmarkGroup {
308+
binary: path,
309+
name: group_name.to_string(),
310+
benchmark_names: benchmarks,
311+
});
314312
}
315-
Message::TextLine(line) => {
316-
println!("{line}")
317-
}
318-
Message::CompilerMessage(msg) => {
319-
let message = msg.message.rendered.unwrap_or(msg.message.message);
320-
messages.push_str(&message);
321-
print!("{message}");
322-
}
323-
_ => {}
324313
}
325314
}
326-
327-
let output = cargo_process.wait()?;
328-
if !output.success() {
329-
Err(anyhow::anyhow!(
330-
"Failed to compile runtime benchmark, exit code {}\n{messages}",
331-
output.code().unwrap_or(1),
332-
))
333-
} else {
334-
let group = group.ok_or_else(|| {
335-
anyhow::anyhow!("Runtime benchmark group `{group_name}` has not produced any binary")
336-
})?;
337-
Ok(group)
338-
}
315+
cargo_iter
316+
.finish()
317+
.with_context(|| format!("Failed to compile runtime benchmark `{group_name}`"))?;
318+
let group = group.ok_or_else(|| {
319+
anyhow::anyhow!("Runtime benchmark group `{group_name}` has not produced any binary")
320+
})?;
321+
Ok(group)
339322
}
340323

341324
/// Starts the compilation of a single runtime benchmark crate.
@@ -345,18 +328,13 @@ fn start_cargo_build(
345328
benchmark_dir: &Path,
346329
target_dir: Option<&Path>,
347330
opts: &RuntimeCompilationOpts,
348-
) -> anyhow::Result<Child> {
331+
) -> anyhow::Result<CargoArtifactIter> {
349332
let mut command = Command::new(&toolchain.components.cargo);
350333
command
351334
.env("RUSTC", &toolchain.components.rustc)
352335
.arg("build")
353336
.arg("--release")
354-
.arg("--message-format")
355-
.arg("json-diagnostic-short")
356-
.current_dir(benchmark_dir)
357-
.stdin(Stdio::null())
358-
.stdout(Stdio::piped())
359-
.stderr(Stdio::null());
337+
.current_dir(benchmark_dir);
360338

361339
if let Some(ref debug_info) = opts.debug_info {
362340
command.env("CARGO_PROFILE_RELEASE_DEBUG", debug_info);
@@ -371,10 +349,8 @@ fn start_cargo_build(
371349
#[cfg(feature = "precise-cachegrind")]
372350
command.arg("--features").arg("benchlib/precise-cachegrind");
373351

374-
let child = command
375-
.spawn()
376-
.map_err(|error| anyhow::anyhow!("Failed to start cargo: {:?}", error))?;
377-
Ok(child)
352+
CargoArtifactIter::from_cargo_cmd(command)
353+
.map_err(|error| anyhow::anyhow!("Failed to start cargo: {:?}", error))
378354
}
379355

380356
/// Uses a command from `benchlib` to find the benchmark names from the given

0 commit comments

Comments
 (0)