The `--backend ui` emitter existed twice: live in logos-qt-sdk's
qt-generator, and as an uncalled successor here. They were byte-identical
until logos-qt-sdk#38 gave the live one the module teardown hook, and this
copy has been ~45 lines behind ever since.
That drift is dangerous in a specific way. ui-host reaches aboutToUnload()
BY NAME through the meta-object; a plugin class that does not declare it has
no such meta-method, invokeMethod returns false, and the host moves on —
INDISTINGUISHABLE from a plugin answering "Synchronous, nothing to wait
for". Had the migration completed with this copy, every view would have
silently and permanently lost its chance to finish, and nothing would have
failed: not a build, not a load, not a call.
This repo is the right home. The emitter needs nothing from logos-qt-sdk —
`--backend ui` is the one qt-generator backend that never reads LIDL, and
its only qt-sdk symbol was lidlToPascalCase, which this copy already
inlines. The LogosView*.in templates the generated glue compiles against
already live here. And the backend owning its glue is the same rule that
puts the cdylib glue generator in logos-plugin-qt.
logos_ui_plugin_context.h HAD TO COME TOO, which the plan did not
anticipate. It was duplicated as well, and stale by the same changeset. The
emitter and that header are a matched pair — the emitter writes a call, the
header declares what it calls — and they stayed consistent only because one
pin versioned both. Moving the emitter alone fails concretely:
`no member named 'maybeUiPluginAboutToUnload' in namespace '_logos_codegen_'`.
The emitter is taken VERBATIM: the .cpp differs from the live copy only in
three generator-name banners, and the .h and the context header are
byte-identical to it. Every difference in the successor was it being behind
or vaguer — two were outright regressions (the .h named LogosModuleContext,
the exact confusion the live comment warns against, and the .cpp described
emitted files by names neither main uses).
main.cpp accepts `--backend ui` and refuses anything else, converging with
the in-flight qml-dep branch rather than colliding with it.
TESTS CARRIED ACROSS, because the old home's blind spot is the whole story:
`--backend ui` had NO test at all, which is how it became the one generated
plugin class without the hook. qt-sdk's five text assertions come over, plus
a new behavioural check: tests/ui-plugin-metaobject compiles the emitted
plugin against the REAL context header and REAL templates, loads it with
QPluginLoader, and dumps the meta-object —
[4] SIGNAL unloadFinished() [5] METHOD initLogos [6] METHOD aboutToUnload -> int
Negative control: origin/master's stale emitter compiles and loads fine and
the check still fails with both named diagnostics. The silent gap, made loud.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LogosView*.in — the view-plugin templates
These four files are configure_file templates instantiated per module by
logos_replica_factory() (this directory's LogosViewModule.cmake) and by
logos_module(REP_FILE ...) in logos-module-builder:
| template | produces | linked into |
|---|---|---|
LogosViewPluginBase.{h,cpp}.in |
<Rep>ViewPluginBase |
the module plugin |
LogosViewReplicaFactory.{h,cpp}.in |
<Rep>ReplicaFactoryPlugin |
<name>_replica_factory |
They are pure Qt: Q_OBJECT, Q_PLUGIN_METADATA, repc-generated
*SimpleSource/*Replica types, qmlRegisterUncreatableMetaObject. Nothing
outside a Qt view plugin can use them.
The rule
There is exactly one copy of each, and it is this one. No consumer keeps a
local copy — not even a test fixture. LogosModule.cmake in
logos-module-builder hard-errors when LOGOS_VIEW_TEMPLATE_DIR is unset rather
than falling back to a sibling copy, and so does tests/rep-file-plugin here:
a silent fallback to a second copy is the failure this layout exists to remove.
They are also siblings of LogosViewModule.cmake by requirement — that
function resolves them through CMAKE_CURRENT_FUNCTION_LIST_DIR. Move the
.cmake without them and configure_file() fails.
Why this repo
This repo owns the ui_qml / interface: universal authoring flavour end to
end: the .rep-driven generator, LogosViewModule.cmake, the
LogosUiPluginContext header, and these templates. A view module is a distinct
authoring flavour rather than a variant of a core module, so changing how views
are built is a one-repo change.
It also can be everyone's copy. This repo is a leaf — its only flake input is
logos-nix — so any consumer can depend on it without a cycle. That is the
property the previous home lacked.
What moved, and why
These templates used to live in logos-plugin-qt (cmake/), and before that
next to LogosModule.cmake in logos-module-builder, with each move driven
by which repo the then-current set of consumers could all reach:
- next to
LogosModule.cmake: the natural home, since that is the code that instantiates them — but therep-file-pluginfixture in logos-plugin-qt also instantiates them and cannot reach logos-module-builder (the dependency runs logos-module-builder → logos-plugin-qt, one way), so the fixture ended up holding a byte-identical second copy with nothing comparing the two; - logos-plugin-qt: fixed that, because logos-module-builder can read it and the fixture lives there — but it made the Qt plugin-loading backend the owner of a view-plugin authoring concern.
logos-plugin-qt now handles exclusively what makes a cdylib module loadable by
logos-module-loader-qt. Its cmake/ directory is gone, this repo took the
templates and the rep-file-plugin fixture, and logos-module-builder reads them
from here. The set of consumers did not change; only the repo all of them can
reach did.
LogosModule.cmake did not come along — that file is
logos-module-builder's build-system contract (LOGOS_API_STYLE,
LOGOS_MODULE_GO_STATIC_LIBS, generated_code/) and stays there. Only the
Qt-specific view templates it instantiates are published from this side.
How consumers get them
- nix:
packages.<system>.logos-view-templates— the four.infiles flat in one directory, which is the shapeLOGOS_VIEW_TEMPLATE_DIRwants.packages.<system>.cmake-modulepublishes the same bytes undershare/cmake/LogosViewModule/, next to the.cmakethat resolves them as siblings. Same content, two addressing schemes, one source. - logos-module-builder passes that directory into every
ui_qmlmodule build — as-DLOGOS_VIEW_TEMPLATE_DIR=and as an exported environment variable, so bothnix buildand a hand-runcmakein a module dev shell resolve it. - CMake: a consumer resolves
LOGOS_VIEW_TEMPLATE_DIR(cache variable first, then environment) and hard-errors when it is unset.
The interface declared inside the templates
LogosViewPlugin and LogosViewReplicaFactory are also declared, separately,
by logos-view-module-runtime — the host that loads these plugins. That
duplication is deliberate and cannot be collapsed: a module plugin must build
against Qt alone, without the host runtime on its include path. The two
declarations meet at runtime via the IID string, where a mismatch is silent.
That pair is enforced instead of documented: logos-module-builder's
view-interface-abi check (it is the one repo that can see both sides)
compares them and fails on any difference. It reads three separate things,
because three separate strings have to line up:
#define <Name>_iidand the ordered pure-virtual list — the abstract shape;- the argument of
Q_DECLARE_INTERFACE, resolved through the#defines, since that is whatqobject_castcompares and it need not be the macro; - on the concrete classes in these templates — the ones carrying
Q_OBJECT,Q_PLUGIN_METADATAandQ_INTERFACES— the exported IID and the declared interface list.
That last group is the runtime binding, and for a while it was outside the
check's window: bumping Q_PLUGIN_METADATA(IID ...) to /2.0, or deleting
Q_INTERFACES, left the guard green while breaking a real view.
Locally, ../tests/rep-file-plugin covers the same two mutations from the other
direction — it builds a plugin from these templates, asserts the exact IID
in the resulting binary (not a substring of it), then QPluginLoaders it and
performs the same qobject_cast the host does.