modulePreConfigure.nix switches the `--backend ui` invocation to
logos-view-generator (logos-view-module), which now owns that emitter and
the logos_ui_plugin_context.h it pairs with.
THE REASON THIS IS NOT COSMETIC. This builder pins logos-qt-sdk at 4a1104c,
which predates b7b82e5 — the commit that added the module teardown hook to
the ui emitter. At 4a1104c BOTH halves lack it, so every build has been
green and self-consistent, and the hook has NEVER REACHED A SINGLE SHIPPED
ui_qml MODULE. Measured on the same module, before and after, by loading the
built plugin with QPluginLoader and dumping its QMetaObject:
BASELINE MIGRATED
initLogos(LogosAPI*) unloadFinished() [signal]
initLogos(LogosAPI*)
int aboutToUnload() [invokable]
So this delivers the hook for the first time rather than preserving it.
LOGOS_VIEW_INCLUDE_DIR is added BEFORE the qt-sdk root in
LogosModule.cmake, so the emitter and its header resolve from ONE pin. That
ordering is load-bearing until logos-qt-sdk stops installing its copy of
logos_ui_plugin_context.h — anything going through logos_module() is safe;
a hand-run cmake putting LOGOS_QT_SDK_ROOT first would silently get the
wrong header. Called out in the code rather than left implicit.
Blast radius is 7 modules, not one: package_manager_ui, chat_ui, wallet_ui,
test_uiqml_probe, test_fullapi_ui, calc_ui_cpp and templates/ui-qml-backend
all match ui_qml + interface:universal. None override aboutToUnload(), and
the base default is Synchronous, so every one answers 0 and no host waits —
the change is additive. Only package_manager_ui was built end to end.
Three assertions in tests/test-module-pre-configure.nix pin the binary
choice; repointing at logos-qt-generator fails them. Also corrects a comment
there that claimed "the ui backend still legitimately uses qt-sdk's".
7/7 checks green, 399 unit tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12 KiB
CMake Reference
Complete reference for LogosModule.cmake functions and options.
Overview
LogosModule.cmake is a CMake module that handles all the boilerplate for building Logos plugins. It provides:
- Automatic SDK and liblogos detection
- Qt6/Qt5 finding and configuration
- Code generation setup
- External library handling
- Platform-specific RPATH configuration
- Install targets
Including LogosModule.cmake
# Method 1: Via environment variable (recommended for nix builds)
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
# Method 2: Local copy
include(cmake/LogosModule.cmake)
# Method 3: Vendor directory
include(vendor/logos-module-builder/cmake/LogosModule.cmake)
logos_module()
The main function to define a Logos module.
Syntax
logos_module(
NAME <module_name>
SOURCES <source_files>...
[REP_FILE <rep_file>]
[QML_URI <uri>]
[QML_TYPE_NAME <type_name>]
[INCLUDE_DIRS <dirs>...]
[EXTERNAL_LIBS <library_names>...]
[FIND_PACKAGES <package_names>...]
[LINK_LIBRARIES <library_names>...]
[LINK_TARGETS <target_names>...]
[AUTOGEN_DEPENDS <target_names>...]
)
Parameters
NAME (required)
The module name. Used for:
- Output filename:
{NAME}_plugin.so/{NAME}_plugin.dylib - CMake target name:
{NAME}_module_plugin
logos_module(
NAME my_module
...
)
SOURCES (required)
List of source files for the module. In the universal authoring model you list
only your impl (or backend) sources — the generated glue is picked up from
generated_code/ automatically (see below) and must not be listed. For a
core module that glue is {name}_cdylib_glue.{h,cpp} (the Qt plugin) plus
{name}_module_impl.cpp / {name}_types.h (the Qt-free C ABI around your impl);
the older {name}_interface.h + {name}_plugin.{h,cpp} file names are no longer
emitted.
For a core module, this is the impl class:
src/{name}_impl.h- Impl class declaration (public methods = API)src/{name}_impl.cpp- Impl class implementation
plus any extra helpers you add:
logos_module(
NAME my_module
SOURCES
src/my_module_impl.h
src/my_module_impl.cpp
src/helper.cpp
src/utils.cpp
)
Classic modules used to omit
"interface"frommetadata.jsonand list the hand-writtensrc/{name}_interface.h,src/{name}_plugin.handsrc/{name}_plugin.cpphere. Acoremodule that ships a plugin (declaresmain) and names nointerfaceis now refused at evaluation — no glue would be generated and every call into it would fail at runtime instead of at build time. Use"universal", or"cdylib"if you bring your own C ABI. Legacytype: "ui"widget modules still build this way.
REP_FILE (optional)
Path to a .rep Qt Remote Objects contract for a universal C++ UI backend
("type": "ui_qml" + "interface": "universal"). repc is run on it and the
generated source (rep_<name>_source.h) is made available to your *Backend
class. Pair it with INCLUDE_DIRS src so the generated header resolves.
logos_module(
NAME my_ui
REP_FILE src/my_ui.rep
SOURCES
src/my_ui_backend.h
src/my_ui_backend.cpp
INCLUDE_DIRS
src
)
INCLUDE_DIRS (optional)
Additional include directories added to the plugin target. Commonly src for
universal UI backends so the generated rep_*_source.h is found.
logos_module(
NAME my_module
SOURCES ...
INCLUDE_DIRS
src
vendor/include
)
EXTERNAL_LIBS (optional)
External libraries to link. Libraries are searched in lib/ directory.
logos_module(
NAME my_module
SOURCES ...
EXTERNAL_LIBS
libfoo
libbar
)
The function will:
- Search for
lib/libfoo.soorlib/libfoo.dylib - Add
lib/to include directories - Link the library
- Copy the library to the output directory
- Fix install names on macOS
FIND_PACKAGES (optional)
CMake packages to find via find_package().
logos_module(
NAME my_module
SOURCES ...
FIND_PACKAGES
Protobuf
Threads
ZLIB
)
LINK_LIBRARIES (optional)
Additional libraries to link (after find_package).
logos_module(
NAME my_module
SOURCES ...
FIND_PACKAGES Threads
LINK_LIBRARIES
Threads::Threads
${ZLIB_LIBRARIES}
)
generated_code/ (automatic)
If generated_code/ exists next to CMakeLists.txt, all *.cpp and *.h files there are added to the plugin target, except logos_sdk.cpp and every per-dependency *_api.cpp — those are #included by logos_sdk.cpp rather than compiled separately. You do not need to list glue or dispatch sources manually. (core_manager_api.cpp used to be excluded by name; it is not generated at all any more — a universal module exposes only its declared dependencies, and an app that must manage the core uses liblogos' C API directly.)
metadata.json (automatic)
metadata.json is copied to CMAKE_CURRENT_BINARY_DIR so Q_PLUGIN_METADATA can resolve it during the build.
Go static archives (CMake cache variable)
When mkLogosModule passes -DLOGOS_MODULE_GO_STATIC_LIBS=name1;name2 (from go_build: true entries in metadata.json), LogosModule.cmake finds lib/lib<name>.a under lib/, links with whole-archive (Linux) or -force_load (macOS), and adds CoreFoundation/Security frameworks on Apple platforms.
LINK_TARGETS (optional)
CMake targets to link directly, as opposed to LINK_LIBRARIES, which takes
names resolved after find_package. Use this for a target you define yourself
in the same CMakeLists.txt — e.g. a protobuf library you build. Each target
must already be defined when logos_module() runs; an undefined one is a
FATAL_ERROR rather than a silently dropped link.
AUTOGEN_DEPENDS (optional)
Sets AUTOGEN_TARGET_DEPENDS on the plugin target, so AUTOMOC waits for the
named targets. Needed when something in LINK_TARGETS generates headers the
plugin's own sources include.
PROTO_FILESno longer exists.logos_module()used to accept it and runfind_package(Protobuf)+protocfor you. It is not among the keywordslogos_module()parses today, so passing it is silently ignored. Compile.protofiles in your ownCMakeLists.txt(declareprotobufundernix.packages.buildandProtobufundernix.cmake.find_packages, then add the generated sources withnix.cmake.extra_sources/LINK_LIBRARIES).
Helper Functions
logos_find_dependencies()
Find and configure the Logos SDK and logos-module.
logos_find_dependencies()
Sets variables:
LOGOS_MODULE_ROOT- Path to logos-module (this is the plugininterface.hroot; there is noLOGOS_LIBLOGOS_ROOT— the plugin never links liblogos)LOGOS_CPP_SDK_ROOT- Path to logos-cpp-sdkLOGOS_MODULE_IS_SOURCE- TRUE if source layoutLOGOS_CPP_SDK_IS_SOURCE- TRUE if source layoutLOGOS_QT_HOST_ROOT- Path the Qt host runtime is taken fromLOGOS_QT_HOST_IS_SOURCE- TRUE if that root is a repo checkoutLOGOS_QT_HOST_PACKAGE/LOGOS_QT_HOST_TARGET- the CMake package and imported target the plugin links for the host runtime
The Qt host runtime — LogosAPI, LogosAPIProvider, LogosProviderBase and
the legacy PluginInterface — lives in logos-plugin-qt and ships as the
logos-qt-host package. Point LOGOS_QT_HOST_ROOT at it (nix builds do).
logos-qt-sdk still forwards the same code, so a build that supplies only
LOGOS_QT_SDK_ROOT keeps working, with a message saying it took the legacy
package; a build that supplies neither is a FATAL_ERROR. LOGOS_QT_SDK_ROOT
stays required regardless — it is where logos_qt_lp_bridge.h and
logos_qt_wire.h come from.
logos_ui_plugin_context.h comes from LOGOS_VIEW_INCLUDE_DIR
(logos-view-module), which is placed on the include path AHEAD of
LOGOS_QT_SDK_ROOT. That header and the view glue emitter are one matched pair
— the emitted glue calls maybeUiPluginAboutToUnload(), which only that header
declares — so both ship from one pin. logos-qt-sdk may still install an older
copy of the same header name; the ordering is what keeps it from being found.
logos_find_qt()
Find Qt6 (or Qt5 fallback) with required components.
logos_find_qt()
Sets:
QT_VERSION_MAJOR- 5 or 6
Environment Variables
LOGOS_MODULE_BUILDER_ROOT
Path to logos-module-builder. Set automatically by nix builds.
export LOGOS_MODULE_BUILDER_ROOT=/path/to/logos-module-builder
LOGOS_CPP_SDK_ROOT
Override path to logos-cpp-sdk.
export LOGOS_CPP_SDK_ROOT=/path/to/logos-cpp-sdk
LOGOS_MODULE_ROOT
Override path to logos-module (source checkout or installed prefix).
export LOGOS_MODULE_ROOT=/path/to/logos-module
LOGOS_QT_SDK_ROOT
Override path to logos-qt-sdk. Required — see logos_find_dependencies() above.
LOGOS_PROTOCOL_ROOT
Override path to logos-protocol (transports + the lp_* C ABI).
LOGOS_QT_HOST_ROOT
Path to the Qt host runtime — an installed logos-qt-host prefix, or a
logos-plugin-qt checkout.
export LOGOS_QT_HOST_ROOT=/path/to/logos-plugin-qt
LOGOS_VIEW_TEMPLATE_DIR
Directory holding the four LogosView*.in templates that REP_FILE
instantiates. Required when — and only when — logos_module() is given a
REP_FILE; logos_module() hard-errors rather than guessing.
The templates are owned by logos-view-module (cmake/), not by this repo,
even though LogosModule.cmake is what instantiates them: that repo owns the
whole ui_qml authoring flavour (LogosViewModule.cmake, the view glue
generator, and the rep-file-plugin fixture that instantiates the templates
and proves the built plugin still loads and casts). It is a leaf — its only
input is logos-nix — so every consumer can read one copy from it, which is
the property logos-plugin-qt did not have. See
logos-view-module/cmake/README.md.
Set automatically by this repo's nix builds and module dev shells —
lib/mkLogosModule.nix and lib/buildCppPlugin.nix pass both the cache
variable and the environment variable. Also accepted as a CMake cache variable
(-DLOGOS_VIEW_TEMPLATE_DIR=...), which takes precedence.
export LOGOS_VIEW_TEMPLATE_DIR=/path/to/logos-view-module/cmake
Generated Targets
For a module named my_module, the following are created:
| Target | Description |
|---|---|
my_module_module_plugin |
Main library target |
run_cpp_generator_my_module |
Code generation target (source layout) |
my_module_replica_factory |
QML replica-factory plugin (only if REP_FILE) |
Output Files
build/
└── modules/
├── my_module_plugin.so # or .dylib
├── libfoo.so # external libs copied here
└── ...
Complete Example
cmake_minimum_required(VERSION 3.14)
project(ChatModulePlugin LANGUAGES CXX)
# Include the helper
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
# Define the module (universal model: list only the impl + helpers)
logos_module(
NAME chat
SOURCES
src/chat_impl.h
src/chat_impl.cpp
src/chat_api.cpp
src/chat_api.h
FIND_PACKAGES
Protobuf
Threads
LINK_LIBRARIES
absl::base
absl::strings
)
Customization
For advanced customization, you can use the helper functions directly:
cmake_minimum_required(VERSION 3.14)
project(CustomModulePlugin LANGUAGES CXX)
# Include helpers
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
# Find dependencies manually
logos_find_dependencies()
logos_find_qt()
# Create library manually
add_library(my_plugin SHARED
my_plugin.cpp
# ... more sources
)
# Custom configuration
target_compile_definitions(my_plugin PRIVATE MY_CUSTOM_DEFINE)
target_include_directories(my_plugin PRIVATE ${CUSTOM_INCLUDE_DIR})
# Link Qt (required)
target_link_libraries(my_plugin PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::RemoteObjects
)