-
Notifications
You must be signed in to change notification settings - Fork 483
feat: support wasm32-unknown-emscripten target #1583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
guybedford
wants to merge
7
commits into
wasm-bindgen:master
Choose a base branch
from
guybedford:feat/emscripten-target
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b5789c1
feat: support wasm32-unknown-emscripten target
guybedford e3fe42d
style: cargo fmt
guybedford a2e2dec
.d.ts: decorate wasm-bindgen output with emscripten factory shape
guybedford 43f2577
Extend coverage: ESM imports + js_namespace (import and export)
guybedford aae268c
test(emscripten): bump fixture to wasm-bindgen 0.2.122
guybedford 876d396
docs(emscripten): link panic=unwind tracking issue
guybedford 4d2253c
review: address pre-merge feedback on emscripten target
guybedford File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| # Building for `wasm32-unknown-emscripten` | ||
|
|
||
| `wasm-pack` supports the `wasm32-unknown-emscripten` target as an alternative | ||
| to the default `wasm32-unknown-unknown`. The two produce wasm binaries that | ||
| behave differently at runtime: emscripten output includes a libc, file system | ||
| shims, POSIX-style APIs, and a richer JavaScript runtime around the wasm. | ||
|
|
||
| You generally want emscripten when: | ||
|
|
||
| - You need `std::time::{Instant, SystemTime}`. | ||
| - You need `std::env::{current_dir, vars}`. | ||
| - You need `std::fs::*` (backed by an in-memory MEMFS). | ||
| - You need `std::collections::HashMap` with default random state. | ||
| - You need `rand::random()` to Just Work (via `getentropy`). | ||
| - You're linking Rust against C/C++ sources via `bindgen`/`cxx`. | ||
|
|
||
| You generally want `wasm32-unknown-unknown` when: | ||
|
|
||
| - Your crate is pure Rust + `js-sys`/`web-sys`. | ||
| - You want the smallest possible runtime overhead. | ||
| - You care about cold-start time on the web. | ||
|
|
||
| Most projects don't need emscripten. The default target stays the right choice | ||
| for the common case. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| `wasm-pack` invokes the Emscripten SDK (`emcc`) during the build. Install | ||
| [emsdk](https://emscripten.org/docs/getting_started/downloads.html) and | ||
| activate it before running `wasm-pack`: | ||
|
|
||
| ```sh | ||
| git clone https://github.com/emscripten-core/emsdk.git ~/emsdk | ||
| cd ~/emsdk | ||
| ./emsdk install latest | ||
| ./emsdk activate latest | ||
| source ./emsdk_env.sh | ||
| ``` | ||
|
|
||
| `emcc --version` should print at least 3.1.60. Older versions lack | ||
| `-sSOURCE_PHASE_IMPORTS=1` and will fail the build with a clear error. | ||
|
|
||
| `wasm-pack` will auto-detect a `~/emsdk` install or honor the `$EMSDK` | ||
| environment variable if `emcc` isn't already on `PATH`. CI configurations | ||
| should `source emsdk_env.sh` before running `wasm-pack` to make `emcc` | ||
| available to child processes. | ||
|
|
||
| ## Quick start | ||
|
|
||
| ```sh | ||
| wasm-pack new my-pkg --emscripten | ||
| cd my-pkg | ||
| wasm-pack build | ||
| ``` | ||
|
|
||
| The first command scaffolds a project that's pre-configured for the | ||
| emscripten target. The second runs the full pipeline: | ||
|
|
||
| ``` | ||
| cargo build → emcc link → wasm-bindgen → emcc post-link | ||
| ``` | ||
|
|
||
| The result in `pkg/` matches the layout for other `wasm-pack` targets, with | ||
| two notable differences: | ||
|
|
||
| - The JS module has an `.mjs` extension (always ESM). | ||
| - There's no `<name>_bg.wasm`; the wasm file is just `<name>.wasm`. | ||
|
|
||
| ## What the template generates | ||
|
|
||
| `wasm-pack new --emscripten <name>` produces a crate with this shape: | ||
|
|
||
| ``` | ||
| <name>/ | ||
| Cargo.toml # crate-type = ["staticlib"] | ||
| .cargo/ | ||
| config.toml # target = "wasm32-unknown-emscripten" | ||
| src/ | ||
| lib.rs # a tiny `greet()` example | ||
| README.md | ||
| ``` | ||
|
|
||
| Two things are notable compared to the default template: | ||
|
|
||
| 1. **`crate-type = ["staticlib"]`** — emcc consumes a static library and | ||
| links it into the final wasm itself. `cdylib` would skip emcc entirely. | ||
|
|
||
| 2. **`.cargo/config.toml`** — selects the emscripten target and sets the | ||
| rustflags emcc expects: | ||
| - `-Cpanic=abort` — `panic=unwind` isn't supported across the wasm-bindgen | ||
| boundary on emscripten yet | ||
| ([wasm-bindgen#5165](https://github.com/wasm-bindgen/wasm-bindgen/issues/5165)). | ||
| - `-Crelocation-model=static` — the staticlib is linked directly; PIC | ||
| isn't needed. | ||
| - `-Cllvm-args=-enable-emscripten-cxx-exceptions=0` — avoids pulling in | ||
| a C++ exception runtime we don't ship. | ||
|
|
||
| If you build your own crate (without using the template), make sure to | ||
| configure both of these. | ||
|
|
||
| ## Build targets | ||
|
|
||
| The wasm-pack `--target` flag still selects the output shape: | ||
|
|
||
| | `--target` | Output | | ||
| |---|---| | ||
| | `bundler` (default) | `<name>.mjs` ESM async factory, env: `web,node` | | ||
| | `web` | same as `bundler` | | ||
| | `module` | `<name>.mjs` with `import source` for the wasm | | ||
| | `nodejs` | `<name>.mjs`, env: `node` | | ||
| | `deno` | `<name>.mjs`, env: `web,node` | | ||
| | `no-modules` | not supported — emcc produces module-shaped output only | | ||
|
|
||
| All shapes are ESM; emscripten doesn't emit CJS. The `nodejs` target is | ||
| distinguished only by the `-sENVIRONMENT=node` setting, which omits | ||
| browser-detection probes from the runtime. | ||
|
|
||
| The `module` target uses [source-phase imports][src-phase] for the wasm: | ||
|
|
||
| ```js | ||
| import source wasmModule from './<name>.wasm'; | ||
| ``` | ||
|
|
||
| This requires a host that understands the proposal (modern bundlers, Node 22+, | ||
| Deno). | ||
|
|
||
| [src-phase]: https://github.com/tc39/proposal-source-phase-imports | ||
|
|
||
| ## Limitations | ||
|
|
||
| A few things differ from the default wasm-pack target: | ||
|
|
||
| - **`wasm-pack test` is not supported.** The `wasm-bindgen-test` runner | ||
| isn't currently wired up for emscripten; tests must run via | ||
| `cargo test` directly. | ||
| - **TypeScript declarations are wasm-bindgen-driven and wasm-pack-decorated.** | ||
| The `.d.ts` in `pkg/` is wasm-bindgen's output (covering every | ||
| `#[wasm_bindgen]` export and class), with a default-export factory | ||
| declaration appended so `import M from "./<name>.mjs"` type-checks | ||
| cleanly. The factory return type is the wasm-bindgen surface only. | ||
| emscripten runtime members (`HEAP*`, `_initialize`, `ccall`/`cwrap`, | ||
| `FS`) aren't attached to the factory return by default and are not | ||
| typed; if you need them you can extend the `.d.ts` in `pkg/` after the | ||
| build. | ||
| - **Optimization is capped at `-O2`.** emcc's `-O3` enables wasm-opt's | ||
| `--minify-imports-and-exports` pass, which renames wasm exports to | ||
| single letters — but wasm-bindgen's generated JS glue references the | ||
| original names. `-O2` produces near-equivalent output without the | ||
| renaming. | ||
| - **`--target module` builds run unoptimized today.** emcc's bundled JS | ||
| optimizer (`acorn-optimizer`) can't parse `import source` syntax. Pass | ||
| `--no-opt` for `--target module` builds, or wait for the upstream emcc | ||
| fix. | ||
|
|
||
| ## How the build pipeline works | ||
|
|
||
| For curiosity: | ||
|
|
||
| 1. **`cargo build`** — cargo invokes `rustc --target wasm32-unknown-emscripten`, | ||
| which under the hood links via emcc to produce a `librustworker.a` | ||
| staticlib. | ||
|
|
||
| 2. **`emcc` link** — `wasm-pack` invokes `emcc --no-entry --oformat=bare` | ||
| to produce a bare `.wasm` from the staticlib. Symbols wasm-bindgen | ||
| needs are preserved via `-sEXPORTED_FUNCTIONS`. | ||
|
|
||
| 3. **`wasm-bindgen`** — runs over the bare `.wasm` with `--keep-lld-exports` | ||
| (no `--target`; emscripten output mode is auto-detected from the | ||
| `__wasm_bindgen_emscripten_marker` custom section). Produces a | ||
| rewritten `_bg.wasm`, a `library_bindgen.js` (an emscripten JS | ||
| library), and a `.d.ts`. | ||
|
|
||
| 4. **`emcc --post-link`** — combines the wasm-bindgen-rewritten wasm with | ||
| `library_bindgen.js` to emit the final `<name>.mjs` (ESM factory | ||
| function) and `<name>.wasm` (final, post-linked binary). | ||
|
|
||
| Each phase is a separate, self-contained tool invocation. Intermediate | ||
| artifacts (`linked.wasm`, `_bg.wasm`, `library_bindgen.js`) are not | ||
| shipped in `pkg/`. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a linked issue / PR here would be helpful. then once the upstream fix is in, readers could see the PR was merged and would know this is no longer the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The upstream fix has now landed for this one, I will merge it in here and reenable after the next emscripten release.