Skip to main content

canic_host/
lib.rs

1//! Host-side App build and desired-state Fleet reconciliation for Canic workspaces.
2
3use std::{
4    io,
5    process::{Child, Command, Output},
6    thread,
7    time::Duration,
8};
9
10const EXECUTABLE_BUSY_RETRY_ATTEMPTS: usize = 8;
11const EXECUTABLE_BUSY_RETRY_DELAY: Duration = Duration::from_millis(10);
12
13mod artifact_io;
14pub mod binaryen;
15mod bootstrap_candid;
16mod bootstrap_coordinator;
17mod bootstrap_store;
18mod build_environment;
19mod build_profile;
20pub mod build_provenance;
21pub mod build_toolchain;
22pub mod candid_endpoints;
23pub mod canic_metadata;
24pub mod canister_build;
25mod canister_protocol;
26mod canonical_root;
27mod fleet_package;
28pub use canister_protocol::{
29    CanisterProtocolError, call_with_arg as call_canister_with_arg,
30    query_with_arg as query_canister_with_arg,
31};
32pub mod canister_ready;
33mod cargo_metadata;
34pub mod component_operation;
35pub mod component_topology;
36pub mod config_discovery;
37pub mod diagnostics;
38pub mod durable_io;
39mod entropy;
40pub mod evidence_envelope;
41pub mod fleet_ensure;
42pub mod format;
43pub mod frontend;
44pub mod ic_wasm;
45pub mod icp;
46pub mod icp_config;
47#[cfg(feature = "local-fleet")]
48pub mod local_fleet;
49pub mod network;
50pub mod observability;
51pub mod observatory;
52pub mod policy_gate;
53pub mod protocol_binding;
54pub mod registry;
55pub mod release_build;
56pub mod release_set;
57pub mod replica_query;
58pub mod role_contract;
59pub mod state_manifest;
60pub mod subnet_catalog;
61pub mod table;
62pub mod terminal;
63#[cfg(test)]
64mod test_support;
65mod workspace_discovery;
66
67pub(crate) fn cargo_command() -> Command {
68    let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
69    let mut command = build_environment::command(cargo);
70
71    if let Some(toolchain) = std::env::var_os("RUSTUP_TOOLCHAIN") {
72        command.env("RUSTUP_TOOLCHAIN", toolchain);
73    }
74    command
75}
76
77fn with_executable_busy_retry<T>(mut launch: impl FnMut() -> io::Result<T>) -> io::Result<T> {
78    for attempt in 0..EXECUTABLE_BUSY_RETRY_ATTEMPTS {
79        match launch() {
80            Err(error)
81                if error.kind() == io::ErrorKind::ExecutableFileBusy
82                    && attempt + 1 < EXECUTABLE_BUSY_RETRY_ATTEMPTS =>
83            {
84                thread::sleep(EXECUTABLE_BUSY_RETRY_DELAY);
85            }
86            result => return result,
87        }
88    }
89    unreachable!("bounded executable-busy retry always returns on its final attempt")
90}
91
92pub(crate) fn output_with_executable_busy_retry(command: &mut Command) -> io::Result<Output> {
93    with_executable_busy_retry(|| command.output())
94}
95
96pub(crate) fn spawn_with_executable_busy_retry(command: &mut Command) -> io::Result<Child> {
97    with_executable_busy_retry(|| command.spawn())
98}
99
100pub(crate) fn should_embed_candid_metadata(build_network: canic_core::ids::BuildNetwork) -> bool {
101    build_network == canic_core::ids::BuildNetwork::Local
102}
103
104#[cfg(test)]
105mod tests;