Files

124 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Build libwebp to a self-contained WASM module exposing a minimal RGBA
# encode/decode API (see webp_wasm.c). Emits everything the shipped codec needs
# into packages/utils/webp/ (where the typed wrapper webp.ts imports them,
# @utils/webp/webp): the synchronous Emscripten CommonJS glue (webp_codec.js),
# the binary (webp_codec.wasm) and the regenerated third-party license
# (webp_codec.LICENSE).
#
# Usage:
# ./build.sh # SIMD build (default; SSE2 -> wasm SIMD)
# SIMD=0 ./build.sh # scalar build (smaller, ~2x slower encode)
#
# libwebp is pinned to a post-1.6.0 `main` commit that contains the Emscripten
# SIMD fixes (5339483 "avoid legacy EMSCRIPTEN definition" and 453a18c "fix SSE2
# check on Emscripten", both 2026-04, unreleased as of v1.6.0 / 2025-07). With
# those, no cmake patching or -DEMSCRIPTEN is needed. Switch to a release tag
# once one ships with the fixes.
#
# Reproducibility: uses whatever emsdk "latest" resolves to (tested emcc 6.0.2).
# Set EMSDK_DIR to reuse an existing emsdk.
#
set -euo pipefail
# Pinned to a commit rather than a release tag on purpose: the latest release
# (v1.6.0, 2025-07-08) predates the upstream Emscripten SIMD fixes (5339483 and
# 453a18c, both 2026-04) and building it needs local patches + -DEMSCRIPTEN.
# This `main` commit already has them, so the build stays patch-free. Move to a
# release tag once one ships with these fixes (expected 1.6.1 / 1.7.0).
LIBWEBP_COMMIT="${LIBWEBP_COMMIT:-3757b8afeb54e305eaef18502812a9a88b7ed662}"
SIMD="${SIMD:-1}"
HERE="$(cd "$(dirname "$0")" && pwd)"
OUT="$(cd "$HERE/../../packages/utils/webp" && pwd)" # shipped codec (imported via @utils/webp/webp)
WORK="${WORK:-$HERE/.build}"
mkdir -p "$WORK"
# --- 1. Emscripten toolchain -------------------------------------------------
if ! command -v emcc >/dev/null 2>&1; then
EMSDK_DIR="${EMSDK_DIR:-$WORK/emsdk}"
if [ ! -d "$EMSDK_DIR" ]; then
git clone --depth 1 https://github.com/emscripten-core/emsdk.git "$EMSDK_DIR"
fi
"$EMSDK_DIR/emsdk" install latest
"$EMSDK_DIR/emsdk" activate latest
# shellcheck disable=SC1091
source "$EMSDK_DIR/emsdk_env.sh"
fi
echo "emcc: $(emcc --version | head -1)"
# --- 2. libwebp source (pinned commit) ---------------------------------------
SRC="$WORK/libwebp-$LIBWEBP_COMMIT"
if [ ! -d "$SRC" ]; then
mkdir -p "$SRC"
curl -sSL "https://github.com/webmproject/libwebp/archive/${LIBWEBP_COMMIT}.tar.gz" \
| tar xz --strip 1 -C "$SRC"
fi
# --- 3. Configure + build libwebp static libs --------------------------------
# On Emscripten, cmake auto-adds -msimd128 to the SIMD sources and libwebp's
# runtime dispatch (dsp/cpu.c) keys off __EMSCRIPTEN__, so only SSE2 (the SIMD
# level that lowers to wasm) is used at runtime. SIMD is opt-in on Emscripten.
BUILD="$WORK/build-$([ "$SIMD" = 1 ] && echo simd || echo scalar)"
emcmake cmake -S "$SRC" -B "$BUILD" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DWEBP_USE_THREAD=OFF \
-DWEBP_ENABLE_SIMD="$SIMD" \
-DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF \
-DWEBP_BUILD_IMG2WEBP=OFF -DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF \
-DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DWEBP_BUILD_ANIM_UTILS=OFF
emmake make -C "$BUILD" webp sharpyuv -j"$(nproc)"
# --- 4. Link the wrapper into a synchronous CommonJS module ------------------
# WASM_ASYNC_COMPILATION=0 instantiates the module with `new WebAssembly.Instance`
# (synchronous), so the exports attach to the module object during the factory
# call and encode/decode are plain synchronous calls (needed for Playwright's
# synchronous image Comparator). CommonJS output (no EXPORT_ES6) inlines cleanly
# into playwright-core's coreBundle via @utils. Sync compilation is Node-only in
# practice (browsers cap synchronous WASM compilation on the main thread).
emcc "$HERE/webp_wasm.c" "$BUILD/libwebp.a" "$BUILD/libsharpyuv.a" \
-I "$SRC/src" -I "$SRC" \
-O3 -flto \
-sMODULARIZE=1 -sENVIRONMENT=node,web \
-sWASM_ASYNC_COMPILATION=0 \
-sEXPORTED_FUNCTIONS=_webp_encode_rgba,_webp_decode_rgba,_webp_free,_malloc,_free \
-sEXPORTED_RUNTIME_METHODS=HEAPU8,HEAPU32 \
-sALLOW_MEMORY_GROWTH=1 -sFILESYSTEM=0 -sMALLOC=emmalloc \
--no-entry \
-o "$OUT/webp_codec.js"
# --- 5. Regenerate the third-party license from the pinned libwebp source ----
# Ships with playwright-core (copied into lib/ by the build; see build.js).
{
cat <<'EOF'
Third-party license notices for the bundled WebP codec (webp_codec.wasm and
its Emscripten glue webp_codec.js).
webp_codec.wasm is compiled from libwebp (https://github.com/webmproject/libwebp)
and webp_codec.js is generated by Emscripten (https://emscripten.org). Their
licenses are reproduced below.
================================================================================
libwebp — BSD 3-Clause License
================================================================================
EOF
cat "$SRC/COPYING"
echo
echo "--------------------------------------------------------------------------------"
cat "$SRC/PATENTS"
cat <<'EOF'
================================================================================
Emscripten (webp_codec.js glue + runtime linked into webp_codec.wasm)
================================================================================
EOF
# Copy the license verbatim from the Emscripten compiler rather than hardcoding.
cat "$(dirname "$(command -v emcc)")/LICENSE"
} > "$OUT/webp_codec.LICENSE"
echo "Built into $OUT: webp_codec.wasm ($(stat -c%s "$OUT/webp_codec.wasm") bytes), webp_codec.js, webp_codec.LICENSE"