1use crate::anyhow::{anyhow, ensure, Context, Result};
2use lazy_static::lazy_static;
3use std::{
4 fs,
5 path::{Path, PathBuf},
6 process,
7};
8
9lazy_static! {
10 static ref WASM_OPT_URL: String = {
11 let version = "123";
13
14 let os = std::env::consts::OS;
15 let mut arch = std::env::consts::ARCH;
16 if arch == "aarch64" {
17 arch = "arm64";
18 }
19
20 format!(
21 "https://github.com/WebAssembly/binaryen/releases/download/version_{version}/binaryen-version_{version}-{arch}-{os}.tar.gz",
22 )
23 };
24}
25
26#[allow(clippy::needless_question_mark)]
27fn download_wasm_opt() -> Result<&'static Path> {
28 lazy_static! {
29 static ref WASM_OPT_PATH: Result<PathBuf> = {
30 fn downloaded_binary_path() -> Result<PathBuf> {
31 let cache =
32 binary_install::Cache::at(crate::metadata().target_directory.as_std_path());
33
34 #[cfg(target_os = "macos")]
35 let binaries = &["wasm-opt", "libbinaryen"];
36 #[cfg(not(target_os = "macos"))]
37 let binaries = &["wasm-opt"];
38
39 log::info!("Downloading wasm-opt");
40 Ok(cache
41 .download(true, "wasm-opt", binaries, &WASM_OPT_URL)
42 .with_context(|| {
43 format!("could not download wasm-opt: {}", &WASM_OPT_URL.as_str())
44 })?
45 .expect("install_permitted is always true; qed")
46 .binary("wasm-opt")?)
47 }
48
49 downloaded_binary_path()
50 };
51 }
52
53 WASM_OPT_PATH.as_deref().map_err(|err| anyhow!("{}", err))
54}
55
56pub struct WasmOpt {
72 pub optimization_level: u32,
74 pub shrink_level: u32,
76 pub debug_info: bool,
78}
79
80impl WasmOpt {
81 pub fn level(optimization_level: u32) -> Self {
83 Self {
84 optimization_level,
85 shrink_level: 0,
86 debug_info: false,
87 }
88 }
89
90 pub fn shrink(mut self, shrink_level: u32) -> Self {
92 self.shrink_level = shrink_level;
93 self
94 }
95
96 pub fn debug(mut self) -> Self {
98 self.debug_info = true;
99 self
100 }
101
102 pub fn optimize(self, binary_path: impl AsRef<Path>) -> Result<Self> {
107 let input_path = binary_path.as_ref();
108 let output_path = input_path.with_extension("opt");
109 let wasm_opt = download_wasm_opt()?;
110
111 let mut command = process::Command::new(wasm_opt);
112 command
113 .stderr(process::Stdio::inherit())
114 .arg(input_path)
115 .arg("-o")
116 .arg(&output_path)
117 .arg("-O")
118 .arg("-ol")
119 .arg(self.optimization_level.to_string())
120 .arg("-s")
121 .arg(self.shrink_level.to_string());
122
123 if self.debug_info {
124 command.arg("-g");
125 }
126
127 #[cfg(target_os = "macos")]
128 {
129 command.env("DYLD_LIBRARY_PATH", wasm_opt.parent().unwrap());
130 }
131
132 log::info!("Optimizing Wasm");
133 ensure!(
134 command.output()?.status.success(),
135 "command `wasm-opt` failed"
136 );
137
138 fs::remove_file(input_path)?;
139 fs::rename(&output_path, input_path)?;
140
141 log::info!("Wasm optimized");
142 Ok(self)
143 }
144}