Sfoglia il codice sorgente

Integrate wasm-opt into the CLI (#2434)

* integrate wasm-opt

* include wasm opt configuration in the default dioxus.toml
Evan Almloff 1 anno fa
parent
commit
9d55bfc1dc

+ 129 - 0
Cargo.lock

@@ -1435,6 +1435,16 @@ dependencies = [
  "objc",
 ]
 
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
 [[package]]
 name = "color_quant"
 version = "1.1.0"
@@ -1933,6 +1943,50 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "cxx"
+version = "1.0.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb497fad022245b29c2a0351df572e2d67c1046bcef2260ebc022aec81efea82"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9327c7f9fbd6329a200a5d4aa6f674c60ab256525ff0084b52a889d4e4c60cee"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "688c799a4a846f1c0acb9f36bb9c6272d9b3d9457f3633c7753c6057270df13c"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928bc249a7e3cd554fd2e8e08a426e9670c50bbfc9a621653cfa9accc9641783"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
 [[package]]
 name = "darling"
 version = "0.20.8"
@@ -2194,6 +2248,7 @@ dependencies = [
  "walkdir",
  "wasm-bindgen-cli-support",
  "wasm-bindgen-shared",
+ "wasm-opt",
  "zip",
 ]
 
@@ -5521,6 +5576,15 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e"
 
+[[package]]
+name = "link-cplusplus"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "linked-hash-map"
 version = "0.5.6"
@@ -7863,6 +7927,12 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
+[[package]]
+name = "scratch"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
+
 [[package]]
 name = "sct"
 version = "0.7.1"
@@ -8735,6 +8805,25 @@ version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
+[[package]]
+name = "strum"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+
+[[package]]
+name = "strum_macros"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "subprocess"
 version = "0.2.9"
@@ -10010,6 +10099,46 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "wasm-opt"
+version = "0.116.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c"
+dependencies = [
+ "anyhow",
+ "libc",
+ "strum",
+ "strum_macros",
+ "tempfile",
+ "thiserror",
+ "wasm-opt-cxx-sys",
+ "wasm-opt-sys",
+]
+
+[[package]]
+name = "wasm-opt-cxx-sys"
+version = "0.116.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e"
+dependencies = [
+ "anyhow",
+ "cxx",
+ "cxx-build",
+ "wasm-opt-sys",
+]
+
+[[package]]
+name = "wasm-opt-sys"
+version = "0.116.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe"
+dependencies = [
+ "anyhow",
+ "cc",
+ "cxx",
+ "cxx-build",
+]
+
 [[package]]
 name = "wasm-streams"
 version = "0.4.0"

+ 54 - 0
packages/cli-config/src/config.rs

@@ -214,6 +214,7 @@ impl Default for DioxusConfig {
                     cert_path: None,
                 },
                 pre_compress: true,
+                wasm_opt: Default::default(),
             },
             bundle: BundleConfig {
                 identifier: Some(format!("io.github.{name}")),
@@ -286,6 +287,55 @@ pub struct WebConfig {
     /// Whether to enable pre-compression of assets and wasm during a web build in release mode
     #[serde(default = "true_bool")]
     pub pre_compress: bool,
+    /// The wasm-opt configuration
+    #[serde(default)]
+    pub wasm_opt: WasmOptConfig,
+}
+
+/// The wasm-opt configuration
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct WasmOptConfig {
+    /// The wasm-opt level to use for release builds [default: s]
+    /// Options:
+    /// - z: optimize aggressively for size
+    /// - s: optimize for size
+    /// - 1: optimize for speed
+    /// - 2: optimize for more for speed
+    /// - 3: optimize for even more for speed
+    /// - 4: optimize aggressively for speed
+    #[serde(default)]
+    pub level: WasmOptLevel,
+
+    /// Keep debug symbols in the wasm file
+    #[serde(default = "false_bool")]
+    pub debug: bool,
+}
+
+/// The wasm-opt level to use for release web builds [default: 4]
+#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
+pub enum WasmOptLevel {
+    /// Optimize aggressively for size
+    #[serde(rename = "z")]
+    Z,
+    /// Optimize for size
+    #[serde(rename = "s")]
+    S,
+    /// Don't optimize
+    #[serde(rename = "0")]
+    Zero,
+    /// Optimize for speed
+    #[serde(rename = "1")]
+    One,
+    /// Optimize for more for speed
+    #[serde(rename = "2")]
+    Two,
+    /// Optimize for even more for speed
+    #[serde(rename = "3")]
+    Three,
+    /// Optimize aggressively for speed
+    #[serde(rename = "4")]
+    #[default]
+    Four,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -587,3 +637,7 @@ impl CrateConfig {
 fn true_bool() -> bool {
     true
 }
+
+fn false_bool() -> bool {
+    false
+}

+ 1 - 0
packages/cli/Cargo.toml

@@ -101,6 +101,7 @@ env_logger = "0.11.3"
 
 tracing-subscriber = { version = "0.3.18", features = ["std", "env-filter"] }
 tracing = { workspace = true }
+wasm-opt = "0.116.1"
 
 # on maco, we need to specify the vendored feature on ssl when cross compiling
 [target.'cfg(target_os = "macos")'.dependencies]

+ 4 - 0
packages/cli/src/assets/dioxus.toml

@@ -17,6 +17,10 @@ asset_dir = "public"
 # hot reload by default
 hot_reload = true
 
+[web.wasm_opt]
+# The level wasm-opt should target. z is the smallest. 4 is the fastest.
+level = "4"
+
 [web.app]
 
 # HTML title tag content

+ 37 - 43
packages/cli/src/builder.rs

@@ -8,7 +8,7 @@ use crate::{
 };
 use anyhow::Context;
 use cargo_metadata::{diagnostic::Diagnostic, Message};
-use dioxus_cli_config::{crate_root, CrateConfig, ExecutableType};
+use dioxus_cli_config::{crate_root, CrateConfig, ExecutableType, WasmOptLevel};
 use indicatif::{ProgressBar, ProgressStyle};
 use lazy_static::lazy_static;
 use manganis_cli_support::{AssetManifest, ManganisSupportGuard};
@@ -174,15 +174,18 @@ pub fn build_web(
         // [3] Bindgen the final binary for use easy linking
         let mut bindgen_builder = Bindgen::new();
 
+        let keep_debug = dioxus_config.web.wasm_opt.debug || (!config.release);
+
         bindgen_builder
             .input_path(&input_path)
             .web(true)
             .unwrap()
-            .debug(true)
-            .demangle(true)
-            .keep_debug(true)
-            .remove_name_section(false)
-            .remove_producers_section(false)
+            .debug(keep_debug)
+            .demangle(keep_debug)
+            .keep_debug(keep_debug)
+            .reference_types(true)
+            .remove_name_section(!keep_debug)
+            .remove_producers_section(!keep_debug)
             .out_name(&dioxus_config.application.name)
             .generate(&bindgen_outdir)
             .unwrap();
@@ -197,43 +200,33 @@ pub fn build_web(
         run_wasm_bindgen();
     }
 
-    // check binaryen:wasm-opt tool
-    tracing::info!("Running optimization with wasm-opt...");
-    let dioxus_tools = dioxus_config.application.tools.clone();
-    if dioxus_tools.contains_key("binaryen") {
-        let info = dioxus_tools.get("binaryen").unwrap();
-        let binaryen = crate::tools::Tool::Binaryen;
-
-        if binaryen.is_installed() {
-            if let Some(sub) = info.as_table() {
-                if sub.contains_key("wasm_opt")
-                    && sub.get("wasm_opt").unwrap().as_bool().unwrap_or(false)
-                {
-                    tracing::info!("Optimizing WASM size with wasm-opt...");
-                    let target_file = out_dir
-                        .join("assets")
-                        .join("dioxus")
-                        .join(format!("{}_bg.wasm", dioxus_config.application.name));
-                    if target_file.is_file() {
-                        let mut args = vec![
-                            target_file.to_str().unwrap(),
-                            "-o",
-                            target_file.to_str().unwrap(),
-                        ];
-                        if config.release {
-                            args.push("-Oz");
-                        }
-                        binaryen.call("wasm-opt", args)?;
-                    }
-                }
-            }
-        } else {
-            tracing::warn!(
-                "Binaryen tool not found, you can use `dx tool add binaryen` to install it."
-            );
-        }
-    } else {
-        tracing::info!("Skipping optimization with wasm-opt, binaryen tool not found.");
+    // Run wasm-opt if this is a release build
+    if config.release {
+        tracing::info!("Running optimization with wasm-opt...");
+        let mut options = match dioxus_config.web.wasm_opt.level {
+            WasmOptLevel::Z => wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively(),
+            WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(),
+            WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(),
+            WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(),
+            WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(),
+            WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(),
+            WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(),
+        };
+        let wasm_file = bindgen_outdir.join(format!("{}_bg.wasm", dioxus_config.application.name));
+        let old_size = wasm_file.metadata()?.len();
+        options
+            // WASM bindgen relies on reference types
+            .enable_feature(wasm_opt::Feature::ReferenceTypes)
+            .debug_info(dioxus_config.web.wasm_opt.debug)
+            .run(&wasm_file, &wasm_file)
+            .map_err(|err| Error::Other(anyhow::anyhow!(err)))?;
+        let new_size = wasm_file.metadata()?.len();
+        tracing::info!(
+            "wasm-opt reduced WASM size from {} to {} ({:2}%)",
+            old_size,
+            new_size,
+            (new_size as f64 - old_size as f64) / old_size as f64 * 100.0
+        );
     }
 
     // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output
@@ -242,6 +235,7 @@ pub fn build_web(
     }
 
     // [5][OPTIONAL] If tailwind is enabled and installed we run it to generate the CSS
+    let dioxus_tools = dioxus_config.application.tools.clone();
     if dioxus_tools.contains_key("tailwindcss") {
         let info = dioxus_tools.get("tailwindcss").unwrap();
         let tailwind = crate::tools::Tool::Tailwind;

+ 0 - 36
packages/cli/src/tools.rs

@@ -13,15 +13,10 @@ use tokio::io::AsyncWriteExt;
 
 #[derive(Debug, PartialEq, Eq)]
 pub enum Tool {
-    Binaryen,
     Sass,
     Tailwind,
 }
 
-// pub fn tool_list() -> Vec<&'static str> {
-//     vec!["binaryen", "sass", "tailwindcss"]
-// }
-
 pub fn app_path() -> PathBuf {
     let data_local = dirs::data_local_dir().unwrap();
     let dioxus_dir = data_local.join("dioxus");
@@ -70,7 +65,6 @@ impl Tool {
     /// from str to tool enum
     pub fn from_str(name: &str) -> Option<Self> {
         match name {
-            "binaryen" => Some(Self::Binaryen),
             "sass" => Some(Self::Sass),
             "tailwindcss" => Some(Self::Tailwind),
             _ => None,
@@ -80,7 +74,6 @@ impl Tool {
     /// get current tool name str
     pub fn name(&self) -> &str {
         match self {
-            Self::Binaryen => "binaryen",
             Self::Sass => "sass",
             Self::Tailwind => "tailwindcss",
         }
@@ -89,7 +82,6 @@ impl Tool {
     /// get tool bin dir path
     pub fn bin_path(&self) -> &str {
         match self {
-            Self::Binaryen => "bin",
             Self::Sass => ".",
             Self::Tailwind => ".",
         }
@@ -98,17 +90,6 @@ impl Tool {
     /// get target platform
     pub fn target_platform(&self) -> &str {
         match self {
-            Self::Binaryen => {
-                if cfg!(target_os = "windows") {
-                    "windows"
-                } else if cfg!(target_os = "macos") {
-                    "macos"
-                } else if cfg!(target_os = "linux") {
-                    "linux"
-                } else {
-                    panic!("unsupported platformm");
-                }
-            }
             Self::Sass => {
                 if cfg!(target_os = "windows") {
                     "windows"
@@ -137,7 +118,6 @@ impl Tool {
     /// get tool version
     pub fn tool_version(&self) -> &str {
         match self {
-            Self::Binaryen => "version_105",
             Self::Sass => "1.51.0",
             Self::Tailwind => "v3.1.6",
         }
@@ -146,13 +126,6 @@ impl Tool {
     /// get tool package download url
     pub fn download_url(&self) -> String {
         match self {
-            Self::Binaryen => {
-                format!(
-                    "https://github.com/WebAssembly/binaryen/releases/download/{version}/binaryen-{version}-x86_64-{target}.tar.gz",
-                    version = self.tool_version(),
-                    target = self.target_platform()
-                )
-            }
             Self::Sass => {
                 format!(
                     "https://github.com/sass/dart-sass/releases/download/{version}/dart-sass-{version}-{target}-x64.{extension}",
@@ -179,7 +152,6 @@ impl Tool {
     /// get package extension name
     pub fn extension(&self) -> &str {
         match self {
-            Self::Binaryen => "tar.gz",
             Self::Sass => {
                 if cfg!(target_os = "windows") {
                     "zip"
@@ -226,7 +198,6 @@ impl Tool {
         let tool_path = tools_path();
 
         let dir_name = match self {
-            Self::Binaryen => format!("binaryen-{}", self.tool_version()),
             Self::Sass => "dart-sass".to_string(),
             Self::Tailwind => self.name().to_string(),
         };
@@ -281,13 +252,6 @@ impl Tool {
         let bin_path = tools_path().join(self.name()).join(self.bin_path());
 
         let command_file = match self {
-            Tool::Binaryen => {
-                if cfg!(target_os = "windows") {
-                    format!("{}.exe", command)
-                } else {
-                    command.to_string()
-                }
-            }
             Tool::Sass => {
                 if cfg!(target_os = "windows") {
                     format!("{}.bat", command)