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