This commit adds support for automatically executing the `wasm-opt` binary from the [Binaryen project][binaryen]. By default `wasm-pack` will now, in release and profiling modes, execute `wasm-opt -O` over the final binary. The goal here is to enable optimizations that further reduce binary size or improve runtime. In the long run it's expected that `wasm-opt`'s optimizations may mostly make their way into LLVM, but it's empirically true today that `wasm-opt` plus LLVM is the best combination for size and speed today. A configuration section for `wasm-opt` has been added as [previously proposed][fitzgen], namely: ```toml [package.metadata.wasm-pack.profile.release] wasm-opt = ['-Os'] ``` The `wasm-opt` binary is downloaded from Binaryen's [own releases](https://github.com/webassembly/binaryen/releases). They're available for the same platforms that we download predownloaded binaries for `wasm-bindgen` on. We'll also opportunistically use `wasm-opt` in `PATH` if it's available. If we're untable to run `wasm-opt`, though, a warning diagnostic is printed informing such. Closes #159 [binaryen]: https://github.com/webassembly/binaryen [fitzgen]: https://github.com/rustwasm/wasm-pack/issues/159#issuecomment-454888890master
parent
7dd889b4be
commit
9b74e43d2d
@ -0,0 +1,108 @@ |
|||||||
|
//! Support for downloading and executing `wasm-opt`
|
||||||
|
|
||||||
|
use crate::child; |
||||||
|
use crate::emoji; |
||||||
|
use crate::target; |
||||||
|
use crate::PBAR; |
||||||
|
use binary_install::Cache; |
||||||
|
use log::debug; |
||||||
|
use std::path::{Path, PathBuf}; |
||||||
|
use std::process::Command; |
||||||
|
|
||||||
|
/// Execute `wasm-opt` over wasm binaries found in `out_dir`, downloading if
|
||||||
|
/// necessary into `cache`. Passes `args` to each invocation of `wasm-opt`.
|
||||||
|
pub fn run( |
||||||
|
cache: &Cache, |
||||||
|
out_dir: &Path, |
||||||
|
args: &[String], |
||||||
|
install_permitted: bool, |
||||||
|
) -> Result<(), failure::Error> { |
||||||
|
let wasm_opt = match find_wasm_opt(cache, install_permitted)? { |
||||||
|
WasmOpt::Found(path) => path, |
||||||
|
WasmOpt::CannotInstall => { |
||||||
|
PBAR.info("Skipping wasm-opt as no downloading was requested"); |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
WasmOpt::PlatformNotSupported => { |
||||||
|
PBAR.info("Skipping wasm-opt because it is not supported on this platform"); |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
PBAR.info("Optimizing wasm binaries with `wasm-opt`..."); |
||||||
|
|
||||||
|
for file in out_dir.read_dir()? { |
||||||
|
let file = file?; |
||||||
|
let path = file.path(); |
||||||
|
if path.extension().and_then(|s| s.to_str()) != Some("wasm") { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
let tmp = path.with_extension("wasm-opt.wasm"); |
||||||
|
let mut cmd = Command::new(&wasm_opt); |
||||||
|
cmd.arg(&path).arg("-o").arg(&tmp).args(args); |
||||||
|
child::run(cmd, "wasm-opt")?; |
||||||
|
std::fs::rename(&tmp, &path)?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Possible results of `find_wasm_opt`
|
||||||
|
pub enum WasmOpt { |
||||||
|
/// Couldn't install wasm-opt because downloads are forbidden
|
||||||
|
CannotInstall, |
||||||
|
/// The current platform doesn't support precompiled binaries
|
||||||
|
PlatformNotSupported, |
||||||
|
/// We found `wasm-opt` at the specified path
|
||||||
|
Found(PathBuf), |
||||||
|
} |
||||||
|
|
||||||
|
/// Attempts to find `wasm-opt` in `PATH` locally, or failing that downloads a
|
||||||
|
/// precompiled binary.
|
||||||
|
///
|
||||||
|
/// Returns `Some` if a binary was found or it was successfully downloaded.
|
||||||
|
/// Returns `None` if a binary wasn't found in `PATH` and this platform doesn't
|
||||||
|
/// have precompiled binaries. Returns an error if we failed to download the
|
||||||
|
/// binary.
|
||||||
|
pub fn find_wasm_opt(cache: &Cache, install_permitted: bool) -> Result<WasmOpt, failure::Error> { |
||||||
|
// First attempt to look up in PATH. If found assume it works.
|
||||||
|
if let Ok(path) = which::which("wasm-opt") { |
||||||
|
debug!("found wasm-opt at {:?}", path); |
||||||
|
return Ok(WasmOpt::Found(path)); |
||||||
|
} |
||||||
|
|
||||||
|
// ... and if that fails download a precompiled version.
|
||||||
|
let target = if target::LINUX && target::x86_64 { |
||||||
|
"x86_64-linux" |
||||||
|
} else if target::MACOS && target::x86_64 { |
||||||
|
"x86_64-apple-darwin" |
||||||
|
} else if target::WINDOWS && target::x86_64 { |
||||||
|
"x86_64-windows" |
||||||
|
} else { |
||||||
|
return Ok(WasmOpt::PlatformNotSupported); |
||||||
|
}; |
||||||
|
let url = format!( |
||||||
|
"https://github.com/WebAssembly/binaryen/releases/download/{vers}/binaryen-{vers}-{target}.tar.gz", |
||||||
|
vers = "version_78", |
||||||
|
target = target, |
||||||
|
); |
||||||
|
|
||||||
|
let download = |permit_install| cache.download(permit_install, "wasm-opt", &["wasm-opt"], &url); |
||||||
|
|
||||||
|
let dl = match download(false)? { |
||||||
|
Some(dl) => dl, |
||||||
|
None if !install_permitted => return Ok(WasmOpt::CannotInstall), |
||||||
|
None => { |
||||||
|
let msg = format!("{}Installing wasm-opt...", emoji::DOWN_ARROW); |
||||||
|
PBAR.info(&msg); |
||||||
|
|
||||||
|
match download(install_permitted)? { |
||||||
|
Some(dl) => dl, |
||||||
|
None => return Ok(WasmOpt::CannotInstall), |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Ok(WasmOpt::Found(dl.binary("wasm-opt")?)) |
||||||
|
} |
@ -0,0 +1,189 @@ |
|||||||
|
use assert_cmd::prelude::*; |
||||||
|
use predicates::prelude::*; |
||||||
|
use utils; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn off_in_dev() { |
||||||
|
let fixture = utils::fixture::Fixture::new(); |
||||||
|
fixture.readme().cargo_toml("foo").file("src/lib.rs", ""); |
||||||
|
fixture.install_local_wasm_bindgen(); |
||||||
|
fixture.install_wasm_opt(); |
||||||
|
|
||||||
|
fixture |
||||||
|
.wasm_pack() |
||||||
|
.arg("build") |
||||||
|
.arg("--dev") |
||||||
|
.assert() |
||||||
|
.stderr(predicates::str::contains("wasm-opt").not()) |
||||||
|
.success(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn on_in_release() { |
||||||
|
let fixture = utils::fixture::Fixture::new(); |
||||||
|
fixture.readme().cargo_toml("foo").file("src/lib.rs", ""); |
||||||
|
fixture.install_local_wasm_bindgen(); |
||||||
|
fixture.install_wasm_opt(); |
||||||
|
|
||||||
|
fixture |
||||||
|
.wasm_pack() |
||||||
|
.arg("build") |
||||||
|
.assert() |
||||||
|
.stderr(predicates::str::contains("wasm-opt")) |
||||||
|
.success(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn disable_in_release() { |
||||||
|
let fixture = utils::fixture::Fixture::new(); |
||||||
|
fixture |
||||||
|
.readme() |
||||||
|
.file( |
||||||
|
"Cargo.toml", |
||||||
|
r#" |
||||||
|
[package] |
||||||
|
authors = [] |
||||||
|
description = "" |
||||||
|
license = "MIT" |
||||||
|
name = "foo" |
||||||
|
repository = "" |
||||||
|
version = "0.1.0" |
||||||
|
|
||||||
|
[lib] |
||||||
|
crate-type = ["cdylib"] |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
wasm-bindgen = "0.2" |
||||||
|
|
||||||
|
[package.metadata.wasm-pack.profile.release] |
||||||
|
wasm-opt = false |
||||||
|
"#, |
||||||
|
) |
||||||
|
.file("src/lib.rs", ""); |
||||||
|
fixture.install_local_wasm_bindgen(); |
||||||
|
fixture.install_wasm_opt(); |
||||||
|
|
||||||
|
fixture |
||||||
|
.wasm_pack() |
||||||
|
.arg("build") |
||||||
|
.assert() |
||||||
|
.stderr(predicates::str::contains("wasm-opt").not()) |
||||||
|
.success(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn enable_in_dev() { |
||||||
|
let fixture = utils::fixture::Fixture::new(); |
||||||
|
fixture |
||||||
|
.readme() |
||||||
|
.file( |
||||||
|
"Cargo.toml", |
||||||
|
r#" |
||||||
|
[package] |
||||||
|
authors = [] |
||||||
|
description = "" |
||||||
|
license = "MIT" |
||||||
|
name = "foo" |
||||||
|
repository = "" |
||||||
|
version = "0.1.0" |
||||||
|
|
||||||
|
[lib] |
||||||
|
crate-type = ["cdylib"] |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
wasm-bindgen = "0.2" |
||||||
|
|
||||||
|
[package.metadata.wasm-pack.profile.dev] |
||||||
|
wasm-opt = true |
||||||
|
"#, |
||||||
|
) |
||||||
|
.file("src/lib.rs", ""); |
||||||
|
fixture.install_local_wasm_bindgen(); |
||||||
|
fixture.install_wasm_opt(); |
||||||
|
|
||||||
|
fixture |
||||||
|
.wasm_pack() |
||||||
|
.arg("build") |
||||||
|
.arg("--dev") |
||||||
|
.assert() |
||||||
|
.stderr(predicates::str::contains( |
||||||
|
"Optimizing wasm binaries with `wasm-opt`", |
||||||
|
)) |
||||||
|
.success(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn custom_args() { |
||||||
|
let fixture = utils::fixture::Fixture::new(); |
||||||
|
fixture |
||||||
|
.readme() |
||||||
|
.file( |
||||||
|
"Cargo.toml", |
||||||
|
r#" |
||||||
|
[package] |
||||||
|
authors = [] |
||||||
|
description = "" |
||||||
|
license = "MIT" |
||||||
|
name = "foo" |
||||||
|
repository = "" |
||||||
|
version = "0.1.0" |
||||||
|
|
||||||
|
[lib] |
||||||
|
crate-type = ["cdylib"] |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
wasm-bindgen = "0.2" |
||||||
|
|
||||||
|
[package.metadata.wasm-pack.profile.release] |
||||||
|
wasm-opt = ['--not-accepted-argument'] |
||||||
|
"#, |
||||||
|
) |
||||||
|
.file("src/lib.rs", ""); |
||||||
|
fixture.install_local_wasm_bindgen(); |
||||||
|
fixture.install_wasm_opt(); |
||||||
|
|
||||||
|
fixture |
||||||
|
.wasm_pack() |
||||||
|
.arg("build") |
||||||
|
.assert() |
||||||
|
.stderr(predicates::str::contains("--not-accepted-argument")) |
||||||
|
.failure(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn misconfigured() { |
||||||
|
let fixture = utils::fixture::Fixture::new(); |
||||||
|
fixture |
||||||
|
.readme() |
||||||
|
.file( |
||||||
|
"Cargo.toml", |
||||||
|
r#" |
||||||
|
[package] |
||||||
|
authors = [] |
||||||
|
description = "" |
||||||
|
license = "MIT" |
||||||
|
name = "foo" |
||||||
|
repository = "" |
||||||
|
version = "0.1.0" |
||||||
|
|
||||||
|
[lib] |
||||||
|
crate-type = ["cdylib"] |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
wasm-bindgen = "0.2" |
||||||
|
|
||||||
|
[package.metadata.wasm-pack.profile.release] |
||||||
|
wasm-opt = 32 |
||||||
|
"#, |
||||||
|
) |
||||||
|
.file("src/lib.rs", ""); |
||||||
|
fixture.install_local_wasm_bindgen(); |
||||||
|
fixture.install_wasm_opt(); |
||||||
|
|
||||||
|
fixture |
||||||
|
.wasm_pack() |
||||||
|
.arg("build") |
||||||
|
.assert() |
||||||
|
.stderr(predicates::str::contains("failed to parse manifest")) |
||||||
|
.failure(); |
||||||
|
} |
Loading…
Reference in new issue