Skip to main content

ic_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
40/// Resolve the wasm artifact path for one crate under a target directory.
41#[must_use]
42pub fn wasm_path(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> PathBuf {
43    target_dir
44        .join("wasm32-unknown-unknown")
45        .join(profile.target_dir_name())
46        .join(format!("{crate_name}.wasm"))
47}
48
49/// Check whether all requested wasm artifacts already exist.
50#[must_use]
51pub fn wasm_artifacts_ready(
52    target_dir: &Path,
53    canisters: &[&str],
54    profile: WasmBuildProfile,
55) -> bool {
56    canisters
57        .iter()
58        .all(|name| wasm_path(target_dir, name, profile).is_file())
59}
60
61/// Read a compiled wasm artifact for one crate.
62#[must_use]
63pub fn read_wasm(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> Vec<u8> {
64    let path = wasm_path(target_dir, crate_name, profile);
65    fs::read(&path).unwrap_or_else(|err| panic!("failed to read {crate_name} wasm: {err}"))
66}
67
68/// Build one or more wasm canisters into the provided target directory.
69pub fn build_wasm_canisters(
70    workspace_root: &Path,
71    target_dir: &Path,
72    packages: &[&str],
73    profile: WasmBuildProfile,
74    extra_env: &[(&str, &str)],
75) {
76    let mut cmd = cargo_command();
77    cmd.current_dir(workspace_root);
78    cmd.env("CARGO_TARGET_DIR", target_dir);
79    cmd.args(["build", "--target", "wasm32-unknown-unknown"]);
80    cmd.args(profile.cargo_args());
81
82    for (key, value) in extra_env {
83        cmd.env(key, value);
84    }
85
86    for name in packages {
87        cmd.args(["-p", name]);
88    }
89
90    let output = cmd.output().expect("failed to run cargo build");
91    assert!(
92        output.status.success(),
93        "cargo build failed: {}",
94        String::from_utf8_lossy(&output.stderr)
95    );
96}
97
98fn cargo_command() -> Command {
99    let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
100    let mut command = Command::new(cargo);
101
102    if let Some(toolchain) = std::env::var_os("RUSTUP_TOOLCHAIN") {
103        command.env("RUSTUP_TOOLCHAIN", toolchain);
104    }
105
106    command
107}