Skip to content

worker-build: let explicit wasm-opt args control wasm names - #1091

Merged
guybedford merged 4 commits into
cloudflare:mainfrom
JesusFileto:worker-build-keep-wasm-names
Oct 19, 2026
Merged

guybedford merged 4 commits into
cloudflare:mainfrom
JesusFileto:worker-build-keep-wasm-names

Conversation

@JesusFileto

@JesusFileto JesusFileto commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

worker-build always appends --debuginfo to the wasm-opt invocation, which keeps the Wasm name section so that CPU profiles and stack traces show Rust function names. That is the right default, but it leaves no working way to drop names for a size-sensitive Worker: whatever the profile's wasm-opt args say, the appended --debuginfo writes the names back out. So users reach for strip = true, which removes every custom section at link time (see wasm-bindgen/wasm-bindgen#5382).

Explicit wasm-opt args now give the user full control over the invocation:

  • wasm-opt = true resolves to -O --debuginfo, so names are kept by default.
  • wasm-opt = [...] is passed through as written, plus only the required --enable-* features. worker-build never appends -g.

Dropping names is just leaving out -g, which is how binaryen already behaves, so --strip, --strip-debug and any future flags work without worker-build parsing them. With wasm-opt = ["-O"], examples/router goes from 162KB to 118KB. Profiles that already set explicit wasm-opt args currently keep names only because worker-build appends --debuginfo, so they need to add -g to keep them, e.g. wasm-opt = ["-O", "-g"]. The READMEs document this in place of strip = true.

This also removes strip = true from the Leptos template, so new Leptos projects keep names in the Worker.

@JesusFileto
JesusFileto force-pushed the worker-build-keep-wasm-names branch 5 times, most recently from 8d76709 to 7f337a8 Compare October 15, 2026 23:04
@logan-gatlin
logan-gatlin self-requested a review October 15, 2026 23:05
With `strip = true`, rust-lld drops the `name` and `target_features`
sections. Profiles lose function names, and wasm-bindgen fails on
catch imports without `target_features`. Release and profiling
builds now link via `cargo rustc` with `--keep-section` for both.
`--no-preserve-wasm-names` drops names to save size.
@JesusFileto
JesusFileto force-pushed the worker-build-keep-wasm-names branch from 7f337a8 to 2e5a28c Compare October 15, 2026 23:28
@guybedford

Copy link
Copy Markdown
Collaborator

Thanks for looking into this one!

In general I would actually advise against using strip = true entirely. Custom sections are useful! For example there is also the producers custom section which we use to be able to tell what toolchain built a Wasm file and also for our own metrics internally.

Towards fixing this issue I've posted wasm-bindgen/wasm-bindgen#5382 which fixes the original issue and also emits a warning that makes it clear that strip = true is not the way.

With that context, we can then reframe this PR as changing the wasm-opt options when running wasm-opt after the wasm bindgen step, and wasm-opt can then be customized for build types further as needed.

Hopefully that framing helps? Happy to discuss further.

@JesusFileto

Copy link
Copy Markdown
Contributor Author

Thanks @guybedford for the reply!

I edited the PR to remove the link-time approach and instead decide to keep the name section during the wasm-opt step. I added this functionality as a flag --remove-name-section when calling worker-build. However I'm wondering should this functionality also be inside the crate profile wasm-opt arguments? Since it seems like wasm-opt settings are added there.

@guybedford guybedford left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reworking this, and to your question: yes, I think the profile's wasm-opt args are the right home for it rather than a new CLI flag.

