canic_host/canister_build/
context.rs1use std::{fs, path::PathBuf, process::Command};
2
3use crate::icp::LocalReplicaTarget;
4
5use super::{
6 CanisterBuildProfile,
7 process::{icp_ancestor_process_id, parent_process_id},
8};
9
10const LOCAL_NETWORK_URL_ENV: &str = "CANIC_ICP_LOCAL_NETWORK_URL";
11const LOCAL_ROOT_KEY_ENV: &str = "CANIC_ICP_LOCAL_ROOT_KEY";
12
13#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct WorkspaceBuildContext {
18 pub role: String,
19 pub profile: CanisterBuildProfile,
20 pub requested_profile: String,
21 pub environment: String,
22 pub build_network: String,
23 pub workspace_root: PathBuf,
24 pub icp_root: PathBuf,
25 pub config_path: PathBuf,
26 pub local_replica: Option<LocalReplicaTarget>,
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.requested_profile != "unset" {
42 lines.push(format!("requested profile: {}", self.requested_profile));
43 }
44 if self.icp_root != self.workspace_root {
45 lines.push(format!("icp root: {}", self.icp_root.display()));
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 pub fn apply_to_command(&self, command: &mut Command) {
69 command
70 .env("ICP_ENVIRONMENT", &self.build_network)
71 .env_remove("CANIC_ICP_BUILD_ENVIRONMENT")
72 .env("CANIC_WORKSPACE_ROOT", &self.workspace_root)
73 .env("CANIC_ICP_ROOT", &self.icp_root)
74 .env("CANIC_CONFIG_PATH", &self.config_path);
75 if let Some(local_replica) = &self.local_replica {
76 command
77 .env(LOCAL_NETWORK_URL_ENV, &local_replica.url)
78 .env(LOCAL_ROOT_KEY_ENV, &local_replica.root_key);
79 } else {
80 command
81 .env_remove(LOCAL_NETWORK_URL_ENV)
82 .env_remove(LOCAL_ROOT_KEY_ENV);
83 }
84 }
85}
86
87pub fn print_workspace_build_context_once(
90 context: &WorkspaceBuildContext,
91) -> Result<(), Box<dyn std::error::Error>> {
92 if workspace_build_context_once(context)? {
93 eprintln!("{}", context.lines().join("\n"));
94 }
95
96 Ok(())
97}
98
99pub fn workspace_build_context_once(
101 context: &WorkspaceBuildContext,
102) -> Result<bool, Box<dyn std::error::Error>> {
103 let marker_dir = context.icp_root.join(".icp");
104 fs::create_dir_all(&marker_dir)?;
105
106 let marker_key = icp_ancestor_process_id()
107 .or_else(parent_process_id)
108 .unwrap_or_else(std::process::id)
109 .to_string();
110 let marker_file = marker_dir.join(format!(".canic-build-context-{marker_key}"));
111
112 if marker_file.exists() {
113 return Ok(false);
114 }
115
116 fs::write(&marker_file, [])?;
117 Ok(true)
118}