4 use std::path::{Path, PathBuf};
5 use std::process::{self, Command, Stdio};
7 use super::path::{Dirs, RelPath};
8 use super::rustc_info::{get_cargo_path, get_rustc_path, get_rustdoc_path, get_wrapper_file_name};
10 #[derive(Clone, Debug)]
11 pub(crate) struct Compiler {
12 pub(crate) cargo: PathBuf,
13 pub(crate) rustc: PathBuf,
14 pub(crate) rustdoc: PathBuf,
15 pub(crate) rustflags: String,
16 pub(crate) rustdocflags: String,
17 pub(crate) triple: String,
18 pub(crate) runner: Vec<String>,
22 pub(crate) fn llvm_with_triple(triple: String) -> Compiler {
24 cargo: get_cargo_path(),
25 rustc: get_rustc_path(),
26 rustdoc: get_rustdoc_path(),
27 rustflags: String::new(),
28 rustdocflags: String::new(),
34 pub(crate) fn clif_with_triple(dirs: &Dirs, triple: String) -> Compiler {
36 RelPath::DIST.to_path(&dirs).join(get_wrapper_file_name("rustc-clif", "bin"));
38 RelPath::DIST.to_path(&dirs).join(get_wrapper_file_name("rustdoc-clif", "bin"));
41 cargo: get_cargo_path(),
42 rustc: rustc_clif.clone(),
43 rustdoc: rustdoc_clif.clone(),
44 rustflags: String::new(),
45 rustdocflags: String::new(),
52 pub(crate) struct CargoProject {
53 source: &'static RelPath,
58 pub(crate) const fn new(path: &'static RelPath, target: &'static str) -> CargoProject {
59 CargoProject { source: path, target }
62 pub(crate) fn source_dir(&self, dirs: &Dirs) -> PathBuf {
63 self.source.to_path(dirs)
66 pub(crate) fn manifest_path(&self, dirs: &Dirs) -> PathBuf {
67 self.source_dir(dirs).join("Cargo.toml")
70 pub(crate) fn target_dir(&self, dirs: &Dirs) -> PathBuf {
71 RelPath::BUILD.join(self.target).to_path(dirs)
74 fn base_cmd(&self, command: &str, cargo: &Path, dirs: &Dirs) -> Command {
75 let mut cmd = Command::new(cargo);
78 .arg("--manifest-path")
79 .arg(self.manifest_path(dirs))
81 .arg(self.target_dir(dirs));
86 fn build_cmd(&self, command: &str, compiler: &Compiler, dirs: &Dirs) -> Command {
87 let mut cmd = self.base_cmd(command, &compiler.cargo, dirs);
89 cmd.arg("--target").arg(&compiler.triple);
91 cmd.env("RUSTC", &compiler.rustc);
92 cmd.env("RUSTDOC", &compiler.rustdoc);
93 cmd.env("RUSTFLAGS", &compiler.rustflags);
94 cmd.env("RUSTDOCFLAGS", &compiler.rustdocflags);
95 if !compiler.runner.is_empty() {
97 format!("CARGO_TARGET_{}_RUNNER", compiler.triple.to_uppercase().replace('-', "_")),
98 compiler.runner.join(" "),
106 pub(crate) fn fetch(&self, cargo: impl AsRef<Path>, dirs: &Dirs) -> Command {
107 let mut cmd = Command::new(cargo.as_ref());
109 cmd.arg("fetch").arg("--manifest-path").arg(self.manifest_path(dirs));
115 pub(crate) fn clean(&self, cargo: &Path, dirs: &Dirs) -> Command {
116 self.base_cmd("clean", cargo, dirs)
120 pub(crate) fn build(&self, compiler: &Compiler, dirs: &Dirs) -> Command {
121 self.build_cmd("build", compiler, dirs)
125 pub(crate) fn test(&self, compiler: &Compiler, dirs: &Dirs) -> Command {
126 self.build_cmd("test", compiler, dirs)
130 pub(crate) fn run(&self, compiler: &Compiler, dirs: &Dirs) -> Command {
131 self.build_cmd("run", compiler, dirs)
136 pub(crate) fn hyperfine_command(
139 prepare: Option<&str>,
143 let mut bench = Command::new("hyperfine");
146 bench.arg("--warmup").arg(warmup.to_string());
150 bench.arg("--runs").arg(runs.to_string());
153 if let Some(prepare) = prepare {
154 bench.arg("--prepare").arg(prepare);
163 pub(crate) fn try_hard_link(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
164 let src = src.as_ref();
165 let dst = dst.as_ref();
166 if let Err(_) = fs::hard_link(src, dst) {
167 fs::copy(src, dst).unwrap(); // Fallback to copying if hardlinking failed
172 pub(crate) fn spawn_and_wait(mut cmd: Command) {
173 if !cmd.spawn().unwrap().wait().unwrap().success() {
178 // Based on the retry function in rust's src/ci/shared.sh
180 pub(crate) fn retry_spawn_and_wait(tries: u64, mut cmd: Command) {
181 for i in 1..tries + 1 {
183 println!("Command failed. Attempt {i}/{tries}:");
185 if cmd.spawn().unwrap().wait().unwrap().success() {
188 std::thread::sleep(std::time::Duration::from_secs(i * 5));
190 println!("The command has failed after {tries} attempts.");
195 pub(crate) fn spawn_and_wait_with_input(mut cmd: Command, input: String) -> String {
197 .stdin(Stdio::piped())
198 .stdout(Stdio::piped())
200 .expect("Failed to spawn child process");
202 let mut stdin = child.stdin.take().expect("Failed to open stdin");
203 std::thread::spawn(move || {
204 stdin.write_all(input.as_bytes()).expect("Failed to write to stdin");
207 let output = child.wait_with_output().expect("Failed to read stdout");
208 if !output.status.success() {
212 String::from_utf8(output.stdout).unwrap()
215 pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) {
216 for entry in fs::read_dir(from).unwrap() {
217 let entry = entry.unwrap();
218 let filename = entry.file_name();
219 if filename == "." || filename == ".." {
222 if entry.metadata().unwrap().is_dir() {
223 fs::create_dir(to.join(&filename)).unwrap();
224 copy_dir_recursively(&from.join(&filename), &to.join(&filename));
226 fs::copy(from.join(&filename), to.join(&filename)).unwrap();
231 pub(crate) fn is_ci() -> bool {
232 env::var("CI").as_deref() == Ok("true")