MODULE, not SHARED
add_library( Circle MODULE Circle.cpp )
set_target_properties( Circle PROPERTIES
PREFIX "lib"
POSITION_INDEPENDENT_CODE ON )
target_link_libraries( Circle PRIVATE plugin )
CMake's MODULE library is a shared object that is only ever dlopened, never linked at build time. That is exactly a plugin. SHARED is for libraries you also link against — the host's Shape support library is SHARED (or STATIC), the plugins are MODULE.
Set PREFIX "lib" if the host sets DSOLoader::prefix("lib") and uses loadAll.
The plugin SDK
Ship these to a plugin author, and nothing else:
- the base class header(s) —
Shape.hh - any
Configstructs (often in the same header) plugin/PluginRegister.hh,plugin/Plugin.hh,plugin/PluginCatalog.hh,plugin/PluginCreator.hh— installed by theplugincomponent to<prefix>/include/plugin/dso/DSOVisibility.hh— pulled in by the plugin headers
The plugin does not link dsold and does not define CREATECATALOG — both belong to the host.
Versioning the ABI
The plugin boundary is a C++ vtable — it is only safe while the base class layout matches on both sides. Two mitigations:
- A
SOVERSIONon the host's support library, bumped on any change to a base class. Plugins built against the old header fail to load against the new soname. - The signed-manifest
abiMajor/abiMinorfields.dso-sign --abi-major N --abi-minor Mstamps them; a host can readVerifyResult::data.abiMajorand refuse a mismatch. (Wiring an ABI floor directly intoloadVerifiedis on the roadmap; today the host checks the field itself.)
Keep base classes small and pure-virtual — no data members, no inline non-virtual methods that could change — and this is a rare event.
Installing into a plugin directory
install( TARGETS Circle Square Ellipse
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/myapp/plugins )
The host then does:
DSOLoader::prefix( "lib" );
DSOLoader::loadAll( ".so", CMAKE_INSTALL_FULL_LIBDIR "/myapp/plugins" );
or reads a path from config / an environment variable via loadAllByEnvironment.
Shipping signed add-ons
If the host uses loadVerified, the add-on package must include the signature. dso-sign modifies the .so in place, so sign as a post-build step:
find_program( DSO_SIGN dso-sign )
if( DSO_SIGN AND DEFINED PLUGIN_SIGNING_KEY )
add_custom_command( TARGET Circle POST_BUILD
COMMAND ${DSO_SIGN} $<TARGET_FILE:Circle>
--key ${PLUGIN_SIGNING_KEY}
--name circle-plugin
--version ${PROJECT_VERSION} )
endif()
Distribute the signer's *.pub (or its fingerprint) through a channel the operator already trusts — the same package repository, a documentation page — so they can populate their trusted-keys directory.
CPack for the host + plugins
plugin's own build splits into plugin (runtime: libdsold.so), plugin_development (headers, plugin-sign.a, plugin.pc) and plugin_apps (the three tools + completions). Mirror that split in your application so a deployment that only runs plugins does not ship the SDK headers.
Next: FAQ.

