桌上並排的多支手機,各自顯示不同畫面比例的網頁內容,象徵前端 Web 技術在不同瀏覽環境中的執行需求

← INSIGHTS & PERSPECTIVES | 前端開發

Emscripten 編譯 WebAssembly 實作筆記:從 MSYS2 到 emmake

整理 WebAssembly 是什麼、Emscripten 如何編譯 C/C++ 專案,以及在 MSYS2 環境處理 libde265.js 與 AV1/AOM 編譯流程。

Emscripten 編譯 WebAssembly 的核心,是把原本依賴 gccg++make 或 CMake 的 C/C++ 專案,改成使用 emccem++emmakeemcmake 產出可在瀏覽器載入的 Wasm 檔案。我的實作重點不是只安裝工具,而是確認整條編譯鏈都真的走 Emscripten,不要中途又回到原生編譯器。

WebAssembly 是什麼?

WebAssembly 是一種安全、可攜、低階的二進位程式碼格式,目標是在 Web 上執行接近原生效能的應用。WebAssembly 常作為 C、C++、Rust 等語言的編譯目標,並透過 JavaScript API 與前端程式協作。

WebAssembly(Wasm)由 W3C WebAssembly Community Group 推動,W3C Core Specification 將 WebAssembly 描述為安全、可攜、低階、適合高效執行與緊湊表示的程式碼格式(W3C,2026-05)。MDN 也把 WebAssembly 定位成一種低階、類組合語言的技術,可作為 C/C++、C#、Rust 等語言的編譯目標,並在瀏覽器中與 JavaScript 一起運作(MDN Web Docs,2026-07)。

我會把 WebAssembly 當成前端效能工具箱裡的一層:畫面、事件與產品邏輯仍然可以交給 JavaScript 或 TypeScript;影像解碼、編碼、壓縮、數學計算這類重運算,才適合移到 WebAssembly。

什麼情境適合用 Emscripten 編譯 WebAssembly?

Emscripten 適合把既有 C/C++ 函式庫搬到瀏覽器,尤其是影音處理、圖像處理、遊戲引擎與演算法模組。若需求只是一般 UI 互動,JavaScript 通常比 WebAssembly 更好維護。

WebAssembly 的常見應用場景包含:

情境為什麼適合 WebAssembly
遊戲開發可把 C/C++ 遊戲引擎或核心邏輯移植到瀏覽器
影片與圖像處理解碼、濾鏡、編碼等重運算可減少 JavaScript 主執行緒壓力
科學計算大量數值運算可利用接近原生的執行效率
既有 C/C++ 函式庫移植可以重用成熟程式碼,而不是在前端重寫整套邏輯

Emscripten 官方文件說明,emcc 可以作為類似 gccclang 的編譯器前端,負責呼叫工具鏈並產出 JavaScript 與 WebAssembly(Emscripten Documentation,存取日期:2026-08-28)。這也是我在移植影音相關專案時會先選 Emscripten 的原因:前端最後需要的是可載入的 .js glue code 和 .wasm,不是只產生一包原生平台的 object file。

如何在 Windows/MSYS2 安裝 Emscripten?

Windows/MSYS2 安裝 Emscripten 時,先準備 MSYS2 UCRT64、編譯工具、Node.js,再安裝 emsdk。重點是讓 shell 的 PATH 找得到 emccem++emmakeemcmake

我當時使用 MSYS2 的 UCRT64 環境處理 C/C++ 專案。先下載 MSYS2 installer:

https://github.com/msys2/msys2-installer/releases/download/2024-01-13/msys2-x86_64-20240113.exe

開啟 ucrt64.exe 後,切到專案目錄:

cd /D/Git/lbde265/

安裝所需套件:

pacman -Syu
pacman -S python3 clang cmake git make autoconf automake gcc
pacman -S mingw-w64-ucrt-x86_64-nodejs
pacman -S mingw-w64-ucrt-x86_64-gcc
pacman -S mingw-w64-ucrt-x86_64-emscripten
pacman -S base-devel mingw-w64-x86_64-toolchain

再安裝 emsdk

