diff --git a/Cargo.lock b/Cargo.lock index a23798c69fe9753778879312f09715cec4447d17..c98ab05ef75ae57ae1d3250d2487dda23412c737 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,6 +301,27 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +[[package]] +name = "binaryen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a51ad23b3c7ab468d9daa948201921879ef0052e561c250fd0b326e6f000f2dd" +dependencies = [ + "binaryen-sys", +] + +[[package]] +name = "binaryen-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5829a7c89f7827e58866704e4dfdf48a635d73c6e5449c1a8a0ba5a319d28a" +dependencies = [ + "cc", + "cmake", + "heck", + "regex", +] + [[package]] name = "bitflags" version = "1.2.1" @@ -477,6 +498,7 @@ dependencies = [ "anyhow", "assert_matches", "async-std", + "binaryen", "blake2", "cargo-xbuild", "cargo_metadata 0.11.4", diff --git a/Cargo.toml b/Cargo.toml index 0c56fba7de99b50f9001bb4c4be66db61a6136b2..1252866ce5f28adeca8907f2687dadb231fdc94a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ serde = { version = "1.0.115", default-features = false, features = ["derive"] } serde_json = "1.0.57" tempfile = "3.1.0" url = { version = "2.1.1", features = ["serde"] } +binaryen = "0.10.0" # dependencies for optional extrinsics feature async-std = { version = "1.6.2", optional = true } diff --git a/README.md b/README.md index e5c0f101ed89a8b13652ac7ad8a844a8b7e0984e..c3d97847ed0e747dfc12b1f686fa197ecc3864f5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ A CLI tool for helping setting up and managing WebAssembly smart contracts writt - **Prerequisites** - **rust-src**: `rustup component add rust-src` - - **wasm-opt**: https://github.com/WebAssembly/binaryen#tools + - A C++14 compiler is required for building the [binaryen](https://github.com/WebAssembly/binaryen) + dependency. `binaryen` is build automatically during the `cargo-contract` build process. - **Install latest version from [crates.io](https://crates.io/crates/cargo-contract)** - `cargo install cargo-contract` diff --git a/src/cmd/build.rs b/src/cmd/build.rs index 4fa2011b6abad37714227bed29cce0675383be28..9c8cfec2e7f42d081b14771254db03444fb6a94d 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -15,10 +15,9 @@ // along with cargo-contract. If not, see . use std::{ - fs::metadata, - io::{self, Write}, + fs::{metadata, File}, + io::{Read, Write}, path::PathBuf, - process::Command, }; use crate::{ @@ -189,42 +188,34 @@ fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> { Ok(()) } -/// Attempts to perform optional wasm optimization using `wasm-opt`. +/// Attempts to perform optional wasm optimization using `binaryen`. /// /// The intention is to reduce the size of bloated wasm binaries as a result of missing /// optimizations (or bugs?) between Rust and Wasm. -/// -/// This step depends on the `wasm-opt` tool being installed. If it is not the build will still -/// succeed, and the user will be encouraged to install it for further optimizations. fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<()> { - // check `wasm-opt` installed - if which::which("wasm-opt").is_err() { - println!( - "{}", - "wasm-opt is not installed. Install this tool on your system in order to \n\ - reduce the size of your contract's Wasm binary. \n\ - See https://github.com/WebAssembly/binaryen#tools" - .bright_yellow() - ); - return Ok(()); - } - let mut optimized = crate_metadata.dest_wasm.clone(); optimized.set_file_name(format!("{}-opt.wasm", crate_metadata.package_name)); - let output = Command::new("wasm-opt") - .arg(crate_metadata.dest_wasm.as_os_str()) - .arg("-O3") // execute -O3 optimization passes (spends potentially a lot of time optimizing) - .arg("-o") - .arg(optimized.as_os_str()) - .output()?; + let codegen_config = binaryen::CodegenConfig { + // execute -O3 optimization passes (spends potentially a lot of time optimizing) + optimization_level: 3, + // the default + shrink_level: 1, + // the default + debug_info: false, + }; - if !output.status.success() { - // Dump the output streams produced by wasm-opt into the stdout/stderr. - io::stdout().write_all(&output.stdout)?; - io::stderr().write_all(&output.stderr)?; - anyhow::bail!("wasm-opt optimization failed"); - } + let mut dest_wasm_file = File::open(crate_metadata.dest_wasm.as_os_str())?; + let mut dest_wasm_file_content = Vec::new(); + dest_wasm_file.read_to_end(&mut dest_wasm_file_content)?; + + let mut module = binaryen::Module::read(&dest_wasm_file_content) + .map_err(|_| anyhow::anyhow!("binaryen failed to read file content"))?; + module.optimize(&codegen_config); + let optimized_wasm = module.write(); + + let mut optimized_wasm_file = File::create(optimized.as_os_str())?; + optimized_wasm_file.write_all(&optimized_wasm)?; let original_size = metadata(&crate_metadata.dest_wasm)?.len() as f64 / 1000.0; let optimized_size = metadata(&optimized)?.len() as f64 / 1000.0;