use crate::durable_io::write_bytes;
use std::{
fs,
io::{Read, Write},
path::Path,
process::Command,
};
use flate2::{Compression, GzBuilder};
pub fn maybe_shrink_wasm_artifact(wasm_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
maybe_shrink_wasm_artifact_with_command("ic-wasm", wasm_path)
}
fn maybe_shrink_wasm_artifact_with_command(
command_name: &str,
wasm_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let shrunk_path = wasm_path.with_extension("wasm.shrunk");
match Command::new(command_name)
.arg(wasm_path)
.arg("-o")
.arg(&shrunk_path)
.arg("shrink")
.output()
{
Ok(output) if output.status.success() => {
fs::rename(shrunk_path, wasm_path)?;
}
Ok(output) => {
let _ = fs::remove_file(shrunk_path);
return Err(format!(
"ic-wasm shrink failed for {} with status {}: {}",
wasm_path.display(),
output.status,
String::from_utf8_lossy(&output.stderr).trim()
)
.into());
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
Err(err) => {
return Err(format!("failed to run ic-wasm for {}: {err}", wasm_path.display()).into());
}
}
Ok(())
}
pub fn write_wasm_artifact(
source_path: &Path,
target_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let bytes = fs::read(source_path)?;
write_bytes(target_path, &bytes)?;
Ok(())
}
pub fn write_gzip_artifact(
wasm_path: &Path,
wasm_gz_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let mut wasm_bytes = Vec::new();
fs::File::open(wasm_path)?.read_to_end(&mut wasm_bytes)?;
let mut encoder = GzBuilder::new()
.mtime(0)
.write(Vec::new(), Compression::best());
encoder.write_all(&wasm_bytes)?;
let gz_bytes = encoder.finish()?;
write_bytes(wasm_gz_path, &gz_bytes)?;
Ok(())
}
pub fn embed_candid_metadata(
wasm_path: &Path,
did_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
embed_candid_metadata_with_command("ic-wasm", wasm_path, did_path)
}
fn embed_candid_metadata_with_command(
command_name: &str,
wasm_path: &Path,
did_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let output = Command::new(command_name)
.arg(wasm_path)
.args(["-o"])
.arg(wasm_path)
.args(["metadata", "candid:service", "-f"])
.arg(did_path)
.args(["-v", "public"])
.output();
let output = match output {
Ok(output) => output,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(err) => {
return Err(format!(
"failed to run ic-wasm metadata for {}: {err}",
wasm_path.display()
)
.into());
}
};
if !output.status.success() {
return Err(format!(
"ic-wasm metadata failed for {}: {}",
wasm_path.display(),
String::from_utf8_lossy(&output.stderr)
)
.into());
}
Ok(())
}
#[cfg(test)]
mod tests;