nano ~/.bashrc
export EMSDK="/D/Git/lbde265/emsdk"
export PATH="$PATH:/D/Git/lbde265/emsdk"
source ~/.bashrc

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest --permanent
source ./emsdk_env.sh

如果 shell 開新視窗後找不到 Emscripten,就把相關路徑補進 ~/.bashrc

nano ~/.bashrc
export PATH="$PATH:/d/Git/lbde265/emsdk/upstream/emscripten"
export PATH="$PATH:/d/Git/lbde265/emsdk"
export PATH="$PATH:/c/msys64/ucrt64/bin"
export PATH="$PATH:/c/msys64/usr/local/bin"
export PATH="$PATH:/c/msys64/usr/bin"
export PATH="$PATH:/c/Windows/System32"
export PATH="$PATH:/c/Windows"
export PATH="$PATH:/c/Windows/System32/Wbem"
export PATH="$PATH:/c/Windows/System32/WindowsPowerShell/v1.0/"
export PATH="$PATH:/c/msys64/usr/bin/site_perl"
export PATH="$PATH:/c/msys64/usr/bin/vendor_perl"
export PATH="$PATH:/c/msys64/usr/bin/core_perl"
source ~/.bashrc

最後確認 emcc 能正常執行:

emcc -v

如何用 Emscripten 編譯既有 C/C++ 專案?

Emscripten 編譯既有 C/C++ 專案時,關鍵是用 emconfigureemcmakeemmake 包住原本的 configure、CMake 與 make 流程。直接執行 make 常會回到原生編譯器,導致產物不能被瀏覽器載入。

Emscripten 官方建議,在 configure 型專案中用 emconfigure ./configureemmake make;在 CMake 型專案中,用 emcmake cmake 讓 CMake 取得正確的 compiler 與 toolchain 設定(Emscripten Documentation,存取日期:2026-08-28)。

我的檢查表會抓三件事:

檢查點我會看什麼
編譯器是否正確verbose log 裡應該看到 emccem++,不是 gccg++
build wrapper 是否使用configure/CMake 用 emconfigureemcmake,make 用 emmake
輸出格式是否合理最後應產出可部署的 .js.wasm,或可再由 emcc link 的 Wasm object

如果 Makefile 裡有寫死 gccg++,就要替換成 emccem++。子目錄裡的 Makefile 也要一起檢查,否則主層改完仍可能在某個 library build 階段失敗。

如何編譯 libde265.js?

libde265.js 編譯流程要先取得專案與 libde265 source,再用 Emscripten 的 make 流程建立 Wasm 相關產物。遇到 Makefile 寫死原生 compiler 時,需要把 gcc/g++ 改成 emcc/em++

我保留當時使用的指令,因為這段最能說明實務上會卡在哪裡:

git clone https://github.com/strukturag/libde265.js.git
chmod +x ./configure
sh build.sh

wget https://github.com/strukturag/libde265/releases/download/v1.0.15/libde265-1.0.15.tar.gz
tar xzf libde265-1.0.15.tar.gz
cd libde265-1.0.15
./configure --disable-sse --disable-dec265 --disable-sherlock265 --enable-log-error --enable-log-info --enable-log-trace
emmake make

這裡要注意兩件事。第一,凡是引用 gccg++ 的地方,針對 WebAssembly 編譯時都要改成 emccem++。第二,如果專案裡有多層子目錄,不能只改最外層 Makefile。

我當時也把 CFLAGS 調整成:

CFLAGS = -g -O2 -Wall

三個選項的意思如下:

參數作用
-g產生除錯資訊,方便排查編譯或執行問題
-O2啟用第二級最佳化,在速度與編譯成本之間取得平衡
-Wall開啟常見警告,提早找出潛在問題

如何編譯 AV1/AOM 的 WebAssembly?

AV1/AOM 這類 C/C++ 專案不能只照一般原生流程執行 make。使用 Emscripten 時,CMake 設定階段要先用 emcmake,建置階段再用 emmake

GoogleChromeLabs 的 wasm-av1 專案把 AOM 帶進 WebAssembly 情境,適合用來理解 AV1 decoder 或 encoder 在瀏覽器環境的編譯方式(GoogleChromeLabs,存取日期:2026-08-28)。我遇到的重點是:只跑一般 make 會失敗,因為 build system 沒有被切到 Emscripten toolchain。

