Skip to content

Commit

Permalink
Use either binaryen-rs dep or wasm-opt binary (use-ink#168)
Browse files Browse the repository at this point in the history
* Fix `#[warn(clippy::ptr_arg)]`

* Use either `binaryen-rs` dep or `wasm-opt` binary

* Implement reviewers suggestions

* Rename `optimization_level` to `optimization_passes`

* Revert "Rename `optimization_level` to `optimization_passes`"

This reverts commit 1fd35bc.

* Update installation instructions

* Update readme

* Add package manager links
  • Loading branch information
Michael Müller authored Feb 4, 2021
1 parent 3954049 commit 0e80763
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 24 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ serde = { version = "1.0.123", default-features = false, features = ["derive"] }
serde_json = "1.0.61"
tempfile = "3.2.0"
url = { version = "2.2.0", features = ["serde"] }
binaryen = "0.12.0"
binaryen = { version = "0.12.0", optional = true }

# dependencies for optional extrinsics feature
async-std = { version = "1.9.0", optional = true }
Expand All @@ -61,6 +61,7 @@ wabt = "0.10.0"

[features]
default = []
binaryen-as-dependency = ["binaryen"]

# Enable this for (experimental) commands to deploy, instantiate and call contracts.
#
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ A CLI tool for helping setting up and managing WebAssembly smart contracts writt

## Installation

- **Prerequisites**
`rust-src` is a prerequisite: `rustup component add rust-src`.

- **rust-src**: `rustup component add rust-src`
- A C++14 compiler and python >= 3.5 is required for building the
[binaryen](https://github.com/WebAssembly/binaryen) dependency.
`binaryen` is built automatically during the `cargo-contract` build process.
We optimize the resulting contract Wasm using `binaryen`. You have two options for installing it:

- **Install latest version from [crates.io](https://crates.io/crates/cargo-contract)**
- `cargo install cargo-contract`
- _The preferred way:_
Install [`binaryen`](https://github.com/WebAssembly/binaryen#tools). Many package managers
have it available nowadays ‒ e.g. it's a package for [Debian/Ubuntu](https://tracker.debian.org/pkg/binaryen),
[Homebrew](https://formulae.brew.sh/formula/binaryen) and [Arch Linux](https://archlinux.org/packages/community/x86_64/binaryen/).
After you've installed the package execute `cargo install --force cargo-contract`.

- _Build `binaryen` as a dependency when installing `cargo-contract`:_
A C++14 compiler and python >= 3.5 is required.
Execute `cargo install --force --features wasm-opt-unavailable cargo-contract`.

## Usage

Expand Down
4 changes: 2 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::{
fs::File,
io::{prelude::*, Write},
iter::Iterator,
path::PathBuf,
path::{Path, PathBuf},
};

use anyhow::Result;
Expand Down Expand Up @@ -64,7 +64,7 @@ fn main() {
);
}

fn zip_dir(src_dir: &PathBuf, dst_file: &PathBuf, method: CompressionMethod) -> Result<()> {
fn zip_dir(src_dir: &Path, dst_file: &Path, method: CompressionMethod) -> Result<()> {
if !src_dir.exists() {
anyhow::bail!("src_dir '{}' does not exist", src_dir.display());
}
Expand Down
88 changes: 74 additions & 14 deletions src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ use std::{
convert::TryFrom,
fs::{metadata, File},
io::{Read, Write},
path::PathBuf,
path::{Path, PathBuf},
};

#[cfg(not(feature = "binaryen-as-dependency"))]
use std::{io, process::Command};

use crate::{
crate_metadata::CrateMetadata,
maybe_println, util,
Expand Down Expand Up @@ -261,23 +264,11 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<OptimizationResult> {
let mut optimized = crate_metadata.dest_wasm.clone();
optimized.set_file_name(format!("{}-opt.wasm", crate_metadata.package_name));

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,
};

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 optimized_wasm = do_optimization(crate_metadata, &optimized, &dest_wasm_file_content, 3)?;

let mut optimized_wasm_file = File::create(optimized.as_os_str())?;
optimized_wasm_file.write_all(&optimized_wasm)?;
Expand All @@ -293,6 +284,75 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<OptimizationResult> {
})
}

/// Optimizes the Wasm supplied as `wasm` using the `binaryen-rs` dependency.
///
/// The supplied `optimization_level` denotes the number of optimization passes,
/// resulting in potentially a lot of time spent optimizing.
///
/// If successful, the optimized Wasm is returned as a `Vec<u8>`.
#[cfg(feature = "binaryen-as-dependency")]
fn do_optimization(
_: &CrateMetadata,
_: &Path,
wasm: &[u8],
optimization_level: u32,
) -> Result<Vec<u8>> {
let codegen_config = binaryen::CodegenConfig {
// number of optimization passes (spends potentially a lot of time optimizing)
optimization_level,
// the default
shrink_level: 1,
// the default
debug_info: false,
};
let mut module = binaryen::Module::read(&wasm)
.map_err(|_| anyhow::anyhow!("binaryen failed to read file content"))?;
module.optimize(&codegen_config);
Ok(module.write())
}

/// Optimizes the Wasm supplied as `crate_metadata.dest_wasm` using
/// the `wasm-opt` binary.
///
/// The supplied `optimization_level` denotes the number of optimization passes,
/// resulting in potentially a lot of time spent optimizing.
///
/// If successful, the optimized Wasm file is created under `optimized`
/// and returned as a `Vec<u8>`.
#[cfg(not(feature = "binaryen-as-dependency"))]
fn do_optimization(
crate_metadata: &CrateMetadata,
optimized_dest: &Path,
_: &[u8],
optimization_level: u32,
) -> Result<Vec<u8>> {
// check `wasm-opt` is installed
if which::which("wasm-opt").is_err() {
anyhow::bail!(
"{}",
"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()
);
}

let output = Command::new("wasm-opt")
.arg(crate_metadata.dest_wasm.as_os_str())
.arg(format!("-O{}", optimization_level))
.arg("-o")
.arg(optimized_dest.as_os_str())
.output()?;

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");
}
Ok(output.stdout)
}

/// Executes build of the smart-contract which produces a wasm binary that is ready for deploying.
///
/// It does so by invoking `cargo build` and then post processing the final binary.
Expand Down

0 comments on commit 0e80763

Please sign in to comment.