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
56#[derive(Debug)]
72pub struct WasmOpt {
73 pub optimization_level: u32,
75 pub shrink_level: u32,
77 pub debug_info: bool,
79}
80
81impl WasmOpt {
82 pub fn level(optimization_level: u32) -> Self {
84 Self {
85 optimization_level,
86 shrink_level: 0,
87 debug_info: false,
88 }
89 }
90
91 pub fn shrink(mut self, shrink_level: u32) -> Self {
93 self.shrink_level = shrink_level;
94 self
95 }
96
97 pub fn debug(mut self) -> Self {
99 self.debug_info = true;
100 self
101 }
102
103 pub fn optimize(self, binary_path: impl AsRef<Path>) -> Result<Self> {
108 let input_path = binary_path.as_ref();
109 let output_path = input_path.with_extension("opt");
110 let wasm_opt = download_wasm_opt()?;
111
112 let mut command = process::Command::new(wasm_opt);
113 command
114 .stderr(process::Stdio::inherit())
115 .arg(input_path)
116 .arg("-o")
117 .arg(&output_path)
118 .arg("-O")
119 .arg("-ol")
120 .arg(self.optimization_level.to_string())
121 .arg("-s")
122 .arg(self.shrink_level.to_string());
123
124 if self.debug_info {
125 command.arg("-g");
126 }
127
128 #[cfg(target_os = "macos")]
129 {
130 command.env("DYLD_LIBRARY_PATH", wasm_opt.parent().unwrap());
131 }
132
133 log::info!("Optimizing Wasm");
134 ensure!(
135 command.output()?.status.success(),
136 "command `wasm-opt` failed"
137 );
138
139 fs::remove_file(input_path)?;
140 fs::rename(&output_path, input_path)?;
141
142 log::info!("Wasm optimized");
143 Ok(self)
144 }
145}