upstream_rs/services/builder/profiles/
mod.rs1use std::{
2 io::{BufRead, BufReader},
3 path::{Path, PathBuf},
4 process::{Command, ExitStatus, Stdio},
5 sync::mpsc,
6 thread,
7 time::Duration,
8};
9
10use anyhow::{Context, Result};
11
12use crate::services::builder::BuildProfile;
13
14pub mod cmake;
15pub mod dotnet;
16pub mod go;
17pub mod rust;
18pub mod zig;
19
20pub fn handlers() -> [Box<dyn BuildProfileHandler>; 5] {
21 [
22 Box::new(rust::RustProfile),
23 Box::new(dotnet::DotnetProfile),
24 Box::new(go::GoProfile),
25 Box::new(zig::ZigProfile),
26 Box::new(cmake::CmakeProfile),
27 ]
28}
29
30pub trait BuildProfileHandler {
31 fn profile(&self) -> BuildProfile;
32 fn detect(&self, workspace: &Path) -> bool;
33 fn run_build(
34 &self,
35 workspace: &Path,
36 package_name: &str,
37 line_callback: &mut Option<&mut dyn FnMut(&str)>,
38 ) -> Result<PathBuf>;
39}
40
41pub fn run_command_with_line_callback(
42 command: &mut Command,
43 context: &str,
44 line_callback: &mut Option<&mut dyn FnMut(&str)>,
45) -> Result<ExitStatus> {
46 command
47 .stdin(Stdio::null())
48 .stdout(Stdio::piped())
49 .stderr(Stdio::piped());
50
51 let mut child = command.spawn().context(context.to_string())?;
52 let (tx, rx) = mpsc::channel();
53
54 if let Some(stdout) = child.stdout.take() {
55 let tx = tx.clone();
56 thread::spawn(move || {
57 for line in BufReader::new(stdout).lines().map_while(Result::ok) {
58 let _ = tx.send(line);
59 }
60 });
61 }
62
63 if let Some(stderr) = child.stderr.take() {
64 let tx = tx.clone();
65 thread::spawn(move || {
66 for line in BufReader::new(stderr).lines().map_while(Result::ok) {
67 let _ = tx.send(line);
68 }
69 });
70 }
71
72 drop(tx);
73
74 loop {
75 match rx.recv_timeout(Duration::from_millis(50)) {
76 Ok(line) => {
77 if let Some(callback) = line_callback.as_deref_mut() {
78 callback(&line);
79 }
80 }
81 Err(mpsc::RecvTimeoutError::Timeout) => {
82 if let Some(status) = child.try_wait()? {
83 for line in rx.try_iter() {
84 if let Some(callback) = line_callback.as_deref_mut() {
85 callback(&line);
86 }
87 }
88 return Ok(status);
89 }
90 }
91 Err(mpsc::RecvTimeoutError::Disconnected) => {
92 return child.wait().context(context.to_string());
93 }
94 }
95 }
96}
97
98pub fn emit_line_callback(line_callback: &mut Option<&mut dyn FnMut(&str)>, line: impl AsRef<str>) {
99 if let Some(callback) = line_callback.as_deref_mut() {
100 callback(line.as_ref());
101 }
102}