]>
Commit | Line | Data |
---|---|---|
f2b60f7d | 1 | use std::env; |
136023e0 | 2 | use std::fs; |
ed00b5ec | 3 | use std::io; |
9c376795 | 4 | use std::path::{Path, PathBuf}; |
ed00b5ec | 5 | use std::process::{self, Command}; |
add651ee | 6 | use std::sync::atomic::{AtomicBool, Ordering}; |
136023e0 | 7 | |
add651ee FG |
8 | use crate::path::{Dirs, RelPath}; |
9 | use crate::shared_utils::rustflags_to_cmd_env; | |
9c376795 | 10 | |
9ffffee4 | 11 | #[derive(Clone, Debug)] |
9c376795 FG |
12 | pub(crate) struct Compiler { |
13 | pub(crate) cargo: PathBuf, | |
14 | pub(crate) rustc: PathBuf, | |
15 | pub(crate) rustdoc: PathBuf, | |
add651ee FG |
16 | pub(crate) rustflags: Vec<String>, |
17 | pub(crate) rustdocflags: Vec<String>, | |
9c376795 FG |
18 | pub(crate) triple: String, |
19 | pub(crate) runner: Vec<String>, | |
20 | } | |
21 | ||
22 | impl Compiler { | |
9ffffee4 FG |
23 | pub(crate) fn set_cross_linker_and_runner(&mut self) { |
24 | match self.triple.as_str() { | |
25 | "aarch64-unknown-linux-gnu" => { | |
26 | // We are cross-compiling for aarch64. Use the correct linker and run tests in qemu. | |
add651ee FG |
27 | self.rustflags.push("-Clinker=aarch64-linux-gnu-gcc".to_owned()); |
28 | self.rustdocflags.push("-Clinker=aarch64-linux-gnu-gcc".to_owned()); | |
9ffffee4 FG |
29 | self.runner = vec![ |
30 | "qemu-aarch64".to_owned(), | |
31 | "-L".to_owned(), | |
32 | "/usr/aarch64-linux-gnu".to_owned(), | |
33 | ]; | |
34 | } | |
35 | "s390x-unknown-linux-gnu" => { | |
36 | // We are cross-compiling for s390x. Use the correct linker and run tests in qemu. | |
add651ee FG |
37 | self.rustflags.push("-Clinker=s390x-linux-gnu-gcc".to_owned()); |
38 | self.rustdocflags.push("-Clinker=s390x-linux-gnu-gcc".to_owned()); | |
9ffffee4 FG |
39 | self.runner = vec![ |
40 | "qemu-s390x".to_owned(), | |
41 | "-L".to_owned(), | |
42 | "/usr/s390x-linux-gnu".to_owned(), | |
43 | ]; | |
44 | } | |
ed00b5ec FG |
45 | "riscv64gc-unknown-linux-gnu" => { |
46 | // We are cross-compiling for riscv64. Use the correct linker and run tests in qemu. | |
47 | self.rustflags.push("-Clinker=riscv64-linux-gnu-gcc".to_owned()); | |
48 | self.rustdocflags.push("-Clinker=riscv64-linux-gnu-gcc".to_owned()); | |
49 | self.runner = vec![ | |
50 | "qemu-riscv64".to_owned(), | |
51 | "-L".to_owned(), | |
52 | "/usr/riscv64-linux-gnu".to_owned(), | |
53 | ]; | |
54 | } | |
9ffffee4 FG |
55 | "x86_64-pc-windows-gnu" => { |
56 | // We are cross-compiling for Windows. Run tests in wine. | |
57 | self.runner = vec!["wine".to_owned()]; | |
58 | } | |
59 | _ => { | |
ed00b5ec | 60 | eprintln!("Unknown non-native platform"); |
9ffffee4 | 61 | } |
9c376795 FG |
62 | } |
63 | } | |
64 | } | |
65 | ||
66 | pub(crate) struct CargoProject { | |
67 | source: &'static RelPath, | |
68 | target: &'static str, | |
69 | } | |
70 | ||
71 | impl CargoProject { | |
72 | pub(crate) const fn new(path: &'static RelPath, target: &'static str) -> CargoProject { | |
73 | CargoProject { source: path, target } | |
74 | } | |
75 | ||
76 | pub(crate) fn source_dir(&self, dirs: &Dirs) -> PathBuf { | |
77 | self.source.to_path(dirs) | |
78 | } | |
79 | ||
80 | pub(crate) fn manifest_path(&self, dirs: &Dirs) -> PathBuf { | |
81 | self.source_dir(dirs).join("Cargo.toml") | |
82 | } | |
83 | ||
84 | pub(crate) fn target_dir(&self, dirs: &Dirs) -> PathBuf { | |
85 | RelPath::BUILD.join(self.target).to_path(dirs) | |
86 | } | |
2b03887a | 87 | |
9ffffee4 | 88 | #[must_use] |
9c376795 FG |
89 | fn base_cmd(&self, command: &str, cargo: &Path, dirs: &Dirs) -> Command { |
90 | let mut cmd = Command::new(cargo); | |
91 | ||
92 | cmd.arg(command) | |
93 | .arg("--manifest-path") | |
94 | .arg(self.manifest_path(dirs)) | |
95 | .arg("--target-dir") | |
9ffffee4 | 96 | .arg(self.target_dir(dirs)) |
fe692bf9 FG |
97 | .arg("--locked"); |
98 | ||
99 | if dirs.frozen { | |
100 | cmd.arg("--frozen"); | |
101 | } | |
9c376795 FG |
102 | |
103 | cmd | |
104 | } | |
105 | ||
9ffffee4 | 106 | #[must_use] |
9c376795 FG |
107 | fn build_cmd(&self, command: &str, compiler: &Compiler, dirs: &Dirs) -> Command { |
108 | let mut cmd = self.base_cmd(command, &compiler.cargo, dirs); | |
109 | ||
110 | cmd.arg("--target").arg(&compiler.triple); | |
111 | ||
112 | cmd.env("RUSTC", &compiler.rustc); | |
113 | cmd.env("RUSTDOC", &compiler.rustdoc); | |
add651ee FG |
114 | rustflags_to_cmd_env(&mut cmd, "RUSTFLAGS", &compiler.rustflags); |
115 | rustflags_to_cmd_env(&mut cmd, "RUSTDOCFLAGS", &compiler.rustdocflags); | |
9c376795 FG |
116 | if !compiler.runner.is_empty() { |
117 | cmd.env( | |
118 | format!("CARGO_TARGET_{}_RUNNER", compiler.triple.to_uppercase().replace('-', "_")), | |
119 | compiler.runner.join(" "), | |
120 | ); | |
121 | } | |
122 | ||
123 | cmd | |
124 | } | |
125 | ||
9ffffee4 FG |
126 | pub(crate) fn clean(&self, dirs: &Dirs) { |
127 | let _ = fs::remove_dir_all(self.target_dir(dirs)); | |
9c376795 FG |
128 | } |
129 | ||
130 | #[must_use] | |
131 | pub(crate) fn build(&self, compiler: &Compiler, dirs: &Dirs) -> Command { | |
132 | self.build_cmd("build", compiler, dirs) | |
133 | } | |
134 | ||
135 | #[must_use] | |
136 | pub(crate) fn test(&self, compiler: &Compiler, dirs: &Dirs) -> Command { | |
137 | self.build_cmd("test", compiler, dirs) | |
138 | } | |
139 | ||
140 | #[must_use] | |
141 | pub(crate) fn run(&self, compiler: &Compiler, dirs: &Dirs) -> Command { | |
142 | self.build_cmd("run", compiler, dirs) | |
143 | } | |
2b03887a FG |
144 | } |
145 | ||
9c376795 | 146 | #[must_use] |
2b03887a FG |
147 | pub(crate) fn hyperfine_command( |
148 | warmup: u64, | |
149 | runs: u64, | |
9c376795 | 150 | prepare: Option<&str>, |
add651ee FG |
151 | cmds: &[(&str, &str)], |
152 | markdown_export: &Path, | |
2b03887a FG |
153 | ) -> Command { |
154 | let mut bench = Command::new("hyperfine"); | |
155 | ||
add651ee FG |
156 | bench.arg("--export-markdown").arg(markdown_export); |
157 | ||
2b03887a FG |
158 | if warmup != 0 { |
159 | bench.arg("--warmup").arg(warmup.to_string()); | |
160 | } | |
161 | ||
162 | if runs != 0 { | |
163 | bench.arg("--runs").arg(runs.to_string()); | |
164 | } | |
165 | ||
166 | if let Some(prepare) = prepare { | |
9c376795 | 167 | bench.arg("--prepare").arg(prepare); |
2b03887a FG |
168 | } |
169 | ||
add651ee FG |
170 | for &(name, cmd) in cmds { |
171 | if name != "" { | |
172 | bench.arg("-n").arg(name); | |
173 | } | |
174 | bench.arg(cmd); | |
175 | } | |
2b03887a FG |
176 | |
177 | bench | |
178 | } | |
179 | ||
9ffffee4 FG |
180 | #[must_use] |
181 | pub(crate) fn git_command<'a>(repo_dir: impl Into<Option<&'a Path>>, cmd: &str) -> Command { | |
182 | let mut git_cmd = Command::new("git"); | |
183 | git_cmd | |
184 | .arg("-c") | |
185 | .arg("user.name=Dummy") | |
186 | .arg("-c") | |
187 | .arg("user.email=dummy@example.com") | |
188 | .arg("-c") | |
189 | .arg("core.autocrlf=false") | |
add651ee FG |
190 | .arg("-c") |
191 | .arg("commit.gpgSign=false") | |
9ffffee4 FG |
192 | .arg(cmd); |
193 | if let Some(repo_dir) = repo_dir.into() { | |
194 | git_cmd.current_dir(repo_dir); | |
195 | } | |
196 | git_cmd | |
197 | } | |
198 | ||
136023e0 XL |
199 | #[track_caller] |
200 | pub(crate) fn try_hard_link(src: impl AsRef<Path>, dst: impl AsRef<Path>) { | |
201 | let src = src.as_ref(); | |
202 | let dst = dst.as_ref(); | |
203 | if let Err(_) = fs::hard_link(src, dst) { | |
204 | fs::copy(src, dst).unwrap(); // Fallback to copying if hardlinking failed | |
205 | } | |
206 | } | |
207 | ||
208 | #[track_caller] | |
209 | pub(crate) fn spawn_and_wait(mut cmd: Command) { | |
ed00b5ec FG |
210 | let status = cmd.spawn().unwrap().wait().unwrap(); |
211 | if !status.success() { | |
212 | eprintln!("{cmd:?} exited with status {:?}", status); | |
136023e0 XL |
213 | process::exit(1); |
214 | } | |
215 | } | |
216 | ||
9ffffee4 FG |
217 | // Based on the retry function in rust's src/ci/shared.sh |
218 | #[track_caller] | |
219 | pub(crate) fn retry_spawn_and_wait(tries: u64, mut cmd: Command) { | |
220 | for i in 1..tries + 1 { | |
221 | if i != 1 { | |
ed00b5ec | 222 | eprintln!("Command failed. Attempt {i}/{tries}:"); |
9ffffee4 FG |
223 | } |
224 | if cmd.spawn().unwrap().wait().unwrap().success() { | |
225 | return; | |
226 | } | |
227 | std::thread::sleep(std::time::Duration::from_secs(i * 5)); | |
228 | } | |
ed00b5ec | 229 | eprintln!("The command has failed after {tries} attempts."); |
9ffffee4 FG |
230 | process::exit(1); |
231 | } | |
232 | ||
9ffffee4 FG |
233 | pub(crate) fn remove_dir_if_exists(path: &Path) { |
234 | match fs::remove_dir_all(&path) { | |
235 | Ok(()) => {} | |
236 | Err(err) if err.kind() == io::ErrorKind::NotFound => {} | |
237 | Err(err) => panic!("Failed to remove {path}: {err}", path = path.display()), | |
238 | } | |
239 | } | |
240 | ||
136023e0 XL |
241 | pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) { |
242 | for entry in fs::read_dir(from).unwrap() { | |
243 | let entry = entry.unwrap(); | |
244 | let filename = entry.file_name(); | |
245 | if filename == "." || filename == ".." { | |
246 | continue; | |
247 | } | |
248 | if entry.metadata().unwrap().is_dir() { | |
249 | fs::create_dir(to.join(&filename)).unwrap(); | |
250 | copy_dir_recursively(&from.join(&filename), &to.join(&filename)); | |
251 | } else { | |
252 | fs::copy(from.join(&filename), to.join(&filename)).unwrap(); | |
253 | } | |
254 | } | |
255 | } | |
f2b60f7d | 256 | |
add651ee FG |
257 | static IN_GROUP: AtomicBool = AtomicBool::new(false); |
258 | pub(crate) struct LogGroup { | |
259 | is_gha: bool, | |
260 | } | |
261 | ||
262 | impl LogGroup { | |
263 | pub(crate) fn guard(name: &str) -> LogGroup { | |
264 | let is_gha = env::var("GITHUB_ACTIONS").is_ok(); | |
265 | ||
266 | assert!(!IN_GROUP.swap(true, Ordering::SeqCst)); | |
267 | if is_gha { | |
268 | eprintln!("::group::{name}"); | |
269 | } | |
270 | ||
271 | LogGroup { is_gha } | |
272 | } | |
273 | } | |
274 | ||
275 | impl Drop for LogGroup { | |
276 | fn drop(&mut self) { | |
277 | if self.is_gha { | |
278 | eprintln!("::endgroup::"); | |
279 | } | |
280 | IN_GROUP.store(false, Ordering::SeqCst); | |
281 | } | |
282 | } |