Some background on how the tools interact here. Release builds already keep names today, because worker-build always appends --debuginfo to the wasm-opt invocation. So what this PR really adds is the opt-out for size, which is the legitimate reason people reach for strip = true (on examples/router it's 162KB -> 118KB).

The catch is that binaryen reads the name section into its IR as function names, and -g writes them back out at the end. --strip-debug only removes the custom section, and the IR names survive, so today this config still emits names because of the appended --debuginfo:

[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-O", "--strip-debug"]

If worker-build just skips --debuginfo when the profile's args contain --strip-debug, that config does what it says. Then no new flag is needed, it's set per profile in Cargo.toml right where strip = true used to live, and the --profiling check goes away. It also avoids the flag silently doing nothing whenever wasm-opt doesn't run (--dev, --no-opt, wasm-opt = false, --emscripten).

With that, the logic is just argument construction, so it can be covered by unit tests in cargo test -p worker-build, and the script and CI step can go. The READMEs would then document the profile recipe instead of the flag. The PR title and description also still describe the earlier link-time approach, so those need updating too.

Comment thread worker-build/src/build/mod.rs Outdated
}
// Keep the Wasm names section
args.push("--debuginfo".into());
if !self.remove_name_section {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like:

// Keep the Wasm names section unless the profile strips debug info
if !args.iter().any(|arg| arg == "--strip-debug") {
    args.push("--debuginfo".into());
}

Pulling the arg construction out into a small function would make it easy to unit test: the default wasm-opt = true gets --debuginfo, and ["-O", "--strip-debug"] doesn't.

Comment thread .github/check-wasm-names.mjs Outdated
@@ -0,0 +1,81 @@
// Checks that a Wasm module's `name` section holds function names from each given crate, or with

@guybedford guybedford Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not add test scripts under .github. With the profile approach the unit tests cover the behaviour worker-build owns, and what -g / --strip-debug do is binaryen's contract, so I think this script and the CI step can be dropped.

worker-build always appended `--debuginfo` to wasm-opt, so a profile
with `wasm-opt = ["-O", "--strip-debug"]` still kept the `name`
section. Skip `--debuginfo` when the profile strips debug info, so
size-sensitive Workers can drop names without `strip = true`.
@JesusFileto
JesusFileto force-pushed the worker-build-keep-wasm-names branch from 2224826 to 79f7f0e Compare October 18, 2026 15:51
@JesusFileto JesusFileto changed the title worker-build: keep wasm names in release builds worker-build: add --strip-debug in profile wasm-opt args Oct 18, 2026
@JesusFileto
JesusFileto marked this pull request as ready for review October 19, 2026 16:51

@guybedford guybedford left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, this is very close. Having looked at it again, I think matching on --strip-debug is the wrong shape, and that's on me since I suggested it last round. It means worker-build has to understand wasm-opt flags, and exact matching already misses aliases like --strip.

A simpler rule is that explicit args give the user full control over the invocation:

  • wasm-opt = true resolves to -O --debuginfo, so names are kept by default.
  • wasm-opt = [...] is passed through as written, plus only the required --enable-* features. worker-build never appends -g.

Then dropping names is just leaving out -g, which is already how binaryen behaves. --strip, --strip-debug and any future flags work with no parsing, and there's nothing for worker-build to keep in sync. Users with explicit args today would need to add -g to keep names, so that's worth a line in the PR description.

Could you also update the Leptos template while you're here? templates/leptos/Cargo.toml still has strip = true in [profile.release], which is exactly what the new README advises against, and new projects get it from cargo generate. I'd remove it so the template uses the default. Note that the profile is shared with the cargo leptos build --release client build, so it's worth checking that the client bundle size doesn't regress.

Comment thread worker-build/src/build/mod.rs Outdated
] {
args.push(format!("--enable-{feature}"));
}
// Keep the Wasm names section unless the profile strips debug info

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the explicit-args rule, this condition goes away. --debuginfo can move into the wasm-opt = true default in CargoWasmPackProfile::wasm_opt_args in manifest.rs:

CargoWasmPackProfileWasmOpt::Enabled(true) => Some(vec!["-O".to_string(), "--debuginfo".to_string()]),

and this function only appends the features. The tests then cover true -> -O --debuginfo plus features, and explicit args -> passed through plus features with no --debuginfo.

Comment thread worker-build/README.md Outdated

```toml
[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-O", "--strip-debug"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With that change, the recipe here becomes "explicit wasm-opt args don't keep names unless they include -g", for example:

[package.metadata.wasm-pack.profile.release]
# names dropped
wasm-opt = ["-O"]
# names kept
# wasm-opt = ["-O", "-g"]

The last sentence on line 33 also repeats the intro and can be dropped.

Matching on `--strip-debug` meant worker-build had to understand
wasm-opt flags, and missed aliases like `--strip`. Instead, explicit
args now give the user full control: `wasm-opt = true` resolves to
`-O --debuginfo`, while `wasm-opt = [...]` is passed through as
written plus only the required `--enable-*` features. Dropping names
is just leaving out `-g`. Profiles with explicit args need to add
`-g` to keep names.

Also remove `strip = true` from the Leptos template, so new projects
keep names in the Worker.
@JesusFileto JesusFileto changed the title worker-build: add --strip-debug in profile wasm-opt args worker-build: let explicit wasm-opt args control wasm names Oct 19, 2026
@guybedford
guybedford enabled auto-merge (squash) October 19, 2026 21:57
@guybedford
guybedford merged commit 1d668cf into cloudflare:main Oct 19, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants