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