大家好,相信各位對於 webassembly 已經有一些基礎的認知了今天就來不免俗的就來做一下官網的 Hello world。
另外筆者的開發環境是 Mac OS 如果其他系統要安裝請參考這邊。
clone emsdk git 專案
$ git clone https://github.com/emscripten-core/emsdk.git
安裝 sdk
$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest
這邊稍為講一下 emsdk 先引一段官網的說明
Emscripten is a toolchain for compiling to asm.js and WebAssembly, built using LLVM, that lets you run C and C++ on the web at near-native speed without plugins.
簡單來說所以就是把 C/C++ 編譯成 asm.js 以及 WebAssembly 的工具集。
$ source ./emsdk_env.sh --build=Release
Note: 這邊的環境變數在 terminal 重啟之後便會失效,若想要永久的設定請添加至 .bashrc 或 .zshrc
例如添加變數至 .zshrc
# emsdk
export PATH=/Users/liyanxin/Life/myprojects/emsdk:$PATH
export PATH=/Users/liyanxin/Life/myprojects/emsdk/fastcomp/emscripten:$PATH
export PATH=/Users/liyanxin/Life/myprojects/emsdk/node/12.9.1_64bit/bin:$PATH
新增專案資料夾
$ mkdir hello && cd hello
建立主要 function
$ cat << EOF > hello.c
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, world!\n");
}
EOF
編譯檔案
$ emcc hello.c -o hello.html
啟動本地伺服器
$ emrun --no_browser --port 8080 .
在 http://localhost:8080 查看網頁
這樣就完成了一個簡單的專案,但是當我們去檢視 build 出來的檔案的時候我們會發現不是只有一個 hello.wasm 的檔案居然還有一個 hello.js 的檔案,那麼這個檔案是做什麼的呢?
原來當初 Mozilla 異想天開為了解決網頁遊戲的效能問題而發展出一套方法可以把 C/C++ 編譯成 javascript,也就是後來的 asm.js。
而其原理基本上就是把 JS 限制成靜態語言的寫法然後當 JS 引擎接到這類型的檔案之後可以跳過語法分析讓執行效能更好。
但是 asm.js 本質上還是 javascript 因此在檔案大小和解析速度上都比不上 wasm。
那麼這個 hello.js 其實就是當初設計拿來載入 asm.js 的程式而現在也可以用在載入 wasm 上面,筆者相信隨著未來 wasm 越來越穩定會有更簡單更 native 的方法載入 wasm。
下面就介紹一下另外一個透過瀏覽器內建的方式載入就無需上面提到的載入 wasm 的程式。
檔案名稱:hello_world.c
int doubler(int x) {
return 2 * x;
}
檔案名稱:hello_world.html
<html>
<head>
<script>
// Check for wasm support.
if (!('WebAssembly' in window)) {
alert('you need a browser with wasm support enabled :(');
}
// Loads a WebAssembly dynamic library, returns a promise.
// imports is an optional imports object
function loadWebAssembly(filename, imports) {
// Fetch the file and compile it
return fetch(filename)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => {
// Create the imports for the module, including the
// standard dynamic library imports
imports = imports || {};
imports.env = imports.env || {};
imports.env.__memory_base = imports.env.__memory_base || 0;
imports.env.__table_base = imports.env.__table_base || 0;
if (!imports.env.memory) {
imports.env.memory = new WebAssembly.Memory({ initial: 256 });
}
if (!imports.env.table) {
imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' } );
}
// Create the instance.
return new WebAssembly.Instance(module, imports);
});
}
// Main part of this example, loads the module and uses it.
loadWebAssembly('hello_world.wasm')
.then(instance => {
var exports = instance.exports; // the exports of that instance
var doubler = exports._doubler; // the "doubler" function (note "_" prefix)
// now we are ready, set up the button so the user can run the code
var button = document.getElementById('run');
button.value = 'Call a method in the WebAssembly module';
button.addEventListener('click', function() {
var input = 21;
alert(input + ' doubled is ' + doubler(input));
}, false);
}
);
</script>
</head>
<body>
<input type="button" id="run" value="(waiting for WebAssembly)"/>
</body>
</html>
編譯
$ emcc hello_world.c -Os -s WASM=1 -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS='["_doubler"]' -o hello_world.wasm
執行
$ emrun --no_browser --port 8080 .