Depend on another module¶
Plugins and subsystems expose C APIs to each other. Using one takes three steps: declare the dependency in the manifest, import its API in C++, and check that the import succeeded.
1. Declare the dependency¶
Add it to your manifest's info.dependencies:
{
"info": {
"id": {
"name": "mycorp.myplugin",
"version": "0.1.0"
},
"display_name": "My Plugin",
"dependencies": [
{
"name": "nos.sys.vulkan",
"version": "8.0"
}
]
},
"binary_path": "Binaries/nosMyPlugin",
"sdk_version": "41.0"
}
Or let the CLI write it:
The version is a minimum within a minor line, matching
nodos install semantics. The toolchain resolves and
fetches it during nodos dev gen, and the engine resolves it again at load time — a plugin whose
hard dependencies cannot be satisfied does not load.
2. Import the API¶
Nodos 1.4
Dependencies go in the NOS_DEPENDENCIES list in PluginMain.cpp. Each entry is the
dependency's *_NAME-style macro, defined in its public header:
#include <Nodos/Plugin.hpp>
#include <mycorpMyplugin/mycorpMyplugin.h>
#include <nosSysVulkan/nosVulkanSubsystem.h>
#define NOS_NODES \
NOS_NODE(Blur)
#define NOS_DEPENDENCIES \
NOS_DEPENDENCY(NOS_VULKAN)
#define NOS_NAMESPACE mycorp::myplugin
#include <Nodos/PluginMain.inl>
PluginMain.inl expands each entry twice: once into NOS_VULKAN_INIT(), which defines the
global API pointer, and once into NOS_VULKAN_IMPORT() inside nosImportDependencies. After
that the dependency's global — nosVulkan here — is usable from any translation unit that
includes its header.
The token you pass to NOS_DEPENDENCY is the prefix of that pair, so check the dependency's
public header for its *_INIT() / *_IMPORT() macros. For nos.sys.vulkan they are
NOS_VULKAN_INIT() and NOS_VULKAN_IMPORT(), hence NOS_DEPENDENCY(NOS_VULKAN).
Doing it without PluginMain.inl
Plugins that predate the macro list, or that need finer control, write the same thing out by hand:
This is what several of the shipped plugins still do; both forms are supported.
3. Check availability¶
A declared dependency is resolved before your plugin loads, so by the time your nodes run the API
pointer is valid. But when you request a subsystem by hand with nosEngine.RequestSubsystem — for
an optional dependency, say — the request can fail because the requested version is not installed.
Always check:
if (ret != NOS_RESULT_SUCCESS)
{
nosEngine.LogE("nos.sys.vulkan is not available at the required version");
return ret;
}
Failing loudly here is much better than a null dereference three frames into execution.
Sharing dependencies across several plugins¶
When several plugins live under one directory tree and share dependencies, add a
NosPluginCommon.cmake to the common parent directory and define:
function(nos_plugin_common dir out_deps out_defs)
set(${out_deps} SomeSharedTarget PARENT_SCOPE)
set(${out_defs} MY_SHARED_DEFINE=1 PARENT_SCOPE)
endfunction()
The toolchain calls it once per directory while scanning and applies the results to every plugin
found beneath it. out_deps are extra CMake targets to link; out_defs are preprocessor
definitions.
You can also define nos_plugin_on_post_target_generated(target name) in the same file to run
logic after each target is created.
Extending a generated target¶
If one plugin needs extra sources, include paths or link libraries, add a CMakeLists.txt next to
its .nosplugin. The toolchain sets NOS_PLUGIN_TARGET and includes your file after creating the
target:
target_include_directories(${NOS_PLUGIN_TARGET} PRIVATE External/include)
target_link_libraries(${NOS_PLUGIN_TARGET} PRIVATE SomeThirdParty)
This is additive. You are extending a target the toolchain already made, not defining your own.
Exposing your own API¶
To let other modules depend on you, fill in the API struct in your public header — the scaffolded
Include/<CamelCase>/<CamelCase>.h — and populate it from the OnRequestAPI callback in your
plugin functions. The *_NAME, *_INIT() and *_IMPORT() macros that consumers need are already
in that header; the scaffold generates them.