Emscriptenチュートリアル

基本レベルでは、Emscriptenの使用は非常に簡単です。このチュートリアルでは、コマンドラインから最初のEmscriptenの例をコンパイルするために必要な手順を説明します。ファイルの使用方法と、主要なコンパイラ最適化フラグの設定方法についても説明します。

まず最初に

ダウンロードしてインストールしたEmscriptenを確認してください(この方法は、使用しているオペレーティングシステム(Linux、Windows、またはMac)によって異なります)。

Emscriptenは、Emscriptenコンパイラフロントエンド(emcc)を使用してアクセスします。このスクリプトは、コードをビルドするために必要な他のすべてのツールを呼び出し、 *gcc* や *clang* などの標準コンパイラと置き換えることができます。./emccまたは./em++を使用してコマンドラインで呼び出されます。

注記

Windowsでは、ツールはわずかに異なる構文emccまたはem++を使用して呼び出されます。このチュートリアルの残りの部分では、Linuxの方法(./emcc)を使用します。

次のセクションでは、コマンドプロンプトを開く必要があります。

  • LinuxまたはmacOSでは、*ターミナル*を開きます。

  • Windowsでは、Emscriptenコマンドプロンプトを開きます。これは、アクティブなEmscriptenツールを指す正しいシステムパスと設定が事前に構成されたコマンドプロンプトです。このプロンプトにアクセスするには、Windows 8のスタート画面で**Emscripten**と入力し、**Emscriptenコマンドプロンプト**オプションを選択します。

コマンドプロンプトを使用して、SDKの下にあるemscriptenディレクトリに移動します。これは、emsdkルートディレクトリの下にあるフォルダーで、通常は**<emsdkルートディレクトリ>/upstream/emscripten/**です。以下の例は、その場所を基準としたファイルの検索に依存します。

注記

古いバージョンのemscriptenでは、ディレクトリ構造が異なっていました。バージョン番号が表示され、バックエンド(fastcomp/upstream)は表示されませんでした。そのため、**<emsdkルートディレクトリ>/emscripten/1.20.0/**などを使用していました。

Emscriptenの検証

まだEmscriptenを実行していない場合は、ここで実行します。

./emcc -v

出力にツールの不足に関する警告が含まれている場合は、デバッグのヘルプについてはEmscripten開発環境の検証を参照してください。それ以外の場合は、コードをビルドする次のセクションに進みます。

Emscriptenの実行

これで、最初のC/C++ファイルをJavaScriptにコンパイルできます。

まず、コンパイルするファイル**hello_world.c**を見てみましょう。これはSDKで最もシンプルなテストコードであり、ご覧のように、コンソールに「hello, world!」を出力して終了するだけです。

/*
 * Copyright 2011 The Emscripten Authors.  All rights reserved.
 * Emscripten is available under two separate licenses, the MIT license and the
 * University of Illinois/NCSA Open Source License.  Both these licenses can be
 * found in the LICENSE file.
 */

#include <stdio.h>

int main() {
  printf("hello, world!\n");
  return 0;
}

このコードのJavaScriptバージョンをビルドするには、*emcc*の後にC/C++ファイルを指定するだけです(C++としてコンパイルを強制するには*em++*を使用します)。

./emcc test/hello_world.c

このコマンドによって、**a.out.js**と**a.out.wasm**という2つのファイルが生成されます。2番目は、コンパイル済みコードを含むWebAssemblyファイルであり、1番目は、それをロードして実行するためのランタイムサポートを含むJavaScriptファイルです。node.jsを使用して実行できます。

node a.out.js

予想通り、コンソールに「hello, world!」が出力されます。

注記

古いバージョンのnode.jsには、まだWebAssemblyのサポートがありません。その場合、WebAssemblyを無効にするために-sWASM=0を使用してビルドすることを提案するエラーメッセージが表示され、emscriptenはコンパイル済みコードをJavaScriptとして出力します。一般的に、WebAssemblyはブラウザでのサポートが広く、実行とダウンロードの両方が効率的であるため(そのためemscriptenはデフォルトで出力します)、推奨されますが、まだ存在していない環境でコードを実行する必要がある場合があり、無効にする必要があります。

ヒント

