upstream_rs/services/builder/profiles/
go.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 GoProfile;
12
13impl GoProfile {
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("go.mod").is_file() {
27 Some(workspace.to_path_buf())
28 } else {
29 None
30 }
31 }
32}
33
34impl BuildProfileHandler for GoProfile {
35 fn profile(&self) -> BuildProfile {
36 BuildProfile::Go
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 go.mod in repository root '{}'.",
52 workspace.display()
53 )
54 })?;
55
56 let artifact = project_dir
57 .join(".upstream-build")
58 .join(Self::binary_name(package_name));
59
60 if let Some(parent) = artifact.parent() {
61 std::fs::create_dir_all(parent).context(format!(
62 "Failed to create go output directory '{}'",
63 parent.display()
64 ))?;
65 }
66
67 emit_line_callback(line_callback, "Running go build -o <artifact> . ...");
68 let root_status = run_command_with_line_callback(
69 Command::new("go")
70 .arg("build")
71 .arg("-o")
72 .arg(&artifact)
73 .arg(".")
74 .current_dir(&project_dir),
75 "Failed to run 'go build -o <artifact> .'. Is Go installed?",
76 line_callback,
77 )?;
78
79 if !root_status.success() {
80 let cmd_target = format!("./cmd/{package_name}");
81 let context = format!(
82 "Failed to run fallback 'go build -o <artifact> {cmd_target}'. Is Go installed?"
83 );
84 emit_line_callback(
85 line_callback,
86 format!("Running go build -o <artifact> {cmd_target} ..."),
87 );
88 let cmd_status = run_command_with_line_callback(
89 Command::new("go")
90 .arg("build")
91 .arg("-o")
92 .arg(&artifact)
93 .arg(&cmd_target)
94 .current_dir(&project_dir),
95 &context,
96 line_callback,
97 )?;
98
99 if !cmd_status.success() {
100 bail!(
101 "Go build failed for '{}': both '.' and './cmd/{}' targets failed",
102 package_name,
103 package_name
104 );
105 }
106 }
107
108 if !artifact.exists() {
109 return Err(anyhow!(
110 "Go build succeeded but artifact was not found at '{}'",
111 artifact.display()
112 ));
113 }
114
115 Ok(artifact)
116 }
117}