reth_bench_compare/
compilation.rs1use crate::git::GitManager;
4use eyre::{eyre, Result, WrapErr};
5use std::{fs, path::PathBuf, process::Command};
6use tracing::{debug, error, info, warn};
7
8#[derive(Debug)]
10pub(crate) struct CompilationManager {
11 repo_root: String,
12 output_dir: PathBuf,
13 git_manager: GitManager,
14}
15
16impl CompilationManager {
17 pub(crate) const fn new(
19 repo_root: String,
20 output_dir: PathBuf,
21 git_manager: GitManager,
22 ) -> Result<Self> {
23 Ok(Self { repo_root, output_dir, git_manager })
24 }
25
26 pub(crate) fn get_cached_binary_path_for_commit(&self, commit: &str) -> PathBuf {
28 let identifier = &commit[..8]; self.output_dir.join("bin").join(format!("reth_{identifier}"))
30 }
31
32 pub(crate) fn compile_reth(&self, commit: &str, features: &str, rustflags: &str) -> Result<()> {
34 let current_commit = self.git_manager.get_current_commit()?;
36 if current_commit != commit {
37 return Err(eyre!(
38 "Git commit mismatch! Expected: {}, but currently at: {}",
39 &commit[..8],
40 ¤t_commit[..8]
41 ));
42 }
43
44 let cached_path = self.get_cached_binary_path_for_commit(commit);
45
46 if cached_path.exists() {
48 info!("Using cached binary (commit: {})", &commit[..8]);
49 return Ok(());
50 }
51
52 info!("No cached binary found, compiling (commit: {})...", &commit[..8]);
53
54 let binary_name = "reth";
55
56 info!(
57 "Compiling {} with profiling configuration (commit: {})...",
58 binary_name,
59 &commit[..8]
60 );
61
62 let mut cmd = Command::new("cargo");
63 cmd.arg("build").arg("--profile").arg("profiling");
64
65 cmd.arg("--features").arg(features);
66 info!("Using features: {features}");
67
68 cmd.current_dir(&self.repo_root);
69
70 cmd.env("RUSTFLAGS", rustflags);
72 info!("Using RUSTFLAGS: {rustflags}");
73
74 info!("Compiling {binary_name} with {cmd:?}");
75
76 let output = cmd.output().wrap_err("Failed to execute cargo build command")?;
77
78 let stdout = String::from_utf8_lossy(&output.stdout);
80 let stderr = String::from_utf8_lossy(&output.stderr);
81
82 for line in stdout.lines() {
83 if !line.trim().is_empty() {
84 debug!("[CARGO] {}", line);
85 }
86 }
87
88 for line in stderr.lines() {
89 if !line.trim().is_empty() {
90 debug!("[CARGO] {}", line);
91 }
92 }
93
94 if !output.status.success() {
95 error!("Cargo build failed with exit code: {:?}", output.status.code());
97
98 if !stdout.trim().is_empty() {
99 error!("Cargo stdout:");
100 for line in stdout.lines() {
101 error!(" {}", line);
102 }
103 }
104
105 if !stderr.trim().is_empty() {
106 error!("Cargo stderr:");
107 for line in stderr.lines() {
108 error!(" {}", line);
109 }
110 }
111
112 return Err(eyre!("Compilation failed with exit code: {:?}", output.status.code()));
113 }
114
115 info!("{} compilation completed", binary_name);
116
117 let source_path =
119 PathBuf::from(&self.repo_root).join(format!("target/profiling/{}", binary_name));
120 if !source_path.exists() {
121 return Err(eyre!("Compiled binary not found at {:?}", source_path));
122 }
123
124 let bin_dir = self.output_dir.join("bin");
126 fs::create_dir_all(&bin_dir).wrap_err("Failed to create bin directory")?;
127
128 fs::copy(&source_path, &cached_path).wrap_err("Failed to copy binary to cache")?;
130
131 #[cfg(unix)]
133 {
134 use std::os::unix::fs::PermissionsExt;
135 let mut perms = fs::metadata(&cached_path)?.permissions();
136 perms.set_mode(0o755);
137 fs::set_permissions(&cached_path, perms)?;
138 }
139
140 info!("Cached compiled binary at: {:?}", cached_path);
141 Ok(())
142 }
143
144 pub(crate) fn is_reth_bench_available(&self) -> bool {
146 match Command::new("which").arg("reth-bench").output() {
147 Ok(output) => {
148 if output.status.success() {
149 let path = String::from_utf8_lossy(&output.stdout);
150 info!("Found reth-bench: {}", path.trim());
151 true
152 } else {
153 false
154 }
155 }
156 Err(_) => false,
157 }
158 }
159
160 pub(crate) fn is_samply_available(&self) -> bool {
162 match Command::new("which").arg("samply").output() {
163 Ok(output) => {
164 if output.status.success() {
165 let path = String::from_utf8_lossy(&output.stdout);
166 info!("Found samply: {}", path.trim());
167 true
168 } else {
169 false
170 }
171 }
172 Err(_) => false,
173 }
174 }
175
176 pub(crate) fn install_samply(&self) -> Result<()> {
178 info!("Installing samply via cargo...");
179
180 let mut cmd = Command::new("cargo");
181 cmd.args(["install", "--locked", "samply"]);
182
183 info!("Installing samply with {cmd:?}");
184
185 let output = cmd.output().wrap_err("Failed to execute cargo install samply command")?;
186
187 let stdout = String::from_utf8_lossy(&output.stdout);
189 let stderr = String::from_utf8_lossy(&output.stderr);
190
191 for line in stdout.lines() {
192 if !line.trim().is_empty() {
193 debug!("[CARGO-SAMPLY] {}", line);
194 }
195 }
196
197 for line in stderr.lines() {
198 if !line.trim().is_empty() {
199 debug!("[CARGO-SAMPLY] {}", line);
200 }
201 }
202
203 if !output.status.success() {
204 error!("Cargo install samply failed with exit code: {:?}", output.status.code());
206
207 if !stdout.trim().is_empty() {
208 error!("Cargo stdout:");
209 for line in stdout.lines() {
210 error!(" {}", line);
211 }
212 }
213
214 if !stderr.trim().is_empty() {
215 error!("Cargo stderr:");
216 for line in stderr.lines() {
217 error!(" {}", line);
218 }
219 }
220
221 return Err(eyre!(
222 "samply installation failed with exit code: {:?}",
223 output.status.code()
224 ));
225 }
226
227 info!("Samply installation completed");
228 Ok(())
229 }
230
231 pub(crate) fn ensure_samply_available(&self) -> Result<()> {
233 if self.is_samply_available() {
234 Ok(())
235 } else {
236 warn!("samply not found in PATH, installing...");
237 self.install_samply()
238 }
239 }
240
241 pub(crate) fn ensure_reth_bench_available(&self) -> Result<()> {
243 if self.is_reth_bench_available() {
244 Ok(())
245 } else {
246 warn!("reth-bench not found in PATH, compiling and installing...");
247 self.compile_reth_bench()
248 }
249 }
250
251 pub(crate) fn compile_reth_bench(&self) -> Result<()> {
253 info!("Compiling and installing reth-bench...");
254
255 let mut cmd = Command::new("make");
256 cmd.arg("install-reth-bench").current_dir(&self.repo_root);
257
258 info!("Compiling reth-bench with {cmd:?}");
259
260 let output = cmd.output().wrap_err("Failed to execute make install-reth-bench command")?;
261
262 let stdout = String::from_utf8_lossy(&output.stdout);
264 let stderr = String::from_utf8_lossy(&output.stderr);
265
266 for line in stdout.lines() {
267 if !line.trim().is_empty() {
268 debug!("[MAKE-BENCH] {}", line);
269 }
270 }
271
272 for line in stderr.lines() {
273 if !line.trim().is_empty() {
274 debug!("[MAKE-BENCH] {}", line);
275 }
276 }
277
278 if !output.status.success() {
279 error!("Make install-reth-bench failed with exit code: {:?}", output.status.code());
281
282 if !stdout.trim().is_empty() {
283 error!("Make stdout:");
284 for line in stdout.lines() {
285 error!(" {}", line);
286 }
287 }
288
289 if !stderr.trim().is_empty() {
290 error!("Make stderr:");
291 for line in stderr.lines() {
292 error!(" {}", line);
293 }
294 }
295
296 return Err(eyre!(
297 "reth-bench compilation failed with exit code: {:?}",
298 output.status.code()
299 ));
300 }
301
302 info!("Reth-bench compilation completed");
303 Ok(())
304 }
305}