Advanced Turbo Module topics
This topic provides additional information that is useful in when creating complex Turbo Modules.
Turbo Module CMAKE helper
The kepler_add_turbo_module_library function is a CMake helper that simplifies the process of defining a Turbo Module library target for use with the React Native for Vega runtime. It handles common configuration and optimization settings required for Turbo Module libraries, streamlining the process by automatically applying the necessary settings and optimizations.
This helper function provides the following benefits:
- Simplified Configuration: Reduces boilerplate CMake code with a single function call
- Consistent Best Practices: Makes sure that all Turbo Modules follow the same configuration patterns and provides a central place to roll out common settings in the future
- Improved Application Binary Interface (ABI) Stability: Applies proper symbol visibility settings automatically
- Optimized Performance: Makes sure that performance optimizations such as link-time and size optimizations are applied
Key symbol contracts for Turbo Module DSO
All TurboModules on Vega platforms must adhere to two critical requirements:
- Export EntryPoint function: The Turbo Module library must export a single symbol named
autoLinkVegaTurboModulesV1. This symbol serves as the entry point that the Vega runtime uses to automatically link and load your Turbo Module. - Hidden Symbol Visibility: All other symbols in the Turbo Module library should be hidden. This prevents symbol conflicts with other libraries, ensures a stable ABI, improves TurboModule loading time, and can reduce the size of the library in many cases.
The kepler_add_turbo_module_library helper automatically configures these requirements for you, eliminating the need for manual symbol visibility management.
Functionality provided
The kepler_add_turbo_module_library function provides several key features that simplify the creation of Turbo Module libraries. These features are organized into three main categories.
Shared library creation
The helper function provides support for shared libraries as follows:
- Creates a shared library with the specified name using the standard CMake
add_librarycommand. - Links the
turbomoduleAPIstatic library, which provides the C++ methods to develop Turbo Modules, and the necessary platform-level functionality, to the library. - Handles the proper setup of the library target, similar to what you would do manually with
add_libraryandtarget_link_libraries.
Symbol visibility control
The helper function automatically configures symbol visibility to ensure ABI stability:
- Sets default visibility to "hidden" for all symbols
- Applies a version script that explicitly exports only the
autoLinkVegaTurboModulesV1symbol - Disables symbol interposition with
-fno-semantic-interpositionand applies local binding of global references with-Bsymbolicto prevent symbol conflicts
Performance optimizations
The helper function applies several optimizations to improve the performance and size of your Turbo Module:
- Enables link-time optimization (LTO) for better performance and smaller binary size
- Applies size optimizations by applying garbage collection of unused sections
Usage
To use the kepler_add_turbo_module_library function, add the following to your CMakeLists.txt.
## TMAPI package required to use `kepler_add_turbo_module_library`
find_package(turbomoduleAPI CONFIG REQUIRED)
## Define source files
set(SOURCES
AutoLinkInit.cpp
MyTurboModule.cpp
)
## Create the TurboModule library
kepler_add_turbo_module_library(MyTurboModule ${SOURCES})
Frequently Asked Questions
Q: Should I migrate my existing TurboModule to use this helper function?
A: Yes. If you have an existing Turbo Module, you are strongly recommended to migrate to this helper function. The current approach of propagating optimization flags as INTERFACE flags via the turbomoduleAPI library will be removed in the future. This helper function is part of a broader strategy to provide more explicit and maintainable configuration for Turbo Modules while avoiding implicit propagation of build settings that can lead to surprising behavior.
Q: How do I migrate my existing TurboModule to use this helper function?
A: Follow these steps to migrate:
- Review your current CMakeLists.txt to understand how your TurboModule is configured
- Make sure that you have the
find_package(turbomoduleAPI CONFIG REQUIRED)line in your CMakeLists.txt - Replace your
add_librarycall and all subsequent configuration with a singlekepler_add_turbo_module_librarycall - Delete the version-script (.map file) previously used for managing the
autoLinkVegaTurboModulesV1export - Test your build to ensure everything works correctly
The following examples show the CMake configuration before and after migration.
Before (manual configuration)
## Find the turbomoduleAPI package
find_package(turbomoduleAPI CONFIG REQUIRED)
## Define source files
set(SOURCES
AutoLinkInit.cpp
MyTurboModule.cpp
)
## Create the shared library
add_library(MyTurboModule ${SOURCES})
## Link to the turbomoduleAPI library
target_link_libraries(MyTurboModule PRIVATE turbomoduleAPI::turbomoduleAPI)
## Apply version script to allow export of stable symbols only.
target_link_options(MyTurboModule PRIVATE -Wl,--version-script,${CMAKE_CURRENT_SOURCE_DIR}/MyTurboModule_symbols.map)
After (using the helper)
## Find the turbomoduleAPI package
find_package(turbomoduleAPI CONFIG REQUIRED)
## Define source files
set(SOURCES
AutoLinkInit.cpp
MyTurboModule.cpp
)
## Create the TurboModule library with all optimizations applied
kepler_add_turbo_module_library(MyTurboModule ${SOURCES})
Q: How do I handle version scripts with this helper function? Can I export additional symbols beyond autoLinkVegaTurboModulesV1?
A: The helper function automatically applies a version script that exports only the autoLinkVegaTurboModulesV1 symbol. If you're already using a version script, remove your existing version script applications from your CMake configuration to avoid conflicts. If you need to export additional symbols beyond autoLinkVegaTurboModulesV1, you can apply another version script after calling the helper function, but ensure there's no overlap between the symbols exported by each script. This approach should be used sparingly, as exporting additional symbols may compromise ABI stability.
Q: How do I debug issues with the kepler_add_turbo_module_library function?
A: You can check the generated compilation database (compile_commands.json found in build directory) to inspect the compiler and linker flags being used. The helper function enables EXPORT_COMPILE_COMMANDS ON by default, which creates this database. You can open the database with IDEs to see the exact flags applied.
Q: How can I add additional target behavior?
A: The helper function already handles platform-specific behavior. If you need additional settings, you can add them after calling the helper function:
kepler_add_turbo_module_library(MyTurboModule ${SOURCES})
target_compile_definitions(MyTurboModule PRIVATE MY_CUSTOM_DEFINE=1)
Symbol inspection
To get the llvm-nm tool path from the Vega SDK, use the following:
> find "$(vega native toolchain aarch64)" -name llvm-nm
~/vega/sdk/vega-sdk/main/0.23.5229/workspace/App/NativeRuntime/build/private/toolchain/aarch64/mac/bin/llvm-nm
List symbols with this command:
> ~/vega/sdk/vega-sdk/main/0.23.5229/workspace/App/NativeRuntime/build/private/toolchain/aarch64/mac/bin/llvm-nm -C -D --defined-only build/aarch64-release/lib/libMyTurboModule.so
00000000000cf408 T autoLinkKeplerTurboModulesV1
Threading
All Turbo Module methods are invoked on the JSThread. To maintain good performance, it is crucial to avoid blocking the JSThread, by using a separate non-JSThread wherever it is beneficial, such as for Promise or Callback usages.
You can use a thread utility library or even std::thread directly to create and manage a separate non-JSThread for your Turbo Module, depending on your specific requirements.
Event handling
Turbo Modules can send events from C++ to JavaScript, but the @amzn/keplerscript-turbomodule-api package currently does not include support for handling these events. To listen for events, you can use NativeEventEmitter from react-native.
Turbo Modules can signal events to JavaScript by using the built-in emit method, which accepts most Turbo Module C++ types as a payload. When on JSThread (i.e., no new thread was created for emitting events), you should use emitSync instead.
void SampleTurboModule::testEmit(std::string eventName) {
std::thread([self = this, eventName]() {
JSObject payload{};
payload["StringValue"] = "stringMessage with JSObject";
payload["intValue"] = 123;
payload["doubleValue"] = 45.67;
self->emit(eventName, payload);
}).detach();
}
The JavaScript side can register to receive events using NativeEventEmitter. The following example builds on the instructions found in Implement JavaScript Layer.
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); // This emits a 'SampleEvent', which triggers above subscription
subscription.remove(); // Make sure to clean up your subscriptions
}
...
}
export default new Pinger();
Error handling
Many of the exceptions which might be encountered when running a Turbo Module are thrown into the JavaScript layer. If you have a JavaScript implementation which is wrapped over the native module, you can use a try/catch to address these errors there.
Related topics
Last updated: Dec 10, 2025

