Skip to main content

canic_testkit/artifacts/
wasm.rs

1use std::{
2    fs,
3    path::{Path, PathBuf},
4    process::Command,
5};
6
7///
8/// WasmBuildProfile
9///
10
11const WASM_RELEASE_PROFILE_NAME: &str = "wasm-release";
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum WasmBuildProfile {
15    Debug,
16    Release,
17}
18
19impl WasmBuildProfile {
20    /// Return the Cargo profile arguments for this build profile.
21    #[must_use]
22    pub const fn cargo_args(self) -> &'static [&'static str] {
23        match self {
24            Self::Debug => &[],
25            Self::Release => &["--profile", WASM_RELEASE_PROFILE_NAME],
26        }
27    }
28
29    /// Return the target-directory component for this build profile.
30    #[must_use]
31    pub const fn target_dir_name(self) -> &'static str {
32        match self {
33            Self::Debug => "debug",
34            Self::Release => WASM_RELEASE_PROFILE_NAME,
35        }
36    }
37
38    /// Return the `RELEASE` environment value expected by Canic local builders.
39    #[must_use]
40    pub const fn dfx_release_value(self) -> &'static str {
41        match self {
42            Self::Debug => "0",
43            Self::Release => "1",
44        }
45    }
46}
47
48/// Resolve the wasm artifact path for one crate under a target directory.
49#[must_use]
50pub fn wasm_path(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> PathBuf {
51    target_dir
52        .join("wasm32-unknown-unknown")
53        .join(profile.target_dir_name())
54        .join(format!("{crate_name}.wasm"))
55}
56
57/// Check whether all requested wasm artifacts already exist.
58#[must_use]
59pub fn wasm_artifacts_ready(
60    target_dir: &Path,
61    canisters: &[&str],
62    profile: WasmBuildProfile,
63) -> bool {
64    canisters
65        .iter()
66        .all(|name| wasm_path(target_dir, name, profile).is_file())
67}
68
69/// Read a compiled wasm artifact for one crate.
70#[must_use]
71pub fn read_wasm(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> Vec<u8> {
72    let path = wasm_path(target_dir, crate_name, profile);
73    fs::read(&path).unwrap_or_else(|err| panic!("failed to read {crate_name} wasm: {err}"))
74}
75
76/// Build one or more wasm canisters into the provided target directory.
77pub fn build_wasm_canisters(
78    workspace_root: &Path,
79    target_dir: &Path,
80    packages: &[&str],
81    profile: WasmBuildProfile,
82    extra_env: &[(&str, &str)],
83) {
84    let mut cmd = Command::new("cargo");
85    cmd.current_dir(workspace_root);
86    cmd.env("CARGO_TARGET_DIR", target_dir);
87    cmd.env("DFX_NETWORK", "local");
88    cmd.args(["build", "--target", "wasm32-unknown-unknown"]);
89    cmd.args(profile.cargo_args());
90
91    for (key, value) in extra_env {
92        cmd.env(key, value);
93    }
94
95    for name in packages {
96        cmd.args(["-p", name]);
97    }
98
99    let output = cmd.output().expect("failed to run cargo build");
100    assert!(
101        output.status.success(),
102        "cargo build failed: {}",
103        String::from_utf8_lossy(&output.stderr)
104    );
105}