1. 概要
タイトルの通り、CMakeを使ってWebAssemblyのビルド環境を作成し、さらにデバッグもできる環境を構築してみました。
具体的には以下のように、VSCode上でデバッグが可能となりました。

この関数を呼び出す仕組みはこちらで作ったものを流用しています。
2. ビルド環境構築
CMakeを使って環境を構築します。まずは以下のツールをインストールまたはダウンロードします。
| ツール | 概要 | 入手方法 |
|---|---|---|
| VSCode | エディタ |
公式HP からインストール |
| CMake | ビルドシステム生成ツール |
公式HP からインストール |
| Emscripten SDK | Emscriptenで ビルドするのに使用 |
公式Gitリポジトリ からClone |
| Ninja | ビルドツール |
公式リリースページ からダウンロード |
2.1 フォルダ構成
以下の構成で進めていきます。
├─Angular
└─Cpp
├─emsdk
├─ninja
│ ninja.exe
│
└─WASM_CMake
│ CMakeLists.txt
│ CMakePresets.json
│ setup.bat
│
├─.vscode
│ launch.json
│ settings.json
│ tasks.json
│
├─build
├─dist
└─src
test.cpp
各フォルダは以下の用途で用います。
| フォルダ | 用途 |
|---|---|
| Angular |
以前作成したソースコード をここに置いているので、そのまま流用します。 |
| Cpp | C++を使って作成するものはここに纏めています。 |
| emsdk | Cloneしたリポジトリをそのままここに配置します。 |
| ninja | リリースページからダウンロードした.exeファイルを置きます。 |
| WASM_CMake | 今回の作業のメインフォルダです。 VSCodeではこのフォルダを開きます。 |
| .vscode | VSCodeで作業する際に必要な設定ファイルです。 詳細は後の章で説明します。 |
| build | CMakeによる生成物はこちらに出力します。 |
| dist | 最終的な.wasmファイルはこちらに配置します。 |
| src | C++で書くソースファイル置き場です。 冒頭のデバッグ画面で見せたtest.cppはここに置いています。 |
2.2 CMakePresets.json
CMakeLists.txtを使って諸々処理するための事前設定をここに定義します。
{
"version": 3,
"configurePresets": [
{
"name": "default",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"CMAKE_MAKE_PROGRAM": "${sourceDir}/../ninja/ninja.exe",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_BUILD_TYPE": "Debug"
}
}
]
}
設定のうち、重要なものをピックアップして説明します。${sourceDir}にはCMakePresets.jsonを配置しているフォルダパスが入ります。
| 設定 | 説明 |
|---|---|
| generator | ビルドシステム構成に使うビルドツールをここで指定します。 |
| binaryDir | 構成したビルドシステムの出力先です。 |
| cacheVariables | CMakeの環境変数をここで定義します。 ここで定義しているものは、CMakeLists.txtで再定義する必要がありません。 |
| CMAKE_TOOLCHAIN_FILE | Tool Chainです。 |
| CMAKE_MAKE_PROGRAM | ビルドツールの本体を記載します。 |
| CMAKE_EXPORT_COMPILE_COMMANDS | C++の補完機能を活用するのに有効。 VSCodeで作業するのであれば、とりあえず有効にしておくといい。 |
※generatorには最初、Visual Studioを設定しようとしていました。しかしそれだとEmscriptenのTool Chainが利用できず、ビルドが通りませんでした。
2.3 CMakeLists.txt
cmake_minimum_required(VERSION 3.30)
project(WasmProject)
set(TARGET_NAME "WasmTest")
set(EMSDK_PATH "${CMAKE_SOURCE_DIR}/../emsdk")
file(GLOB SRC_FILES
"${CMAKE_SOURCE_DIR}/src/*.cpp"
"${CMAKE_SOURCE_DIR}/src/*.h"
)
add_executable(${TARGET_NAME} ${SRC_FILES})
target_include_directories(${TARGET_NAME} PRIVATE
"${EMSDK_PATH}/upstream/emscripten/cache/sysroot/include"
)
target_compile_options(${TARGET_NAME} PRIVATE -g)
target_link_options(${TARGET_NAME} PRIVATE
"-g"
"-sMODULARIZE=1"
"-sEXPORT_NAME=${TARGET_NAME}"
"--no-entry"
)
set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".js")
set(OUTPUT_DIST_DIR "${CMAKE_SOURCE_DIR}/dist")
install(FILES
"${CMAKE_BINARY_DIR}/${TARGET_NAME}.js"
"${CMAKE_BINARY_DIR}/${TARGET_NAME}.wasm"
DESTINATION "${OUTPUT_DIST_DIR}"
)
こちらも重要な部分だけをピックアップして説明します。
| 設定 | 説明 |
|---|---|
| target_include_directories | SDKのincludeパスを指定します。 includeフォルダはパスに"cache"が含まれないものもありますが、そちらはincludeするとビルドエラーとなるよう設定されていますので、上記のパスを指定してください。 |
| target_link_options | ビルド時のリンクオプションです。 "-g"はデバッグビルドの際に必要なオプションです。 "-sMODULARIZE=1"は他のプログラムから関数を呼び出せるようモジュール化するための設定です。 EXPORT_NAMEはTypeScriptのコードで処理を呼び出す際のクラス名となります。 "--no-entry"でmain関数を作らなくて済むようにしています。 |
| set_target_properties | ビルド後の生成物に関する指定です。 .jsファイルが生成され、さらに(当然ですが).wasmファイルも生成されます。 |
| install | どの生成物をどこに配置するか、ここで指定します。 |
2.4 セットアップスクリプト
これまでの設定ファイルを使用してビルド環境を構築するスクリプトです。
@echo off
setlocal
set EMSDK_PATH="..\emsdk"
call %EMSDK_PATH%"\emsdk_env.bat"
mkdir dist
mkdir build
cd build
cmake -S .. --preset=default
cmake --build .
cmake --install .
endlocal
- emsdk_env.batを実行することで、ビルドに必要な初期化処理を行います
- "cmake -S .. --preset=default"で、CMakePresets.jsonの"default"設定を読み込み、CMakeLists.txtを使ってビルドシステムを構築します
- "cmake --build ."で、構成したビルドシステムを使ってビルドします
- "cmake --install ."で、生成物をCMakeLists.txtのinstallで指定した通りに処理します
3. VSCodeでビルド
VSCodeでビルドするため準備します。
3.1 拡張機能のインストール
以下の拡張機能をインストールしてください。
- C/C++ (Microsoftによって作成されているやつ)
- CMake Tools
- WebAssembly DWARF Debugging
- JavaScript Debugger
3.2 settings.json
{
"cmake.configurePreset": "default",
"cmake.buildPreset": "default",
"cmake.sourceDirectory": "${workspaceFolder}",
"cmake.buildDirectory": "${workspaceFolder}/build",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}
"C_Cpp.default.configurationProvider"に"ms-vscode.cmake-tools"を設定すると、インテリセンスがCMakeLists.txtで設定した情報(インクルードパスなど)を参照できるようになります。
3.3 tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "CMake: Build",
"type": "shell",
"command": "cmake",
"args": [
"--build",
"${workspaceFolder}/build"
],
"problemMatcher": []
},
{
"label": "CMake: Install to dist",
"type": "shell",
"command": "cmake",
"args": [
"--install",
"${workspaceFolder}/build"
],
"problemMatcher": []
},
{
"label": "CMake: Build and Install",
"dependsOn": [
"CMake: Build",
"CMake: Install to dist"
],
"dependsOrder": "sequence",
"type": "shell",
"command": "echo",
"args": ["Build and Install completed."],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
ビルド実行時のタスクを設定しています。
- "CMake: Build"で、cmake --buildに相当する処理を実行します
- "CMake: Install to dist"で、cmake --installに相当する処理を実行します
- "CMake: Build and Install"で、これら2つをまとめて実行します。groupのkind, isDefaultを上記の通り設定することでビルドに紐づけることができます
3.4 launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "attach",
"name": "Attach to Chrome (WASM)",
"port": 9222,
"webRoot": "${workspaceFolder}/../../Angular/WebAssembly/Application/public",
"sourceMaps": true
}
]
}
デバッグ実行時の処理をここに書きます。.wasmファイルを読み込んでいるブラウザにアタッチすることでデバッグを行います。
webRootは実際にWebアプリが読み込んでいる.wasmファイルのパスを設定する必要があります。
※今回はAngularとEmscriptenとでフォルダが分かれていますので、CMakeLists.txtのinstallで指定したdistフォルダから手動でAngularの方へコピーしています。.wasmにデバッグシンボルが含まれていますので、distに配置されていなくてもアタッチ&デバッグは可能です。
3.5 ソースコードを記述
C++でコードを記述します。
#include <emscripten/emscripten.h>
extern "C" {
EMSCRIPTEN_KEEPALIVE
int add(int x, int y) {
return x + y;
}
}
3.6 ビルド
Ctrl + Shift + Bを同時押しすることで、tasks.jsonで設定した"CMake: Build and Install"が実行されます。
成功すれば、distフォルダにWasmTest.jsとWasmTest.wasmが生成されます。
※タスクバーのビルドボタンを押しても"CMake: Build and Install"は実行されません。
4. デバッグ
distに生成された.wasmファイルを所定の場所にコピーし、Webアプリを起動します。
こちらの場合だとApplication/wasmが配置場所なので、ここへコピーします。
4.1 大まかな流れ
- ng serveでAngularのWebサーバを立ち上げる(Angularを使用されていない方は、ご自身の環境に合わせてサーバーを立ち上げてください)
- リモートデバッグを有効にしてブラウザを起動する
- ブラウザのURLに、立ち上げたサーバーのアドレスを入力する
- VSCodeからブラウザにアタッチ
- ブラウザを操作し、WebAssemblyのコードが実行されるようにする
リモートデバッグを有効にしてブラウザを起動する際は、コマンドプロンプトで以下のコマンドを実行してください。ポートはlaunch.jsonに合わせる必要があります。
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="%TEMP%\chrome-wasm-debug"
またブラウザへのアタッチは、launch.jsonに定義を書いていますので、デバッグ実行を開始することでアタッチが行われます。
4.2 Angularアプリを変更
以前作成したものはそのまま使用できないので、以下の点を変更します。
- 読み込む.wasmファイルを、"test.wasm"から"WasmTest.wasm"に変更
- 参照するモジュール名をWASMからWasmTestに変更
- デバッグ確認しやすいように、add関数をsetIntervalで定期的に実行
4.3 ブレイクを仕込んでみた
add関数が呼ばれたところにブレイクを仕込みました。それが冒頭に見せたこの画面です。変数のウォッチも機能しています。

5. まとめ
というわけで、CMakeを使ってデバッグ環境を構築しました。
WebAssemblyは有用なのですが、今まではコマンドでビルドを実行していて面倒でした。しかし今回、開発環境を整えることができたので、かなり作業しやすくなりました。
次は開発環境だけでなく、C++のソースコードに手を加えて、高速に動くWebアプリの作成に挑戦していきたいです。