upstream_rs/services/builder/profiles/
cmake.rs1use std::path::{Path, PathBuf};
2use std::process::Command;
3
4use anyhow::{Context, Result, anyhow, bail};
5
6use crate::services::builder::{
7 BuildProfile,
8 profiles::{BuildProfileHandler, emit_line_callback, run_command_with_line_callback},
9};
10
11pub struct CmakeProfile;
12
13impl CmakeProfile {
14 fn binary_name(package_name: &str) -> String {
15 #[cfg(windows)]
16 {
17 format!("{package_name}.exe")
18 }
19 #[cfg(not(windows))]
20 {
21 package_name.to_string()
22 }
23 }
24
25 fn find_project_dir(workspace: &Path) -> Option<PathBuf> {
26 if workspace.join("CMakeLists.txt").is_file() {
27 Some(workspace.to_path_buf())
28 } else {
29 None
30 }
31 }
32}
33
34impl BuildProfileHandler for CmakeProfile {
35 fn profile(&self) -> BuildProfile {
36 BuildProfile::Cmake
37 }
38
39 fn detect(&self, workspace: &Path) -> bool {
40 Self::find_project_dir(workspace).is_some()
41 }
42
43 fn run_build(
44 &self,
45 workspace: &Path,
46 package_name: &str,
47 line_callback: &mut Option<&mut dyn FnMut(&str)>,
48 ) -> Result<PathBuf> {
49 let project_dir = Self::find_project_dir(workspace).ok_or_else(|| {
50 anyhow!(
51 "Could not find CMakeLists.txt in repository root '{}'.",
52 workspace.display()
53 )
54 })?;
55
56 let build_dir = project_dir.join(".upstream-build").join("cmake");
57 std::fs::create_dir_all(&build_dir).context(format!(
58 "Failed to create CMake build directory '{}'",
59 build_dir.display()
60 ))?;
61
62 emit_line_callback(line_callback, "Running cmake configure ...");
63 let configure = run_command_with_line_callback(
64 Command::new("cmake")
65 .arg("-S")
66 .arg(&project_dir)
67 .arg("-B")
68 .arg(&build_dir)
69 .arg("-DCMAKE_BUILD_TYPE=Release")
70 .current_dir(&project_dir),
71 "Failed to run 'cmake -S . -B <build-dir> -DCMAKE_BUILD_TYPE=Release'. Is CMake installed?",
72 line_callback,
73 )?;
74
75 if !configure.success() {
76 bail!("CMake configure failed for '{}'", package_name);
77 }
78
79 emit_line_callback(line_callback, "Running cmake build ...");
80 let build = run_command_with_line_callback(
81 Command::new("cmake")
82 .arg("--build")
83 .arg(&build_dir)
84 .arg("--config")
85 .arg("Release")
86 .current_dir(&project_dir),
87 "Failed to run 'cmake --build <build-dir> --config Release'. Is CMake installed?",
88 line_callback,
89 )?;
90
91 if !build.success() {
92 bail!("CMake build failed for '{}'", package_name);
93 }
94
95 let artifact = build_dir.join(Self::binary_name(package_name));
96
97 if !artifact.exists() {
98 return Err(anyhow!(
99 "CMake build succeeded but artifact was not found at '{}'",
100 artifact.display()
101 ));
102 }
103
104 Ok(artifact)
105 }
106}