cargo_pgo/
lib.rs

1//! This crate contains a Cargo subcommand designed for simplifying the usage of
2//! feedback-directed optimizations for Rust crates.
3//!
4//! You can find a usage guide for `cargo-pgo` at its [repository](https://github.com/kobzol/cargo-pgo).
5
6pub mod bolt;
7pub mod build;
8pub mod check;
9pub mod clean;
10pub(crate) mod cli;
11pub mod pgo;
12pub(crate) mod utils;
13pub(crate) mod workspace;
14
15use anyhow::anyhow;
16use std::ffi::OsStr;
17use std::path::{Path, PathBuf};
18use std::process::{Command, ExitStatus};
19
20pub use workspace::get_cargo_ctx;
21
22pub(crate) fn resolve_binary(path: &Path) -> anyhow::Result<PathBuf> {
23    Ok(which::which(path)?)
24}
25
26#[derive(Debug)]
27struct Utf8Output {
28    stdout: String,
29    stderr: String,
30    status: ExitStatus,
31}
32
33impl Utf8Output {
34    pub fn ok(self) -> anyhow::Result<Self> {
35        if self.status.success() {
36            Ok(self)
37        } else {
38            Err(anyhow::anyhow!(
39                "Command ended with {}\nStderr\n{}\nStdout\n{}",
40                self.status,
41                self.stderr,
42                self.stdout
43            ))
44        }
45    }
46}
47
48/// Runs a command with the provided arguments and returns its stdout and stderr.
49fn run_command<S: AsRef<OsStr>, Str: AsRef<OsStr>>(
50    program: S,
51    args: &[Str],
52) -> anyhow::Result<Utf8Output> {
53    let mut cmd = Command::new(program);
54    for arg in args {
55        cmd.arg(arg);
56    }
57    log::debug!("Running command {:?}", cmd);
58    cmd.stdout(std::process::Stdio::piped());
59    cmd.stderr(std::process::Stdio::piped());
60
61    let output = cmd.output()?;
62    Ok(Utf8Output {
63        stdout: String::from_utf8(output.stdout)?,
64        stderr: String::from_utf8(output.stderr)?,
65        status: output.status,
66    })
67}
68
69/// Tries to find the default target triple used for compiling on the current host computer.
70pub fn get_default_target() -> anyhow::Result<String> {
71    get_rustc_info("host: ")
72}
73
74pub fn get_rustc_version() -> anyhow::Result<semver::Version> {
75    let version = get_rustc_info("release: ")?;
76    let version = semver::Version::parse(&version)?;
77    Ok(version)
78}
79
80fn get_rustc_info(field: &str) -> anyhow::Result<String> {
81    // Query rustc for defaults.
82    let output = run_command("rustc", &["-vV"])?;
83
84    // Parse the field from stdout.
85    let host = output
86        .stdout
87        .lines()
88        .find(|l| l.starts_with(field))
89        .map(|l| l[field.len()..].trim())
90        .ok_or_else(|| anyhow!("Failed to parse field {} from rustc output.", field))?
91        .to_owned();
92    Ok(host)
93}
94
95/// Clears all files from the directory, and recreates it.
96fn clear_directory(path: &Path) -> std::io::Result<()> {
97    std::fs::remove_dir_all(path)?;
98    ensure_directory(path)
99}
100
101/// Make sure that directory exists.
102fn ensure_directory(path: &Path) -> std::io::Result<()> {
103    std::fs::create_dir_all(path)
104}