use camino::Utf8PathBuf;
use colored::Colorize;
use sha2::{Digest, Sha256};
use unc_abi::BuildInfo;
use super::super::abi_command::abi;
use super::super::abi_command::abi::{AbiCompression, AbiFormat, AbiResult};
use crate::common::ColorPreference;
use crate::types::{manifest::CargoManifestPath, metadata::CrateMetadata};
use crate::util;
use crate::util::wasm32_target_libdir_exists;
const COMPILATION_TARGET: &str = "wasm32-unknown-unknown";
pub fn run(args: super::BuildCommand) -> color_eyre::eyre::Result<util::CompilationArtifact> {
let color = args.color.unwrap_or(ColorPreference::Auto);
color.apply();
util::handle_step("Checking the host environment...", || {
if !wasm32_target_libdir_exists() {
color_eyre::eyre::bail!("rust target `{}` is not installed", COMPILATION_TARGET);
}
Ok(())
})?;
let crate_metadata = util::handle_step("Collecting cargo project metadata...", || {
let manifest_path: Utf8PathBuf = if let Some(manifest_path) = args.manifest_path {
manifest_path.into()
} else {
"Cargo.toml".into()
};
CrateMetadata::collect(CargoManifestPath::try_from(manifest_path)?)
})?;
let out_dir = args
.out_dir
.map_or(Ok(crate_metadata.target_directory.clone()), |out_dir| {
let out_dir = Utf8PathBuf::from(out_dir);
util::force_canonicalize_dir(&out_dir)
})?;
let mut build_env = vec![("RUSTFLAGS", "-C link-arg=-s")];
let mut cargo_args = vec!["--target", COMPILATION_TARGET];
let mut cargo_feature_args = vec![];
if !args.no_release {
cargo_args.push("--release");
}
if let Some(features) = args.features.as_ref() {
cargo_feature_args.extend(&["--features", features]);
}
if args.no_default_features {
cargo_feature_args.push("--no-default-features");
}
let mut abi = None;
let mut min_abi_path = None;
if !args.no_abi {
let mut contract_abi = abi::generate_abi(
&crate_metadata,
!args.no_doc,
true,
&cargo_feature_args,
color.clone(),
)?;
contract_abi.metadata.build = Some(BuildInfo {
compiler: format!("rustc {}", rustc_version::version()?),
builder: format!("unc {}", env!("CARGO_PKG_VERSION")),
image: None,
});
if !args.no_embed_abi {
let path = util::handle_step("Compressing ABI to be embedded..", || {
let AbiResult { path } = abi::write_to_file(
&contract_abi,
&crate_metadata,
AbiFormat::JsonMin,
AbiCompression::Zstd,
)?;
Ok(path)
})?;
min_abi_path.replace(util::copy(&path, &out_dir)?);
}
abi = Some(contract_abi);
}
cargo_args.extend(cargo_feature_args);
if let (false, Some(abi_path)) = (args.no_embed_abi, &min_abi_path) {
cargo_args.extend(&["--features", "unc-sdk/__abi-embed"]);
build_env.push(("CARGO_UNC_ABI_PATH", abi_path.as_str()));
}
util::print_step("Building contract");
let mut wasm_artifact = util::compile_project(
&crate_metadata.manifest_path,
&cargo_args,
build_env,
"wasm",
false,
color,
)?;
wasm_artifact.path = util::copy(&wasm_artifact.path, &out_dir)?;
util::print_success("Contract successfully built!");
let mut messages = vec![(
"Binary",
wasm_artifact.path.to_string().bright_yellow().bold(),
)];
if let Some(mut abi) = abi {
let mut hasher = Sha256::new();
hasher.update(std::fs::read(&wasm_artifact.path)?);
let hash = hasher.finalize();
let hash = bs58::encode(hash).into_string();
abi.metadata.wasm_hash = Some(hash);
let AbiResult { path } =
abi::write_to_file(&abi, &crate_metadata, AbiFormat::Json, AbiCompression::NoOp)?;
let pretty_abi_path = util::copy(&path, &out_dir)?;
messages.push(("ABI", pretty_abi_path.to_string().yellow().bold()));
}
if let Some(abi_path) = min_abi_path {
messages.push(("Embedded ABI", abi_path.to_string().yellow().bold()));
}
let max_width = messages.iter().map(|(h, _)| h.len()).max().unwrap();
for (header, message) in messages {
eprintln!(" - {:>width$}: {}", header, message, width = max_width);
}
Ok(wasm_artifact)
}