mirror of
https://github.com/logos-co/logos-module-builder.git
synced 2026-08-27 10:11:10 +00:00
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>
392 lines
12 KiB
Markdown
392 lines
12 KiB
Markdown
# 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
|
|
|
|
```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
|
|
|
|
```cmake
|
|
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`
|
|
|
|
```cmake
|
|
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:
|
|
|
|
```cmake
|
|
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"` from `metadata.json` and list the
|
|
> hand-written `src/{name}_interface.h`, `src/{name}_plugin.h` and
|
|
> `src/{name}_plugin.cpp` here. A `core` module that ships a plugin (declares
|
|
> `main`) and names no `interface` is 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.
|
|
> Legacy `type: "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.
|
|
|
|
```cmake
|
|
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.
|
|
|
|
```cmake
|
|
logos_module(
|
|
NAME my_module
|
|
SOURCES ...
|
|
INCLUDE_DIRS
|
|
src
|
|
vendor/include
|
|
)
|
|
```
|
|
|
|
#### EXTERNAL_LIBS (optional)
|
|
External libraries to link. Libraries are searched in `lib/` directory.
|
|
|
|
```cmake
|
|
logos_module(
|
|
NAME my_module
|
|
SOURCES ...
|
|
EXTERNAL_LIBS
|
|
libfoo
|
|
libbar
|
|
)
|
|
```
|
|
|
|
The function will:
|
|
1. Search for `lib/libfoo.so` or `lib/libfoo.dylib`
|
|
2. Add `lib/` to include directories
|
|
3. Link the library
|
|
4. Copy the library to the output directory
|
|
5. Fix install names on macOS
|
|
|
|
#### FIND_PACKAGES (optional)
|
|
CMake packages to find via `find_package()`.
|
|
|
|
```cmake
|
|
logos_module(
|
|
NAME my_module
|
|
SOURCES ...
|
|
FIND_PACKAGES
|
|
Protobuf
|
|
Threads
|
|
ZLIB
|
|
)
|
|
```
|
|
|
|
#### LINK_LIBRARIES (optional)
|
|
Additional libraries to link (after find_package).
|
|
|
|
```cmake
|
|
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 `#include`d 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_FILES` no longer exists.** `logos_module()` used to accept it and run
|
|
> `find_package(Protobuf)` + `protoc` for you. It is not among the keywords
|
|
> `logos_module()` parses today, so passing it is silently ignored. Compile
|
|
> `.proto` files in your own `CMakeLists.txt` (declare `protobuf` under
|
|
> `nix.packages.build` and `Protobuf` under `nix.cmake.find_packages`, then add
|
|
> the generated sources with `nix.cmake.extra_sources` / `LINK_LIBRARIES`).
|
|
|
|
## Helper Functions
|
|
|
|
### logos_find_dependencies()
|
|
|
|
Find and configure the Logos SDK and logos-module.
|
|
|
|
```cmake
|
|
logos_find_dependencies()
|
|
```
|
|
|
|
Sets variables:
|
|
- `LOGOS_MODULE_ROOT` - Path to logos-module (this is the plugin `interface.h`
|
|
root; there is no `LOGOS_LIBLOGOS_ROOT` — the plugin never links liblogos)
|
|
- `LOGOS_CPP_SDK_ROOT` - Path to logos-cpp-sdk
|
|
- `LOGOS_MODULE_IS_SOURCE` - TRUE if source layout
|
|
- `LOGOS_CPP_SDK_IS_SOURCE` - TRUE if source layout
|
|
- `LOGOS_QT_HOST_ROOT` - Path the Qt host runtime is taken from
|
|
- `LOGOS_QT_HOST_IS_SOURCE` - TRUE if that root is a repo checkout
|
|
- `LOGOS_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.
|
|
|
|
```cmake
|
|
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.
|
|
|
|
```bash
|
|
export LOGOS_MODULE_BUILDER_ROOT=/path/to/logos-module-builder
|
|
```
|
|
|
|
### LOGOS_CPP_SDK_ROOT
|
|
Override path to logos-cpp-sdk.
|
|
|
|
```bash
|
|
export LOGOS_CPP_SDK_ROOT=/path/to/logos-cpp-sdk
|
|
```
|
|
|
|
### LOGOS_MODULE_ROOT
|
|
Override path to logos-module (source checkout or installed prefix).
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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
|
|
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
|
|
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
|
|
)
|
|
```
|