*emcc*を呼び出したときにエラーが発生した場合は、-vオプションを付けて実行して、多くの便利なデバッグ情報を表示してください。

注記

このセクションでは、後でtest/フォルダーからいくつかのファイルを実行します。そのフォルダーには、Emscriptenテストスイートのファイルが含まれています。一部はスタンドアロンで実行できますが、他のファイルはテストハーネス自体を通じて実行する必要があります。詳細については、Emscriptenテストスイートを参照してください。

HTMLの生成

Emscriptenは、埋め込みJavaScriptをテストするためのHTMLも生成できます。HTMLを生成するには、-o出力)コマンドを使用し、ターゲットファイルとしてhtmlファイルを指定します。

./emcc test/hello_world.c -o hello.html

これで、Webブラウザでhello.htmlを開くことができます。

注記

残念ながら、Chrome、Safari、Internet Explorerなど、いくつかのブラウザはfile:// XHRリクエストをサポートしておらず、HTMLに必要な追加ファイル(.wasmファイルや、後述のパッケージ化されたファイルデータなど)を読み込むことができません。これらのブラウザでは、ローカルウェブサーバーを使用してファイルを配信し、http://localhost:8000/hello.htmlを開く必要があります。

ブラウザでHTMLを読み込んだら、ネイティブコードのprintf()呼び出しの出力を表示するためのテキストエリアが表示されます。

HTMLの出力はテキストの表示だけに限定されません。SDL APIを使用して、<canvas>要素にカラーキューブを表示することもできます(対応するブラウザの場合)。例として、hello_world_sdl.cテストコードをビルドし、ブラウザを更新してください。

./emcc test/hello_world_sdl.c -o hello.html

2番目の例のソースコードを以下に示します。

// Copyright 2011 The Emscripten Authors.  All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License.  Both these licenses can be
// found in the LICENSE file.

#include <stdio.h>
#include <SDL/SDL.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

int main(int argc, char** argv) {
  printf("hello, world!\n");

  SDL_Init(SDL_INIT_VIDEO);
  SDL_Surface *screen = SDL_SetVideoMode(256, 256, 32, SDL_SWSURFACE);

#ifdef TEST_SDL_LOCK_OPTS
  EM_ASM("SDL.defaults.copyOnLock = false; SDL.defaults.discardOnLock = true; SDL.defaults.opaqueFrontBuffer = false;");
#endif

  if (SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < 256; j++) {
#ifdef TEST_SDL_LOCK_OPTS
      // Alpha behaves like in the browser, so write proper opaque pixels.
      int alpha = 255;
#else
      // To emulate native behavior with blitting to screen, alpha component is ignored. Test that it is so by outputting
      // data (and testing that it does get discarded)
      int alpha = (i+j) % 255;
#endif
      *((Uint32*)screen->pixels + i * 256 + j) = SDL_MapRGBA(screen->format, i, j, 255-i, alpha);
    }
  }
  if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
  SDL_Flip(screen);

  printf("you should see a smoothly-colored square - no sharp lines but the square borders!\n");
  printf("and here is some text that should be HTML-friendly: amp: |&| double-quote: |\"| quote: |'| less-than, greater-than, html-like tags: |<cheez></cheez>|\nanother line.\n");

  SDL_Quit();

  return 0;
}

ファイルの使用

注記

C/C++コードは、通常のlibc stdio API(fopenfcloseなど)を使用してファイルにアクセスできます。

JavaScriptは通常、ローカルファイルシステムに直接アクセスすることなく、Webブラウザのサンドボックス化された環境で実行されます。Emscriptenは、コンパイルされたC/C++コードから通常のlibc stdio APIを使用してアクセスできるファイルシステムをシミュレートします。

アクセスするファイルは、仮想ファイルシステムにプリロードまたは埋め込みする必要があります。プリロード(または埋め込み)は、コンパイル時のファイルシステム構造に対応する仮想ファイルシステムを、カレントディレクトリを基準に生成します。

hello_world_file.cppの例は、ファイルの読み込み方法を示しています(テストコードと読み込むファイルの両方を以下に示します)。

// Copyright 2012 The Emscripten Authors.  All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License.  Both these licenses can be
// found in the LICENSE file.

