Skip to content

Commit 1842497

Browse files
authored
Allow customization of wasm-opt args (#486)
# Objective Closes #442. For further optimization, the user might want to get full control over the flags that are passed to `wasm-opt`. At the same time, users also need an easy way to enable or disable `wasm-opt`, e.g. disabling it even in release mode or enabling it in dev mode. # Solution I changed the ArgBuilder to deal with OsString instead of String, since that's what we need to pass to the Command anyway. This simplified this change, sorry for the noise in the diff. - The `--wasm-opt` CLI flag now also accepts multiple string values. These are passed as args to `wasm-opt`. Alternatively, you can still use `true` to enable the default flags or `false` to disable `wasm-opt` entirely. - The `wasm-opt` config option now also accepts an array of args: ```toml [package.metadata.bevy_cli.web.release] wasm-opt = ["-Oz", "--enable-bulk-memory"] ``` # Testing In <https://github.com/TimJentzsch/bevy_complex_repo>, the `workspace` folder now contains a `wasm_opt` package where the new options are used in the `Cargo.toml`.
1 parent 2725dac commit 1842497

File tree

15 files changed

+343
-103
lines changed

15 files changed

+343
-103
lines changed

CHANGELOG.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
1111

1212
**All Changes**: [`cli-v0.1.0-alpha.1...main`](https://github.com/TheBevyFlock/bevy_cli/compare/cli-v0.1.0-alpha.1...main)
1313

14+
### Changed
15+
16+
- You can now customize the flags passed to `wasm-opt` in both CLI and `Cargo.toml`. Simply pass a list of flags you want to use, e.g. `--wasm-opt=-Oz --wasm-opt=--enable-bulk-memory` in the CLI or `wasm-opt = ["-Oz", "--enable-bulk-memory"]` in the config.
17+
1418
## v0.1.0-alpha.1 - 2025-05-23
1519

1620
**All Changes**: [`cli-v0.1.0-alpha.1`](https://github.com/TheBevyFlock/bevy_cli/commits/cli-v0.1.0-alpha.1)
1721

1822
### Added
1923

2024
- `bevy new`: create new projects from a template using `cargo-generate` ([#2](https://github.com/TheBevyFlock/bevy_cli/pull/2))
21-
- [`bevy_new_minimal`](https://github.com/TheBevyFlock/bevy_new_minimal) is the default template if none is specified ([#80](https://github.com/TheBevyFlock/bevy_cli/pull/80))
22-
- There are shortcuts for templates from [TheBevyFlock](https://github.com/TheBevyFlock). For example, `-t 2d` uses [`bevy_new_2d`](https://github.com/TheBevyFlock/bevy_new_2d) ([#82](https://github.com/TheBevyFlock/bevy_cli/pull/82))
25+
- [`bevy_new_minimal`](https://github.com/TheBevyFlock/bevy_new_minimal) is the default template if none is specified ([#80](https://github.com/TheBevyFlock/bevy_cli/pull/80))
26+
- There are shortcuts for templates from [TheBevyFlock](https://github.com/TheBevyFlock). For example, `-t 2d` uses [`bevy_new_2d`](https://github.com/TheBevyFlock/bevy_new_2d) ([#82](https://github.com/TheBevyFlock/bevy_cli/pull/82))
2327
- `bevy lint`: invoke the linter if `bevy_lint` is installed ([#4](https://github.com/TheBevyFlock/bevy_cli/pull/4))
2428
- `bevy build` and `bevy run`: build and run your program with Bevy-specific configuration ([#76](https://github.com/TheBevyFlock/bevy_cli/pull/76), [#103](https://github.com/TheBevyFlock/bevy_cli/pull/103), [#102](https://github.com/TheBevyFlock/bevy_cli/pull/102), [#120](https://github.com/TheBevyFlock/bevy_cli/pull/120))
25-
- You can use `bevy build web` and `bevy run web` to build and run your program for the web using Wasm.
26-
- Web binaries can be optimized with `wasm-opt` ([#206](https://github.com/TheBevyFlock/bevy_cli/pull/206), [#430](https://github.com/TheBevyFlock/bevy_cli/pull/430))
27-
- You can pass `--bundle` to pack all files needed for the web into a single folder ([#195](https://github.com/TheBevyFlock/bevy_cli/pull/195))
29+
- You can use `bevy build web` and `bevy run web` to build and run your program for the web using Wasm.
30+
- Web binaries can be optimized with `wasm-opt` ([#206](https://github.com/TheBevyFlock/bevy_cli/pull/206), [#430](https://github.com/TheBevyFlock/bevy_cli/pull/430))
31+
- You can pass `--bundle` to pack all files needed for the web into a single folder ([#195](https://github.com/TheBevyFlock/bevy_cli/pull/195))
2832
- `bevy completions`: generate terminal auto-complete scripts for a variety of shells ([#265](https://github.com/TheBevyFlock/bevy_cli/pull/265))
2933
- The CLI can be configured with `[package.metadata.bevy_cli]` ([#331](https://github.com/TheBevyFlock/bevy_cli/pull/331), [#355](https://github.com/TheBevyFlock/bevy_cli/pull/355), [#351](https://github.com/TheBevyFlock/bevy_cli/pull/351))

MIGRATION.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,23 @@ bevy run --no-default-features false
2323
bevy build --no-default-features
2424
bevy run
2525
```
26+
27+
### `--wasm-opt` needs a value
28+
29+
You now need to provide an explicit value to the `--wasm-opt` flag.
30+
If you were using `--wasm-opt` you now need to use `--wasm-opt=true`.
31+
32+
```sh
33+
# v0.1.0-alpha.1
34+
bevy build web --wasm-opt
35+
36+
# v0.1.0-alpha.2
37+
bevy build web --wasm-opt=true
38+
```
39+
40+
On the flip side, you can now customize the flags that are passed to `wasm-opt`:
41+
42+
```sh
43+
# v0.1.0-alpha.2
44+
bevy build web --wasm-opt=-Oz --wasm-opt=--enable-bulk-memory
45+
```

docs/src/cli/configuration.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ The CLI supports two targets:
1313

1414
For both these targets a release and dev [`profile`] exists that will automatically be chosen by the CLI:
1515

16-
| **Profile Name** | **Configuration Section** |
17-
|--------------------------|----------------------------------------------------|
18-
| `release` | `[package.metadata.bevy_cli.native.release]` |
19-
| `dev` | `[package.metadata.bevy_cli.native.dev]` |
20-
| `web-release` | `[package.metadata.bevy_cli.web.release]` |
21-
| `web` | `[package.metadata.bevy_cli.web.dev]` |
16+
| **Profile Name** | **Configuration Section** |
17+
| ---------------- | -------------------------------------------- |
18+
| `release` | `[package.metadata.bevy_cli.native.release]` |
19+
| `dev` | `[package.metadata.bevy_cli.native.dev]` |
20+
| `web-release` | `[package.metadata.bevy_cli.web.release]` |
21+
| `web` | `[package.metadata.bevy_cli.web.dev]` |
2222

2323
> **Note**
2424
>
@@ -50,6 +50,7 @@ wasm-opt = true
5050
[package.metadata.bevy_cli.release]
5151
default-features = false
5252
```
53+
5354
When building for web in release mode, the final merged configuration will be:
5455

5556
```toml
@@ -69,4 +70,3 @@ default-features = true
6970
```
7071

7172
[`profile`]: https://doc.rust-lang.org/cargo/reference/profiles.html
72-

docs/src/cli/configuration/reference.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
The following fields exist and can be configured:
44

5-
- [features](#features)
6-
- [default-features](#default-features)
7-
- [rustflags](#rustflags)
8-
- [wasm-opt](#wasm-opt)
5+
- [Configuration Reference](#configuration-reference)
6+
- [`features`](#features)
7+
- [`default-features`](#default-features)
8+
- [`rustflags`](#rustflags)
9+
- [`wasm-opt`](#wasm-opt)
910

1011
## `features`
1112

@@ -27,6 +28,6 @@ The following fields exist and can be configured:
2728

2829
## `wasm-opt`
2930

30-
- Type: boolean
31-
- Default: true for release web builds
32-
- Note: Whether or not to use [`wasm-opt`](https://github.com/WebAssembly/binaryen?tab=readme-ov-file#wasm-opt) to optimize the web binary.
31+
- Type: boolean or array of strings
32+
- Default: true for web release builds, false for web dev builds and native builds
33+
- Note: Whether or not to use [`wasm-opt`](https://github.com/WebAssembly/binaryen?tab=readme-ov-file#wasm-opt) to optimize the web binary. The specific flags to be used can be passed as array of strings or `true` can be passed to use default options (`--strip-debug` and `-Os`).

docs/src/cli/web.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,41 @@ opt-level = "z"
4747
4848
Alternatively, you can change the profile entirely, e.g. `bevy run --profile=foo web`.
4949

50+
## Optimization
51+
52+
In addition to the compilation profiles, you can also optimize the Wasm binary via [`wasm-opt`](https://docs.rs/wasm-opt/latest/wasm_opt/).
53+
This can significantly reduce the size of the binary, but also improves performance.
54+
55+
`wasm-opt` is enabled by default for release builds.
56+
If you haven't installed it, you will be asked to install it automatically.
57+
58+
The optimization can be configured both via CLI args and `Cargo.toml`.
59+
You can either disable optimization with `false`, enable default values with `true` or provide a list of flags to pass to `wasm-opt` for full control.
60+
61+
Setting it to `true` currently applies the `--strip-debug` and `-Os` flags.
62+
63+
Examples with the CLI:
64+
65+
- `--wasm-opt=false` to disable `wasm-opt` completely.
66+
- `--wasm-opt=true` to enable the default `wasm-opt` configuration.
67+
- `--wasm-opt=-Oz --wasm-opt=--enable-bulk-memory` to run `wasm-opt` with the `-Oz` and `--enable-bulk-memory` flags.
68+
69+
The same can be accomplished with the following `Cargo.toml` configs:
70+
71+
```toml
72+
[package.metadata.bevy_cli.web.release]
73+
# Disable wasm-opt even for release builds
74+
wasm-opt = false
75+
76+
[package.metadata.bevy_cli.web.dev]
77+
# Enable wasm-opt even for dev builds, using the default configuration
78+
wasm-opt = true
79+
80+
[package.metadata.bevy_cli.web.release]
81+
# Enable wasm-opt with custom flags
82+
wasm-opt = ["-Oz", "--enable-bulk-memory"]
83+
```
84+
5085
## Feature configuration
5186

5287
Often, you want to enable certain features only in development mode or only for native and not web builds.

src/build/args.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use clap::{ArgAction, Args, Subcommand};
22

3+
#[cfg(feature = "web")]
4+
use crate::external_cli::external_cli_args::ExternalCliArgs;
35
use crate::{
46
config::CliConfig,
57
external_cli::{
@@ -68,15 +70,15 @@ impl BuildArgs {
6870
self.cargo_args.args_builder(self.is_web())
6971
}
7072

71-
/// Whether to use `wasm-opt`.
72-
///
73-
/// Defaults to `true` for release builds.
73+
/// The flags to use for `wasm-opt` if building for the web.
7474
#[cfg(feature = "web")]
75-
pub(crate) fn use_wasm_opt(&self) -> bool {
75+
pub(crate) fn wasm_opt_args(&self) -> ExternalCliArgs {
76+
use crate::external_cli::external_cli_args::ExternalCliArgs;
77+
7678
if let Some(BuildSubcommands::Web(web_args)) = &self.subcommand {
77-
web_args.use_wasm_opt.map_or(self.is_release(), |v| v)
79+
ExternalCliArgs::from_raw_args(web_args.wasm_opt.clone())
7880
} else {
79-
false
81+
ExternalCliArgs::Enabled(false)
8082
}
8183
}
8284

@@ -107,10 +109,13 @@ impl BuildArgs {
107109
.clone()
108110
.or(config.rustflags());
109111

112+
#[cfg(feature = "web")]
113+
let is_release = self.is_release();
114+
110115
#[cfg(feature = "web")]
111116
if let Some(BuildSubcommands::Web(web_args)) = self.subcommand.as_mut() {
112-
if web_args.use_wasm_opt.is_none() {
113-
web_args.use_wasm_opt = config.wasm_opt();
117+
if web_args.wasm_opt.is_empty() {
118+
web_args.wasm_opt = config.wasm_opt(is_release).to_raw();
114119
}
115120
}
116121
}
@@ -127,10 +132,14 @@ pub enum BuildSubcommands {
127132
/// Additional Arguments for building a Bevy web project.
128133
#[derive(Debug, Args, Default)]
129134
pub struct BuildWebArgs {
130-
// Bundle all web artifacts into a single folder.
135+
/// Bundle all web artifacts into a single folder.
131136
#[arg(short = 'b', long = "bundle", action = ArgAction::SetTrue, default_value_t = false)]
132137
pub create_packed_bundle: bool,
133-
// Use `wasm-opt` to optimize the wasm binary
134-
#[arg(long = "wasm-opt")]
135-
pub use_wasm_opt: Option<bool>,
138+
/// Use `wasm-opt` to optimize the wasm binary
139+
///
140+
/// Defaults to `true` for release builds.
141+
/// Can be set to `false` to skip optimization.
142+
/// You can also specify custom arguments to use.
143+
#[arg(long = "wasm-opt", allow_hyphen_values = true)]
144+
pub wasm_opt: Vec<String>,
136145
}

src/config.rs

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use serde::Serialize;
77
use serde_json::{Map, Value};
88
use tracing::warn;
99

10+
use crate::external_cli::external_cli_args::ExternalCliArgs;
11+
1012
/// Configuration for the `bevy_cli`.
1113
///
1214
/// Allows customizing:
@@ -26,7 +28,7 @@ pub struct CliConfig {
2628
/// Additional flags for `rustc`
2729
rustflags: Vec<String>,
2830
/// Use `wasm-opt` to optimize wasm binaries.
29-
wasm_opt: Option<bool>,
31+
wasm_opt: Option<ExternalCliArgs>,
3032
}
3133

3234
impl CliConfig {
@@ -73,10 +75,17 @@ impl CliConfig {
7375
Some(self.rustflags.clone().join(" "))
7476
}
7577

76-
/// Whether to use `wasm-opt`.
78+
/// The `wasm-opt` configuration.
7779
#[cfg(feature = "web")]
78-
pub fn wasm_opt(&self) -> Option<bool> {
79-
self.wasm_opt
80+
pub fn wasm_opt(&self, is_release: bool) -> ExternalCliArgs {
81+
self.wasm_opt.clone().unwrap_or({
82+
// Enable by default for release builds
83+
if is_release {
84+
ExternalCliArgs::Enabled(true)
85+
} else {
86+
ExternalCliArgs::Enabled(false)
87+
}
88+
})
8089
}
8190

8291
/// Determine the Bevy CLI config as defined in the given package.
@@ -155,7 +164,7 @@ impl CliConfig {
155164
features: extract_features(metadata)?,
156165
default_features: extract_default_features(metadata)?,
157166
rustflags: extract_rustflags(metadata)?,
158-
wasm_opt: extract_use_wasm_opt(metadata)?,
167+
wasm_opt: extract_wasm_opt(metadata)?,
159168
})
160169
}
161170

@@ -167,7 +176,7 @@ impl CliConfig {
167176
self.target = with.target.clone().or(self.target);
168177
self.default_features = with.default_features.or(self.default_features);
169178

170-
self.wasm_opt = with.wasm_opt.or(self.wasm_opt);
179+
self.wasm_opt = with.wasm_opt.clone().or(self.wasm_opt);
171180

172181
// Features and Rustflags are additive
173182
self.features.extend(with.features.iter().cloned());
@@ -254,12 +263,26 @@ fn extract_rustflags(cli_metadata: &Map<String, Value>) -> anyhow::Result<Vec<St
254263
}
255264
}
256265

257-
fn extract_use_wasm_opt(cli_metadata: &Map<String, Value>) -> anyhow::Result<Option<bool>> {
258-
if let Some(use_wasm_opt) = cli_metadata.get("wasm-opt") {
259-
match use_wasm_opt {
260-
Value::Bool(use_wasm_opt) => Ok(Some(use_wasm_opt).copied()),
266+
fn extract_wasm_opt(cli_metadata: &Map<String, Value>) -> anyhow::Result<Option<ExternalCliArgs>> {
267+
if let Some(wasm_opt) = cli_metadata.get("wasm-opt") {
268+
match wasm_opt {
269+
Value::Bool(enabled) => Ok(Some(ExternalCliArgs::Enabled(*enabled))),
270+
Value::Array(arr) => {
271+
let args = arr
272+
.iter()
273+
.map(|value| {
274+
value
275+
.as_str()
276+
.map(std::string::ToString::to_string)
277+
.ok_or_else(|| {
278+
anyhow::anyhow!("each wasm-opt argument must be a string")
279+
})
280+
})
281+
.collect::<Result<Vec<String>, _>>()?;
282+
Ok(Some(ExternalCliArgs::Args(args)))
283+
}
261284
Value::Null => Ok(None),
262-
_ => bail!("wasm-opt must be a boolean"),
285+
_ => bail!("wasm-opt must be a boolean or an array of arguments to pass to wasm-opt"),
263286
}
264287
} else {
265288
Ok(None)
@@ -528,4 +551,54 @@ mod tests {
528551
assert!(extract_features(&cli_metadata).is_err());
529552
}
530553
}
554+
555+
mod extract_wasm_opt {
556+
use super::*;
557+
558+
#[test]
559+
fn should_return_none_if_no_wasm_opt_specified() -> anyhow::Result<()> {
560+
let cli_metadata = Map::new();
561+
assert_eq!(extract_wasm_opt(&cli_metadata)?, None);
562+
Ok(())
563+
}
564+
565+
#[test]
566+
fn should_return_enabled_if_wasm_opt_is_true() -> anyhow::Result<()> {
567+
let mut cli_metadata = Map::new();
568+
cli_metadata.insert("wasm-opt".to_owned(), true.into());
569+
assert_eq!(
570+
extract_wasm_opt(&cli_metadata)?,
571+
Some(ExternalCliArgs::Enabled(true))
572+
);
573+
Ok(())
574+
}
575+
576+
#[test]
577+
fn should_return_disabled_if_wasm_opt_is_false() -> anyhow::Result<()> {
578+
let mut cli_metadata = Map::new();
579+
cli_metadata.insert("wasm-opt".to_owned(), false.into());
580+
assert_eq!(
581+
extract_wasm_opt(&cli_metadata)?,
582+
Some(ExternalCliArgs::Enabled(false))
583+
);
584+
Ok(())
585+
}
586+
587+
#[test]
588+
fn should_return_args_if_wasm_opt_is_array() -> anyhow::Result<()> {
589+
let mut cli_metadata = Map::new();
590+
cli_metadata.insert(
591+
"wasm-opt".to_owned(),
592+
vec!["-Oz".to_owned(), "--enable-bulk-memory".to_owned()].into(),
593+
);
594+
assert_eq!(
595+
extract_wasm_opt(&cli_metadata)?,
596+
Some(ExternalCliArgs::Args(vec![
597+
"-Oz".to_owned(),
598+
"--enable-bulk-memory".to_owned()
599+
]))
600+
);
601+
Ok(())
602+
}
603+
}
531604
}

0 commit comments

Comments
 (0)