Skip to main content

upstream_rs/services/builder/profiles/
mod.rs

1use 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}