ターボモジュールに関する高度なトピック
このトピックでは、複雑なターボモジュールを作成するときに役立つ関連情報を紹介します。
ターボモジュールのCMAKEヘルパー
kepler_add_turbo_module_library関数はCMakeヘルパーで、これにより、Vega向けReact Nativeランタイムで使用するターボモジュールライブラリのターゲットを定義するプロセスが簡略化されます。ターボモジュールライブラリに必要な一般的な構成と最適化の設定を処理し、必要な設定と最適化を自動的に適用して、プロセスを合理化します。
このヘルパー関数には次のような利点があります。
- シンプルな構成: 定型のCMakeコードを削減し、1回の関数呼び出しに置き換えます。
- 一貫性のあるベストプラクティス: すべてのターボモジュールが同じ構成パターンに従うようにして、将来的に共通設定を一元的に展開するしくみを提供します。
- アプリケーションバイナリインターフェイス(ABI)の安定性の向上: シンボルの適切な可視性設定を自動的に適用します。
- パフォーマンスの最適化: リンク時最適化やサイズ最適化など、パフォーマンスの最適化を確実に適用します。
ターボモジュールDSOのシンボルに関する主要なコントラクト
Vegaプラットフォームのすべてのターボモジュールは、次の2つの重要な要件に従う必要があります。
- EntryPoint関数のエクスポート: ターボモジュールライブラリは、
autoLinkVegaTurboModulesV1という名前のシンボルを1つエクスポートする必要があります。このシンボルは、Vegaランタイムがターボモジュールを自動的にリンクして読み込むために使用するエントリポイントとして機能します。 - シンボルの隠蔽: ターボモジュールライブラリ内のその他のシンボルはすべて隠蔽する必要があります。これにより、ほかライブラリとのシンボルの競合が回避され、ABIの安定性が確保され、ターボモジュールの読み込み時間が短縮されます。また、多くの場合はライブラリのサイズを削減できます。
kepler_add_turbo_module_libraryヘルパーを使用すると、これらの要件が自動的に構成されるため、シンボルの可視性を手動で管理する必要がなくなります。
提供される機能
kepler_add_turbo_module_library関数は、ターボモジュールライブラリの作成を簡単にするいくつかの主な機能を提供します。これらの機能は、大きく次の3つのカテゴリーに分類されます。
共有ライブラリの作成
ヘルパー関数は、次のように共有ライブラリをサポートします。
- CMakeの標準の
add_libraryコマンドを使用して、指定された名前で共有ライブラリを作成します。 turbomoduleAPI静的ライブラリをリンクします。これにより、ターボモジュールを開発するためのC++メソッドと、必要なプラットフォームレベルの機能がライブラリに提供されます。- ライブラリターゲットを適切に設定します。手動で
add_libraryとtarget_link_librariesを使用する場合と同様の処理が行われます。
シンボルの可視性の制御
ヘルパー関数は、シンボルの可視性を自動的に構成してABIの安定性を確保します。
- すべてのシンボルのデフォルトの可視性を「隠蔽」に設定します。
autoLinkVegaTurboModulesV1シンボルのみを明示的にエクスポートするバージョンスクリプトを適用します。-fno-semantic-interpositionを使用してシンボルの割り込みを無効にし、-Bsymbolicを使用してグローバル参照のローカルバインドを適用することで、シンボルの競合を防ぎます。
パフォーマンスの最適化
ヘルパー関数は、ターボモジュールのパフォーマンスとサイズを改善するいくつかの最適化を適用します。
- パフォーマンスの向上とサイズの削減のために、リンク時最適化(LTO)を有効にします。
- 未使用セクションのガベージコレクションを有効にして、サイズの最適化を適用します。
使用方法
kepler_add_turbo_module_library関数を使用するには、CMakeLists.txtに以下を追加します。
## kepler_add_turbo_module_libraryを使用するにはTMAPIパッケージが必要です。
find_package(turbomoduleAPI CONFIG REQUIRED)
## ソースファイルを定義します。
set(SOURCES
AutoLinkInit.cpp
MyTurboModule.cpp
)
## ターボモジュールライブラリを作成します。
kepler_add_turbo_module_library(MyTurboModule ${SOURCES})
よくある質問(FAQ)
Q: このヘルパー関数を使用するように既存のターボモジュールを移行する必要はありますか?
A: はい。既にターボモジュールがある場合は、このヘルパー関数を使用するように移行することを強くお勧めします。turbomoduleAPIライブラリを介して最適化フラグをINTERFACEフラグとして伝播する現在のアプローチは、将来廃止される予定です。このヘルパー関数はより広範な戦略の一部であり、戦略全体の目標は、予期しない動作につながる可能性のあるビルド設定の暗黙的な伝播を防ぎながら、ターボモジュールの構成をより明確で管理しやすいものにすることです。
Q: このヘルパー関数を使用するように既存のターボモジュールを移行するにはどうすればよいですか?
A: 移行するには、次の手順に従ってください。
- 現在のCMakeLists.txtを確認して、ターボモジュールがどのように構成されているかを理解します。
- CMakeLists.txtに
find_package(turbomoduleAPI CONFIG REQUIRED)という行があることを確認します。 add_libraryの呼び出しと後続のすべての構成を、単一のkepler_add_turbo_module_library呼び出しに置き換えます。- これまで
autoLinkVegaTurboModulesV1エクスポートの管理に使用していたバージョンスクリプト(.mapファイル)を削除します。 - ビルドをテストして、すべてが正しく動作することを確認します。
移行前と移行後のCMake構成の例を以下に示します。
移行前(手動構成)
## turbomoduleAPIパッケージを探します。
find_package(turbomoduleAPI CONFIG REQUIRED)
## ソースファイルを定義します。
set(SOURCES
AutoLinkInit.cpp
MyTurboModule.cpp
)
## 共有ライブラリを作成します。
add_library(MyTurboModule ${SOURCES})
## turbomoduleAPIライブラリにリンクします。
target_link_libraries(MyTurboModule PRIVATE turbomoduleAPI::turbomoduleAPI)
## バージョンスクリプトを適用して、安定したシンボルのみエクスポートを許可します。
target_link_options(MyTurboModule PRIVATE -Wl,--version-script,${CMAKE_CURRENT_SOURCE_DIR}/MyTurboModule_symbols.map)
移行後(ヘルパーを使用)
## turbomoduleAPIパッケージを探します。
find_package(turbomoduleAPI CONFIG REQUIRED)
## ソースファイルを定義します。
set(SOURCES
AutoLinkInit.cpp
MyTurboModule.cpp
)
## すべての最適化が適用されたターボモジュールライブラリを作成します。
kepler_add_turbo_module_library(MyTurboModule ${SOURCES})
Q: このヘルパー関数でバージョンスクリプトを処理するにはどうすればよいですか? autoLinkVegaTurboModulesV1以外のシンボルを追加でエクスポートすることはできますか?
A: このヘルパー関数は、autoLinkVegaTurboModulesV1シンボルのみをエクスポートするバージョンスクリプトを自動的に適用します。既にバージョンスクリプトを使用している場合は、競合を避けるために、CMake構成から既存のバージョンスクリプトの適用箇所を削除してください。autoLinkVegaTurboModulesV1以外のシンボルを追加でエクスポートする必要がある場合は、ヘルパー関数の呼び出し後に別のバージョンスクリプトを適用できます。ただし、各スクリプトでエクスポートされるシンボルが重複しないようにしてください。追加のシンボルをエクスポートするとABIの安定性が損なわれる可能性があるため、このアプローチの使用は控えめにする必要があります。
Q: kepler_add_turbo_module_library関数にかかわる問題をデバッグするにはどうすればよいですか?
A: 生成されたコンパイルデータベース(buildディレクトリにあるcompile_commands.json)を確認して、コンパイラとリンカーのフラグを調べることができます。ヘルパー関数は、デフォルトでEXPORT_COMPILE_COMMANDS ONを有効にしてこのデータベースを作成します。IDEでデータベースを開いて、適用された正確なフラグを確認することができます。
Q: ターゲットの動作を追加するにはどうすればよいですか?
A: ヘルパー関数は既にプラットフォーム固有の動作を処理するようになっています。追加の設定が必要な場合は、ヘルパー関数の呼び出し後に追加できます。
kepler_add_turbo_module_library(MyTurboModule ${SOURCES})
target_compile_definitions(MyTurboModule PRIVATE MY_CUSTOM_DEFINE=1)
シンボルの隠蔽
VegaターボモジュールAPI(TMAPI)は下位互換性を備えた安定したAPIです。そのため、APIの変更に対応するためにターボモジュールを頻繁に更新する必要がなくなります。この安定性により、通信がABIセーフになり、ターボモジュールのコンパイル済み共有ライブラリはどのバージョンのVega OSともバイナリ互換になります。
コンパイルされたネイティブバイナリには、変数、関数、クラス、名前空間などの名前付きエンティティであるシンボルが含まれます。場合によっては、ターボモジュールライブラリ内のシンボルが、アプリ内の他のライブラリやOSのsysrootから生成されたシンボルと衝突することがあります。これにより、シンボルが意図せず公開され、ABIの問題が発生する可能性があります。ターボモジュールの全体的な安定性を確保するために、Amazonではシンボルを隠蔽することを推奨しています。これにより、ライブラリの内部シンボルがデフォルトで隠蔽され、漏洩や衝突を回避できます。
ターボモジュールライブラリの内部シンボルを隠蔽することは、以下の理由で重要です。
- シンボルの動的オーバーライド: 内部シンボルが公開されている場合、プロセスにロードされる順序によっては、異なるバイナリのシンボルが互いにオーバーライドする可能性があります。これにより、意図しない動作、互換性の問題、および潜在的なセキュリティ上の脆弱性が生じる可能性があります。
- 標準C++ライブラリのさまざまなバージョン: 異なるC++標準ライブラリ(libstdc++やlibcppなど)でコンパイルされたバイナリは、シンボルが公開されている場合に互換性の問題を引き起こす可能性があります。
ターボモジュールプロジェクトのシンボルを隠蔽する
Vega SDK v0.9以前で作成されたVegaターボモジュールプロジェクトのシンボルを隠蔽するには、ソースで以下の変更を行います。V0.10以降、Vegaテンプレートではシンボルに関する改善が行われ、すべての新しいプロジェクトでシンボルがデフォルトで隠蔽されています。
-
シンボルマップを追加します。
シンボルマップファイルは、エクスポートされたシンボルにラベル付けされるバージョンノードを指定し、ローカルシンボルを隠蔽するためにも使用されます。Vegaは、ターボモジュールの登録に使用される自動リンク機能からシンボルを選択して公開し、他のすべてのシンボルを隠蔽します。Vegaテンプレートを使用して作成されたターボモジュールプロジェクトの場合、使用される関数は
autoLinkKeplerTurboModulesV1です。# ./sampleturbomodule_symbols.map { global: autoLinkVegaTurboModulesV1; local: *; };注: SymbolVersioningとシンボルマップファイルの詳細については、GNU wikiのSymbolVersioningページ(英語のみ)を参照してください。
-
CMakeLists.txtを更新して、cmakeリンカーオプションでシンボルマップファイルを使用します。# ./CMakeLists.txt ... add_library(SampleTurboModule SHARED ${HEADERS} ${SOURCES}) ... # バージョンスクリプトを適用して、安定したシンボルのみをエクスポートできるようにする。 target_link_options( SampleTurboModule PRIVATE -Wl, --version-script,${CMAKE_CURRENT_SOURCE_DIR}/sampleturbomodule_symbols.map )target_link_optionsコマンドは、リンクプロセス中に指定されたシンボルマップファイルを使用するtmappターゲットに--version-scriptリンカーオプションを適用します。
シンボルが正しく隠蔽されていることを検証する
シンボル隠蔽の効果を検証するため、シンボル隠蔽の変更を適用する前や適用した後に、ターボモジュールバイナリによって公開されたシンボルを調べることができます。Vega SDKに予め用意されているllvm-nmツールを使用すると、ライブラリから公開されているシンボルを表示できます。
Kepler SDKキャッシュフォルダからllvm-nmツールのパスを取得する
❯ find ~/.kepler -name llvm-nm
~/.kepler/conan/p/keple5543e70392195/p/bin/llvm-nm
更新なしでシンボルを一覧表示する
シンボルを隠蔽しない場合、ターボモジュールバイナリは、ターボモジュールの機能の実装に関連するものを含め、多数の内部シンボルを公開します。
❯ ~/.kepler/conan/p/keple5543e70392195/p/bin/llvm-nm -C -D --defined-only build/vega-tv2023-armv7-release/lib/libtmapp.so | grep "tmapp:"
000f55a4 T tmappTurboModule::tmapp::getConstants()
000f57b4 T tmappTurboModule::tmapp::getArrayBuffer(com::amazon::kepler::turbomodule::ArrayBuffer)
000f55f8 T tmappTurboModule::tmapp::getMajorVersion()
000f5600 T tmappTurboModule::tmapp::getMinorVersion()
000f5608 T tmappTurboModule::tmapp::getPatchVersion()
000f5904 T tmappTurboModule::tmapp::getValueWithPromise(bool)
000f58b0 T tmappTurboModule::tmapp::getValueWithCallback(com::amazon::kepler::turbomodule::Callback)
000f5664 T tmappTurboModule::tmapp::getBool(bool)
000f5760 T tmappTurboModule::tmapp::getArray(com::amazon::kepler::turbomodule::JSArray)
000f585c T tmappTurboModule::tmapp::getValue(double, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, com::amazon::kepler::turbomodule::JSObject)
000f5610 T tmappTurboModule::tmapp::voidFunc()
000f56b8 T tmappTurboModule::tmapp::getNumber(double)
000f5808 T tmappTurboModule::tmapp::getObject(com::amazon::kepler::turbomodule::JSObject)
000f570c T tmappTurboModule::tmapp::getString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>)
000f5568 T tmappTurboModule::tmapp::tmapp()
000f5568 T tmappTurboModule::tmapp::tmapp()
000f558c T tmappTurboModule::tmapp::~tmapp()
000f5588 T tmappTurboModule::tmapp::~tmapp()
000f5588 T tmappTurboModule::tmapp::~tmapp()
シンボル隠蔽の更新後にシンボルを一覧表示する
シンボル隠蔽を実装すると、ターボモジュールバイナリは、目的にかなう安定したパブリックAPIシンボルのみを公開します。
❯ ~/.kepler/conan/p/keple5543e70392195/p/bin/llvm-nm -C -D --defined-only build/vega-tv2023-armv7-release/lib/libtmapp.so
000757e8 T autoLinkVegaTurboModulesV1
スレッド
すべてのターボモジュールメソッドはJSThreadで呼び出されます。良好なパフォーマンスを維持するには、PromiseやCallback向けなどの用途にはJSThread以外の別のスレッドを適宜使用して、JSThreadをブロックしないようにすることが重要です。
特定の要件に応じて、スレッドユーティリティライブラリまたはstd:: threadを直接使用して、ターボモジュール用にJSThread以外の別のスレッドを作成して管理できます。
イベントハンドリング
ターボモジュールはC++からJavaScriptにイベントを送信できますが、@amzn /keplerscript-turbomodule-apiパッケージには現在、これらのイベントを処理するためのサポートが含まれていません。イベントをリッスンするには、react-nativeのNativeEventEmitterを使用します。
ターボモジュールは、組み込みのemitメソッドを使用してJavaScriptにイベントを通知できます。このメソッドは、ほとんどのターボモジュールのC++型をペイロードとして受け入れます。JSThreadを使用している場合(つまり、イベントを発生させるための新しいスレッドが作成されなかった場合)は、代わりにemitSyncを使用する必要があります。
void SampleTurboModule::testEmit(std::string eventName) {
std::thread([self = this, eventName]() {
JSObject payload{};
payload["StringValue"] = "JSObjectのstringMessage";
payload["intValue"] = 123;
payload["doubleValue"] = 45.67;
self->emit(eventName, payload);
}).detach();
}
JavaScript側では、NativeEventEmitterを使用してイベントを受信するように登録できます。次の例は、JavaScriptレイヤーの実装に記載されている手順を基にしたものです。
import { NativeEventEmitter } from 'react-native';
import PingerModule from './NativePinger';
class Pinger {
__eventEmitter: NativeEventEmitter = null;
constructor() {
this.__eventEmitter = new NativeEventEmitter();
}
testEmit: (key: string) => {
const eventName = 'SampleEvent';
const subscription = this.__eventEmitter.addListener(
eventName,
(args) => {
console.log('From JS: addListener ', args);
}
);
SampleTurboModule.testEmit(eventName); // これにより「SampleEvent」が発行され、上記のサブスクリプションがトリガーされます
subscription.remove(); // サブスクリプションは必ずクリーンアップしてください
}
...
}
export default new Pinger();
エラー処理
ターボモジュールを実行しているときに発生する可能性のある例外の多くは、JavaScriptレイヤーにスローされます。ネイティブモジュールにラップされているJavaScript実装を使用している場合は、try/catchを使用してこれらのエラーに対処することができます。
関連トピック
Last updated: 2025年9月30日

