polyhorn_cli/core/
cargo_rustc.rs

1use cargo_metadata::{Message, MetadataCommand};
2use std::io::{BufReader, Result};
3use std::path::{Path, PathBuf};
4use std::process::{Command, Stdio};
5
6/// Builder for cargo build commands.
7#[derive(Default)]
8pub struct CargoRustc {
9    manifest_path: PathBuf,
10    cfg: Option<String>,
11    crate_type: Option<String>,
12    target: Option<String>,
13    profile: Option<String>,
14    flags: Vec<String>,
15    release: bool,
16}
17
18impl CargoRustc {
19    /// Returns a new builder for a cargo build command that operates on the
20    /// manifest residing at the given path.
21    pub fn new(manifest_path: &Path) -> CargoRustc {
22        CargoRustc {
23            manifest_path: manifest_path.to_owned(),
24            ..Default::default()
25        }
26    }
27
28    /// Builds a crate with the given cfg.
29    pub fn cfg(mut self, cfg: &str) -> CargoRustc {
30        self.cfg = Some(cfg.to_owned());
31        self
32    }
33
34    /// Builds a crate with the given profile.
35    pub fn profile(mut self, profile: &str) -> CargoRustc {
36        self.profile = Some(profile.to_owned());
37        self
38    }
39
40    /// Builds a crate of the given type, overriding the type that is written in
41    /// the manifest.
42    pub fn crate_type(mut self, crate_type: &str) -> CargoRustc {
43        self.crate_type = Some(crate_type.to_owned());
44        self
45    }
46
47    /// Changes the profile of this build.
48    pub fn release(mut self, release: bool) -> CargoRustc {
49        self.release = release;
50        self
51    }
52
53    /// Changes the target of this build.
54    pub fn target(mut self, target: &str) -> CargoRustc {
55        self.target = Some(target.to_owned());
56        self
57    }
58
59    /// Changes the flags that are passed to `rustc`.
60    pub fn flags(mut self, flags: &[&str]) -> CargoRustc {
61        self.flags = flags.into_iter().map(|flag| flag.to_string()).collect();
62        self
63    }
64
65    /// Executes the build and returns the crate name of the first target that
66    /// matches the requested crate type for this build.
67    pub fn build(self) -> Result<PathBuf> {
68        let metadata = MetadataCommand::new()
69            .manifest_path(&self.manifest_path)
70            .exec()
71            .unwrap();
72        let root_id = metadata.resolve.unwrap().root.unwrap();
73        let package = metadata
74            .packages
75            .iter()
76            .find(|package| package.id == root_id)
77            .unwrap();
78
79        let mut command = Command::new("cargo");
80        command.arg("rustc");
81        command.arg("--manifest-path");
82        command.arg(&self.manifest_path);
83
84        if let Some(target) = self.target.as_ref() {
85            command.arg("--target");
86            command.arg(target);
87        }
88
89        if let Some(profile) = self.profile.as_ref() {
90            command.arg("--profile");
91            command.arg(profile);
92        }
93
94        if self.release {
95            command.arg("--release");
96        }
97
98        command.arg("--message-format=json-render-diagnostics");
99
100        command.arg("--");
101
102        if let Some(cfg) = self.cfg.as_ref() {
103            command.arg("--cfg");
104            command.arg(cfg);
105        }
106
107        if let Some(crate_type) = self.crate_type.as_ref() {
108            command.arg("--crate-type");
109            command.arg(crate_type);
110        }
111
112        command.args(&self.flags);
113
114        command.stdout(Stdio::piped());
115        command.stderr(Stdio::inherit());
116
117        let mut process = command.spawn().unwrap();
118
119        let reader = BufReader::new(process.stdout.take().unwrap());
120
121        let mut path = None;
122
123        for message in Message::parse_stream(reader) {
124            match message.unwrap() {
125                Message::CompilerArtifact(artifact) => {
126                    if artifact.package_id == package.id {
127                        path = artifact.filenames.first().cloned();
128                    }
129                }
130                _ => {}
131            }
132        }
133
134        match process.wait()? {
135            status if status.success() => {}
136            status => {
137                std::process::exit(status.code().unwrap());
138            }
139        }
140
141        Ok(path.unwrap())
142    }
143}