use std::{
fs,
path::{Path, PathBuf},
process::Command,
};
pub const INTERNAL_TEST_ENDPOINTS_ENV: (&str, &str) = ("CANIC_INTERNAL_TEST_ENDPOINTS", "1");
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WasmBuildProfile {
Debug,
Fast,
Release,
}
impl WasmBuildProfile {
#[must_use]
pub const fn cargo_args(self) -> &'static [&'static str] {
match self {
Self::Debug => &[],
Self::Fast => &["--profile", "fast"],
Self::Release => &["--release"],
}
}
#[must_use]
pub const fn target_dir_name(self) -> &'static str {
match self {
Self::Debug => "debug",
Self::Fast => "fast",
Self::Release => "release",
}
}
#[must_use]
pub const fn canic_wasm_profile_value(self) -> &'static str {
match self {
Self::Debug => "debug",
Self::Fast => "fast",
Self::Release => "release",
}
}
}
#[must_use]
pub fn wasm_path(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> PathBuf {
target_dir
.join("wasm32-unknown-unknown")
.join(profile.target_dir_name())
.join(format!("{crate_name}.wasm"))
}
#[must_use]
pub fn wasm_artifacts_ready(
target_dir: &Path,
canisters: &[&str],
profile: WasmBuildProfile,
) -> bool {
canisters
.iter()
.all(|name| wasm_path(target_dir, name, profile).is_file())
}
#[must_use]
pub fn read_wasm(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> Vec<u8> {
let path = wasm_path(target_dir, crate_name, profile);
fs::read(&path).unwrap_or_else(|err| panic!("failed to read {crate_name} wasm: {err}"))
}
pub fn build_wasm_canisters(
workspace_root: &Path,
target_dir: &Path,
packages: &[&str],
profile: WasmBuildProfile,
extra_env: &[(&str, &str)],
) {
let mut cmd = cargo_command();
cmd.current_dir(workspace_root);
cmd.env("CARGO_TARGET_DIR", target_dir);
cmd.env("DFX_NETWORK", "local");
cmd.args(["build", "--target", "wasm32-unknown-unknown"]);
cmd.args(profile.cargo_args());
for (key, value) in extra_env {
cmd.env(key, value);
}
for name in packages {
cmd.args(["-p", name]);
}
let output = cmd.output().expect("failed to run cargo build");
assert!(
output.status.success(),
"cargo build failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
fn cargo_command() -> Command {
let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
let mut command = Command::new(cargo);
if let Some(toolchain) = std::env::var_os("RUSTUP_TOOLCHAIN") {
command.env("RUSTUP_TOOLCHAIN", toolchain);
}
command
}
pub fn build_internal_test_wasm_canisters(
workspace_root: &Path,
target_dir: &Path,
packages: &[&str],
profile: WasmBuildProfile,
) {
build_wasm_canisters(
workspace_root,
target_dir,
packages,
profile,
&[INTERNAL_TEST_ENDPOINTS_ENV],
);
}
pub fn build_internal_test_wasm_canisters_with_env<'a>(
workspace_root: &Path,
target_dir: &Path,
packages: &[&str],
profile: WasmBuildProfile,
extra_env: &[(&'a str, &'a str)],
) {
let mut build_env = Vec::with_capacity(extra_env.len() + 1);
build_env.push(INTERNAL_TEST_ENDPOINTS_ENV);
build_env.extend_from_slice(extra_env);
build_wasm_canisters(workspace_root, target_dir, packages, profile, &build_env);
}