canic_host/canister_build/
context.rs1use std::{fs, path::PathBuf, process::Command};
2
3use canic_core::ids::{BuildNetwork, RELEASE_BUILD_ID_ENV, ReleaseBuildId};
4
5use crate::icp::LocalReplicaTarget;
6
7use super::{
8 CanisterBuildProfile,
9 process::{icp_ancestor_process_id, parent_process_id},
10};
11
12#[derive(Clone, Debug, Eq, PartialEq)]
16pub struct WorkspaceBuildContext {
17 pub role: String,
18 pub profile: CanisterBuildProfile,
19 pub environment: String,
20 pub build_network: BuildNetwork,
21 pub workspace_root: PathBuf,
22 pub icp_root: PathBuf,
23 pub config_path: PathBuf,
24 pub local_replica: Option<LocalReplicaTarget>,
25 pub refresh_canonical_wasm_store_did: bool,
26 pub release_build_id: Option<ReleaseBuildId>,
27}
28
29impl WorkspaceBuildContext {
30 #[must_use]
31 pub fn lines(&self) -> Vec<String> {
32 let mut lines = vec![
33 "Canic build:".to_string(),
34 format!("role: {}", self.role),
35 format!("profile: {}", self.profile.target_dir_name()),
36 format!("environment: {}", self.environment),
37 format!("build network: {}", self.build_network),
38 format!("workspace: {}", self.workspace_root.display()),
39 ];
40
41 if self.icp_root != self.workspace_root {
42 lines.push(format!("icp root: {}", self.icp_root.display()));
43 }
44 if let Some(release_build_id) = self.release_build_id {
45 lines.push(format!("release build: {release_build_id}"));
46 }
47
48 lines
49 }
50
51 #[must_use]
53 pub fn with_profile(&self, profile: CanisterBuildProfile) -> Self {
54 let mut context = self.clone();
55 context.profile = profile;
56 context
57 }
58
59 #[must_use]
61 pub fn with_role(&self, role: impl Into<String>) -> Self {
62 let mut context = self.clone();
63 context.role = role.into();
64 context
65 }
66
67 #[must_use]
69 pub fn with_release_build_id(&self, release_build_id: ReleaseBuildId) -> Self {
70 let mut context = self.clone();
71 context.release_build_id = Some(release_build_id);
72 context
73 }
74
75 pub fn apply_to_command(&self, command: &mut Command) {
77 command
78 .env_remove(RELEASE_BUILD_ID_ENV)
79 .env("ICP_ENVIRONMENT", self.build_network.as_str())
80 .env(
81 canic_core::role_contract::CANONICAL_BUILD_ICP_ROOT_ENV,
82 &self.icp_root,
83 )
84 .env(
85 canic_core::role_contract::CANONICAL_BUILD_CONFIG_PATH_ENV,
86 &self.config_path,
87 );
88 if let Some(release_build_id) = self.release_build_id {
89 command.env(RELEASE_BUILD_ID_ENV, release_build_id.to_string());
90 }
91 }
92}
93
94pub fn print_workspace_build_context_once(
97 context: &WorkspaceBuildContext,
98) -> Result<(), Box<dyn std::error::Error>> {
99 if workspace_build_context_once(context)? {
100 eprintln!("{}", context.lines().join("\n"));
101 }
102
103 Ok(())
104}
105
106pub fn workspace_build_context_once(
108 context: &WorkspaceBuildContext,
109) -> Result<bool, Box<dyn std::error::Error>> {
110 let marker_dir = context.icp_root.join(".icp");
111 fs::create_dir_all(&marker_dir)?;
112
113 let marker_key = icp_ancestor_process_id()
114 .or_else(parent_process_id)
115 .unwrap_or_else(std::process::id)
116 .to_string();
117 let marker_file = marker_dir.join(format!(".canic-build-context-{marker_key}"));
118
119 if marker_file.exists() {
120 return Ok(false);
121 }
122
123 fs::write(&marker_file, [])?;
124 Ok(true)
125}