cargo_unc/commands/build_command/
build.rs

1use camino::Utf8PathBuf;
2use colored::Colorize;
3use sha2::{Digest, Sha256};
4use unc_abi::BuildInfo;
5
6use crate::commands::abi_command::abi::{AbiCompression, AbiFormat, AbiResult};
7use crate::common::ColorPreference;
8use crate::types::{manifest::CargoManifestPath, metadata::CrateMetadata};
9use crate::util;
10use crate::{commands::abi_command::abi, util::wasm32_target_libdir_exists};
11
12const COMPILATION_TARGET: &str = "wasm32-unknown-unknown";
13
14pub fn run(args: super::BuildCommand) -> color_eyre::eyre::Result<util::CompilationArtifact> {
15    let color = args.color.unwrap_or(ColorPreference::Auto);
16    color.apply();
17
18    util::handle_step("Checking the host environment...", || {
19        if !wasm32_target_libdir_exists() {
20            color_eyre::eyre::bail!("rust target `{}` is not installed", COMPILATION_TARGET);
21        }
22        Ok(())
23    })?;
24
25    let crate_metadata = util::handle_step("Collecting cargo project metadata...", || {
26        let manifest_path: Utf8PathBuf = if let Some(manifest_path) = args.manifest_path {
27            manifest_path.into()
28        } else {
29            "Cargo.toml".into()
30        };
31        CrateMetadata::collect(CargoManifestPath::try_from(manifest_path)?)
32    })?;
33
34    let out_dir = args
35        .out_dir
36        .map_or(Ok(crate_metadata.target_directory.clone()), |out_dir| {
37            let out_dir = Utf8PathBuf::from(out_dir);
38            util::force_canonicalize_dir(&out_dir)
39        })?;
40
41    let mut build_env = vec![("RUSTFLAGS", "-C link-arg=-s")];
42    let mut cargo_args = vec!["--target", COMPILATION_TARGET];
43    let mut cargo_feature_args = vec![];
44
45    if !args.no_release {
46        cargo_args.push("--release");
47    }
48
49    if let Some(features) = args.features.as_ref() {
50        cargo_feature_args.extend(&["--features", features]);
51    }
52
53    if args.no_default_features {
54        cargo_feature_args.push("--no-default-features");
55    }
56
57    let mut abi = None;
58    let mut min_abi_path = None;
59    if !args.no_abi {
60        let mut contract_abi = abi::generate_abi(
61            &crate_metadata,
62            !args.no_doc,
63            true,
64            &cargo_feature_args,
65            color.clone(),
66        )?;
67        contract_abi.metadata.build = Some(BuildInfo {
68            compiler: format!("rustc {}", rustc_version::version()?),
69            builder: format!("cargo-unc {}", env!("CARGO_PKG_VERSION")),
70            image: None,
71        });
72        if !args.no_embed_abi {
73            let path = util::handle_step("Compressing ABI to be embedded..", || {
74                let AbiResult { path } = abi::write_to_file(
75                    &contract_abi,
76                    &crate_metadata,
77                    AbiFormat::JsonMin,
78                    AbiCompression::Zstd,
79                )?;
80                Ok(path)
81            })?;
82            min_abi_path.replace(util::copy(&path, &out_dir)?);
83        }
84        abi = Some(contract_abi);
85    }
86
87    cargo_args.extend(cargo_feature_args);
88
89    if let (false, Some(abi_path)) = (args.no_embed_abi, &min_abi_path) {
90        cargo_args.extend(&["--features", "unc-sdk/__abi-embed"]);
91        build_env.push(("CARGO_UNC_ABI_PATH", abi_path.as_str()));
92    }
93
94    util::print_step("Building contract");
95    let mut wasm_artifact = util::compile_project(
96        &crate_metadata.manifest_path,
97        &cargo_args,
98        build_env,
99        "wasm",
100        false,
101        color,
102    )?;
103
104    wasm_artifact.path = util::copy(&wasm_artifact.path, &out_dir)?;
105
106    // todo! if we embedded, check that the binary exports the __contract_abi symbol
107    util::print_success("Contract successfully built!");
108    let mut messages = vec![(
109        "Binary",
110        wasm_artifact.path.to_string().bright_yellow().bold(),
111    )];
112    if let Some(mut abi) = abi {
113        let mut hasher = Sha256::new();
114        hasher.update(std::fs::read(&wasm_artifact.path)?);
115        let hash = hasher.finalize();
116        let hash = bs58::encode(hash).into_string();
117        abi.metadata.wasm_hash = Some(hash);
118
119        let AbiResult { path } =
120            abi::write_to_file(&abi, &crate_metadata, AbiFormat::Json, AbiCompression::NoOp)?;
121        let pretty_abi_path = util::copy(&path, &out_dir)?;
122        messages.push(("ABI", pretty_abi_path.to_string().yellow().bold()));
123    }
124    if let Some(abi_path) = min_abi_path {
125        messages.push(("Embedded ABI", abi_path.to_string().yellow().bold()));
126    }
127
128    let max_width = messages.iter().map(|(h, _)| h.len()).max().unwrap();
129    for (header, message) in messages {
130        eprintln!("     - {:>width$}: {}", header, message, width = max_width);
131    }
132
133    Ok(wasm_artifact)
134}