不要只執行:

make

改用這組流程:

emcmake cmake ./third_party/aom -DAOM_TARGET_CPU=generic
emmake make

-DAOM_TARGET_CPU=generic 讓目標 CPU 設定保守一點,避免產生瀏覽器 WebAssembly 環境不適合的原生 CPU 假設。若後續要做更細的最佳化,再依目標瀏覽器、Wasm feature 與 codec 需求逐步收窄。

編譯失敗時要先檢查什麼?

Emscripten 編譯失敗時,我會先檢查工具鏈是否被原生 compiler 偷偷接回去。多數問題不是 WebAssembly 本身壞掉,而是 configure、CMake、Makefile 或環境變數仍指向 gccg++、原生 library。

我通常照這個順序排查:

  1. 執行 emcc -v,確認目前 shell 真的吃到 Emscripten。
  2. 用 verbose build log 檢查實際 compiler,確定不是 gccg++
  3. 檢查 Makefile、CMake cache、子目錄 build script 是否寫死原生 compiler。
  4. 確認需要停用的原生指令集,例如 SSE、平台專用 decoder 或 GUI tool。
  5. 把 configure/CMake 階段重跑一次,不要沿用原生 build 留下來的 cache。

Emscripten 官方文件也提醒,make 產物可能是 .o.a.so 或其他副檔名;副檔名本身不能保證內容可用,必要時要用 build log 或檔案檢查確認產物是否來自 Wasm object 或 LLVM bitcode(Emscripten Documentation,存取日期:2026-08-28)。

常見問題

QWebAssembly 和 JavaScript 是替代關係嗎?

WebAssembly 和 JavaScript 不是單純替代關係。WebAssembly 適合搬運高效能運算核心,JavaScript 適合處理 DOM、事件、資料流與前端整合;實務上兩者通常一起使用。

QEmscripten 一定會輸出 WebAssembly 嗎?

新版 Emscripten 預設會建置 WebAssembly,除非在 linking 階段明確關掉 Wasm 輸出。Emscripten 文件也說明,是否輸出 WebAssembly 主要取決於 linking 階段設定,而不是每個 object file 的副檔名。

Q為什麼直接執行 make 會失敗?

直接執行 make 可能讓專案繼續使用原生 gccg++。Emscripten 專案通常要用 emconfigureemcmakeemmake 包住既有 build 流程,讓 compiler、linker 與 library path 全部切到 WebAssembly 目標。

Q什麼時候要把 gcc 改成 emcc?

當 Makefile 或 build script 寫死 gcc 時,就要把 C compiler 改成 emcc。如果是 C++ 程式,則要把 g++ 改成 em++,並同步檢查子目錄是否也有寫死 compiler。

QCFLAGS 的 -g、-O2、-Wall 可以保留嗎?

-g-O2-Wall 通常可以保留。-g 方便除錯,-O2 提供常用最佳化,-Wall 能讓警告提早浮出;真正需要小心的是平台專用參數,例如 SSE 或只適用原生 CPU 的旗標。

QAV1/AOM 編譯 WebAssembly 為什麼要用 emcmake?

AV1/AOM 這類 CMake 專案需要在設定階段就指定 Emscripten toolchain。emcmake cmake ./third_party/aom -DAOM_TARGET_CPU=generic 會讓 CMake 產生適合 WebAssembly 的建置設定,後續再用 emmake make 執行編譯。

QEmscripten 適合所有前端功能嗎?

Emscripten 不適合拿來取代一般前端功能。表單、路由、互動 UI、資料顯示仍然適合 JavaScript 或 TypeScript;WebAssembly 比較適合重運算、既有 C/C++ 函式庫移植、影像影音處理等工作。

參考資料

延伸閱讀

最後更新

2026-08-28

關於作者

Claire Chang | 企業 AI 導入與流程轉型顧問。專注於 AI Agent 架構設計、ERP 系統整合與企業 AI 治理。

首次發布:2024-09-12