| Add a WebAssembly backend for the example 728acaa nandithebull 20h ago | 1 | #!/usr/bin/env bash |
| 2 | # V -> WebAssembly, for the Flutter web build of the example. |
| 3 | # |
| 4 | # Nothing is installed system-wide. tool/bin/emcc is a DotSlash file pinning |
| 5 | # emscripten by content hash; DotSlash fetches it on first run (~285 MB) and |
| 6 | # caches it under ~/.cache/dotslash. Only `dotslash` itself has to be on PATH. |
| 7 | # |
| 8 | # Output lands in example/web/ and is loaded by a <script> tag in |
| 9 | # example/web/index.html. Re-run after editing src/vflutter.v. |
| 10 | set -euo pipefail |
| 11 | cd "$(dirname "$0")/.." |
| 12 | |
| 13 | command -v v >/dev/null || { echo "V compiler not on PATH"; exit 1; } |
| 14 | command -v node >/dev/null || { echo "node not on PATH (emscripten needs it)"; exit 1; } |
| 15 | command -v dotslash >/dev/null || { |
| 16 | echo "dotslash not on PATH — https://dotslash-cli.com/docs/installation/" >&2 |
| 17 | exit 1 |
| 18 | } |
| 19 | |
| 20 | out=example/web |
| 21 | mkdir -p build "$out" |
| 22 | |
| 23 | # Fetches on first call, then just prints the cached path. |
| 24 | emcc_path="$(dotslash -- fetch tool/bin/emcc)" |
| 25 | root="$(dirname "$(dirname "$emcc_path")")" |
| 26 | |
| 27 | # emcc wants a config file; the DotSlash cache is content-addressed and must |
| 28 | # stay read-only, so the config and the sysroot cache live under build/. |
| 29 | cat > build/emscripten.config <<EOF |
| 30 | LLVM_ROOT = '$root/bin' |
| 31 | BINARYEN_ROOT = '$root' |
| 32 | NODE_JS = '$(command -v node)' |
| 33 | CACHE = '$PWD/build/emcache' |
| 34 | EOF |
| 35 | export EM_CONFIG="$PWD/build/emscripten.config" |
| 36 | |
| 37 | # V emits C for the emscripten target; emcc turns that into wasm. -gc none |
| 38 | # matches every other build, so the ownership rules are the same ones the |
| 39 | # native library exercises. |
| 40 | v -os wasm32_emscripten -gc none -o build/vflutter_em.c src/vflutter.v |
| 41 | |
| 42 | # GLOBAL_BASE is not cosmetic. V's builtin vmemcpy refuses any copy whose |
| 43 | # source or destination is <= 0xFFFF: |
| 44 | # |
| 45 | # if (n == 0 || (u64)dest <= 0xFFFF || (u64)const_src <= 0xFFFF) return dest; |
| 46 | # |
| 47 | # That is a null-pointer heuristic written for 64-bit desktop, where the low |
| 48 | # 64 KB is never mapped. On wasm32 emscripten packs static data down at |
| 49 | # address ~1300 by default, so every copy out of a string literal silently |
| 50 | # does nothing and V strings come back as runs of zero bytes -- no crash, no |
| 51 | # diagnostic. (It only bites at -O1 and above; at -O0 the data happens to land |
| 52 | # above the threshold, which makes this look like an optimiser bug.) Starting |
| 53 | # static data at 1 MB puts it, and the stack and heap that follow it, clear of |
| 54 | # the guard. Verified by tool/test_wasm.mjs, which round-trips a string. |
| 55 | # |
| 56 | # ENVIRONMENT includes node so the module can be smoke-tested headlessly |
| 57 | # (tool/test_wasm.mjs) without a browser. |
| 58 | dotslash tool/bin/emcc build/vflutter_em.c -O3 -o "$out/vflutter.js" \ |
| 59 | -sGLOBAL_BASE=1048576 \ |
| 60 | -sMODULARIZE=1 \ |
| 61 | -sEXPORT_NAME=createVFlutterModule \ |
| 62 | -sENVIRONMENT=web,worker,node \ |
| 63 | -sALLOW_MEMORY_GROWTH=1 \ |
| 64 | -sEXPORTED_FUNCTIONS='["_vf_init","_vf_add","_vf_greet","_vf_free","_vf_mandelbrot","_malloc","_free"]' \ |
| 65 | -sEXPORTED_RUNTIME_METHODS='["ccall","cwrap","UTF8ToString","stringToUTF8","lengthBytesUTF8","HEAPU8"]' |
| 66 | |
| 67 | echo "built: $out/vflutter.js ($(stat -c%s "$out/vflutter.js") bytes)," \ |
| 68 | "$out/vflutter.wasm ($(stat -c%s "$out/vflutter.wasm") bytes)" |