Electronの内部構造: ライブラリとしてのNodeの使用
これは、Electronの内部構造について説明する連載の2番目の投稿です。まだ読んでいない場合は、イベントループの統合に関する最初の投稿を確認してください。
ほとんどの人はサーバーサイドアプリケーションにNodeを使用していますが、Nodeの豊富なAPIセットと活気のあるコミュニティのため、組み込みライブラリにも最適です。この投稿では、NodeがElectronでライブラリとしてどのように使用されているかを説明します。
ビルドシステム
NodeとElectronの両方で、ビルドシステムとしてGYP
を使用しています。Nodeをアプリに埋め込みたい場合は、ビルドシステムとしても使用する必要があります。
GYP
は初めてですか?この投稿の続きを読み進める前に、このガイドをお読みください。
Nodeのフラグ
Nodeのソースコードディレクトリにあるnode.gyp
ファイルには、Nodeのビルド方法と、Nodeのどの部分を有効にするか、特定の構成を開くかどうかを制御する多くのGYP
変数が記述されています。
ビルドフラグを変更するには、プロジェクトの.gypi
ファイルに変数を設定する必要があります。Nodeのconfigure
スクリプトは、いくつかの一般的な構成を生成できます。たとえば、./configure --shared
を実行すると、Nodeを共有ライブラリとしてビルドするように指示する変数を含むconfig.gypi
が生成されます。
Electronには独自のビルドスクリプトがあるため、configure
スクリプトは使用しません。Nodeの構成は、Electronのルートソースコードディレクトリにあるcommon.gypi
ファイルで定義されています。
ElectronとNodeをリンクする
Electronでは、GYP
変数node_shared
をtrue
に設定することにより、Nodeが共有ライブラリとしてリンクされているため、Nodeのビルドタイプがexecutable
からshared_library
に変更され、Nodeのmain
エントリポイントを含むソースコードはコンパイルされません。
ElectronはChromiumに同梱されているV8ライブラリを使用するため、Nodeのソースコードに含まれているV8ライブラリは使用されません。これは、node_use_v8_platform
とnode_use_bundled_v8
の両方をfalse
に設定することで行われます。
共有ライブラリまたは静的ライブラリ
Nodeとリンクする場合、2つのオプションがあります。Nodeを静的ライブラリとしてビルドして最終的な実行可能ファイルに含めるか、共有ライブラリとしてビルドして最終的な実行可能ファイルと一緒に出荷することができます。
Electronでは、Nodeは長い間静的ライブラリとしてビルドされていました。これにより、ビルドが簡単になり、最適なコンパイラの最適化が可能になり、Electronを余分なnode.dll
ファイルなしで配布することができました。
ただし、これはChromeがBoringSSLを使用するように切り替えた後に変更されました。BoringSSLは、いくつかの未使用のAPIを削除し、既存の多くのインターフェイスを変更するOpenSSLのフォークです。Nodeは依然としてOpenSSLを使用しているため、一緒にリンクすると、コンパイラは競合するシンボルにより多数のリンクエラーを生成します。
Electronは、NodeでBoringSSLを使用したり、ChromiumでOpenSSLを使用したりすることができなかったため、唯一の選択肢は、Nodeを共有ライブラリとしてビルドし、それぞれのコンポーネントでBoringSSLとOpenSSLのシンボルを非表示にすることでした。
この変更により、Electronにいくつかの良い副作用がありました。この変更前は、ネイティブモジュールを使用する場合、実行可能ファイルの名前がインポートライブラリにハードコードされていたため、WindowsでElectronの実行可能ファイルの名前を変更できませんでした。Nodeが共有ライブラリとしてビルドされた後、すべてのネイティブモジュールがnode.dll
にリンクされるようになったため、この制限はなくなりました。node.dll
の名前を変更する必要はありませんでした。
ネイティブモジュールのサポート
Nodeにおけるネイティブモジュールは、Nodeがロードするためのエントリー関数を定義し、その後NodeからV8とlibuvのシンボルを検索することで機能します。これは、Nodeをライブラリとしてビルドする際にデフォルトでV8とlibuvのシンボルが隠蔽されているため、ネイティブモジュールがシンボルを見つけることができずロードに失敗してしまうため、埋め込みを行う開発者にとっては少し厄介です。
そのため、ネイティブモジュールを機能させるために、V8とlibuvのシンボルはElectronで公開されました。V8については、Chromiumの設定ファイル内のすべてのシンボルを強制的に公開することによって行われています。libuvについては、BUILDING_UV_SHARED=1
定義を設定することによって実現されています。
アプリケーション内でのNodeの起動
Nodeをビルドしてリンクするすべての作業が終わった後、最後のステップはアプリケーション内でNodeを実行することです。
Nodeは、他のアプリケーションに自身を埋め込むための公開APIをあまり提供していません。通常は、node::Start
とnode::Init
を呼び出すだけで、Nodeの新しいインスタンスを起動できます。ただし、Nodeをベースにした複雑なアプリケーションを構築している場合は、node::CreateEnvironment
のようなAPIを使用して、すべてのステップを正確に制御する必要があります。
Electronでは、Nodeは2つのモードで起動します。公式のNodeバイナリと同様にメインプロセスで実行されるスタンドアロンモードと、WebページにNode APIを挿入する埋め込みモードです。この詳細については、今後の記事で説明します。