mirror of
https://github.com/logos-co/logos-plugin-qt.git
synced 2026-08-27 08:51:07 +00:00
master
36
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
335edec4e2 |
chore(deps): protocol 303ab08 — the wildcard deferred subscription
logos-protocol#74 lets `onEventWhenAvailable` take an EMPTY event name as the
wildcard, the way the plain `onEvent` always has. This repo is one of only two
that CARRY the runtime `liblogos_protocol.dylib` into a consumer's closure
(`logos-liblogos` is the other), so the fix does not reach anything until this
lock moves — a consumer bumping only its OWN logos-protocol input rebuilds
green against the library staged from here.
Lock-only; one logos-protocol node in this lock, 42460e5 -> 303ab08.
Verified at the artifact rather than the lock, because that distinction is
exactly what bit here: the rebuilt qt-host closure resolves to
`n7yilrs2...-logos-protocol-lib-0.8.0`, which is the build of 303ab08, and that
dylib carries the fix while the one it replaces does not:
NEW "(any)" (UTF-16) x1 old warning x0 new warning x2
OLD "(any)" x0 old warning x1 new warning x1
(`strings` cannot see "(any)" — QStringLiteral is UTF-16. And the new warning
text appears once in BOTH, because whenObjectAvailable has always used it; the
second occurrence is the fixed onEventWhenAvailable. Checking either alone
gives the wrong answer.)
`nix flake check`: 7 checks, all green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f668ef212f |
fix(glue): the OTHER void site discarded the refusal too
#28 fixed one of two. The single-concurrency path returns early -- `if (kVoidMethods.contains(methodName)) return QVariant(true);` -- before reaching the branch that patch changed, and its own comment said the quiet part out loud: "void methods answer QVariant(true) WHATEVER the cdylib put on the C ABI". That is the one case where the cdylib has something to say. Measured, which is the only reason this was caught: with #28 merged, on plugin-qt master, reached through module-builder b06c287 and pinned by logos-test-modules -- verified by following the lock root-to-leaf, and by content (`__rejected` present in the emitter) -- the six failure/B/arity/too-many-zero-parameter cells were STILL xfail. The fix was in the build and the cells had not moved, so the fix was not on their path. THE TEST IS WHY IT LOOKED DONE, twice over, and both are fixed here: * It asserted `grep -q '__rejected'`, which passes on one site as happily as on two. * The first attempt to strengthen that counted occurrences in $cm -- the MULTI glue file -- while the site #28 missed emits into $c, the SINGLE one. Checking the right property in the wrong file is the same mistake one step over, and the suite caught it. Each site is now asserted against the file it lives in, so patching one path again fails here rather than at the far end of a matrix run. An ordinary void reply still answers QVariant(true) on both paths; only a provider refusal -- the closed set dispatch_failed / invalid_args / unknown_method, matched on an object with a string `code` -- is propagated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
048152f2a1 |
fix(glue): route informModuleToken through the INBOUND door (#26)
* fix(tokens): follow logos-protocol's inbound/outbound store split TokenManager now keeps INBOUND (caller -> what I issued them) and OUTBOUND (callee -> what I present) in separate maps, with the trust anchor as a scalar credential rather than a map entry. Call sites here move to the accessor that names the direction they meant. The pre-split single map made a grant one way a grant BOTH ways: a token minted so M could call B was found by B's client when B called M, so requestModule was skipped and the access policy never ran. See logos-protocol's companion change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(glue): route informModuleToken through the INBOUND door The emitted <Provider>::informModuleToken wrote the same value through both doors: LogosProviderBase::informModuleToken (inbound, correct) and logos_module_accept_token (which is the OUTBOUND door). The value is a CALLER's token — capability_module saying "moduleName may call you" — so the second write filed a caller's inbound token as an outbound credential inside the cdylib's own protocol copy. That is the one-way-grant bypass, reproduced one image deeper. It now calls logos_module_accept_inbound_token (protocol 0.8). onInit's anchor seeding keeps logos_module_accept_token, because THAT one is genuinely outbound: it is the module's own credential for calling core and capability_module. The comment says so on both sides — the two paths look interchangeable and are not. Below 0.8 the old write stays in an #else: dropping it would break the module's outbound calls to that peer, which is a regression, not a fix. Guards are expanded MAJOR-aware arithmetic, unifdef-resolvable. Requires logos-protocol fix/token-direction-key-namespace (59b27ef). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deps): relock logos-protocol to 0.8, and raise the consumer bound with it WHAT MOVED. logos-protocol b37a2e9f -> 42460e5b (0.7 -> 0.8), the single node in flake.lock; nothing else in the lock changed. WHY IT HAD TO. This branch emits the informModuleToken glue through logos_module_accept_inbound_token, which joins the module-impl C ABI at protocol 0.8 and does not exist before it (21 -> 23 logos_module_* lines in logos_module_impl.h). The emission sits behind an expanded MINOR >= 8 guard, so at the old pin the door was simply compiled away: the qt-host-generator check's `grep -q logos_module_accept_inbound_token inform-0.8` had nothing to find. The lock was the whole of the failure -- no code defect underneath it. Note the pin this moves is b37a2e9f and not the 6c24fcb1 this branch forked from: master merged #27 in between, and its lock had already moved. That merge is the commit below this one. Without it, the relocked flake.lock conflicts with master on the same three lines and CI -- which builds the PR MERGE ref -- cannot check the branch out at all. THE BOUND. #27 shipped cpp/logos_consumer.h with the fleet's only UPPER bound, `MINOR > 7` spelled as an #error, precisely so that a protocol bump past the consumer-admission contract stops the build instead of silently emptying every isolated identity's token store. Relocking to 0.8 fires it by design. The review it asks for, carried out rather than assumed: * bootstrapKeys(), adoptCredential() and adoptCredentialFor() are signature- and semantics-identical across b37a2e9f -> 42460e5b. 0.8 moved direction into the KEY NAMESPACE -- inbound a reserved-prefix key, outbound the bare peer name -- and deliberately left TokenManager's layout byte-identical. * credential() became DERIVED from bootstrapKeys() rather than cached, which strengthens this path: a cached field read empty on a store another image wrote and then refused every push. * 0.8's own adoptCredential() contract documents both halves admitConsumer depends on -- outbound, capability_module's proxy resolves the presented credential from the caller-keyed INBOUND record rather than an anchor key, so the caller is named as the identity and not as the host; inbound, capability_module pushes with getToken(moduleName), which IS that credential, so informModuleToken's trusted-channel gate still passes. So the bound is raised 7 -> 8, with that reasoning recorded at the guard. The oracle is the consumer-admission check and not the argument: it runs a real ModuleProxy in Local mode, and its "NO LOCKOUT: the consumer's own credential authorizes at capability_module" / "and it is NAMED as itself, not as the host" assertions are exactly the failure the #error exists to prevent. Both pass at 0.8. It is negative-validated upstream (removing the adopt step fails 5 checks, swapping the order fails 2), so its green is worth something. ALL 12 CHECKS BUILT INDIVIDUALLY, x86_64-linux, from source against cache.nixos.org only (cache.nix.logos.co is returning 502): PASS vanilla-plugin /nix/store/ab6l4a0czh4nd4h153i8hz82qayb4ah4-logos-plugin-qt-vanilla-test-0.0.1 PASS header-generator-guard /nix/store/1dxrvzk4d1r3babyfx6hfnv9va1im5v6-logos-plugin-qt-header-generator-guard-test PASS headers-emitter-routing /nix/store/yrjzx24bq010m3z0xm0djvmr3n1wn1fx-logos-plugin-qt-headers-emitter-routing-test PASS consumer-api-style-gate /nix/store/r5h073ygr1zfy763dmhxssgw9mcl96y8-logos-plugin-qt-consumer-api-style-gate-test PASS qt-host /nix/store/mndrdrxcad6kq056jcrxf946iycp5yqg-logos-qt-host-0.1.0 PASS shared-runtime-layering /nix/store/npzah7n5zj7gdlbs1d59ry0idd9vp6si-logos-qt-host-shared-runtime-layering PASS qt-host-generator /nix/store/xqlhpbz7bnfvz7c17x1aanfassglmrq6-logos-qt-host-generator-test PASS unload-contract /nix/store/2y64h1im2biyqpbmg0bi591rznl860yx-logos-qt-host-unload-contract-test PASS caller-contract /nix/store/w1lz78kfy91xcyfd35i277f030jjf0ag-logos-qt-host-caller-contract-test PASS caller-invokable /nix/store/68b83pvv90xsqszihgshpb5g3fikfmj4-logos-qt-host-caller-invokable-test-0.1.0 PASS consumer-admission /nix/store/hiwnlafxhh5gz0f1pkdi53glw66qm3rq-logos-qt-host-consumer-admission-test-0.1.0 PASS glue-compiles /nix/store/bx9qfp520yazhmmc1in37frsscs6iii8-logos-qt-host-glue-compiles-test-0.1.0 The qt-host closure references logos-protocol-lib-0.8.0, so the relock is in the artefact and not merely in the lock file. CI itself runs only 4 of these 12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fa68922404 |
fix(glue): a void method's REFUSAL is not a successful void call
The void arm discarded the dispatch reply. `value = QVariant(true)` was
unconditional: the JSON was parsed, then thrown away, so a provider answering
{"code":"invalid_args", ...} reached the caller as a successful void call.
Invisible until the providers gained something to refuse with. With the arity
upper bound in place (logos-cpp-sdk #150, logos-rust-sdk #50) the provider DOES
refuse `doVoid("junk")` -- the built module carries the `expected 0 arguments,
got ` literal, checked in the exact store path the failing run consumed -- and
the conformance cell still reported `true`, on both providers and all three
consumers. The twelve cells of the non-void arity cases went green in the same
run; these six did not, because their refusal died here rather than at the
provider.
Which is the same defect one layer down from the one the arity work started
from: an answer the caller cannot distinguish from a real one. There the
provider dropped an argument and answered anyway; here the provider refused
correctly and the glue reported success.
The closed set is the one logos-qt-sdk's consumer detector uses --
dispatch_failed / invalid_args / unknown_method -- matched only on an OBJECT
carrying a string `code`. A void method whose provider legitimately answers an
object is unaffected, and there is no such method: the contract says it returns
nothing. An ordinary reply still answers `true`; this is a branch, not a
replacement, and the test pins both sides.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
7cad5ec381 |
feat(consumer): logos::admitConsumer — one home for admitting a non-module (#27)
* feat(consumer): logos::admitConsumer — one home for admitting a non-module Two hosts hand-rolled the same three steps independently — isolate an identity, mint a credential, register it with capability_module — and the duplication had already produced a bug: basecamp did the registration inside the has-a-backend branch, below an early return, so pure-QML plugins registered nothing. It survived only because those calls went out on the host's ambient ring, where every token already existed and the handshake was never reached. Remove the ambient ring (logos-protocol #71) and that becomes a hard failure. So the operation gets one home. admitConsumer isolates, mints, registers and installs the credential as a single step, and hands back a ConsumerIdentity. Both hosts lose their private copies: basecamp -16 net lines, standalone-app -14. It lives here rather than in logos-liblogos, which is where it was first proposed. Both hand-rolled sites call LogosAPI, which is this repo's, and the operation needs a capability_module client to register through — going via liblogos's C API would add a hop for hosts already holding the Qt object. logos-liblogos needs no change at all. Named for what it does: a CONSUMER is admitted, not registered as a module. It is never published to the registry, never callable, never in --modules-dir. Two guards, because the sharp edges here are silent ones: * adoptConsumerCredential REFUSES an isolated store. It exists for a co-process adopting its parent's credential into its own process ring, where the store IS that ring. Pointed at an in-process private store it would install whatever it was handed — including the host anchor — straight past adoptCredentialFor's refusal, putting the elevation #71 removes one call away again. * A #error when logos-protocol is newer than the contract this file implements. The wave order is mandatory and was unenforced: bump the protocol alone and every isolated identity gets an empty store, while nothing fails to build (every MINOR guard in the fleet is >=) and the integration tests stay green (the ui-host half keeps working off its stdin credential). The bound fired on its first build — this change ships protocol 0.7 and it was set to 6 — and was proven non-inert by going red at 0.8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deps): relock logos-protocol onto master, which now carries #71 admitConsumer calls TokenManager::adoptCredentialFor and adoptCredential. Both arrived with logos-protocol#71 ("a private store is created EMPTY"), so the lock had to move past it — this branch still pinned 6c24fcb1, which is 0.6.0 and has neither: logos_consumer.cpp:89: error: 'adoptCredentialFor' is not a member of 'TokenManager' logos_consumer.cpp:155: error: 'class TokenManager' has no member named 'adoptCredential' Now b37a2e9f, protocol master with #71 merged. This is the wave order the header documents, seen from the other side: the empty-store change lands in protocol, and everything that makes it survivable lands here. The guard in logos_consumer.h fires when this repo is BEHIND the protocol; this commit is the ordinary case of catching up to it. Verified on x86_64-linux with the relocked input: consumer-admission PASS qt-host PASS caller-contract PASS glue-compiles PASS Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c459e47787 |
feat(glue): pull the caller from the host image and push it across the C ABI
Everything under this has merged — the protocol resolves the caller and opens a scope (#70), both backends define logos_module_set_call_caller (cpp-sdk #147, rust-sdk #47) — and nothing connected the two, so currentCaller() returned Unknown everywhere. This is the wire. Per dispatch, all on the dispatch thread: pull the caller document back out of the HOST image, push it into the module image, dispatch, pop. WHY THE PULL IS AN invokeMethod AND NOT A CALL The host binary and the module plugin EACH define LogosAPI — meta-object included — with their own statics at distinct addresses and no undefined reference to the other's. Mach-O is TWOLEVEL, PE has no interposition. A direct logosAPI()->currentCallerJson() binds to the PLUGIN copy and reads the PLUGIN thread-local: empty, forever, silently, on macOS and Windows. invokeMethod resolves through metaObject()/qt_metacall, which are virtual and whose vptr the HOST constructor wrote, so it lands in host code on the calling thread. Same channel initLogos and aboutToUnload already use. IN MULTI THE PULL IS BEFORE THE CAPTURE callMethod is entered on the dispatch thread and captures by value into QThread::create, so the pull happens there and the JSON rides along; the push/pop triple moves verbatim into the worker. Pulling inside the worker also compiles, and every multi call would read Unknown because that thread never had a scope. A test asserts the source ORDER, and it was driven red by making exactly that mistake. The invokable's name was not chosen here: logos-protocol master already names currentCallerJson in three places, including a CMakeLists comment saying the header ships so this file can answer it. Two checks beyond the wire, because the by-name test alone left gaps: * test-glue-compiles — NOTHING in this repo compiled the emitted glue. test-qt-host-generator.cpp documents a bug that escaped through exactly that hole. Both branches now build as a real Qt plugin with the C ABI stubbed and -Wl,--no-undefined, so a missing symbol fails at link rather than at a user's dlopen. * test-caller-invokable — six runtime assertions, including that a scope open on one thread is INVISIBLE on another, which is the actual justification for the multi placement. The contract test strips comments before its negative assertions (both files legitimately NAME the forbidden calls in prose) and guards that with a positive control, so the negatives cannot pass vacuously. Guard is MAJOR-aware expanded arithmetic. Dropping only the MAJOR > 0 arm still passes a grep, so the test resolves at 1.0 and asserts the call survives; that mutation was driven red too. 11 checks green on x86_64-linux, the three new ones green on aarch64-darwin, and the Windows mingw cross builds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
23a6a95aac |
chore(deps): track logos-lidl master, for lidl/identity.hpp
logos-cpp-sdk's generator now includes lidl/identity.hpp, which arrived in
logos-lidl ae3ffe0f ("derive name() and version() from the module
declaration", #10). This repo still pinned ffeebf2e, so building the
qt-host generator against a current cpp-sdk fails with
fatal error: 'lidl/identity.hpp' file not found
logos-qt-sdk already tracks ae3ffe0f; this brings plugin-qt alongside it.
checks.<sys>.qt-host-generator passes on the bumped lock.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ef11c2108a |
feat(qt-host): share the teardown helper, and guard the glue on MAJOR too
Two changes to the same surface, both about the by-name teardown hook this repo's qt-host-generator emits. 1. logos::runPluginAboutToUnload(), ported verbatim from logos-module-loader-qt's logos_host.cpp, where it was a file-local helper. ui-host (logos-view-module-runtime) needs the identical algorithm before `delete pluginObject`, and a second hand-written copy of a nested event loop plus deadline is not a thing to have twice. Both load-bearing properties are intact: the hook is invoked BY NAME rather than through the vtable (PluginInterface is compiled separately into every plugin, so a new virtual would shift the vtable under every plugin already built), and `deadline.isActive()` remains the only bit distinguishing "finished" from "gave up". The grace CONSTANT deliberately does not move with it. It stays a parameter because the two hosts have different budgets: the container gives logos_host 5000ms and it carves out 3000, while ViewModuleHost::stop() gives ui-host 3000ms total. Porting the constant would have consumed ui-host's entire budget and hard-killed every async view mid-teardown — exactly what the hook exists to prevent. It lives HERE because this repo emits the surface it reaches. Both halves under one roof is what makes checks.unload-contract possible: it generates real glue, scrapes the symbol names out of it, and greps the consumer for those same names. Nothing in either build tied them together before. 2. The two version guards this generator emits tested LOGOS_PROTOCOL_VERSION_MINOR and ignored LOGOS_PROTOCOL_VERSION_MAJOR. At protocol 1.0.0 the MINOR resets to 0, both go false, and the glue stops emitting the grant_host_services call (0.3) and the teardown pair (0.5). Not a link error and not a dlopen failure: logos-cpp-sdk guards the matching DEFINITIONS the same way, so the calls and the definitions disappear together and everything still builds and loads. Modules just quietly stop having teardown. logos-rust-sdk would keep emitting all ten exports, because it compares the (major, minor) tuple — so the two sides would silently disagree. The arithmetic is emitted expanded rather than behind a function-like macro, because the generated sources are resolved by unifdef in logos-cpp-sdk's ABI check and unifdef silently no-ops on what it cannot parse. Matching logos-cpp-sdk#145 character for character. unload-contract, qt-host-generator, qt-host and vanilla-plugin all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1aa3e31c02 |
feat(qt-host): add logos_qt_host_shared, and assert its layering (#22)
* feat(qt-host): add logos_qt_host_shared, and assert its layering
PR-3 of the shared-runtime migration. Requires logos-protocol#65, which exports
logos_protocol_shared from the CMake package; without it this build hard-fails
by design rather than falling back.
WHY. LogosAPI (here) and TokenManager / LogosAPIClient / StoreRegistry (in
logos-protocol) must exist EXACTLY ONCE per process. Every image linking a
static archive gets its own copy of every function-local static inside it, so
the host writes a capability token into one store and another in-process image
reads an empty one -- with no build diagnostic. Linking ONE shared library is
what replaces the whole-archive + generated-.def scheme in which liblogos_core
absorbed both archives and re-exported them.
THE STATIC ARCHIVE STAYS, and not transitionally. Module plugins and ui_qml
backends run in their OWN processes, so their own copy is the CORRECT
per-process singleton; staying static also keeps a .lgx self-contained, since a
.lgx records an empty nix closure and a shared library would not travel with it.
In-process images link the shared target; out-of-process images link the archive.
THE ONE LINE THAT MATTERS is that logos_qt_host_shared PUBLIC-links
${LP_SHARED_TARGET}, not ${LP_TARGET}. Linking the static archive there would
embed a second TokenManager INSIDE liblogos_qt_host itself -- the same bug one
layer down, and invisible to the consumer-side symbol gate, which would treat
this library as a provider and pass. It is a one-word mistake that builds,
links, installs and loads, and surfaces only as refused calls at runtime in a
different repo. So this PR carries its own check:
checks.<system>.shared-runtime-layering
asserting liblogos_qt_host.{dylib,so} DEFINES LogosAPI, DEFINES NEITHER
TokenManager NOR LogosAPIClient, IMPORTS TokenManager, and has
liblogos_protocol on its link line. The positive assertion doubles as the
validity control: if nm or c++filt were broken it reports 0 for LogosAPI and
fails, rather than reporting a reassuring zero for the other two.
LogosAPI switches from LOGOS_SHARED_API to LOGOS_QT_HOST_API (logos-protocol#64)
because the two are not the same choice in one translation unit: building this
library, LogosAPI must be EXPORTED while TokenManager must be IMPORTED. On
Windows the shared target also compiles with LOGOS_SHARED_USE_DLL so the
protocol-owned types become dllimport; LOGOS_QT_HOST_BUILDING_SHARED is tested
FIRST in logos_shared_api.h, so LogosAPI still resolves to dllexport there.
ARCHIVE DESTINATION on the install is load-bearing on Windows and inert
elsewhere: a shared library's import library (.dll.a) is the ARCHIVE artifact
while the DLL is RUNTIME, so omitting it installs no import library and a
consumer gets an imported target whose IMPORTED_IMPLIB does not exist.
VERIFIED, aarch64-darwin:
liblogos_qt_host.dylib defines LogosAPI:: 23
defines TokenManager:: 0, LogosAPIClient:: 0
imports TokenManager:: 5, LogosAPIClient:: 1
links @rpath/liblogos_protocol.dylib
static archive symbol tables IDENTICAL to master (1494 lines), one
byte differing -- ar metadata, not content. The macro
switch is behaviour-preserving for every existing
consumer.
all 7 checks PASS
The layering check was confirmed discriminating rather than vacuous by running
its logic against liblogos_core.dylib, which defines TokenManager 32 times and
is correctly rejected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): take logos-protocol master, which now exports the shared library
logos-protocol#65 merged as 2e3344a. The lock pinned 0d2a3c0, the commit BEFORE
it, so this branch would have configured against a protocol with no exported
logos_protocol_shared and tripped its own guard:
logos-protocol::logos_protocol_shared is not a target, so
logos_qt_host_shared cannot be built.
That guard is deliberate -- the alternative is silently falling back to the
static archive and embedding a second TokenManager -- but it means the lock bump
is a hard prerequisite rather than housekeeping.
Also moves protocol 0.4.0 -> 0.5.0 (#63, the module teardown surface), which is
why the Windows export table grows 360 -> 839: more symbols in the archive, all
of them generated rather than curated.
All 7 checks PASS against the merged protocol, including shared-runtime-layering.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
55713b9c79 |
fix(glue): guard the teardown body on protocol 0.5 (#21)
#20 emitted calls to logos_module_about_to_unload() and logos_module_set_unload_done_callback() unconditionally, so a module built against an older logos-protocol has no such C symbols and fails on generated code its author never wrote. logos-protocol#63 gives the surface a MINOR (0.5) so it is detectable; this guards on it, matching logos-cpp-sdk's emitter. The BODY is guarded, not the DECLARATION, and the difference matters: * moc emits a call to aboutToUnload(), so guarding the declaration away is an undefined symbol at link; * the host finds the hook by NAME through the meta-object, and that surface should look the same on every module rather than varying with the protocol a module happened to be built against. On anything older than 0.5 the body returns 0 -- Synchronous -- which is exactly right: a module whose ABI cannot carry teardown has nothing to wait for, and the host proceeds as it always did. Two assertions, one for each half: the body carries the guard, and the header does NOT. The second is the one that would otherwise rot silently, because guarding the declaration too still compiles the generator and only fails much later, at link, inside a module build. flake.lock tracks protocol master (0d2a3c0), where 0.5 landed. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0a26c6d2b0 |
feat(glue): reach the module's teardown hook from the plugin class (#20)
* feat(glue): reach the module's teardown hook from the plugin class
The generated plugin class gains the surface the host uses to ask a module to
finish before it is torn down:
Q_INVOKABLE int aboutToUnload();
Q_SIGNALS: void unloadFinished();
Q_INVOKABLE and a SIGNAL specifically, because the host reaches both through the
META-OBJECT rather than the vtable. PluginInterface is compiled separately into
every module .so, so adding a virtual there would shift the vtable under every
plugin already built; a plugin without the hook has to be a no-op, not undefined
behaviour. `initLogos` is delivered by name for the same reason. A plain method
would compile and be silently unreachable -- teardown would look wired and never
fire -- so the test greps for the Q_INVOKABLE and Q_SIGNALS spellings, not just
the names.
Returns int rather than LogosShutdown: the host resolves this by signature
string and must not need the SDK enum to do it.
The body forwards across the C ABI, installing the completion callback BEFORE
asking (an impl that finishes inline would otherwise signal into an empty slot,
costing the full grace period for a module already done -- the test asserts the
emission order). The callback fires on whichever thread the module finished on,
so it marshals the emission back with a queued invocation rather than touching
the plugin directly.
Six new assertions on the generator's output, which is this repo's only test
surface for it. Requires logos-protocol#62; flake.lock pins that branch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): track logos-protocol master now that the teardown ABI has landed
logos-co/logos-protocol#62 merged as 9664ae2. The lock pointed at the PR branch
while it was open.
The narHash is unchanged across the move (sha256-JTREoJn2kjQmYyYHg2RQb4...), so
the merged tree is byte-identical to the branch this was built and tested
against.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
9b2c64e5a4 |
feat: the Qt host runtime and cdylib-glue generator (#19)
* feat: the Qt host runtime and the cdylib-glue generator
This repo was the Qt plugin BUILD backend — pure Nix functions plus a CMake
module, with no C++ target at all. But the two things a Qt host actually needs
at runtime lived in logos-qt-sdk, where they are neither a language wrapper over
the protocol C API nor codegen:
LogosAPI / LogosAPIProvider owns a LogosTransportHost per transport,
constructs ModuleProxy / ModuleHandshakeProxy,
publishes the handshake surface, seeds trust
anchors, injects the token validator
LogosProviderBase the base every generated provider derives
PluginInterface the Qt plugin loading contract
the cdylib->Qt glue generator the emitter that wraps a language-neutral
cdylib in a Qt plugin
They move here, so "swap the plugin technology" is a one-repo change.
ADDITIVE: the sources are COPIED and logos-qt-sdk is untouched. Removing them
there now would turn nine downstream masters red at once; that comes later,
after consumers are repointed.
qt_provider_object (and logos_qt_arg_decode, which its QMetaObject dispatch
needs) is carried deliberately even though it is legacy: logos_api_provider
falls back to wrapping a plain QObject in it, and the modules that rely on that
have not been migrated yet. It goes once they are.
The generator links Qt Core and logos-lidl only — never logos-cpp-sdk. It needed
exactly one helper from that SDK's shared frontend, lidlToPascalCase (~12
lines), which is inlined instead, the same way logos-view-module does it. It
also REFUSES `--backend <anything but cdylib>` rather than ignoring the flag:
callers are migrating from a tool where --backend was required and dispatched
on, so silently treating `--backend qt` as cdylib would emit confidently wrong
artifacts with a zero exit.
`rawLib`, `lib` and `cmake-module` deliberately do not reference the new
derivations, so a consumer that only wants the Nix build functions never
realises a Qt/protocol build. Proven, not assumed: with both new inputs
overridden to a bogus flake, cmake-module and rawLib still evaluate while
logos-qt-host fails — so the override bites and the cheap outputs really never
touch it.
Behaviour preservation is the whole claim of a relocation, so it is measured:
the emitted glue is BYTE-IDENTICAL to logos-qt-generator --backend cdylib over
every one of the 20 .lidl contracts in the workspace, in both single and
concurrency:multi mode (40 pairs, exit codes included), and single vs multi do
differ from each other, so both code paths were really exercised. The built
liblogos_qt_host.a is byte-identical to liblogos_qt_sdk.a, exporting the same
390 symbols.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: qt-style consumers take their wrappers from logos-qt-generator
The qt api-style path emitted its per-dependency wrappers from
logos-cpp-generator's legacy ApiStyle::Qt emitter — a second implementation of
the consumer surface, with its own conversion table and its own transport. The
qt-generator `--backend consumer` veneer exists to replace it so there is ONE
transport, one codec and one Qt type mapper under both consumer surfaces. It was
golden-tested and wired into nothing.
The umbrella is what made this non-obvious. `--general-only` emits both the
per-dep wrappers AND logos_sdk.{h,cpp} (the LogosModules aggregate);
`--backend consumer` emits no umbrella at all, and the umbrella is live —
storage-ui and wallet-ui both use LogosModules. Nor is the umbrella
style-agnostic: makeUmbrellaHeaderFromDeps branches hard on apiStyle, emitting
`LogosModules(LogosAPI*)` with `dep(api)` members for qt and a
default-constructible struct with `dep("<origin>")` for lp.
What it IS agnostic to is WHICH generator produced the wrappers for a given
style — the qt branch's two constructor shapes are exactly what the consumer
backend emits for Static and Bound, and both spell the member's type with the
same PascalCase routine, shared through cpp-sdk's share/lidl-frontend. So
cpp-generator still emits the umbrella into a scratch dir, from which only
logos_sdk.{h,cpp} is copied, and qt-generator emits the wrappers. No legacy
wrapper text reaches the build tree; the shipped wrapper has exactly one author.
--dep is dropped from the umbrella call (those flags only drive wrapper
emission; the umbrella's members come from metadata.json dependencies), while
--interface is kept, because the bind_<name> factories come from the interface
names and a cross-repo interface can only reach the generator through a flag.
The lp path is emitted verbatim as before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: drop PROVIDER_HEADER from LogosModule.cmake
Follows the removal of logos-cpp-generator --provider-header. Nothing in the
workspace passed PROVIDER_HEADER to logos_module().
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(glue): forward the host-services grant across the C ABI (C3)
The cdylib glue now reads a `hostServices` property off the LogosAPI object and
calls logos_module_grant_host_services with it, on the SAME property-stamping
channel the host already uses for authToken/modulePath/instanceId — no new
host<->plugin API.
It has to cross the C ABI rather than being recorded host-side: the host binary
and the cdylib each link their own copy of logos-protocol, so each has its own
process-global grant state, exactly as each has its own TokenManager (which is
why the auth token is re-seeded here too). Measured, not assumed: a built
plugin DEFINES 25 lp_* symbols and imports zero.
Ordered before the context forward, so a privileged impl may already use the
granted services from its context-ready hook.
An ordinary module has no such property and stays fail-closed with no call at
all — lp_grant_host_services REPLACES the current grant, so pushing an empty
string would be a pointless clear. A refused grant (malformed JSON, or a name
outside the closed set) is reported rather than swallowed: the module keeps
running UNPRIVILEGED, and the symptom otherwise surfaces far away as an
unexplained empty token registry.
Tests assert the forwarding, the empty-property guard, the refusal warning, and
the ORDER (by comparing emitted line numbers, since that is the kind of thing a
refactor silently reverses and which then breaks a trust-root module at
startup).
Note ws test ran only 1 of this repo's 4 checks, so the new test was run
directly by name — see the ws check-discovery gap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: document the two-copy drift this generator test now guards
logos-qt-sdk ships the same cdylib-glue emitter. Both compile and both emit
loadable glue, so calling the wrong one is not an error — it silently emits
OLDER glue, which is how the host-services grant went undelivered for a whole
phase while every build stayed green.
The grant assertions added with the forwarding are what make that visible: if
the builder is pointed back at qt-sdk's generator, these fail instead of a
module quietly coming up unprivileged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(glue): guard the grant forwarding, and include the header that defines it
Same protocol-MINOR guard as the C-ABI export, so glue for a module built
against logos-protocol < 0.3 still compiles.
The guard needs logos_protocol.h, which the glue did NOT include — and an
undefined macro in `#if defined(X) && X >= 3` is simply false, so the first
version of this change compiled everywhere and silently stopped forwarding the
grant: logos-test-modules ipc-tests went PASS -> FAIL again, with capability
refusing every requestModule exactly as it had before.
That is the second time this seam has failed by going quiet rather than loud.
Verified in BOTH directions this time: template_module builds against the old
protocol (guard off), and ipc-tests passes against the new one (guard on).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(tokens): a LogosAPI can be built on its own token store
Origin was never consulted on the hot path — LogosAPIClient reads its store
first and mints only on a miss — so a per-plugin `LogosAPI(origin=name)` was
a no-op while this ctor hard-wired `&TokenManager::instance()`. What has to
differ is the STORE.
The existing ctors now resolve their store through
`TokenManager::forIdentity(module_name)`, which returns instance() itself —
pointer-identical — for every name nobody has isolated, so no existing caller
changes by one byte. New: a ctor taking an explicit store, and
`LogosAPI::forIdentity(name)`, which isolates first and then constructs (the
required order: a client captures its store by raw pointer) and returns
nullptr rather than a half-isolated identity.
* feat: a module's own qt wrapper comes from its contract, not its plugin
buildHeaders was the last place the legacy Qt consumer emitter was reached on
purpose, and BOTH of its paths reached it: the native one introspected the
compiled plugin (`<plugin.so> --module-only --api-style qt`), the cross one ran
`--general-only --dep` and then deleted the umbrella it incidentally emitted.
B5 moved buildPlugin's consume-time wrappers to `logos-qt-generator --backend
consumer`; the wrapper a module PUBLISHES for its own consumers kept coming
from the other emitter, so the same file name had two authors depending on
which derivation produced it.
Contract-first now. A module that publishes a LIDL contract — every universal
and cdylib module does, and a handcrafted one can commit src/<name>.lidl — has
its qt wrapper generated from that contract, natively and under cross alike.
Introspection survives exactly where nothing else describes the module: a
legacy Qt plugin whose QMetaObject is the only account of its API that exists.
That residue dies with the last such module, not here.
The public declaration set does not move. Six real providers, emitted both ways
and diffed over the surface a consumer can name: capability_module 12,
chat_module 84, storage_module 102, package_manager 131, test_fullapi_cpp 113,
test_fullapi_rust 113 — identical in every case. The entire diff is private:
`ensureReplica` / `packVariantList` / `m_client` / `m_eventReplica` collapse to
one `logos::qt::LpBridge* m_bridge`. That is the transport, which is the point
of the exercise.
Two consequences worth naming. The qt variant no longer references the compiled
plugin at all, so capability_module's headers-qt went from depending on the SDK
plus the module .dylib to depending on logos-qt-generator plus a .lidl — a
contract module's headers now cost a parse instead of a Qt build. And the cross
path stops being a DIFFERENT emitter from the native one: `--api-style qt` at
host=x86_64-w64-mingw32 takes the same branch darwin takes, which retires the
`--general-only` + `rm logos_sdk.*` shape that only ever existed because
introspection is impossible there.
The failure mode this file has to defend against is silence, not breakage: a
routing bug leaves every build green while the wrapper quietly comes from the
old emitter again. So the selection is announced with its reason
(`buildHeaders: emitter=…`), the contract path asserts its own output names
LpBridge before installing it, and the one branch where a contract EXISTS but
the caller passed no generator is reported as a REGRESSION on stderr rather
than silently demoted. tests/test-headers-emitter-routing.nix pins all four
selections against the real routing code.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(b2b): repoint this repo's LogosModule.cmake at logos-qt-host
This file is not a spare copy. logos-module-builder's buildCppPlugin sets
LOGOS_MODULE_BUILDER_ROOT only when the MODULE'S OWN repo ships a
cmake/LogosModule.cmake, and none do -- so for every ui_qml / view plugin, and
for the counter fixture, the CMake module that actually runs is this one, taken
from the backend root. logos-module-builder's copy is what mkLogosModule's core
path uses. Two live copies, selected by a pathExists probe.
That was invisible while logos-qt-sdk forwarded the host runtime's headers: this
copy probed ${LOGOS_QT_SDK_ROOT}/include/cpp/logos_api.h, put
${LOGOS_QT_SDK_ROOT}/include{,/cpp,/core} on the include path, and linked
logos-qt-sdk::logos_qt_sdk -- all of which resolved through the forwarders and
the INTERFACE hop. With the forwarders gone it configures straight into
"logos-qt-sdk not found" while looking at a perfectly good prefix. I found this
by deleting the file on the theory that it was dead and watching eight module
builds fail; the theory was wrong and the build said so.
Repointed the same way logos-module-builder's copy already was:
- LOGOS_QT_HOST_ROOT selects the host runtime, defaulting to this repo (the
CMake module sits next to the cpp/ it needs) and hard-erroring otherwise.
There is no logos-qt-sdk fallback -- it would configure and then fail in the
compile.
- the source layout compiles ${LOGOS_QT_HOST_ROOT}/cpp/*.cpp, now including
logos_qt_arg_decode.cpp, which qt_provider_object.cpp's dispatch needs and
the qt-sdk-era list omitted.
- the installed layout links logos-qt-host::logos_qt_host, refusing a package
that resolves without defining its target.
- LOGOS_QT_SDK_ROOT stays, probed by logos_qt_wire.h (a name this SDK owns;
logos_api.h is in neither of its layouts any more) and contributing only the
Qt-typed consumer headers.
Every plugin built through this file is byte-identical before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: stop shipping a second LogosModule.cmake
This repo's cmake/LogosModule.cmake was a duplicate of
logos-module-builder's, and the build chose between them by module type:
logos-module-builder set LOGOS_MODULE_BUILDER_ROOT only when a MODULE carried
that file (none does), so every ui_qml plugin configured with THIS copy while
every core module configured with the builder's. Both compiled. That is how a
stale generator, a stale host-runtime repoint and a missing source file each
shipped in a green tree.
The CMake module is the builder's build-system contract -- it reads
LOGOS_API_STYLE, LOGOS_MODULE_GO_STATIC_LIBS and generated_code/, none of
which this backend knows about -- and it cannot live here anyway: the
dependency runs builder -> backend, so this repo could never re-export the
builder's file. It is deleted, not forwarded. logos-module-builder now passes
LOGOS_MODULE_BUILDER_ROOT for both module types (its 0ddac92), so the deletion
lands after the routing, not before it.
Consequently:
- lib/default.nix no longer defaults LOGOS_MODULE_BUILDER_ROOT to backendRoot.
A caller that forgets it now gets a FATAL_ERROR from the module's own
CMakeLists instead of a build against whatever this repo contains. That was
backendRoot's only use, so the parameter goes too.
- packages.cmake-module is gone; `default` is logos-qt-host, the output this
repo now produces that a consumer can build.
- cmake/ is gone. LogosViewPluginBase.{h,cpp}.in were read only by the deleted
file. The two LogosViewReplicaFactory templates are read only by
tests/test-rep-file-plugin.nix, so they move into that fixture's own cmake/
and are what they are: a test fixture, not a shipped template.
Checks: header-generator-guard, headers-emitter-routing, qt-host-generator,
rep-file-plugin, vanilla-plugin OK. qt-host fails identically at the pre-change
baseline -- this repo's lock pins a logos-protocol whose TokenManager has no
forIdentity/isolateIdentity, so cpp/logos_api.cpp will not compile from it.
It builds green in the workspace closure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: the view templates exist once, and this repo is where
The four LogosView*.in templates had two byte-identical copies:
logos-module-builder/cmake/ (LIVE — LogosModule.cmake)
logos-plugin-qt/tests/rep-file-plugin/cmake/ (fixture — rep-file-plugin)
Nothing compared them. This is the fifth defect of that exact shape in this
refactor, and it was created by the fix for the fourth: when this repo's
duplicate LogosModule.cmake was deleted, the templates it read were MOVED into
the fixture rather than removed, because the fixture still needed them.
It needed them because it cannot reach where they went. The edge runs
logos-module-builder -> logos-plugin-qt, one way, so a fixture inside this repo
can never consume logos-module-builder's copy, and this repo cannot re-export
it either. "Own them in the builder" is not reachable; a copy in the fixture is
what "own them in the builder" degrades into.
So ownership follows the direction that works. The templates are this repo's:
* cmake/ holds the one copy, with cmake/README.md stating the rule and the
argument for it.
* packages.<sys>.logos-view-templates publishes them as a nameable output.
* lib.buildPlugin / lib.generate set LOGOS_VIEW_TEMPLATE_DIR (cmake flag and
env var) on every plugin build, so logos-module-builder's LogosModule.cmake
receives the directory without either repo growing a new input.
* devShellInputs exports it too, so a hand-run cmake in a module dev shell
resolves it the same way.
LogosModule.cmake itself does NOT come back here. That file is the builder's
build-system contract and stays there; only the Qt-specific templates it
instantiates are published from this side of the edge.
The fixture now takes the directory from the harness and has nothing to fall
back to — a missing LOGOS_VIEW_TEMPLATE_DIR is a FATAL_ERROR, not a quiet
second copy. Proof it reads the owned file and not a leftover: changing the IID
in cmake/LogosViewReplicaFactory.h.in makes rep-file-plugin fail its
"IID found in binary" assertion.
rep-file-plugin is also added to CI. It is the check that instantiates these
templates and it was never listed in the workflow, so the templates had no CI
coverage at all — part of why a duplicate could sit in the fixture unnoticed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(view): assert the exact IID, and load the plugin the way the host does
`rep-file-plugin` asserted `strings | grep -q "logos.view.replica_factory"`.
That is a SUBSTRING match, and it is satisfied by every way the plugin can be
broken. Two mutations were run through it and it stayed green on both:
* `Q_PLUGIN_METADATA(IID LogosViewReplicaFactory_iid)` changed to the literal
"logos.view.replica_factory/2.0" — the built dylib then carries BOTH
strings, because Q_DECLARE_INTERFACE still emits /1.0, and the substring
matched either one.
* `Q_INTERFACES(LogosViewReplicaFactory)` deleted — no string in the binary
changes at all; only moc's qt_metacast entry disappears, so the host's
qobject_cast returns nullptr and the view is blank.
They are two independent bindings, so they need two assertions:
* the IID is now compared EXACTLY. Every logos.view.replica_factory/<v>
string in the binary is collected and the set must be exactly the one
expected IID, which is what makes the /2.0 case fail: a binary carrying
both is proof that Q_PLUGIN_METADATA and Q_DECLARE_INTERFACE disagree.
* the plugin is then actually loaded. load_factory_check.cpp does what
LogosQmlBridge::loadFactory does — QPluginLoader::instance() followed by
qobject_cast<LogosViewReplicaFactory*> — and also checks the factory is
wired to the repc-generated RepTestReplica. Deleting Q_INTERFACES fails
here, at the same call the host would have failed at.
The checker deliberately has AUTOMOC OFF: it includes the generated header
only for the interface declaration and its Q_DECLARE_INTERFACE, and must not
moc the concrete plugin class that header also declares. It carries no copy of
the interface — that duplication is the thing being guarded against.
Comments in cmake/ updated to say what the two checks actually cover now, and
that the module side is checked from the binary end here while agreement with
the HOST side is logos-module-builder's `view-interface-abi`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(windows): logos-qt-host builds for the x86_64-windows target
The Qt host runtime moved here from logos-qt-sdk, but its Windows target did
not come with it. logos-qt-sdk keys `packages` by `forAllTargets`; this repo
keyed them by `forAllSystems` over four unix systems, so
`packages.x86_64-windows.logos-qt-host` did not exist and every consumer that
threads the pseudo-system through — logos-liblogos, and through it
logos-basecamp — failed at EVALUATION with
error: attribute 'x86_64-windows' missing
before a single derivation was built. The split traded a Windows-capable
provider for one that is not.
`packages` now uses `logos-nix.lib.forAllTargets`, the same helper
logos-protocol and logos-lidl already build their Windows legs with. `checks`
and `devShells` deliberately stay on `forAllSystems`: every check either runs
what it built or loads a plugin, and a PE does not run on the Linux builder.
Two things the cross target needs, both gated on the HOST platform so the
native derivation does not move:
* `$cmakeFlags` is forwarded to the hand-rolled cmake line. mkDerivation puts
-DCMAKE_SYSTEM_NAME / -DCMAKE_SYSTEM_PROCESSOR / the host-system triple
there for a cross build and leaves it empty natively, so a hand-rolled
configure that omits it is precisely the shape that silently drops them.
* the Qt cross flags (QT_HOST_PATH, QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH)
plus dontWrapQtApps. CMAKE_AUTOMOC is on and RemoteObjects is required, so
moc and repc must come from the BUILD platform's Qt; and the Qt wrapper
hooks cannot evaluate for a mingw host, while qtbase's setup hook aborts in
qtPreHook unless one of them ran or dontWrapQtApps is set. There is nothing
to wrap in any case — this package installs an archive and headers.
NATIVE DERIVATION UNCHANGED, measured rather than argued: `nix derivation show`
for aarch64-darwin before and after differs in the `src` snapshot alone (`src =
./.`, so any commit moves it). Every other env var is byte-identical and
inputDrvs compares equal, so nothing downstream rebuilds for a reason other
than this repo's source having changed.
`logos-qt-host-generator` is deliberately NOT published for the Windows target.
It is a HOST TOOL, executed during a consumer's build, which is why every
caller in logos-module-builder already reaches for it as
`packages.${buildSystemFor system}.logos-qt-host-generator` — resolving to
x86_64-linux there. A PE under `packages.x86_64-windows` would only offer the
Linux builder a binary it cannot run.
LAZINESS PROVEN BY CONTROL, not by grep. With BOTH inputs the host runtime
needs overridden to a flake that provides no packages at all:
lib -> evaluates (7 attrs)
rawLib -> evaluates (7 attrs)
packages.aarch64-darwin.logos-view-templates -> evaluates
packages.x86_64-windows.logos-view-templates -> evaluates
packages.aarch64-darwin.logos-qt-host -> FAILS at logos-protocol
packages.x86_64-windows.logos-qt-host -> FAILS at logos-protocol
packages.aarch64-darwin.logos-qt-host-generator -> FAILS at logos-lidl
So the override really bites, the cheap outputs a consumer of the Nix build
functions asks for still never realise a Qt + protocol build, and the new
Windows branch does not drag one in either.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): rev-pin logos-protocol at c8bab12 (per-client token store)
LogosAPI is now constructible on its own token store, which needs
TokenManager::forIdentity / isolateIdentity. Those landed on
logos-protocol's feat/per-client-token-store branch and are NOT on its
master — master is still LOGOS_PROTOCOL_VERSION_MINOR 2, so the
previously locked e6d5b57 (protocol master's tip) cannot compile
cpp/logos_api.cpp, and the cdylib glue's `MINOR >= 3` guard would
silently drop the host-services grant.
Rev-pinned in the URL rather than left master-tracking, because
`nix flake update` cannot reach a commit that is not on the tracked
branch. Re-point at master once that branch merges.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(buildPlugin): drop the transitional header-copy fallback
For a dependency that published no LIDL contract, the plugin build copied that
dependency's PRE-BUILT `headers-<apiStyle>` output into generated_code/ --
which also forced the dependency's whole plugin to be compiled just to obtain
headers. Every dependency now arrives as `--dep <name>=<name>.lidl` and its
wrapper is generated from the contract instead.
The path is dead, measured rather than assumed. `moduleDepIncludes` was
interpolated into the plugin derivation's `preConfigure`, so its liveness is
readable at EVAL time: I probed `.preConfigure` on every package reachable
within two hops of the workspace flake (303 derivations) plus every variant of
every module in logos-test-modules (40 more) and grepped for the block's own
"(legacy header-copy)" echo. 343 non-empty preConfigure strings, ZERO hits --
including test_ipc_module and test_ipc_new_api_module, the two modules with
concrete `dependencies` and no `interface` declaration.
That probe has a control, because a zero-hit grep is worth nothing without one:
patching mkLogosModule's `depIsLidl` to `false` makes the same eval print
"Copying qt-typed include files from test_basic_module (legacy header-copy)..."
for both of test_ipc_module's deps. The probe sees the block when it is there.
`moduleDeps` is still ACCEPTED, and asserted EMPTY. The caller fills it from
`legacyHeaderDepNames` -- deps for which `depIsLidl` is false -- and a caller
that predates this change keeps passing them. Silently dropping those would
leave the module with neither a `--dep` wrapper nor a copied header, and the
build would die inside a generated translation unit on a missing
`<dep>_api.h`, pointing at the generator instead of at the stale dependency.
So it throws, naming the deps. Verified with the same forced-`depIsLidl` patch:
the error names test_basic_module and test_extlib_module.
Verified:
* all six checks built by name, each exit 0 -- header-generator-guard,
headers-emitter-routing, qt-host, qt-host-generator, rep-file-plugin,
vanilla-plugin
* test_ipc_module, test_ipc_new_api_module, test_fullapi_cpp and the
workspace's logos-accounts-ui all build against this tree (exit 0), with
reach proven by store-path diff: test_ipc_module is
867kcfxr... with the override and pnj60rsc... without it
* test_qml_backend fails, IDENTICALLY with and without the override
("no member named 'add' in 'TestBasicModule'") -- pre-existing at the
pinned revs, not caused by this change
NOT removed, and it is worth saying why: the Windows cross-compile branch in
buildHeaders.nix (`crossNoIntrospect` -> `crossBuildPhase`) does NOT serve this
fallback. It is the emitter for `--api-style lp` headers under cross, selected
because a Linux builder cannot dlopen a PE, and its `!contractIsLidl` half is
the hard error that stops a contract-less module shipping an empty API under
cross. Both halves are still reachable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(buildPlugin): honour the consumer surface, and gate origin binding
Backend half of codegen.consumer_api_style (logos-module-builder 08ae7ac).
`apiStyle` now honours `config.consumer_api_style`, which also selects which
pre-built `dep.headers-${apiStyle}` variant a consumer takes.
Adds `originBound = isQt && packagedAsCdylib`, which is what actually passes
`--binding origin` to the generators. Deriving it here rather than reading a
key means the hazardous combination cannot be requested: only a cdylib-packaged
image reaches it, and that is the image whose TokenManager is filled by
logos_module_accept_token rather than by LpBridge::syncTokens. A Qt plugin given
an origin-bound wrapper would be silently unauthenticated.
`assertConsumerApiStyle` restates the gate here as well as in parseMetadata,
because callers can reach rawLib.generate directly and bypass metadata parsing
entirely.
The flag is appended as a suffix on an existing argument
(`--api-style qt${bindingFlag}`) so the emitted shell is byte-identical when
empty — verified by A/B'ing the generated tree for a Qt plugin, not just the
built output.
New check `consumer-api-style-gate` drives the real `generate` and asserts on
the emitted generator command line across 16 configurations. Its mutation
control is the important one: setting `originBound = isQt` — dropping the
packagedAsCdylib conjunct — makes it fail by name on legacy and ui_qml, so the
suite pins the conjunct itself rather than only the lp refusal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(qt-host-generator): capture isVoidMethod in the multi worker lambda
`concurrency: "multi"` runs the handler on a QThread, and the lambda it hands
to QThread::create takes no capture-default on purpose — it outlives the frame,
so every capture has to be by value deliberately. The capture list was written
out by hand in two variants selected on `resultMethods`, but the BODY reads two
flags, `isVoidMethod` and `isResultMethod`, each emitted under its own
condition. Two lists cannot cover four combinations:
void + result -> captured isResultMethod only; body still read isVoidMethod
void, no result -> captured neither; body still read isVoidMethod
result, no void -> correct
neither -> correct
So any module with a void method failed to compile under multi, at the
generated glue rather than anywhere the author could see. Building the list
from the same two conditions that decide whether the locals exist removes the
possibility of them disagreeing.
Found by giving test_ipc_new_api_module `concurrency: "multi"` — it has a void
method (triggerBasicEvent) and no result method, i.e. the doubly-broken case.
The existing multi test did not catch this because it only ever GREPPED the
emitted text and never compiled it, and its fixture happens to declare both a
void and a result method — so it was reading output that would not build. The
assertions added here close that specific gap rather than the general one: for
each flag the body reads, the capture list must name it. A second contract
covers void-without-result, the combination neither hand-written list could
express. Both were confirmed to FAIL against the pre-fix generator (the first
reports the actual emitted list) and pass after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* ci: use logos-co/setup-nix-cache-action for Nix setup and caching
Replaces the per-repo installer + cachix pair with the shared action, which
installs Nix with the Logos Attic cache (cache.nix.logos.co) preconfigured and
publishes what the job builds — master to the public cache, every other ref to
ci.
Each converted job also gains
environment: ${{ github.ref == 'refs/heads/master' && 'public-cache' || '' }}
because ATTIC_TOKEN_PUBLIC only exists inside that environment. Without it the
secret resolves empty on master and publishing is silently skipped — the job
still passes, so the omission would not show up as a failure.
The action installs Nix itself on every runner, macOS included. That is a
deliberate reversal of the workaround these files carried: the comments here
said cachix/install-nix-action collides with the runner's pre-existing _nixbld
users (eDSRecordAlreadyExists), so DeterminateSystems' installer was used
instead. It no longer reproduces — logos-delivery-module has already been
converted the plain way and its `build-and-test (macos-latest)` leg passes.
Keeping the workaround would have meant a second installer plus a duplicated
substituter/key block in ten files, guarding against something two green runs
say does not happen. If it ever recurs it fails loudly at install, which is
recoverable; the silent-skip above is the failure mode worth engineering
against.
One property is deliberately NOT carried over: the old cachix step ran with
`continue-on-error: true` so a failed cache push could not fail a job whose
tests passed. The action exposes no equivalent, and adding one here would also
swallow genuine setup failures now that the same step installs Nix rather than
only publishing at the end.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): track logos-protocol master again
The rev pin on feat/per-client-token-store existed because the trust-root
surface it needed lived only on that branch while protocol master was still
LOGOS_PROTOCOL_VERSION_MINOR 2. Both comments here said to re-point once it
merged; it has (logos-protocol#59), and master is 0.4.0 — MINOR 4, carrying
lp_token_keys, lp_inform_module_token_to, lp_grant_host_services and
TokenManager::forIdentity / isolateIdentity.
That matters beyond compiling: the cdylib glue's grant forwarding is guarded on
MINOR >= 3, so a master pin taken too early would not have failed loudly — it
would have dropped the grant silently. The guard now opens.
Verified against master rather than assumed: #default builds and the checks pass
(cpp-sdk `tests`, plugin-qt `qt-host-generator`).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: mark the provider macros vestigial and fix the glue banner
logos_provider_object.h still presented LOGOS_PROVIDER / LOGOS_METHOD as "the
developer-facing API" and said logos-cpp-generator --provider-header produces
the dispatch from them. That scanner was removed, so nothing reads LOGOS_METHOD
any more; the macros are kept only so an older TU still compiles. Module code
no longer derives LogosProviderBase either — it derives logos-cpp-sdk's Qt-free
LogosModuleContext, and LogosProviderBase is now the base the *generated*
<name>_cdylib_glue.cpp derives from.
The drift guard in tests/ was written when logos-qt-sdk shipped a rival copy of
this emitter; that copy is gone and the flag now refuses, so the comment is
recast as history. The assertion stays — it pins the property that made the
divergence detectable.
Also fixes the banner this generator stamps into its own output, which
credited "logos-cpp-generator --cdylib". Nothing pins the old string.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: shed the view-plugin templates, keeping this repo to cdylib loading
This repo's job is making a cdylib module loadable by logos-module-loader-qt.
The LogosView*.in templates, the logos-view-templates output, the
LOGOS_VIEW_TEMPLATE_DIR plumbing and the .rep fixture are ui_qml AUTHORING, a
different concern; they now live in logos-view-module, which already owned that
flavour and inputs only logos-nix so nothing can cycle through it.
Removed: cmake/ (the four templates + README), the logos-view-templates
package, common.nix's viewTemplateDir and its -DLOGOS_VIEW_TEMPLATE_DIR entry,
the LOGOS_VIEW_TEMPLATE_DIR env entries, and tests/rep-file-plugin with its
check.
logos-module-builder supplies LOGOS_VIEW_TEMPLATE_DIR from its own
logos-view-module input through the extraCmakeFlags/extraEnv seams it already
uses for the other five roots, so nothing here needed a new parameter — the
change is pure deletion.
Three sites carried the variable, not two: buildPlugin's env, generate's env
(dead — mkBuildPlugin.generate sets dontConfigure, so cmake never runs and
nothing reads it), and devShellInputs' shellHook, which logos-module-builder
splices verbatim into every module dev shell. Dropping the third without the
builder taking it over would have left `nix develop` broken for every ui_qml
module, visible only when someone ran cmake by hand.
buildPlugin.nix keeps its <name>_replica_factory packaging: the LIBRARY is a
build artifact of the module tree this backend compiles, exactly like the
_plugin library beside it, and packaging what a plugin build emits needs the
output filename convention, not view knowledge.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8846fc5626 |
feat(windows): stage an external library's DLL from <drv>/bin (#18)
externalLibCopies flattens each external library into the module's ./lib so CMake's find_library can see it, but it only ever reads <drv>/lib. A library that follows the WINDOWS convention ships its runtime half in bin/ -- that is CMake's own RUNTIME destination, and what openssl, postgres and every autotools port in nixpkgs do. For those, the staged lib/ ends up holding the import library or the static archive but no .dll, and LogosModule.cmake then hard-fails with "found no companion DLL in .../lib" -- or, worse, silently links the static archive instead. Take that library's own .dll from bin/ as well. Matching only *.dll keeps this inherently Windows-only (no native package ships one in bin/), so no platform flag has to be threaded in and a native build provably cannot pick up a stray executable. Only THIS library's files are copied, never all of bin/: the dependency DLLs that nixpkgs' win-dll-link hook stages there must stay symlinks, created by the postFixup pass below. Copying them as real files would make $out/lib look complete while leaving the Nix closure empty again -- a PE embeds no store paths, so those symlinks are the only thing the reference scanner can see. Verified by cross-building logos-delivery-module for x86_64-windows against a logos-delivery that installs its .dll to bin/: the plugin links the import library, and loads and dispatches on real Windows. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b8b9b4141f |
feat(windows): cross-compile the Qt plugin backend and stage its DLL closure (#17)
* feat(windows): make the Qt plugin backend cross-compilable
Three changes, all needed before any module can target mingw:
* meta.platforms gained windows in the three generated derivations. Without
it every module fails at EVALUATION with "not available on the requested
hostPlatform".
* getPluginFilename returned ".so" on Windows while getLibExtension two
lines above already answered "dll". They disagreed because each carried
its own copy of the platform branch, so the attribute set is now `rec`
and getPluginFilename REUSES getLibExtension rather than repeating it.
* wrapQtAppsNoGuiHook is gated behind !isWindows and dontWrapQtApps is set
on both plugin derivations. Both halves are required and neither is
optional: the hook does not even EVALUATE for a mingw host, and would be
inert anyway (wrap-qt-apps-hook skips anything that is not ELF or
Mach-O), while qtbase's own setup hook hard-errors in qtPreHook unless
dontWrapQtApps is set.
* fix(windows): install the plugin DLL, and cross-compile the Qt backend
The installPhase chain listed only .dylib and .so, so a perfectly good mingw
build of <name>_plugin.dll fell through to "No plugin library file found".
Replaced with an explicit `for ... [ -f ] ... break` loop rather than a
nullglob array. That distinction matters and I got it wrong once already:
`${config.name}` is a NIX interpolation, so every candidate is a fully
resolved LITERAL path with no wildcard in it, and nullglob only removes
patterns that CONTAIN a wildcard and fail to match -- a literal survives
verbatim, so the array form still handed `cp` the .dylib name on Windows.
This is the same shape as the _replica_factory loop directly below, which
had it right already; that loop gains its own missing .dll candidates here.
logos_find_qt in the module builder had to become a macro for any of this to
be reached -- see that commit for the Qt include_guard story.
capability_module_plugin.dll (13.4 MB) now builds and installs.
* feat(windows): generate typed headers from the LIDL contract under cross
buildHeaders recovers a module's typed consumer API by LOADING the built
plugin and introspecting it. A Linux builder cannot dlopen a PE, so that can
never work when cross-compiling to Windows.
Adds an optional `contractLidl` argument and, gated on
hostPlatform.isWindows, a separate build phase that drives the generator from
that contract instead:
logos-cpp-generator --metadata <module metadata> --general-only \
--api-style <qt|lp> --dep <name>=<contract.lidl>
This uses the generator's EXISTING --dep backend; no generator change was
needed. Note --dep is a silent no-op without --general-only, and the
superficially-obvious `--lidl --module-only` is the wrong tool: it emits only
the Qt flavour and rejects `-> void`, which real sidecars contain.
The native path is character-for-character unchanged, soft failure included.
The CROSS path deliberately does NOT inherit that soft failure: with no
contract it exits 1 naming the module and the target, because the alternative
is a build that goes green while shipping a module nothing can call in a typed
way. Verified by moving the contract aside -- rc=1, no .no-api written.
Proven equivalent, not argued: the cross-generated capability_module_api.{h,cpp}
are byte-identical (md5) to the native introspection output, for both qt and lp
api styles.
* feat(windows): stage a plugin's DLL closure into $out/lib
nixpkgs' win-dll-link.sh already does exactly this, but its entry point
_linkDLLs only ever processes $prefix/bin. A module plugin lives in
$out/lib, so it was skipped entirely and shipped as a bare .dll with
none of its dependencies. linkDLLsInfolder is that same hook's reusable
worker, so this points the upstream mechanism at the right directory
rather than reimplementing it.
Measured before: capability_module's output was a lone 13 MB
capability_module_plugin.dll needing 7 non-system DLLs, and
`nix-store -qR` on it returned literally 1 -- itself -- because a PE
embeds no store paths for the reference scanner to find. After: the 13
dependency DLLs sit beside the plugin.
This is what makes an .lgx payload ($SRC_DRV/lib) complete, and what
lets a module ship a genuinely private vendored library on Windows.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(windows): walk external libraries' closures for the DLL search
A module's DLL walk could not see its external libraries' dependencies:
buildInputs contributes each library's own lib/ and bin/, but its
dependencies live in THEIR store paths. closureInfo enumerates the whole
graph, which is the only way to be right without hand-listing deps per
module.
Verified load-bearing by A/B rather than assumed: with the
logos-package-downloader fix in place but this change reverted,
libcurl-4.dll is absent from the installed module; with both, it is
present along with its transitive closure (brotli, nghttp2/3, ngtcp2,
idn2, psl, ssh2, unistring), and package_downloader loads on Windows.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): re-pin logos-nix and logos-module to their merged revs
L1 (logos-nix) and L2 (logos-module) are on their default branches now, so the
lock can name the merged revs instead of the pre-merge branch tips it was
resolving against while those PRs were open.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
1f6f4fcc0a |
fix(headers): a failing logos-cpp-generator must fail the build (#16)
* fix(headers): a failing logos-cpp-generator must fail the build
buildHeaders.nix swallowed the generator's exit status:
logos-cpp-generator ... || {
echo "Warning: ... may be expected if the module has no public API"
touch ./generated_headers/.no-api
}
so every real failure — a plugin the generator cannot dlopen (SDK/ABI
skew), an --api-style the pinned SDK rejects, a missing plugin file —
produced an EMPTY header set and exit 0. `nix build .#headers-<style>`
looked green with an include/ holding nothing but a `.generated`
placeholder, and the first symptom was an unresolved symbol when a
downstream module linked, far from the cause.
The escape hatch was aimed at a case that does not exist in this mode:
the generator introspects the plugin's QMetaObject, so a module with no
Q_INVOKABLE method still yields a wrapper class (with zero methods) and
exits 0. Measured against logos-cpp-sdk's generator:
Q_INVOKABLE present -> exit 0, <name>_api.{h,cpp}
no Q_INVOKABLE at all -> exit 0, <name>_api.{h,cpp} (no methods)
plugin fails to dlopen -> exit 3, nothing written
retired/unknown api-style-> exit 1, nothing written
plugin file missing -> exit 2, nothing written
So "no public API" is signalled by SUCCESS, not by a status, and every
non-zero status is a build error. buildPlugin.nix already treats the
generator's status as fatal; buildHeaders.nix was the outlier.
The generator call moves to lib/generate-module-headers.sh, which:
* fails the build on any non-zero status, echoing the status, the
plugin, the api style and the likely causes (pin mismatch first);
* fails a zero status that produced no <module>_api.{h,cpp} pair, so
an empty include/ can never be installed;
* reports a missing generator instead of a bare "command not found".
The install phase's "no headers -> empty include/" branch becomes an
error for the same reason.
tests/test-header-generator-guard.nix drives that script directly (so
the guard under test is the guard the build runs) across: no-public-API
success, generator failure, rejected api style, exit-0-with-no-output,
half-written output, missing generator, and argument forwarding
(--module-only / --api-style / --events-from).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* ci: run the header-generator guard check
The guard is the regression test for the swallowed exit status; CI only
built vanilla-plugin, so nothing would have caught a re-introduction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: the stub generator needs a store shebang, not /usr/bin/env
The guard test failed 11 assertions on Linux CI while passing on macOS. The
stubs never ran:
/build/work/bin-recorder/logos-cpp-generator:
/usr/bin/env: bad interpreter: No such file or directory
Error: logos-cpp-generator failed (exit status 126).
The nix build sandbox on Linux has no /usr/bin/env, so every stub exited 126
before recording anything, and the guard under test then reported a generator
failure -- a TRUE statement about a broken stub. The feature was working; the
fixture was not.
Stub shebang now points at ${pkgs.bash}/bin/bash (pkgs.bash is already in
nativeBuildInputs). lib/generate-module-headers.sh keeps its env shebang and
is unaffected: both this test and lib/buildHeaders.nix:98 invoke it as
`bash ${script}`, so its shebang is never used.
VERIFICATION IS INCOMPLETE, deliberately stated. The test passes on this
machine BOTH with and without the fix -- macOS's sandbox has /usr/bin/env, so
the negative control could not reproduce the failure and my local green proves
nothing. Docker was unavailable for a Linux run. The evidence for the fix is
the CI log naming the exact interpreter error, plus the mechanism being
unambiguous; Linux CI is the actual check.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
c0d62a1515 |
docs(nix): the header variants are qt and lp, not qt and std (#15)
Comment-only. These three comments still described the qt/std pair from before lp replaced std: `apiStyle` here only ever takes "qt" or "lp" (buildPlugin.nix picks between exactly those two), and logos-module- builder no longer builds a std variant for buildHeaders to be called with. Evaluation is unchanged — a module's drvPath is byte-identical before and after. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f33f264abb |
feat: Qt-typed api-style for universal ui_qml plugins (#14)
A universal UI plugin (type: ui_qml) derives a QtRO SimpleSource whose .rep slots are Qt-typed, so there's no reason to pin it to the std api-style: flip it to `qt` so its modules().<dep> dependency wrappers come out Qt-typed (QString/int), matching the view with no std<->Qt conversions. core universal and cdylib modules stay on the Qt-free `lp` surface; legacy stays `qt`. Pairs with logos-qt-sdk's LogosUiPluginContext (logos-co/logos-qt-sdk#3). Part of a 4-repo change (qt-sdk + plugin-qt + module-builder + tutorial). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
dca52b9fd5 |
feat: add generate mode to the plugin backend (#13)
Factor the code-generation half of buildPlugin's preConfigure into a shared generationScript, and add a `generate` builder that runs the exact same generators then snapshots the resulting source tree instead of compiling it. logos-module-builder uses this to expose a `.#generate` output — a ready-to-build codebase (module source + fully-populated generated_code/). The normal `build` path is behaviorally unchanged. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3aefbc55b5 |
feat(universal): route core universal modules through the cdylib mechanism (#12)
apiStyle for `interface: "universal"` core modules becomes "lp" (was "std"): they now build as header-first cdylibs — Qt-free impl + the common module-impl C ABI export seam + uniform Qt glue, with outbound modules().<dep> calls hitting logos-protocol's lp_* C ABI directly. An unchanged universal module recompiles Qt-free in its impl/wrapper TUs and still loads via the same Qt plugin (load + call ABI preserved). UI universal backends (type: ui_qml) derive a Qt SimpleSource and cannot be Qt-free, so they stay on the std surface. Everything else (legacy) stays qt. Pairs with logos-module-builder modulePreConfigure.universalCodegen, which now derives the LIDL from the impl header and runs the cdylib backend. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
82fe9fa76f |
cdylib: generate the Qt-free (lp) outbound surface (#11)
For interface: cdylib modules, drive the umbrella + dependency-wrapper codegen with --api-style lp so modules().<dep>... calls the logos-protocol C ABI directly (logos::LpClient) instead of LogosAPIClient — the module keeps Qt out of its own translation units. universal stays std, others qt. |
||
|
|
650ecfa063 |
Retarget Qt backend to logos-qt-sdk: three-root resolution in LogosModule.cmake (#10)
* Link modules against logos-qt-sdk + logos-protocol (Qt split)
The SDK stack split: logos-cpp-sdk is now the Qt-free header-only base
(detected via logos_module_context.h and linked as
logos-cpp-sdk::logos_headers), the Qt developer layer (LogosAPI,
provider glue, legacy PluginInterface) lives in logos-qt-sdk, and the
transports + lp_* C ABI live in logos-protocol.
logos_module() now resolves LOGOS_QT_SDK_ROOT / LOGOS_PROTOCOL_ROOT
(env / CMake var / sibling checkout), links
logos-qt-sdk::logos_qt_sdk (which chains logos-protocol and its
OpenSSL/Boost/nlohmann link interface), and adds the new include roots.
Source-layout builds compile qt-sdk's sources into the plugin and take
logos-protocol installed or as a subproject.
* cmake: compile the generated universal-UI glue into the plugin
type=ui_qml + interface=universal backends get their plugin class
(Q_PLUGIN_METADATA + initLogos wiring) generated into generated_code/ by
logos-qt-generator — this copy of LogosModule.cmake only picked up
logos_sdk.cpp, so the built dylib had no Qt plugin metadata at all
('Failed to extract plugin meta data'). Scoped append (not a broad glob)
so the provider-dispatch handling below can't double-compile.
|
||
|
|
68090d0a97 |
feat: pass concrete deps to the generator as --dep (LIDL, no dep build) (#9)
* feat: pass concrete dependencies to the generator as --dep (LIDL) buildPlugin accepts a `staticDeps` list (resolved by mkLogosModule from each dep's published `lidl` output) and emits one escaped `--dep=<name>=<lidl>[=<impl_class>]` flag per entry to logos-cpp-generator — generating the name-baked modules().<dep> wrapper WITHOUT building the dep's plugin. default.nix forwards the new arg. The header-copy `moduleDepIncludes` is now the TRANSITIONAL fallback: it skips any dep present in staticDeps (those come via --dep) and copies headers only for deps that don't publish a LIDL output yet (which still builds them). Banner-commented for removal once every module exposes packages.<sys>.lidl. Requires logos-cpp-sdk#77 (the --dep generator flag); driven by logos-module-builder. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * address review: use builtins.map for staticDepNames Consistent with the nearby builtins.elem (map is a global builtin, so this is a no-op functionally — clears the review note). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f58fbaa25a |
feat: pass interface dependencies to the generator (#8)
buildPlugin accepts an interfaceDeps list and emits one
--interface <name>=<path>[=<impl_class>] flag per entry to
logos-cpp-generator during preConfigure; default.nix forwards the new arg.
The resolved paths come from mkLogosModule (local files from src, cross-repo
files from a flake input), so the generator never touches flake inputs.
Generated <name>_api.{h,cpp} flow through the existing generated_code glob —
no CMake change needed.
Requires logos-cpp-sdk#74; driven by logos-module-builder#108.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8f8260e6f9 | adapt to latest cpp-sdk | ||
|
|
8c45eca86d | fix logosSdk dependency (#7) | ||
|
|
78a04d603d |
Merge pull request #6 from logos-co/feat/supportForRepInterfaces
feat: add support for rep files and its various interfaces needed |
||
|
|
343eafb294 | feat: add support for rep files and its various interfaces needed | ||
|
|
b30f00cbbc |
Merge pull request #5 from logos-co/move_non_qt_logic
move non-qt specific logic out; add tests |
||
|
|
1ee1fb2536 | move non-qt specific logic out | ||
|
|
d37ad1dc16 |
Merge pull request #3 from logos-co/use_new_providers
update flake |
||
|
|
d9f6dbbf52 |
support new cpp-sdk and logos-module for universal interface
update flake copy headers update flake |
||
|
|
07f457ff49 |
Merge pull request #4 from logos-co/copy-external-bundled-libs
bundle external bundled libs |
||
|
|
9896e01ae0 |
Merge pull request #2 from logos-co/dont_strip_metadata
don't strip module metadata |
||
|
|
49b486efd3 | don't strip module metadata | ||
|
|
5305602e80 | update readme | ||
|
|
68c372c894 | Initial commit: extract Qt plugin backend from logos-module-builder to enable swapping the plugin technology without chaning the module builder or the modules |