canic_core/macros/
build.rs

1//! Build-script helpers that embed and validate `canic.toml`.
2
3/// Embed the shared Canic configuration into a canister crate's build script.
4///
5/// Reads the provided TOML file (relative to the workspace root), validates it
6/// using [`Config`](crate::config::Config), exposes relevant `cfg` flags, and
7/// sets `CANIC_CONFIG_PATH` for later use by `include_str!`. Canister crates
8/// typically invoke this from `build.rs`.
9#[macro_export]
10macro_rules! build {
11    ($file:expr) => {{
12        $crate::__canic_build_internal! {
13            $file,
14            |cfg_str, cfg_path, cfg| {
15                use canic::core::{log, log::Level, ids::CanisterRole};
16                use std::path::PathBuf;
17
18                // Infer canister name from directory structure: .../canisters/<name>/...
19                let canister_dir = {
20                    let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
21
22                    manifest_dir
23                        .ancestors()
24                        .find(|p| p.file_name().map_or(false, |n| n == "canisters"))
25                        .and_then(|canisters_dir| manifest_dir.strip_prefix(canisters_dir).ok())
26                        .and_then(|rel| rel.components().next())
27                        .and_then(|c| c.as_os_str().to_str())
28                        .map(|s| s.to_string())
29                        .or_else(|| std::env::var("CARGO_BIN_NAME").ok())
30                        .or_else(|| std::env::var("CARGO_PKG_NAME").ok())
31                        .expect("cannot infer canister name; place crate under canisters/<name>/ or set CARGO_BIN_NAME")
32                };
33
34            }
35        }
36    }};
37}
38
39/// Embed the shared configuration for the root orchestrator canister.
40///
41/// Performs the same validation as [`macro@canic_build`] and also marks the
42/// build with the `canic_root` cfg.
43#[macro_export]
44macro_rules! build_root {
45    ($file:expr) => {{
46        $crate::__canic_build_internal! {
47            $file,
48            |_cfg_str, _cfg_path, _cfg| {
49                // Mark this build as the root canister
50                println!("cargo:rustc-cfg=canic_root");
51            }
52        }
53    }};
54}
55
56/// Internal helper shared by [`macro@canic_build`] and
57/// [`macro@canic_build_root`].
58#[doc(hidden)]
59#[macro_export]
60macro_rules! __canic_build_internal {
61    ($file:expr, |$cfg_str:ident, $cfg_path:ident, $cfg:ident| $body:block) => {{
62        // Use the workspace root so every crate gets the same base
63        let ws_root = std::env::var("CARGO_WORKSPACE_ROOT")
64            .unwrap_or_else(|_| std::env::var("CARGO_MANIFEST_DIR").unwrap());
65
66        let $cfg_path = std::path::PathBuf::from(ws_root).join($file);
67
68        // check config file exists (fails the build early if invalid)
69        let $cfg_str = std::fs::read_to_string(&$cfg_path)
70            .unwrap_or_else(|e| panic!("Failed to read {}: {}", $cfg_path.display(), e));
71
72        // Init Config
73        let $cfg = canic::core::config::Config::init_from_toml(&$cfg_str)
74            .expect("Invalid Canic config");
75
76        // declare the cfg names
77        println!("cargo:rustc-check-cfg=cfg(canic)");
78        println!("cargo:rustc-check-cfg=cfg(canic_github_ci)");
79        println!("cargo:rustc-check-cfg=cfg(canic_root)");
80
81        // everything gets the top level marker
82        println!("cargo:rustc-cfg=canic");
83
84        // Auto-enable the cfg when running under GitHub Actions.
85        if std::env::var("GITHUB_ACTIONS").as_deref() == Ok("true") {
86            println!("cargo:rustc-cfg=canic_github_ci");
87        }
88
89        // Run the extra body (per-canister or nothing)
90        $body
91
92        // Export an ABSOLUTE path for include_str!
93        let abs = $cfg_path.canonicalize().expect("canonicalize canic config path");
94        println!("cargo:rustc-env=CANIC_CONFIG_PATH={}", abs.display());
95        println!("cargo:rerun-if-changed={}", abs.display());
96    }};
97}