#include <stdio.h>

int main() {
  FILE *file = fopen("test/hello_world_file.txt", "rb");
  if (!file) {
    printf("cannot open file\n");
    return 1;
  }
  while (!feof(file)) {
    char c = fgetc(file);
    if (c != EOF) {
      putchar(c);
    }
  }
  fclose (file);
  return 0;
}
==
This data has been read from a file.
The file is readable as if it were at the same location in the filesystem, including directories, as in the local filesystem where you compiled the source.
==

注記

この例は、test/hello_world_file.txtにあるファイルを読み込むことを想定しています。

FILE *file = fopen("test/hello_world_file.txt", "rb");

仮想ファイルシステムがコンパイル時ディレクトリを基準にして正しい構造で作成されるように、testの上のディレクトリから例をコンパイルします。

次のコマンドを使用して、コンパイルされたコードを実行する前に、Emscriptenの仮想ファイルシステムにプリロードするデータファイルを指定します。この方法は、ブラウザが(Web Workerを除いて)ネットワークからのデータのみを非同期的に読み込むことができるのに対し、多くのネイティブコードは同期的なファイルシステムアクセスを使用するため、役立ちます。プリロードにより、コンパイルされたコードがEmscriptenファイルシステムにアクセスする前に、データファイルの非同期ダウンロードが完了し(ファイルが使用可能になります)。

./emcc test/hello_world_file.cpp -o hello.html --preload-file test/hello_world_file.txt

上記のコマンドを実行してから、Webブラウザでhello.htmlを開き、hello_world_file.txtのデータが表示されていることを確認します。

ファイルシステムに関する詳細については、ファイルシステムの概要ファイルシステムAPI、および同期仮想XHRバックエンドファイルシステムの使用を参照してください。

コードの最適化

Emscriptenは、gccやclangと同様に、デフォルトでは最適化されていないコードを生成します。-O1コマンドライン引数を使用して、わずかに最適化されたコードを生成できます。

./emcc -O1 test/hello_world.cpp

a.out.jsで作成された「Hello World」コードは、最適化する必要がないため、最適化されていないバージョンと比較して速度の違いは見られません。

ただし、生成されたコードを比較して違いを確認できます。-O1はいくつかのマイナーな最適化を適用し、いくつかのランタイムアサーションを削除します。たとえば、生成されたコードではprintfputsに置き換えられます。

-O2こちらを参照)によって提供される最適化ははるかに積極的です。次のコマンドを実行して生成されたコード(a.out.js)を調べると、大きく異なることがわかります。

./emcc -O2 test/hello_world.cpp

コンパイラの最適化オプションの詳細については、コードの最適化emccツールのリファレンスを参照してください。

Emscriptenテストスイートとベンチマーク

Emscriptenには包括的なテストスイートがあり、事実上すべてのEmscripten機能を網羅しています。これらのテストは、ほとんどの機能の実際的な例を提供し、mainブランチで正常にビルドされることがわかっているため、開発者にとって優れたリソースです。

詳細については、Emscriptenテストスイートを参照してください。

一般的なヒントと次のステップ

このチュートリアルでは、コマンドラインからEmscriptenを呼び出す最初のステップを説明しました。もちろん、このツールでは他にも多くのことができます。以下は、Emscriptenを使用するためのその他の一般的なヒントです。

  • このサイトには、プロジェクトのコンパイルとビルドネイティブコードとWeb環境の統合コードのパッケージ化、および公開に関する多くの情報があります。

  • Emscriptenテストスイートは、Emscriptenの使用方法の例を探すのに最適な場所です。たとえば、emcc --pre-jsオプションの動作をよりよく理解したい場合は、テストスイートで--pre-jsを検索してください。テストスイートは広範囲にわたっており、少なくともいくつかの例がある可能性が高いです。

  • 高度な方法でEmscriptenを使用する方法を学ぶには、コンパイラオプションについて説明しているsrc/settings.jsemcc、およびEmscriptenでコンパイルされた場合にC/C++プログラムで使用できるJavaScript固有のC APIの詳細についてはemscripten.hを参照してください。

  • FAQをお読みください。

  • わからない場合は、お問い合わせください