soroban_cli/commands/contract/
optimize.rs

1use clap::{arg, command, Parser};
2use std::fmt::Debug;
3#[cfg(feature = "opt")]
4use wasm_opt::{Feature, OptimizationError, OptimizationOptions};
5
6use crate::wasm;
7
8#[derive(Parser, Debug, Clone)]
9#[group(skip)]
10pub struct Cmd {
11    #[command(flatten)]
12    wasm: wasm::Args,
13    /// Path to write the optimized WASM file to (defaults to same location as --wasm with .optimized.wasm suffix)
14    #[arg(long)]
15    wasm_out: Option<std::path::PathBuf>,
16}
17
18#[derive(thiserror::Error, Debug)]
19pub enum Error {
20    #[error(transparent)]
21    Wasm(#[from] wasm::Error),
22    #[cfg(feature = "opt")]
23    #[error("optimization error: {0}")]
24    OptimizationError(OptimizationError),
25    #[cfg(not(feature = "opt"))]
26    #[error("Must install with \"opt\" feature, e.g. `cargo install --locked soroban-cli --features opt")]
27    Install,
28}
29
30impl Cmd {
31    #[cfg(not(feature = "opt"))]
32    pub fn run(&self) -> Result<(), Error> {
33        Err(Error::Install)
34    }
35
36    #[cfg(feature = "opt")]
37    pub fn run(&self) -> Result<(), Error> {
38        let wasm_size = self.wasm.len()?;
39
40        println!(
41            "Reading: {} ({} bytes)",
42            self.wasm.wasm.to_string_lossy(),
43            wasm_size
44        );
45
46        let wasm_out = self.wasm_out.as_ref().cloned().unwrap_or_else(|| {
47            let mut wasm_out = self.wasm.wasm.clone();
48            wasm_out.set_extension("optimized.wasm");
49            wasm_out
50        });
51
52        let mut options = OptimizationOptions::new_optimize_for_size_aggressively();
53        options.converge = true;
54
55        // Explicitly set to MVP + sign-ext + mutable-globals, which happens to
56        // also be the default featureset, but just to be extra clear we set it
57        // explicitly.
58        //
59        // Formerly Soroban supported only the MVP feature set, but Rust 1.70 as
60        // well as Clang generate code with sign-ext + mutable-globals enabled,
61        // so Soroban has taken a change to support them also.
62        options.mvp_features_only();
63        options.enable_feature(Feature::MutableGlobals);
64        options.enable_feature(Feature::SignExt);
65
66        options
67            .run(&self.wasm.wasm, &wasm_out)
68            .map_err(Error::OptimizationError)?;
69
70        let wasm_out_size = wasm::len(&wasm_out)?;
71        println!(
72            "Optimized: {} ({} bytes)",
73            wasm_out.to_string_lossy(),
74            wasm_out_size
75        );
76
77        Ok(())
78    }
79}