Contents
Go言語でWebAssemblyをビルドする具体的な手順と注意点
Go言語を使用してWebAssembly(WASM)を構築するには、環境準備からモジュールの実行まで一連の手順が必要です。本記事ではGo言語 WebAssembly ビルド 方法に焦点を当て、コマンドライン操作やコードサンプルを通じて具体的な手順を解説します。特にwasm_exec.jsとの連携やメモリ管理の注意点など、実務でよく遭遇する課題にも対応します。
Go言語環境の準備とWebAssemblyビルドの基礎
Go 1.18以降は標準でWebAssemblyターゲットをサポートしており、特別なツールなしでビルド可能です。まずは環境が整っているか確認し、プロジェクト構築を行います。
Goのインストール確認
Goのバージョンはgo versionコマンドで確認します。以下の出力が表示される場合、1.18以降のバージョンです。
|
1 2 |
go version go1.20.3 darwin/arm64 |
注意点: 一部のOSではデフォルトでWebAssemblyサポートが有効になっていない可能性があります。公式ドキュメントを参照してください。
modファイルの初期化手順
プロジェクトディレクトリを作成し、go mod initでモジュールを初期化します。
|
1 2 3 4 |
mkdir wasm-go-sample cd wasm-go-sample go mod init example.com/wasm-go-sample |
このとき生成されるgo.modファイルは、依存関係管理に必要です。WebAssemblyのビルドには特に影響しませんが、プロジェクト構造としては重要です。
WebAssemblyモジュールの作成とコンパイル
GoでWebAssemblyを実装する際には、通常のGoコードを.wasm形式に変換します。以下の手順に従ってください。
サンプルコードの記述例
main.goを作成し、以下のようにシンプルな関数を定義します。
|
1 2 3 4 5 6 7 8 9 10 11 |
package main import "fmt" //export SayHello func SayHello() { fmt.Println("Hello from Go!") } func main() {} |
注意点:
//exportコメントで関数をエクスポートする必要があります。JavaScriptから呼び出す際のインターフェースとなるため、必須です。
wasmファイルへのビルドコマンド
以下のコマンドで.wasmファイルに変換します。
|
1 2 |
GOOS=js GOARCH=wasm go build -o output.wasm main.go |
生成されるoutput.wasmは、ブラウザ上で実行可能なWebAssemblyモジュールです。このファイルとwasm_exec.jsを組み合わせてJavaScriptから呼び出す準備が必要です。
wasm_exec.jsとの連携方法
wasm_exec.jsはGoが提供するJavaScriptランタイムで、WebAssemblyモジュールを初期化します。HTMLとJavaScriptのコード例を解説します。
HTMLファイルの構成例
以下のようなHTMLファイルを作成し、.wasmファイルとwasm_exec.jsを読み込みます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Go WebAssembly Sample</title> </head> <body> <script src="wasm_exec.js"></script> <script> const go = new Go(); WebAssembly.instantiateStreaming(fetch("output.wasm"), go.importObject).then(obj => { go.run(obj.instance); }); </script> </body> </html> |
注意点:
wasm_exec.jsはGoの公式リポジトリから取得可能です。プロジェクトディレクトリに配置する必要があります。
JavaScriptからのモジュール呼び出し
エクスポートされた関数をJavaScriptで実行するには、以下のようにします。
|
1 2 3 4 5 6 7 8 9 |
const goInstance = new Go(); WebAssembly.instantiateStreaming(fetch("output.wasm"), goInstance.importObject).then(obj => { const instance = obj.instance; const exports = instance.exports; // エクスポートされた関数を呼び出す exports.SayHello(); }); |
このコードでは、SayHello()関数が実行され、ブラウザのコンソールに「Hello from Go!」が出力されます。
ブラウザでの実行確認フロー
WebAssemblyモジュールを実際にブラウザで動かすには、ローカルサーバーを立ち上げてHTMLファイルをアクセスします。
ローカルサーバー立ち上げ
http-serverやgo runを使って簡易サーバーを起動します。
|
1 2 3 |
npm install -g http-server http-server . |
またはGoで起動する場合:
|
1 2 |
go run main.go |
注意点:
http-serverはNode.js環境が必要ですが、go runではGo標準ライブラリで簡易サーバーが起動可能です。
Chromeデベロッパーツールの確認
- ブラウザで
localhost:8080にアクセスします。 - Developer Toolsを開き、Consoleタブで出力ログを確認します。
Hello from Go!が表示されれば成功です。
メモリ管理とパフォーマンス注意点
WebAssemblyのメモリモデルはGoのガベージコレクションと異なるため、注意が必要です。
ガベージコレクションの影響
GoのGCはWebAssembly環境でも動作しますが、モジュールのライフサイクル管理に注意が必要です。以下のコード例のように、不要なオブジェクトを明示的に解放することでメモリリークを防ぎましょう。
|
1 2 3 4 5 6 |
func cleanup() { // 不要な変数を再代入で強制開放 var buffer []byte buffer = nil } |
注意点:
wasm_exec.jsが管理するメモリ領域とGoのメモリは別々です。互いにポインタを参照することはできません。
メモリポインタの扱い方
WebAssemblyでは「線形メモリ」と呼ばれる一連のバッファでデータを管理します。GoからJavaScriptにデータを渡す際には、[]byte型で配列を生成し、wasm_exec.js経由でポインタを取得する必要があります。
実践的な手順とよくある間違い
WebAssembly開発では、以下のようなミスが頻繁に発生します。それぞれの回避策を解説します。
エクスポート関数の定義ミス
エクスポートされた関数がJavaScriptから呼び出せない場合は、以下の点を確認してください。
//exportコメントが正しく記述されているか- JavaScript側で正しい関数名を使用しているか
例: 関数名がSayHelloなのにJavaScript側でsayHello()と実装している場合、エラーになります。
クロージャーの不正使用
GoのクロージャーはWebAssembly環境では正しく動作しない可能性があります。代わりにグローバル変数やコールバック関数を使用します。
例: 以下のコードはwasm_exec.jsで正しく動かず、エラーを起こすことがあります。
|
1 2 3 4 5 6 |
func makeClosure() func() { return func() { fmt.Println("Closure called") } } |
技術的正確性の注意点: WebAssembly環境ではクロージャーが正しく動作しない可能性があります。これはGoのランタイムとWebAssemblyのメモリモデルの違いによるものであり、グローバル変数やコールバック関数を代替として使用する必要があります。
まとめ
本記事ではGo言語を使ってWebAssemblyモジュールをビルドする手順と、実務でよく遭遇する注意点を解説しました。以下の要点を確認してください。
- Go環境の準備: Go 1.18以降が必要
- wasm_exec.jsとの連携: HTMLとJavaScriptでの初期化フローが重要
- メモリ管理: ガベージコレクションとWebAssemblyの違いに注意
- よくある間違い: 関数エクスポートやクロージャーの誤用
記事内のサンプルコードを実際に動かして、WebAssemblyの動作原理を体験してください。