この FAQ には、IRC およびメーリングリストで尋ねられた多くの質問への回答が含まれています。
Emscripten チュートリアル および emcc を参照してください。
Emscripten テストスイート のすべてのテストは、テストインフラストラクチャでビルドおよび合格することがわかっているため、ローカルでエラーが発生する場合は、環境に何らかの問題がある可能性があります。(まれに、一時的な破損が発生する可能性がありますが、タグ付きリリースバージョンでは発生しません。)
最初に emcc --check
を呼び出します。これにより、基本的な健全性チェックが実行され、役立つ環境情報が出力されます。それでも解決しない場合は、Emscripten 開発環境の検証 の指示に従ってください。
また、Emscripten の変更に合わせて更新されているため、Emscripten チュートリアル をもう一度確認することをお勧めします。
また、依存関係の新しいバージョンを含め、SDK セクションで指定されている Emscripten を実行するために必要な要件があることを確認してください。
問題を解決するのに役立つ可能性のある一般的な手順
最適化なし(-O0、または最適化レベルを指定しない)で問題が発生するかどうかを確認します。最適化がない場合、emscripten はコンパイル時および実行時に多くのアサーションを有効にします。これにより、問題が検出され、修正方法の提案とともにエラーメッセージが表示される場合があります。
このサイトのドキュメントを検索します。
Emscripten テストスイート で失敗している機能のテストがあるかどうかを確認します(test/ で
grep -r
を実行します)。それらはすべて合格する必要があります(ごくまれな例外を除いて)。したがって、さまざまなオプションとコードがどのように使用されているかの具体的な「既知の正常」な例を提供します。
ほとんどの場合、Emscripten でプロジェクトの現在のビルドシステムを使用できます。プロジェクトのビルド を参照してください。
Emscripten は、生成されたコードをより高速かつ小さくするために、リンク時間が長くなるという犠牲を払っています。たとえば、-flto(リンク時最適化)で標準ライブラリの一部をビルドします。これにより、いくつかの追加の最適化が可能になりますが、ビルドに時間がかかる場合があります。また、(最適化されたビルドでは)LTO なしでも、出力全体で Binaryen オプティマイザーを実行します。
注記
環境で EMCC_DEBUG=1
を使用してコンパイルし、デバッグログ(デフォルトでは /tmp/emscripten_temp
にあります)を確認することで、どのコンパイル手順に最も時間がかかるかを判断できます。デバッグモードでのコンパイルは、多くの中間手順をディスクに出力するため、通常よりも時間がかかることに注意してください。したがって、デバッグには役立ちますが、実際のコンパイルには役立ちません。
ビルド時間を改善するための主なヒントは次のとおりです
高速反復ビルドには -O0
を使用します。引き続きより高い最適化レベルでコンパイルできますが、リンク中に -O0
を指定すると、リンク手順がはるかに速くなります。
より多くのコアを持つマシンでコンパイルします
ソースファイルのコンパイルには、並列ビルドシステムを使用します(たとえば、make
では、make -j8
のように 8 コアを使用して実行できます)。
リンク手順の場合、Emscripten は一部の最適化を並行して実行できます(具体的には、Wasm の Binaryen 最適化と JavaScript 最適化)。コア数を増やすと、ほぼ線形の改善が得られます。Emscripten は、利用可能な場合は自動的により多くのコアを使用しますが、環境で EMCC_CORES=N
を使用して制御できます(多くのコアはあるが、比較的メモリが少ない場合に役立ちます)。
-O2
を使用してビルドすることでコードを最適化するようにしてください(コンパイル時間が大幅に増加するという犠牲を払って、さらに積極的な最適化も利用できます)。
コードが完全に最適化および縮小されるように、-O3
または -Os
でビルドするようにしてください。クロージャーコンパイラ、Webサーバーでの gzip 圧縮などを使用する必要があります。 コードの最適化に関するセクションを参照してください。
Emscripten バンドルのシステムヘッダーを使用していることを確認してください。emcc を使用するとデフォルトでそうしますが、emcc
でローカルシステムヘッダーを使用すると問題が発生する可能性があります。
最適化されたビルドを実行していることを確認してください(ビルドが小さいほど、起動が速くなります)。
ネットワーク遅延も起動時間の要因となる可能性があります。ブラウザがコードベースの起動と並行してネットワークダウンロードを開始できるように、ファイルロードコードを生成されたコードとは別のスクリプト要素に配置することを検討してください(ファイルパッカーを実行し、ファイルロードコードを1つのスクリプト要素に配置し、生成されたコードベースを後続のスクリプト要素に配置します)。
このエラーは、一部のブラウザでは機能するが、他のブラウザでは機能しない file://
URL を使用してページをロードすると発生する可能性があります。代わりに、ローカル Web サーバーを使用することをお勧めします。たとえば、Python には組み込みの Web サーバーがあり、Python 3 では python -m http.server
、Python 2 では python -m SimpleHTTPServer
です。これを実行した後、http://localhost:8000/
にアクセスできます。emrun FILENAME.html
を使用することもできます(これにより、Python Web サーバーが実行されます)。
ローカルテストを迅速に行う場合、ローカル Web サーバー以外のオプションとして、-sSINGLE_FILE
を使用してすべてを単一のファイルにバンドルすることです(その場合、file://
URL に XHR が作成されなくなるため)。
それ以外の場合、これをデバッグするには、ページ自体、ブラウザの開発ツール(Web コンソールとネットワークタブ)、または Web サーバーのログに報告されたエラーを探してください。
machine type must be wasm32
または unknown file type
が表示されるのはなぜですか?¶これは、このリンカーの入力ファイルの1つ以上が Emscripten によってビルドされていない(または、より具体的には、正しいターゲットアーキテクチャ用にビルドされていない)ことを意味します。
多くの場合、問題となるファイルは、ホストマシン用にビルドされた ELF ファイルまたは Mach-O ファイルです。 file
コマンドラインユーティリティを実行すると、実際に何が含まれているかを確認できます。
一般的な問題は次のとおりです。
ホストシステム用にビルドされたライブラリにリンクしようとしている。たとえば、リンクコマンドに -L/usr/lib
のようなものがある場合、これらのシステムディレクトリにあるライブラリはほぼ確実に Emscripten でビルドされていないため、ほぼ常にエラーが発生します。この解決策は、依存するすべてのライブラリを Emscripten を使用してビルドし、ホストライブラリを絶対に使用しないことです。
プロジェクト内の一部のライブラリまたはオブジェクトファイルが、emscripten コンパイラではなくホストコンパイラを使用してビルドされた。autoconf または cmake を使用している場合は、emconfigure/emmake ラッパーを使用してください。 プロジェクトのビルドを参照してください。
古いバックエンドからの LLVM IR。プロジェクトを 1.39.0 より前のバージョン(デフォルトで古いバックエンドを使用していた)でビルドし、現在インクリメンタル再ビルドを行っている場合。これを修正するには、ライブラリを含むプロジェクトのすべてのファイルを完全にスクラッチから再ビルドしてください(このエラーは、サードパーティのプリビルドライブラリがある場合に発生することがよくあります。それらも新しいバックエンドで再コンパイルする必要があります)。
{"text":"asm"}
)に関するエラーメッセージでコードのコンパイルが失敗するのはなぜですか?¶Emscripten はインラインアセンブリコードをコンパイルできません(そのアセンブリコードが特に WebAssembly をターゲットに記述されている場合を除く)。
インラインアセンブリが使用されている場所を見つけて、それを無効にするか、プラットフォームに依存しないコードで置き換える必要があります。
ブラウザのイベントモデルは、協調型マルチタスクを使用しています。各イベントには実行する「順番」があり、他のイベントを処理できるようにブラウザのイベントループに制御を返す必要があります。 HTML ページがハングする一般的な原因は、完了せずにブラウザに制御を返さない JavaScript です。
グラフィカル C++ アプリケーションには通常、イベント処理、処理、レンダリングが行われる無限のメインループがあり、その後フレームレートを適切に保つための遅延(SDL アプリケーションの SDL_DELAY
)が続きます。メインループは完了しない(無限である)ため、ブラウザに制御を返すことができず、アプリケーションはハングします。
無限のメインループを使用するアプリケーションは、ループの単一の反復のアクションを単一の「有限」関数に入れるように再コーディングする必要があります。ネイティブビルドでは、この関数は以前と同様に無限ループで実行できます。 Emscripten ビルドでは、メインループ関数として設定され、指定された頻度でブラウザによって呼び出されます。
このトピックの詳細については、Emscripten ランタイム環境を参照してください。
C 関数を繰り返し実行するには、emscripten_set_main_loop()
を使用します(これについては、Emscripten ランタイム環境で説明しています)。 emscripten.hの関連関数も便利で、メインループをブロックするイベントなどを追加できます。
ブラウザイベントに応答するには、通常の方法で SDL API を使用します。 SDL テスト(test/runner.pyで SDL を検索)に例があります。
こちらも参照してください:HTML アプリケーションがハングするのはなぜですか?
動作する例については、SDL の自動テストを参照してください:test/runner.py browser
。
Emscripten に含まれているシステムライブラリは、コンパイル時に自動的にリンクされます(必要な部分のみ)。これには、libc、libc++(C++ 標準ライブラリ)、およびSDLが含まれます。
Emscripten に含まれていないライブラリ(Boost など)は、プロジェクトのモジュールであるかのようにコンパイルしてプログラムにリンクする必要があります。
Emscripten Ports という便利な使用のために Emscripten に移植されたライブラリのセットがあります。プロジェクトのビルドを参照してください。
別のオプションは、必要な C API を JavaScript ライブラリとして実装することです(emcc およびJavaScript で C API を実装するの --js-library
を参照してください)。 Emscripten 自体は、libc(malloc を除く)およびSDL(ただし、libc++ または malloc は除く)に対してこれを行います。
注記
他のコンパイラとは異なり、SDL を含めるために -lSDL
は必要ありません(指定しても害はありません)。
Boost の特定のケースでは、boost ヘッダーのみが必要な場合は、何もコンパイルする必要はありません。
Emscripten は、SDL1 および 2 オーディオと OpenAL を部分的にサポートしています。
SDL1 オーディオを使用するには、#include <SDL/SDL_mixer.h>
として含めます。プラットフォーム統合のために SDL1、SDL2、または別のライブラリと一緒に使用できます。
SDL2 オーディオを使用するには、#include <SDL2/SDL_mixer.h>
として含めて、-sUSE_SDL_MIXER=2 を使用します。現在、サポートされているフォーマットは、OGG、WAV、MID、および MOD に限定されています。
Emscripten は、データがプリロードされるか、遅延ロードのために URL にリンクされる仮想ファイルシステムを使用します。詳細については、ファイルシステムの概要を参照してください。
ブラウザで実行される Emscripten で生成されたコードは、ローカルファイルシステムのファイルにアクセスできません。代わりに、プリロードと埋め込みを使用して、同期ファイル IO の欠如を回避できます。詳細については、ファイルシステムの概要を参照してください。
node.js で実行されるコードに対してローカルファイルシステムへのアクセスを許可することは可能です。 NODEFS ファイルシステムオプションを使用してください。
(native function `x` called before runtime initialization
のようなエラーが表示される場合は、この回答が必要になる場合があります。これは ASSERTIONS
ビルドで有効になっているチェックです。)
ページが完全にロードされる前にコンパイルされた関数を呼び出すと、ファイルが存在しない場合に関数を呼び出すとエラーが発生する可能性があります(たとえば、プリロードされたファイルは非同期でロードされるため、コンパイルされたコードを呼び出す JavaScript を --post-js
に配置した場合、そのコードは結合された JS ファイルの最後に同期的に呼び出され、非同期イベントが発生する前になる可能性があるため、これは良くありません)。
ロードが完了したことを確認する最も簡単な方法は、main()
関数を追加し、その中で JavaScript 関数を呼び出して、ロードが完了したことをコードに通知することです。
注記
main()
関数は、スタートアップが完了した後に、コンパイルされた任意のメソッドを安全に呼び出すことができるというシグナルとして呼び出されます。
たとえば、すべてが準備できたときに呼び出したい JavaScript 関数が allReady()
である場合は、次のように実行できます。
#include <emscripten.h>
int main() {
EM_ASM( allReady() );
}
別のオプションは、onRuntimeInitialized
関数を定義することです。
Module['onRuntimeInitialized'] = function() { ... };
このメソッドは、ランタイムの準備が整い、コンパイルされたコードを呼び出しても安全なときに呼び出されます。実際には、これは main()
が呼び出されるのとまったく同じタイミングであるため、onRuntimeInitialized
では新しいことは何もできませんが、柔軟な方法でランタイム時に JavaScript から設定できます。
以下に、その使用方法の例を示します。
<script type="text/javascript">
var Module = {
onRuntimeInitialized: function() {
Module._foobar(); // foobar was exported
}
};
</script>
<script type="text/javascript" src="my_project.js"></script>
重要なのは、Emscripten 出力を含むスクリプト(この例では my_project.js
)がロードされる前に、Module
が存在し、onRuntimeInitialized
プロパティを持っていることです。
別のオプションとして、MODULARIZE
オプションを -sMODULARIZE
を使って使用する方法があります。これは、生成されたすべての JavaScript をファクトリ関数に入れ、それを使用してモジュールのインスタンスを作成できます。ファクトリ関数は、モジュールインスタンスで解決される Promise を返します。Promise は、コンパイルされたコードがダウンロードされ、インスタンス化された後、コンパイルされたコードを呼び出すことが安全になった時点で解決されます。たとえば、-sMODULARIZE -s 'EXPORT_NAME="createMyModule"'
を指定してビルドした場合、次のようにすることができます。
createMyModule(/* optional default settings */).then(function(Module) {
// this is reached when everything is ready, and you can call methods on Module
});
MODULARIZE
モードでは、デフォルト値のためにグローバルな Module オブジェクトを探さないことに注意してください。デフォルト値は、ファクトリ関数へのパラメータとして渡す必要があります(詳細は settings.js を参照)。
atexit()
が実行されないのですか?¶(atexit() called, but EXIT_RUNTIME is not set
または stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1
のようなエラーが表示された場合は、この回答が必要になる可能性があります。)
デフォルトでは、Emscripten は EXIT_RUNTIME=0
を設定します。これは、ランタイムをシャットダウンするコードを含めないことを意味します。つまり、main()
が終了したときに、stdio ストリームをフラッシュしたり、グローバル C++ オブジェクトのデストラクタを呼び出したり、atexit
コールバックを呼び出したりしません。これにより、デフォルトで小さなコードを出力できるようになり、通常は Web で必要なものになります。つまり、main()
が終了しても、後で実行したい非同期処理が発生する可能性があるということです。
ただし、main()
が終了したときにランタイムをシャットダウンする、より「コマンドライン」のようなエクスペリエンスが必要な場合があります。-sEXIT_RUNTIME
を使用してビルドすると、atexits
などが呼び出されます。ASSERTIONS
を使用してビルドすると、これが必要な場合に警告が表示されるはずです。たとえば、プログラムが改行なしで何かを出力する場合、
#include <stdio.h>
int main() {
printf("hello"); // note no newline
}
ランタイムをシャットダウンして stdio ストリームをフラッシュしないと、「hello」は出力されません。ASSERTIONS
ビルドでは、stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1
という通知が表示されます。
Emscripten は、コンパイルされたコードから呼び出されない関数のデッドコード削除を実行します。これにより、コードサイズが最小化されますが、自分で呼び出す予定の関数(コンパイルされたコードの外側)が削除される可能性があります。
C 関数が通常の JavaScript から呼び出し可能であることを確認するには、emcc コマンドラインを使用して EXPORTED_FUNCTIONS に追加する必要があります。たとえば、関数 my_func()
と main()
が削除/名前変更されないようにするには、emcc を次のように実行します。
emcc -sEXPORTED_FUNCTIONS=_main,_my_func ...
注記
main() 関数がある場合は、上記の例のように _main がエクスポートリストに含まれている必要があります。そうしないと、デッドコードとして削除されます。デフォルトで main() を保持する特別なロジックはありません。
注記
EXPORTED_FUNCTIONS は JavaScript へのコンパイルに影響します。最初にオブジェクトファイルにコンパイルしてから、オブジェクトを JavaScript にコンパイルする場合は、2 番目のコマンドでそのオプションが必要です。
関数が他の関数で使用されている場合、LLVM はそれをインライン化する可能性があり、JavaScript 内で一意の関数として表示されません。関数を EMSCRIPTEN_KEEPALIVE
で定義してインライン化を防ぎます。
void EMSCRIPTEN_KEEPALIVE yourCfunc() {..}
EMSCRIPTEN_KEEPALIVE は、EXPORTED_FUNCTIONS にあるかのように関数もエクスポートします。
注記
EXPORTED_FUNCTIONS
または EMSCRIPTEN_KEEPALIVE
を介して保持されていないすべての関数は、削除される可能性があります。これらのいずれかまたは両方の方法を使用して、必要なものを保持してください。
エクスポートされた関数は、(C++ の名前マングリングを避けるために)C 関数である必要があります。
コードを EMSCRIPTEN_KEEPALIVE
で装飾することは、明示的にエクスポートする関数を追跡する必要がない場合や、これらのエクスポートが変更されない場合に役立ちます。他のライブラリから関数をエクスポートするのには必ずしも適していません。たとえば、C 標準ライブラリのソースコードを装飾して再コンパイルするのは良い考えではありません。同じソースを複数の方法でビルドし、エクスポートするものを変更する場合は、コマンドラインでエクスポートを管理する方が簡単です。
emcc を -sLINKABLE
を指定して実行すると、リンク時の最適化とデッドコード削除も無効になります。これにより、コードが大きくなり、最適化が少なくなるため、推奨されません。
コードが欠落するもう 1 つの原因は、.a
ファイルの不適切なリンクです。.a
ファイルは、コマンドラインの前のファイルに必要な内部オブジェクトファイルのみをリンクするため、ファイルの順序が重要であり、これは驚くべきことかもしれません。.a
ファイルをリンクする場合は、それらがファイルのリストの最後にあること、およびそれらの間で正しい順序になっていることを確認してください。または、プロジェクトで代わりに .so
ファイルを使用してください。
ヒント
環境に EMCC_DEBUG=1
を設定してコンパイルすると便利です(Linux では EMCC_DEBUG=1 emcc ...
、Windows では set EMCC_DEBUG=1
)。これにより、コンパイル手順が分割され、/tmp/emscripten_temp
に保存されます。次に、コードがどの段階で消えるかを確認できます(ビットコード段階を読み取るには llvm-dis
を、llvm-nm
などを行う必要があります)。
Closure Compiler はファイルサーバー API コードを最小化します。ファイルシステムを使用するコードは、emcc の --pre-js
オプション を使用して、ファイルシステム API とともに最適化する必要があります。
-O2 --closure 1
を使用すると、コードが壊れて奇妙なエラーが発生するのはなぜですか?¶Closure Compiler は変数名を最小化するため、i
、j
、xa
などの非常に短い変数名になります。他のコードがグローバルスコープで同じ名前の変数を宣言している場合、これは深刻な問題を引き起こす可能性があります。
これは、-O2
を設定し、--closure
を設定せずにコンパイルされたコードを正常に実行できる場合に、原因となる可能性が高くなります。
1 つの解決策は、グローバルスコープで小さな変数名を使用しないことです(多くの場合、これは間違いです。変数に代入するときに var
を使用するのを忘れています)。
別の方法は、次のように生成されたコード(または他のコード)をクロージャでラップすることです。
var CompiledModule = (function() {
.. GENERATED CODE ..
return Module;
})();
TypeError: Module.someThing is not a function
が表示されるのはなぜですか?¶Module
オブジェクトには、エクスポートされたメソッドが含まれます。そこに何かを表示するには、コンパイルされたコードの場合は EXPORTED_FUNCTIONS
に、ランタイムメソッド(getValue
など)の場合は EXPORTED_RUNTIME_METHODS
に追加する必要があります。例えば、
emcc -sEXPORTED_FUNCTIONS=_main,_my_func ...
C メソッド my_func
を(この例では main
に加えて)エクスポートします。そして、
emcc -sEXPORTED_RUNTIME_METHODS=ccall ...
ccall
をエクスポートします。どちらの場合も、Module
オブジェクトでエクスポートされた関数にアクセスできます。
注記
コンパイラーがそれらの使用を確認できる場合は、エクスポートせずにランタイムメソッドを直接使用できます。たとえば、EM_ASM
コードまたは --pre-js
で getValue
を直接呼び出すことで使用できます。オプティマイザは、使用されていることを認識しているため、その JS ランタイムメソッドを削除しません。Module.getValue
を使用する必要があるのは、コンパイラーが認識できる JS コードの外部からそのメソッドを呼び出したい場合のみであり、その場合はエクスポートする必要があります。
注記
Emscripten は以前は多くのランタイムメソッドをデフォルトでエクスポートしていました。これにより、コードサイズが増加したため、デフォルトを変更しました。以前にエクスポートされていたものに依存している場合は、最適化されていないビルドまたは ASSERTIONS
が有効になっているビルドで、ソリューションを示す警告が表示されるはずです。これにより、ご迷惑をおかけするのを最小限に抑えられることを願っています。詳細については、ChangeLog.md
を参照してください。
Runtime
が存在しなくなったのはなぜですか?Runtime.someThing
にアクセスしようとするとエラーが発生するのはなぜですか?¶1.37.27 では、Runtime
オブジェクトを削除するリファクタリングが行われました。これにより、生成されるコードの効率性とコンパクト性が向上しましたが、Runtime.*
API を使用していた場合は、わずかな変更が必要になります。Runtime.
プレフィックスを削除するだけで済みます。これらの関数は、トップスコープのシンプルな関数になったためです(-O0
でのエラーメッセージ、またはアサーションが有効になっているビルドでは、これが示唆されます)。つまり、次のように置き換える必要があります。
x = Runtime.stackAlloc(10);
を
x = stackAlloc(10);
注記
上記は、--pre-js
または JS ライブラリ内のコード、つまり、emscripten の出力とともにコンパイルされるコードに対して有効です。コンパイルされたコードの外から Runtime.*
メソッドにアクセスしようとする場合は、その関数をエクスポートする必要があります(EXPORTED_RUNTIME_METHODS
を使用)。そして、Module オブジェクトで使用してください。 この FAQ エントリを参照してください。
-s
オプションを使用すると、NameError
または "-s" の後にコンテンツを評価中に問題が発生しました
というエラーが表示されるのはなぜですか?¶これは、-s
引数に複雑な文字列が含まれており、シェルのクォート/エスケープが正しく行われていない場合に発生することがあります。
よりシンプルなリスト形式(引用符、スペース、角括弧なし)を使用すると、問題が解決することがあります。
emcc a.c -sEXPORTED_RUNTIME_METHODS=foo,bar
また、**レスポンスファイル**を使用することもできます。例えば、次のようにします。
emcc a.c -sEXPORTED_RUNTIME_METHODS=@extra.txt
extra.txt
が、foo
と bar
を別々の行に含むプレーンテキストファイルの場合。
-s
オプションを指定するにはどうすればよいですか?¶次のような簡単なことは、CMakeLists.txt
ファイル内でそのまま機能するはずです。
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -sUSE_SDL=2")
ただし、一部の -s
オプションでは、引用符が必要な場合や、target_link_options
などの使用時に、-s
と次の引数の間のスペースが CMake を混乱させる場合があります。これらの問題を回避するには、-sX=Y
のように、スペースなし、角括弧なし、引用符なしの表記を使用できます。
# same as before but no space after -s
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -sUSE_SDL=2")
# example of target_link_options with a list of names
target_link_options(example PRIVATE "-sEXPORTED_FUNCTIONS=_main")
また、_main
は文字列名ですが、引用符で囲む必要はありません(emcc
は、EXPORTED_FUNCTIONS
への引数が文字列のリストであることを認識しているため、a
または a,b
などを受け入れます)。
file=..
や f'..'
で始まる文字列で、Python の SyntaxError: invalid syntax
が表示されるのはなぜですか?¶Emscripten には、比較的新しいバージョンの Python が必要です。古いバージョンの Python(例えば 2.*
など)では、print ステートメントがデフォルトでサポートされていないため、print('..', file=..)
のような構文でエラーが発生します。また、古い 3.*
Python は、f'..'
のような f-文字列をサポートしていない場合があります。
SDK の手順で指定されているように、十分に新しいバージョンの Python がインストールされていること、および emcc がそれを使用していること(例えば、その Python を使用して emcc.py
を実行するなど)を確認してください。
CI 環境では、デフォルトで十分なバージョンではない場合、使用する Python バージョンを指定する必要がある場合があります。例えば、Netlify の場合、PYTHON_VERSION
を使用できます。
RangeError: Maximum call stack size exceeded
または類似のエラー) のはなぜですか?¶node.js のスタックサイズを増やす必要があるかもしれません。
Linux および Mac macOS では、Emscripten コンパイラー構成ファイル (.emscripten) で NODE_JS = ['/path/to/node', '--stack_size=8192']
を指定するだけで済みます。Windows (v19 より古いバージョンの node の場合) では、--max-stack-size=8192
も必要であり、editbin /stack:33554432 node.exe
も実行する必要があります。
-sWASM_BIGINT フラグを使用してビルドする場合、int64_t および uint64_t は JS で bigint 値として表されます。-sWASM_BIGINT フラグがない場合、値は JS で number として表されますが、これは int64 を表現できないため、エクスポートされた関数(JS から呼び出すことができる)では、i64 引数を 2 つの i32(下位ビットと上位ビット)に変換することによって型を「合法化」し、i64 の戻り値は i32 になり、getTempRet0 というヘルパー関数を呼び出すことによって上位ビットにアクセスできます。
Emscripten の出力はデフォルトでは単なるコードです。スクリプトタグに配置した場合、そのコードはグローバルスコープに存在することになります。そのため、同じページに複数のそのようなモジュールが存在すると、機能しません。
ただし、各モジュールを関数スコープに配置することで、その問題は回避されます。Emscripten には、このためのコンパイルフラグ MODULARIZE
があり、EXPORT_NAME
と組み合わせて使用すると便利です(詳細は settings.js を参照)。
ただし、同じ Module オブジェクト(キャンバス、テキスト出力領域などを定義)が別々のモジュール間で使用されている場合は、依然として問題があります。デフォルトでは、Emscripten の出力はグローバルスコープの Module を検索しますが、MODULARIZE
を使用する場合は、Module をパラメーターとして渡して呼び出す必要のある関数を取得するため、その問題は回避されます。ただし、各モジュールはおそらく独自のキャンバス、テキスト出力領域などを必要とすることに注意してください。デフォルトの HTML シェルからのように同じ Module オブジェクトを渡すだけでは、機能しない場合があります。
したがって、MODULARIZE
を使用して、各モジュールに適切な Module オブジェクトを作成し、それらを渡すことで、複数のモジュールが問題なく機能します。
別の方法として、iframe を使用することもできます。その場合、それぞれが独自のキャンバスなどを持つため、デフォルトの HTML シェルがそのまま機能します。ただし、これは小さなプログラムにとっては過剰であり、上記のようにモジュール式に実行できます。
はい、settings.js
の ENVIRONMENT オプションを使用できます。例えば、emcc -sENVIRONMENT=web
でビルドすると、Web でのみ実行されるコードが生成され、Node.js およびその他の環境のサポートコードは含まれません。
これは、コードサイズを削減するのに役立つ場合があり、また、Webpack が処理して不要なコードを含めることになる require()
を使用する Node.js サポートコードのような問題を回避するのにも役立ちます。
なぜだか分かりません。完全に 妥当な 言葉です!