mirror of
https://github.com/logos-co/logos-module-builder.git
synced 2026-08-31 12:11:08 +00:00
fix/protocol-version-fail-closed
41
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9259fe86a1 |
fix: a protocol version that will not parse must not silently vanish
A Rust module's EXPORT SET is decided by the --protocol-version string:
lidl-gen gates logos_module_grant_host_services on >= 0.3 and the teardown
pair on >= 0.5. It was passed as
${lib.optionalString (protocolVersion != null) "--protocol-version ..."}
and that does not make a bad version WRONG — it makes the flag VANISH.
lidl-gen then falls back to "0.1.0", emits the seven founding exports, and
exits 0. Every Rust module in the workspace would quietly regenerate
incomplete, link cleanly, and fail at dlopen() on Linux with an undefined
symbol — invisible on macOS, three repos away from the cause, and reported
by the runtime as a module that LOADED.
protocolVersion goes null in two different situations and they are two
different bugs, so they now throw with two different messages: no
logos-protocol input at all (a caller error), versus a header that did not
parse (a bug in the regex right above it). Neither is a thing to fall back
from.
checks.module-impl-abi-nm (#207) already DETECTS this by reading the built
plugin's symbol table. This is the other half — refuse at the point of the
mistake, so it never reaches a build. Metadata stamping is untouched: null
there legitimately means "pre-protocol, load as legacy".
Proven: renaming the macro in the split pattern used to yield a
silently-broken plugin. It now yields
error: logos-module-builder: could not read LOGOS_PROTOCOL_VERSION_STRING
from /nix/store/...-source/cpp/logos_protocol.h, needed to generate the
Rust cdylib scaffold for 'rust_native_dep_module'. The header moved or
changed shape — fix the parse above; do NOT let it fall back, because
the fallback silently emits an incomplete module-impl C ABI.
Also deletes a dead duplicate of the same parse. It sat in the devShells
block with no consumer anywhere in it — an exhaustive grep finds exactly
four mentions of protocolVersion: this definition, the two uses in
`packages`, and that orphan. A duplicated, half-dead parse of the value
this whole failure mode hinges on is not something to leave lying around.
All eight checks green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8cd62c7427 |
feat: take the view templates from logos-view-module (#203)
* fix(cmake): one LogosModule.cmake, the union of the two that had diverged
There were two copies — here and in logos-plugin-qt — and the received wisdom
that "the builder's shadows the backend's" is wrong. Selection is BY MODULE
CLASS, and each copy was missing what the other had:
mkLogosModule.nix:79 tests the BUILDER root, always true, so CORE
modules take this copy;
buildCppPlugin.nix:191 tests the MODULE'S OWN src, almost never true, so
every ui_qml module falls through to the backend's.
This copy had no REP_FILE, no logos_replica_factory, no LogosViewPluginBase — a
core module passing REP_FILE had it silently swallowed into
MODULE_UNPARSED_ARGUMENTS. The backend's had no generated_code/*.cpp glob, no
metadata.json configure_file, no LOGOS_API_STYLE and no Rust static-lib block.
Merged as a strict union, 721 -> 888 lines. logos_find_qt stays a macro(): the
function() form is the older one, and the macro IS the mingw fix. The four
LogosView*.in templates come along because the replica-factory function
resolves them as siblings through CMAKE_CURRENT_FUNCTION_LIST_DIR.
Prerequisite for relocating the view-plugin templates, not a cleanup — that
move cannot be reasoned about while two files disagree about what a view module
even is.
Verified: a core module (test_basic_module_cpp) and a ui_qml module
(test_fullapi_ui) both build through this copy, and all five of the builder's
own checks pass, including rust-native-dep, which is what exercises the
LOGOS_MODULE_RUST_STATIC_LIBS block the merge carried.
NOTE this de-duplicates nothing yet: buildCppPlugin.nix still routes ui_qml at
the backend's copy, so every in-tree REP_FILE consumer still compiles against
logos-plugin-qt's. Flipping that routing, and having the backend re-export this
file, is the follow-up.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(qml): resolve interface_dependencies in the QML pipeline
buildCppPlugin.nix never resolved interface_dependencies, so a ui_qml module's
interfaces reached the generator only through cpp-generator's own re-read of
metadata.json — invisible to the plugin backend, and silently skipping any entry
with an `input:` (a cross-repo interface), which can only be resolved to a path
by the builder.
Now resolved and passed as interfaceDeps, exactly as mkLogosModule.nix does for
core modules. Needed because the qt consumer wrappers are now emitted per
dependency and per interface by the backend, which has to be told what they are.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: retire interface "provider"; throw instead of generating nothing
Drops providerCodegen and the PROVIDER_HEADER plumbing in LogosModule.cmake.
autoCodegen ends in a catch-all `else ""`, so simply deleting the branch would
make `interface: "provider"` mean "generate no glue at all": the module builds
green and is then un-callable from every consumer, with nothing in the log to
say why. It throws instead, naming the replacement.
tests/test-module-pre-configure.nix covers exactly that: a mutation control
confirms the suite fails with `expected expression to throw, but it succeeded
with ""` when the throw is removed. 258 -> 265 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: parse and validate metadata.json#host_services (C2)
A closed set — token_registry, token_delivery, dynamic_calls — parsed and
VALIDATED, unlike the older `capabilities` field, which parseMetadata.nix never
reads. A security decision must not ride on a key nothing looks at, which is why
this is a new field rather than a new meaning for that one.
Two tiers, and the distinction is the point:
* token_registry / token_delivery are TRUST-ROOT — a module holding them can
enumerate the token store or hand authority to an arbitrary target — so they
are additionally restricted to a hardcoded allowlist of module names
(capability_module). Hardcoded rather than configurable: an allowlist a
module could extend from its own metadata would not be an allowlist.
* dynamic_calls is elevated but NOT name-restricted, because a third-party
module that genuinely forwards untyped calls (a webview shell) has to be
able to ask for it.
An unknown service name is refused OUTRIGHT rather than filtered out: it means
the module believes it holds a privilege that does not exist, and silently
dropping the entry hides that from whoever wrote it. Same reasoning as
lp_grant_host_services, which rejects an unknown name wholesale and leaves the
existing grant untouched.
The declaration is advisory. Authority is the host's grant, pushed into the
module's own image over the module-impl C ABI.
265 -> 273 tests. The allowlist case carries a mutation control: with the
trust-root check removed the suite fails with `expected expression to throw, but
it succeeded with ... "name":"sneaky_module","host_services":["token_registry"]`,
so the test genuinely guards the boundary rather than restating it. Includes the
bypass attempt of mixing a permitted service in with a restricted one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: emit cdylib glue with logos-qt-host-generator, not qt-sdk's stale copy
B1 relocated the cdylib Qt-plugin glue generator to logos-plugin-qt (the Qt
plugin BACKEND owns the glue; the SDK does not), but universalCodegen and
cdylibCodegen still called `logos-qt-generator` from logos-qt-sdk, which ships
an OLDER copy of the same emitter. Both copies compile and both emit working
glue, so nothing failed — the builds simply used stale glue.
That is not theoretical. It is why the Phase C host-services grant never
reached a module: the new glue reads the `hostServices` property and forwards
it across the C ABI, the old one does not, and every build stayed green while
capability_module refused every requestModule for want of a grant that was
delivered to its process and then dropped on the floor.
Measured: `hostServices` appears 4 times in logos-plugin-qt's copy and 0 times
in logos-qt-sdk's. After the switch the built capability_module plugin
references logos_module_grant_host_services twice (the export plus the glue's
call) where it previously referenced it once.
Threads the plugin-qt FLAKE (not its lib — the generator is a package of it)
from flake.nix through lib/default.nix into buildCppPlugin, mkLogosModule and
mkLogosModuleTests, and puts logos-qt-host-generator on PATH everywhere
logos-qt-generator already was. `--backend ui` still uses qt-sdk's generator:
the view backend has not moved.
logos-test-modules ipc-tests: FAIL -> PASS, which is the end-to-end proof that
a universal capability_module now mints tokens under a host-granted privilege.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: thread logos-plugin-qt through the ui_qml build path too
mkLogosQmlModule imports buildCppPlugin at its OWN call site, separate from
lib/default.nix's, and that one was left unwired — so every ui_qml module with a
C++ backend hit `logos-plugin-qt = null` and failed to evaluate.
Caught by the regression batch: logos-accounts-ui went from building to FAIL
while all five test-modules checks passed, which is the shape of a defect in one
module CLASS rather than in the generator switch itself.
logos-accounts-ui builds again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: pin the cdylib glue to the maintained generator
The existing assertion ("universal still emits the cdylib Qt glue") checked for
the string `logos-qt-generator` and so failed the moment the builder switched to
`logos-qt-host-generator` — the test doing its job. Retargeted at the invariant
that actually matters (`--backend cdylib` is still emitted) and extended into a
drift guard:
* both codegen paths MUST name logos-qt-host-generator
* neither may fall back to `logos-qt-generator ` (trailing space, so it cannot
match the host generator; `--backend ui` still legitimately uses qt-sdk's)
Worth the four assertions because the failure they catch is invisible: both
copies of the emitter compile and both produce loadable glue, so reverting to
qt-sdk's would emit stale glue and stay green — which is precisely how the
host-services grant went undelivered for a whole phase.
265 -> 277 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(fixtures): declare universal authoring where the fixture already claimed it
The `core-universal-module` fixture is named "universal", is described as a
"Universal interface module fixture (mirrors logos-accounts-module pattern)",
and sits under a test section headed "Universal interface core module" -- but
it carried no `interface` key at all, so parseMetadata resolved it to
"legacy". The fixture was not testing the thing its name claims. The real
logos-accounts-module is `interface: universal`.
Declare `interface: universal` on the three `type: core` fixtures whose
legacy-ness was incidental, and pin the value so it cannot silently regress:
core-universal-module mirrors logos-accounts-module (universal)
core-module mirrors the minimal template (universal)
extlib-module mirrors the external-lib template and the real
vendor-extlib core modules (storage/wallet/libp2p/
blockchain) -- all universal
No `type: core` module in the workspace is still legacy-authored.
The other four legacy fixtures are LEFT legacy on purpose, and are now pinned
with an assertion + the evidence, so the coverage is deliberate instead of
accidental:
ui-qml-backend-module mirrors logos-package-manager-ui, which is STILL
legacy; the universal ui_qml shape is already covered
on disk by the ui-qml-backend TEMPLATE
qml-module QML-only: no C++ backend to derive a contract from;
module-with-deps mkLogosQmlModule never reads `interface`, and
universal+ui_qml routes to uiCodegen which demands a
.rep neither fixture has
ui-module `type: ui` has no template and one real instance
(logos-basecamp), which is legacy
These are metadata-parse fixtures only -- none of the three edited fixtures
contains any C++, so this is a metadata-shape correction, not a code port.
No fixture in this repo held a hand-written Qt plugin to migrate.
Verified: all 5 checks run BY NAME (default, qml-integration, rust-native-dep,
static-extlib, test-framework-integration) pass before and after. `default`
goes 277 -> 284 assertions, exactly the 7 added. qml-integration,
static-extlib and test-framework-integration produce byte-identical store
paths to baseline. rust-native-dep's path changes only because mkLogosModule
embeds the builder's own flake source (its fixture src is byte-identical:
xa6m5ci38fc5adcbi0hfyqvgzrmyr3j9) -- attributed by rebuilding with the change
stashed. Positive control: reverting core-universal-module to legacy makes
`default` FAIL on the new assertion, so it bites.
NOT verified: no plugin is built from any edited fixture (they are metadata
only), so there is no `lm methods` surface to diff -- the method-surface check
does not apply here. Nothing downstream of this repo was rebuilt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: hand buildHeaders the qt generator, so a contract can drive the wrapper
`contractLidl` already reached buildHeaders on every call — the qt backend just
declined to read it outside cross-compilation. What was missing was the tool:
logos-qt-generator went into buildPlugin's extraNativeBuildInputs and nowhere
near buildHeaders, so the contract-driven branch could not have run even if it
had been selected.
Passing it is the whole change on this side. `logosQtGenerator` is already the
BUILD-platform binary (buildSystemFor), which is what a cross build needs from
a pure tool role, and the backend puts only the generator its selected emitter
actually uses on PATH — so an lp variant and a contract-less module keep the
derivation they had.
Deliberately NOT passed to moduleIncludeLp: logos-qt-generator has no lp
backend. The lp wrapper still comes from logos-cpp-generator's lp emitter,
which is not the legacy Qt one and is not what this migration is about.
buildCppPlugin — the ui_qml pipeline — is left on the legacy path and now says
why. It computes no `lidl` output for the module it builds, so it has no
contract to hand over; and it costs nothing today, because mkLogosQmlModule
reads `moduleLib` and never `moduleInclude`, so that derivation is never
realised. A ui_qml module is a leaf and nothing consumes its client wrapper.
Fixing it therefore starts with giving that pipeline a contract, not with
adding a flag here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: link the Qt host runtime from logos-qt-host, not logos-qt-sdk
The Qt host runtime -- LogosAPI, LogosAPIProvider, LogosProviderBase and the
legacy PluginInterface -- moved out of logos-qt-sdk into logos-plugin-qt, where
it ships as the `logos-qt-host` CMake package. LogosModule.cmake now takes it
from LOGOS_QT_HOST_ROOT and links logos-qt-host::logos_qt_host; mkLogosModule,
buildCppPlugin, mkLogosModuleTests and the module dev shell all pass that root.
logos-qt-sdk is NOT dropped. It stays required for the Qt-typed headers that
were never part of the host runtime -- logos_qt_lp_bridge.h and logos_qt_wire.h,
which the generated Qt consumer wrappers #include by name, and
logos_ui_plugin_context.h -- and for the logos-qt-generator that emits them. Its
include dirs are kept, but AFTER the host runtime's, so the host headers win the
five names the two roots share.
mkLogosModuleTests now also passes -DLOGOS_QT_HOST_ROOT to logos-test-framework,
whose LogosTest.cmake already prefers it and falls back to LOGOS_QT_SDK_ROOT.
logos-plugin-qt's logos-protocol input gains a `follows` (as logos-qt-sdk's
already had). Building logos-qt-host makes that input load-bearing for the first
time, and a second logos-protocol on the link line would mean a second
TokenManager singleton.
Nothing here fails open. A LOGOS_QT_HOST_ROOT with no runtime under it is a
FATAL_ERROR rather than a fall-through to logos-qt-sdk; a host package that
resolves without defining its target is a FATAL_ERROR rather than a plugin
linked without a runtime; and the legacy logos-qt-sdk fallback, which stays
available while other consumers migrate, announces itself in the configure log.
The new `qt-host-repoint` check pins all four selection paths plus both guards.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(b2b): probe logos-qt-sdk by a header it owns, and drop the qt-sdk fallback
Two changes, both forced by logos-qt-sdk no longer forwarding the host runtime's
headers.
The presence probe tested ${LOGOS_QT_SDK_ROOT}/cpp/logos_api.h or
include/cpp/logos_api.h. Neither exists any more: the host split moved
logos_api.h out of qt-sdk's cpp/, and the forwarder that kept it in include/cpp/
is gone. A correct root would have been reported as "logos-qt-sdk not found"
while CMake was looking straight at it. It now probes logos_qt_wire.h, which
this SDK does own -- along with logos_qt_lp_bridge.h and
logos_ui_plugin_context.h, the only reason the root is still required at all.
(The source branch was already dead on arrival: qt-sdk's cpp/ has held no
logos_api.h since B1, so LOGOS_QT_SDK_IS_SOURCE could never be TRUE.)
The legacy fallback -- no LOGOS_QT_HOST_ROOT means take the host runtime from
logos-qt-sdk's forwarding package -- is deleted. Its premise is false now, and
what it would do instead is worse than failing: find_package(logos-qt-sdk)
still succeeds and its INTERFACE target still chains logos-qt-host, so the
build would work by accident through one more hop while the include path it set
up (${LOGOS_QT_SDK_ROOT}/include{,/cpp,/core}) contained none of the headers it
was chosen for. LOGOS_QT_HOST_ROOT is now the one source, and its absence is a
FATAL_ERROR that says so.
test-qt-host-repoint.nix follows: its test 3 asserted the fallback happened and
was announced; it now asserts the fallback is gone -- a build with no
LOGOS_QT_HOST_ROOT aborts even with a good logos-qt-sdk root present. Test 3b is
new and pins the probe fix directly: that same run must NOT say "logos-qt-sdk
not found", and a qt-sdk root scaffolded the way the real prefix now looks
(logos_qt_wire.h, no logos_api.h) is still found.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: LogosModule.cmake is this repo's, and every module type now gets it
logos-plugin-qt shipped a second copy of cmake/LogosModule.cmake, and which
one a module configured with was decided by its TYPE:
buildCppPlugin.nix set LOGOS_MODULE_BUILDER_ROOT only when the MODULE's own
repo carried a cmake/LogosModule.cmake. No module does, so every ui_qml
plugin fell through to the value logos-plugin-qt's lib/default.nix sets --
its own root -- while mkLogosModule's core path used this repo's copy.
Both compiled, so the two drifted with nothing to say so: this copy carried
A1's union (the generated_code/*.cpp glob, LOGOS_API_STYLE, the metadata
configure_file, Go/Rust static-lib linking, the external-library FATAL_ERRORs,
logos_find_qt as a Windows-safe macro) while plugin-qt's had B2b's addition of
logos_qt_arg_decode.{cpp,h} to the host-runtime source list -- which
qt_provider_object.cpp's dispatch needs, and whose absence is a link error, not
a configure error. That file is ported here.
Routing: both nix entry points now pass LOGOS_MODULE_BUILDER_ROOT
unconditionally, so a ui_qml plugin and a core module configure with the same
file. The old module-local branch is gone with it -- an unconditional value is
what makes "there is one copy" a property of the code rather than of what
happens to be on disk -- and a missing file now throws instead of falling back
to something that quietly configures differently.
logos_module() prints the file it came from, so a future fork shows up in any
configure log rather than after a phase of debugging.
Verified: counter, counter_qml, logos-storage-ui, logos-package-manager-ui,
test_fullapi_ui, logos-accounts-ui, logos-blockchain-ui, logos-wallet-ui and
logos-evm-wallet-ui (ui_qml) plus test_basic_module_cpp and capability_module
(core) all build, and their configure logs name this file. Checks by name:
default, qml-integration, qt-host-repoint, static-extlib OK; rust-native-dep
and test-framework-integration fail identically at the pre-change baseline
(this repo's own lock pins a logos-protocol without TokenManager::forIdentity,
so logos-qt-host will not compile from it -- green in the workspace closure).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: read the view templates from their owner, and check the ABI they declare
Two changes, one theme: nothing in this build should be able to pick a second
copy of a file without saying so.
1. cmake/LogosView*.in are deleted. They now live once, in logos-plugin-qt's
cmake/, and _logos_module_add_replica_factory takes the directory from
LOGOS_VIEW_TEMPLATE_DIR (cache variable, then environment) instead of
probing for a sibling of this .cmake file.
The sibling probe is what made the duplication possible: it meant the
templates had to sit next to LogosModule.cmake, but logos-plugin-qt's
rep-file-plugin fixture instantiates them too and cannot depend on this
repo, so it kept a byte-identical copy. logos-plugin-qt is the only place
both consumers can read one copy from; see its cmake/README.md.
There is no fallback. An unset variable, or a directory missing any of the
four templates, is a FATAL_ERROR naming what it wanted.
2. A new `view-interface-abi` check, in CI.
LogosViewPlugin and LogosViewReplicaFactory are declared TWICE and always
will be: module side in logos-plugin-qt's templates, host side in
logos-view-module-runtime's headers. They cannot share a header — a module
plugin has to compile against Qt alone, and logos-view-module-runtime
depends on logos-plugin-qt, so the include could only point the wrong way.
They bind at runtime through the IID string, and a mismatch there is silent
the whole way: both sides compile, the plugin loads, qobject_cast returns
nullptr, the view is blank.
Until now the only thing holding those two in agreement was a comment
asking a human to keep them in sync — which had already rotted (it pointed
at src/, the headers are in include/) and prevented none of the five
duplicate-source defects in this refactor.
This repo depends on logos-plugin-qt AND on logos-view-module-runtime, and
is the only one that does, so the comparison lives here. tests/
view-interface-abi.py extracts the IID and the ordered pure-virtual list
from both sides and fails on any difference — including a class rename,
which would otherwise turn the comparison into a vacuous pass.
Proven by mutation: adding an argument to enableRemoting on the module side,
bumping the IID on the host side, and renaming the host-side class each turn
the check red with a specific message; reverting each turns it green.
Note: like the already-red rust-native-dep and test-framework-integration
checks, view-interface-abi does not evaluate until this repo's logos-plugin-qt
pin advances past the commit that adds packages.<sys>.logos-view-templates. It
says exactly that, and names logos-plugin-qt as the input to move.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(view-abi): check the class that actually binds at runtime, and let it run
The guard had two independent faults; either one alone made it decorative.
1. IT DID NOT CATCH WHAT IT NAMED. The script read `#define <Name>_iid` and
the `virtual ...;` list inside `class <Name>`. Both of those are the
abstract shape. The runtime binding is declared on the CONCRETE class the
template also contains — @LOGOS_FACTORY_CLASS@, with Q_OBJECT,
Q_PLUGIN_METADATA and Q_INTERFACES — and that class was never inspected.
Two mutations that both reach production were run against it and it stayed
green on both:
Q_PLUGIN_METADATA(IID LogosViewReplicaFactory_iid)
-> Q_PLUGIN_METADATA(IID "logos.view.replica_factory/2.0")
Q_INTERFACES(LogosViewReplicaFactory) deleted
It now parses every class in the file, finds the ones deriving from the
interface, and requires: at least one exists (so a dropped base class or a
rename cannot make the comparison vacuous); a QObject-derived one has
Q_OBJECT; a QObject-derived one names the interface in Q_INTERFACES; and
any Q_PLUGIN_METADATA IID resolves to the IID the HOST casts on.
Two more windows were open and are now closed: the argument of
Q_DECLARE_INTERFACE was only checked for EXISTENCE, though it is the string
qobject_cast compares and need not be the #define — it is now resolved
through the file's #defines and compared both across sides and against that
side's own #define; and the interface's own base list, which is its vtable
layout, was not compared at all.
Proven by mutation, 12 cases, each reverted to green afterwards: the 2
above, plus a changed Q_DECLARE_INTERFACE IID, a dropped interface base, a
renamed interface, a dropped Q_OBJECT, a base added to the interface, a
*ViewPluginBase that stops implementing LogosViewPlugin, the 2 the old
guard already caught (regression), and 2 null controls — a comment edit and
a reflowed declaration — which must stay GREEN and do.
The nix wrapper also sets `set -o pipefail` explicitly and folds stderr
into $out. `... | tee $out` takes tee's exit status; that it worked at all
depended on stdenv happening to set pipefail, which is not a thing a guard
should rest on.
2. IT COULD NOT EXECUTE. With no overrides — the CI condition — the check
EVAL-THREW: this flake pinned logos-plugin-qt at b8b9b414, which predates
packages.<sys>.logos-view-templates. So the step was unconditionally red
from the day it landed, camouflaged by test-framework-integration being red
beside it for the same stale pin.
Both logos-plugin-qt and logos-plugin-core (the same repo, selected per
module type — a split pin gives core and ui modules two different
LogosModule.cmake and two Qt host runtimes) now name rev fcf5a29 in the
URL, not just in the lock, so `nix flake update` cannot walk them back onto
a master that still lacks those outputs.
That bump fixes the EVAL failure for all three affected checks.
view-interface-abi is now GREEN with no overrides. rust-native-dep and
test-framework-integration now evaluate and build, and fail later on an
unrelated pre-existing problem: logos-qt-host does not compile against the
pinned logos-protocol, whose TokenManager has no forIdentity /
isolateIdentity. That is the same failure logos-plugin-qt's own #qt-host
check has at fcf5a29 with its own lock and no overrides, so it is not
introduced here and cannot be fixed here.
CI: qt-host-repoint is added — it is hermetic and it existed without ever
being listed. rust-native-dep is deliberately still not listed, with the
reason recorded next to it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(view-abi): follow bases transitively, or the guard watches an empty window
Implementor detection matched only the immediate base clause, so one level of
indirection skipped every concrete-class check — and the "no implementors"
backstop did not fire either, because the helper base still counted as an
implementor in its own right.
That is not a hypothetical shape. lidl_gen_ui.cpp emits every real ui_qml
plugin deriving from LogosViewPlugin INDIRECTLY, via <Rep>ViewPluginBase, so
for that half of the pair the window this guard exists to watch was empty in
production.
Resolve each class's transitive base set through the other classes in the file
before testing membership, and make the QObject check transitive with it. The
two mutations that escaped — an indirect base with the IID bumped to /2.0, and
an indirect base with Q_INTERFACES deleted — now fail, each naming its own
cause; indirection alone stays green and reports both implementors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): bump the B3/B4 stack, and rev-pin the inputs update cannot reach
Moves every input this builder shares with the SDK codegen stack onto the
revisions that stack has actually pushed:
logos-protocol 03842db -> c8bab12 feat/per-client-token-store
logos-cpp-sdk e3744fb -> a04b278 feat/sdk-codegen-b3-d11
logos-qt-sdk c6be61d -> 8a06b87 feat/sdk-codegen-b3-d11
logos-plugin-qt fcf5a29 -> cc24fa1 feat/b4-qt-host-windows-target
logos-plugin-core fcf5a29 -> cc24fa1 same rev, per the type split
logos-view-module-runtime 471dd56 -> 5510acd feat/sdk-codegen-b4-qt-host
logos-standalone-app 288fec2 -> 39f4f2b feat/sdk-codegen-b4-qt-host
logos-test-framework eb1600c -> c382ab1 feat/sdk-codegen-b4-test-framework
Every one of those is a BRANCH TIP, not master. Six of them were plain
`github:logos-co/<repo>` urls, so `nix flake update` would have relocked them
onto master and the bump would silently not have happened -- the update
succeeds, the lock changes, and the rev is still wrong. They are rev-pinned
here for the same reason logos-plugin-qt already was. Each is a fast-forward
from its own master (03842db, e3744fb, c6be61d, 8846fc5, 471dd56, 288fec2 and
eb1600c are ancestors of their targets), so pinning gives up nothing; drop the
revs as the branches land.
logos-plugin-qt/-core go to cc24fa1, the tip of feat/b4-qt-host-windows-target,
NOT to the sibling feat/b4-qt-host-windows-target-8ccb1fc. The sibling
re-baselines onto 8ccb1fc and drops the LogosModule.cmake repoint and the
view-templates commit, so its flake exposes no packages.<sys>.logos-view-templates
and `view-interface-abi` would hit its own throw instead of running. cc24fa1
carries the old fcf5a29 pin's content under rebased shas (4c581a6/e4ea357/fcf5a29
are fe780a6/34704d1/3d7e3e6 there).
logos-test-framework's pin is the least obvious and the one worth keeping: this
branch teaches mkLogosModuleTests to pass -DLOGOS_QT_HOST_ROOT, and it is
LogosTest.cmake on c382ab1 that prefers it -- master's copy knows only
LOGOS_QT_SDK_ROOT, so a master lock links the unit tests against the wrong
runtime root without failing.
The follows keep one logos-protocol on the link line: root, logos-plugin-qt,
logos-plugin-core, logos-qt-sdk and logos-cpp-sdk all resolve to c8bab12.
All seven checks build on aarch64-darwin: default, qml-integration,
qt-host-repoint, rust-native-dep, static-extlib, test-framework-integration,
view-interface-abi.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(metadata): codegen.consumer_api_style, gated on how the image gets tokens
Lets a module declare its CONSUMER type surface independently of its provider
packaging. A cdylib-packaged module can now hold Qt-typed dependency wrappers —
the combination that was previously inexpressible, and the only reason
`interface: "provider"` and `--provider-header` are still alive.
The enabling codegen landed first (logos-cpp-sdk 620f2e1, logos-qt-sdk 6b88630):
a default-constructible Qt umbrella and a consumer binding that takes an
explicit origin instead of a LogosAPI. An earlier attempt at this key FAILED
because it was added without that codegen, so its only reachable outcome was a
compile error inside generated code. It is meaningful now.
── The predicate ───────────────────────────────────────────────────────────
packagedAsCdylib = interface == "cdylib"
|| (interface == "universal" && type != "ui_qml")
character-for-character what modulePreConfigure.autoCodegen branches on when it
decides to emit the module-impl C ABI, so the two cannot drift.
The distinction is NOT "this image has no LogosAPI" — a cdylib module's plugin
does contain one, in the Qt glue that receives tokens. What separates the two
worlds is where the image's own TokenManager is FILLED:
cdylib-packaged : logos_module_accept_token -> lp_token_save, same image.
An origin-bound wrapper's null sync hook costs nothing.
Qt plugin : only LpBridge::syncTokens, installed exclusively by
forTarget(api, …). An origin-bound wrapper there would be
silently unauthenticated — the exact defect syncTokens
exists for.
Measured with `nm -gU`, not argued: `logos_module_accept_token` is defined in
exactly the shapes the predicate calls true, across six real plugins.
`universal` + `ui_qml` is the trap — it looks cdylib-shaped but uiCodegen emits
only view glue, so it is a Qt object holding a LogosAPI. Excluded.
`--binding origin` is not selectable from metadata at all: it is derived as
`isQt && packagedAsCdylib` in the backend, an AND no key can reach from the
wrong side. What a key CAN express wrongly is the mirror move — `lp` on a Qt
plugin — unsafe for the identical reason, and refused at eval naming the key.
Verified additive on seven modules spanning legacy/core, universal/core,
universal/ui_qml and QML-only: `diff -r` clean, narHash identical where
comparable, and `test_qml_only` byte-identical by store path. Note compiled
modules cannot share a store path across any builder edit, since mkLogosModule
bakes LOGOS_MODULE_BUILDER_ROOT — narHash is the honest comparison.
Three mutation controls, each failing loudly: removing the eval throw, nulling
the backend's own assertion, and dropping the packagedAsCdylib conjunct from
`originBound` — the last being the one that pins the token-mirror hazard rather
than just the lp refusal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): take the backend that honours consumer_api_style
|
||
|
|
d256a42faa |
feat(windows): give a cross Rust module winpthreads, headers and archive (#200)
nixpkgs builds mingw-w64 against mcfgthread, so winpthreads is a separate package on no default path. Plenty of vendored C assumes the standard mingw environment, where it is simply present. aws-lc-sys is the case that surfaced this, and it matters because a module cannot route around it: aws-lc-rs arrives through third-party crates (reqwest -> hyper-rustls -> rustls, and the alloy stack), and Cargo features are additive, so a consumer cannot switch rustls to `ring` from its own manifest. Two halves, and the first alone is a trap: - Headers. Without them the build dies on `fatal error: sched.h: No such file or directory`, which reads like the platform is unsupported when it is only unwired. cc-rs appends CFLAGS_<triple>/CXXFLAGS_<triple> to the compiler invocations it drives, so this reaches build-script C without touching the Rust compile. - The archive. With <sched.h> reachable, aws-lc compiles aws-lc's thread_pthread.c, and the plugin link then wants pthread_rwlock_*, pthread_once, pthread_key_create, sched_yield. winpthreads goes in as a buildInput so its lib/ lands on NIX_LDFLAGS, and LogosModule.cmake's WIN32 branch names `pthread`. Adding `pthread` to that branch is free for modules that do not need it: ld pulls archive members on demand. Verified — keystore_module's Windows plugin imports no libwinpthread and stages no pthread DLL, and both its targets still build. The linker half of this same story (`-L native=<pthreads>/lib` for windows-gnu std) landed in #197; this completes it. Native builds are untouched: every branch is guarded by `rustCrossTarget != null`. Note for review: the resulting image carries both libmcfgthread-2.dll (libstdc++) and libwinpthread-1.dll (aws-lc). Both are thin layers over Win32 primitives and coexist in ordinary mingw distributions, but the DLL has not yet been exercised on real Windows — a green link is not a green run. Verified: chat_module cross-builds to a 40MB PE32+ x86-64 plugin exporting qt_plugin_instance / qt_plugin_query_metadata_v2, with the crypto stack (aws-lc-rs, rustls, de-mls, libchat) really linked in and libwinpthread-1.dll staged into its DLL closure. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ea3b393438 |
fix(cross): fail loudly when a dep publishes nothing for the target system (#199)
* fix(cross): fail loudly when a dep publishes nothing for the target system
Two fixes, both found while chasing why chat_ui's Windows build died on
`fatal error: chat_module_api.h: No such file or directory`.
**The silent fallback.** Resolving a transitional header-copy dependency
did `input.packages.${system} or null`, and on null fell back to `input`
itself. For a flake input that is the dependency's SOURCE TREE, so the
plugin build gets a header root with no generated headers in it and fails
far away inside a generated TU — or, worse, succeeds against whatever
stale headers happen to be checked in. chat_module v0.2.2 publishes only
the four native systems, so every x86_64-windows consumer of it resolved
its headers to the chat_module checkout.
A flake that publishes `packages` but nothing for this system is now an
error naming the dep, the system, and what it does publish. The fallback
survives only for a genuinely bare-derivation input (no `packages` attr
at all), which is the pre-refactor shape it exists for.
**One resolver, not two.** That logic was copy-pasted into
mkLogosModule.nix (core modules) and buildCppPlugin.nix (ui_qml view
modules). It now lives once in common.nix and both call it. This is not
tidying: chat_ui is a view module, so a fix applied only to
mkLogosModule.nix left the case that motivated it untouched — which is
exactly what happened on the first attempt here.
Also single-sources the Windows build platform from
logos-nix's `windowsBuildSystems` instead of repeating "x86_64-linux" as
a literal in two places. Pinning it (rather than using the evaluating
system) is deliberate: it keeps packages.x86_64-windows.* one
well-defined derivation whoever evaluates it, so a Darwin and a Linux
checkout agree and share a cache. Widening it is now a change in
logos-nix, not here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cross): fail loudly in collectAllModuleDeps too
Same silent-fallback shape as the header-copy resolver, one level out:
when a dependency published no usable package for the target system,
`collectAllModuleDeps` fell through to `input` -- putting the dep's
SOURCE TREE where an LGX package belongs. mkStandaloneApp then ships a
directory of .cpp files in place of a module, and the failure only shows
up at runtime as a module that never loads.
The two autoBundleLgx throws right above it already guard the adjacent
case ("a silent fallback would cause mkStandaloneApp to silently omit
the dependency at runtime"), so this closes the remaining hole in the
same function.
A bare-derivation input (no `packages` attr at all) still takes the
fallback -- that is the shape it exists for.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f007edf1d7 |
feat(windows): stage declared runtime files, and read <drv>/bin (#196)
Two changes, both about runtime libraries that reach a module's output
through nothing the build can infer.
1. metadata `include` now does something.
The field is parsed (parseMetadata.nix:31) and read by NOTHING: it drives
no copy, no error, not even a log line, and nix-bundle-lgx omits it from
the manifest allowlist. Three modules declare it today.
Implement what it always advertised: each declared filename is looked up
in the module's runtime nix packages and resolved external libs -- in
both lib/ and bin/ -- and copied into $out/lib.
Nothing else can do this. The Windows DLL walk (logos-plugin-qt's
postFixup -> linkDLLsInfolder) is IMPORT-TABLE driven, so a library
reached only through dlopen appears in no table and is invisible to it;
on Unix there is equally no DT_NEEDED entry to follow. delivery_module
hit exactly this with libpq: declared, needed at runtime, and silently
absent from the module output.
A name that matches nothing is NORMAL, not an error: the list is a
deliberate cross-platform superset (modules name the .so, .dylib and .dll
spellings side by side), so at most one spelling can ever match.
It runs before the module's own postInstall, so author hooks can react to
what was staged, and before the Windows postFixup, so linkDLLsInfolder
then also walks the staged library's OWN imports -- libpq pulls in
libssl/libcrypto that way.
2. copyExternalLibsToLib also reads <drv>/bin.
Mirror of logos-plugin-qt#18 for the tests/QML staging path: a library
following the Windows convention ships its runtime half in bin/. Matching
only *.dll keeps it inherently Windows-only, so no platform flag is
needed here (this file only receives `lib`).
Verified by cross-building logos-delivery-module for x86_64-windows: the
module output gains libpq.dll ("staged libpq.dll" in the build log), and
the plugin loads and dispatches on real Windows.
Depends on logos-co/logos-plugin-qt#18 for the plugin build path.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
95869a1ced |
feat(windows): cross-compile Rust modules to x86_64-pc-windows-gnu (#197)
A `codegen.rust` module could not be built for Windows. Two independent halves were missing. **The crate compile.** The Rust toolchain has to RUN on the builder and merely TARGET Windows, so it is taken from the build system with `targets = [ "x86_64-pc-windows-gnu" ]` rather than from the cross set — asking the cross set for `rust-bin` evaluates `targetPackages.threads.package`, which only the MinGW branch defines. That choice then has a consequence: nixpkgs' `cargoBuildHook` derives `--target` from the stdenv's HOST platform, so left alone it silently builds for the BUILDER and yields a perfectly good Linux archive that cannot link into a PE. The cross case therefore drives cargo directly. Alongside that, `CC_/CXX_/AR_<triple>` are set so cc-rs compiles build-script C for Windows too, `nix.rust` build packages resolve from `buildPackages` (they are host tools), and `lidl-gen` resolves from the build system (it is a generator, not a shipped artifact). **The plugin link.** `LogosModule.cmake` chose the native libraries Rust's `std` leaves undefined with a two-way `if(APPLE)/else()`, so "else" meant "Linux" and put `pthread dl` on the Windows link line — `dl` does not exist on Windows, and `pthread` lives in a separate mingw_w64-pthreads package that is not on the sysroot search path. The new `elseif(WIN32)` list is derived from the archive's own undefined symbols rather than guessed, and the finished DLL's import table confirms all of it is reachable and none of it redundant. Every cross branch keys off `rustCrossTarget != null`, and `buildSystemFor` is the identity on native systems, so native builds take exactly the path they did before. Verified: keystore_module and token_list_module both produce PE32+ x86-64 plugin DLLs exporting qt_plugin_instance / qt_plugin_query_metadata_v2, with the Rust crypto and TLS stacks (eth-keystore, coins-bip39, alloy-signer-local; ring, rustls, webpki, hyper, reqwest) really linked in, and both still build for x86_64-linux unchanged. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9d3b7cc1f5 |
feat(windows): give mkLogosModule an x86_64-windows target (#194)
* feat(windows): give mkLogosModule an x86_64-windows target
Routes every package-set construction in the builder through one helper,
common.mkPkgs, and teaches that helper the "x86_64-windows" pseudo-system.
This is the leverage point for the whole module ecosystem: modules do not
construct pkgs themselves, so fixing it here lets all ~40 of them target
Windows without each re-deriving the cross plumbing.
x86_64-windows cannot be produced by `import nixpkgs { system = ...; }` --
a cross derivation's `system` attribute is its BUILD platform, so it needs
localSystem/crossSystem plus the mingw overlays, which is exactly what
logos-nix.lib.mkWindowsPkgs wraps. logos-nix was already an input of this
flake but was never threaded into the lib; it now reaches common.nix via
lib/default.nix, and the pseudo-system is only advertised when it is
present, so a caller without it is unaffected.
Ten sites moved: common.nix, buildCppPlugin.nix x2, mkLogosQmlModule.nix
x2, mkLogosModuleTests.nix, mkLogosModule.nix x4. `systems` and
`forAllSystems` moved into the let block, since an attribute set is not
recursive and both now reference each other.
The Rust path keeps its rust-overlay via mkPkgsWith, which THROWS for a
Windows target rather than silently dropping the overlay and handing back a
package set that is not what the caller asked for. Rust modules on Windows
were already out of scope; this makes that explicit at eval time instead of
producing a subtly wrong build.
Verified: logos-capability-module evaluates unchanged on aarch64-darwin and
now also evaluates for x86_64-windows.
* feat(windows): split host tools from target artifacts in the builder
Follow-up to the x86_64-windows target: getting the pseudo-system to
EVALUATE was not enough, because several things the builder puts on the
build machine were being taken from the TARGET package set.
* common.buildSystemFor names the system a build for a target actually runs
on (identity natively, x86_64-linux for x86_64-windows). Host tools now
come from there.
* logos-cpp-sdk's default output serves two roles at once -- it carries the
logos-cpp-generator BINARY and the target headers/CMake package. Under
cross those must come from different package sets, so the 15 consumers
are now split by role: everything landing in nativeBuildInputs (plus
buildHeaders and the moduleLidl generator invocation) takes the new
logosSdkBuild, while every -DLOGOS_CPP_SDK_ROOT / LOGOS_CPP_SDK_ROOT /
buildInputs slot keeps the untouched target logosSdk. Getting this
backwards is worse than the failure it fixes: it would SUCCEED and link
the wrong architecture. mkLogosModuleTests is the proof case -- same
derivation, logosSdkBuild in nativeBuildInputs, logosSdk in buildInputs.
(The symptom was "logos-cpp-generator: command not found" rather than an
exec-format error because logos-cpp-sdk/nix/bin.nix:39 guards the copy on
the unsuffixed name with no else, so a mingw build silently ships an
EMPTY bin/. Worth fixing there too.)
* pkgs.jq -> pkgs.buildPackages.jq: jq is target-typed as well and runs in
preConfigure.
* extraCmakeFlags now prepend pkgs.logosQtCrossCmakeFlags, which point Qt at
its host TOOL packages (repc et al). Absent -- and so empty -- natively,
hence no isWindows guard. The symptom is misleading: CMake names
Qt6RemoteObjects, but the target config resolves fine and it is
Qt6RemoteObjectsTools that is missing.
capability-module now gets through evaluation, generation and Qt discovery.
It does NOT build yet: Qt 6.11.1 then hits a duplicate imported-target error
in Qt6EntryPointMinGW32Target.cmake, which is a Qt-on-MinGW CMake issue
rather than a Logos one.
* fix(windows): make logos_find_qt a macro, and pass the header contract
Two changes that together get a module cross-building.
1. logos_find_qt was a function(). Qt's mingw Qt6EntryPointMinGW32Target.cmake
guards itself with a bare include_guard(), which CMake scopes to the most
recent FUNCTION scope, while add_library(IMPORTED) creates a DIRECTORY-scoped
target. So the guard variable died at endfunction() while the target
survived, and the next find_package(Qt6) -- via logos-protocol's or
logos-qt-sdk's find_dependency -- re-entered and hit "cannot create imported
target EntryPointMinGW32".
This is upstream Qt: QTCREATORBUG-32887, confirmed by a Qt maintainer,
never fixed in qtbase (the file is byte-identical 6.7 through dev). Qt's own
fix was consumer-side, converting a function to a macro for exactly this
reason (qt-creator 9ded3246). Every other target file in that tree guards on
TARGET existence, which is scope-proof.
The two PARENT_SCOPE qualifiers had to go: under a macro they would have
written to logos_module's CALLER.
2. Threads a per-module header contract through to the backend's buildHeaders,
so a legacy module without a derived LIDL can still produce typed headers
when the plugin cannot be introspected. See logos-plugin-qt.
* fix(windows): find and ship external libraries under mingw
LogosModule.cmake's EXT_LIB_NAMES listed only .dylib/.so/.a, so every
module declaring nix.external_libraries failed to cross-compile with
"External library '<x>' ... was not found in .../lib" -- which reads like
a staging bug rather than a missing filename spelling.
On Windows a shared library is TWO files: you LINK the import library
(lib<x>.dll.a under mingw) and SHIP the .dll. Both are now searched.
The runtime copy needed care of its own: the import library's name ends
in ".a", so the existing `NOT MATCHES "\\.a$"` test would classify it as
a static archive and skip the copy -- producing a plugin that links
cleanly and then fails to load with no DLL beside it. When a .dll.a or
.lib was linked, the companion .dll is resolved and copied instead, and
its absence is a hard error rather than a silent omission.
Unblocks logos-package-downloader-module and every other external-library
module under cross.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): re-pin the L1-L5 inputs to their merged revs
logos-nix (L1); logos-protocol, logos-module (L2); logos-cpp-sdk,
logos-plugin-qt, logos-plugin-core, nix-bundle-lgx, logos-design-system (L3);
logos-qt-sdk, nix-bundle-logos-module-install (L4); and logos-view-module-runtime
(L5) are all 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.
Deliberately NOT repinned, because they carry no Windows work and are not part
of this chain: rust-overlay, logos-standalone-app, logos-test-framework, and
logos-rust-sdk (which is pinned to an explicit rev in flake.nix).
Co-authored-by: Cursor <cursoragent@cursor.com>
* test: update the systems assertion for the x86_64-windows pseudo-system
|
||
|
|
01bb03f91f |
chore: stop producing and requesting the std header variant (#175)
Every module was building a third `headers-std` derivation (a full
logos-cpp-generator run) that nothing ever consumed. buildPlugin.nix
picks a dep's headers with `dep."headers-${apiStyle}"`, and apiStyle is
only ever "lp" (cdylib / core universal) or "qt" (everything else) —
the "std" branch has been unreachable since lp replaced it. Measured:
the recursive derivation closure of a built module contains zero
headers-std, and no revision of buildPlugin.nix ever selected "std".
So drop the producer (`moduleIncludeStd`) and both output aliases
(`<name>-headers-std` and `headers-std`).
The std rung also sat in the middle of the dep-resolution fallback
chain (`headers-lp = ps.headers-lp or ps.headers-std or ps.headers-qt
or ...`). Rather than let it collapse to headers-qt, the lp chain now
throws: handing Qt-typed wrappers to a module whose own codegen ran
with `--api-style lp` fails deep inside a generated source file with a
wall of unrelated-looking Qt type errors. The throw names the dep and
says to rebuild/re-pin it against a current builder. It is lazy, so it
only fires when an lp consumer actually reads `headers-lp` — a Qt
consumer with the same stale dep still resolves to headers-qt exactly
as before.
buildCppPlugin.nix gets the same treatment. Its struct had no
headers-lp entry at all, so an lp consumer reaching that path died with
"cannot coerce a set to a string" from the header copy; now it gets the
same actionable message. (In practice everything built through there is
a ui_qml view module, which is always typed "qt".)
The generator still accepts `--api-style std`; removing that is a
separate step, ordered after this lands and is re-pinned.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e14e438d24 |
feat(builder): per-module rust toolchain via nix.rust.toolchain (#137)
Adds a `rust-overlay` flake input + a `nix.rust.toolchain` metadata field. When set (e.g. "1.96.0"), mkLogosModule builds the crate with a rust-overlay `makeRustPlatform` at that version instead of `pkgs.rustPlatform` — so a Rust cdylib module whose deps need a newer rustc than nixpkgs ships (e.g. alloy 1.8 / ruint needing >=1.91) can pin its own toolchain. Default (null) is unchanged: modules keep using `pkgs.rustPlatform`. Prerequisite for the RAILGUN module. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0c7ec19040 |
Per-module concurrent dispatch: concurrency metadata flag (#133)
* feat: thread --concurrency to the multi-aware generators parseMetadata exposes top-level `concurrency` (default "single"). mkLogosModule passes `--concurrency multi` to the Rust generator (thread-safe scaffold) and modulePreConfigure to the Qt generator (deferring glue). The C++ cdylib needs nothing — its logos_module_dispatch is already concurrency-safe — so `--concurrency` is intentionally NOT passed to logos-cpp-generator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: compile metadata nix.cmake.extra_sources into the plugin extra_sources was parsed (parseMetadata.nix) but never consumed — the author's own .cpp files were silently dropped from the build. logos_module() compiles only the generated glue + SDK sources, so a module that splits a declarations-only header (the form the cpp-generator scans, to keep inline std calls out of the header) from its impl body had the body's symbols left UNDEFINED. The plugin still links (-undefined dynamic_lookup) and loads, then SIGSEGVs the first time the dispatch calls one of those methods — the call goes through a symbol stub that resolves to null (bl -> 0x0). This is what crashed the universal-cdylib concurrent-dispatch driver on its first modules() call (FanoutDriverModuleImpl::fanOut / ::peak were `U` in the dylib). Thread extra_sources through like go_static_lib_names: the Nix build emits -DLOGOS_MODULE_EXTRA_SOURCES=a;b;c (buildCppPlugin.nix) and logos_module() appends them to PLUGIN_SOURCES (LogosModule.cmake), resolving relative paths against the module's CMakeLists dir. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump plugin backend to the extra_sources consumer (plugin-qt bea4ff8) Pairs with buildCppPlugin.nix emitting -DLOGOS_MODULE_EXTRA_SOURCES: the backend's LogosModule.cmake (logos-plugin-qt, shared by core+ui) now appends those sources to PLUGIN_SOURCES, so metadata.json nix.cmake.extra_sources is actually compiled — the split declarations-header + impl-body pattern works from metadata alone, no hand-written logos_module(SOURCES ...). (Temporary rev pin to the chore/extra-sources-autowire branch for validation; re-point to master once it lands.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Revert "chore: bump plugin backend to the extra_sources consumer (plugin-qt bea4ff8)" This reverts commit |
||
|
|
898d7bf49a |
uppercase the lib name in LOGOS_EXT_ROOT_<NAME> to match shell env var convention
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
b76f3f9e01 |
resolve external lib store paths in dev shell via LOGOS_EXT_ROOT_<name> to bypass hardcoded ./lib/ dir
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
f7452d980c |
feat(builder): nix.rust block for Rust external build dependencies (#127)
Rust cdylib modules (codegen.rust) compile their crate via buildRustPackage in mkLogosModule's `rustStaticLib`, which received no nativeBuildInputs/buildInputs/ env and offered no metadata knob to add them — so any crate (or transitive *-sys dep) needing a system build tool/library (pkg-config + openssl for reqwest native-tls, libsqlite3-sys, libclang for bindgen, protoc) failed in the nix sandbox. The only workaround was hand-rolling a buildRustPackage in the module's own flake, discarding the trivial-flake benefit. Add a `nix.rust` metadata block that feeds the crate compile: nix.rust.packages.build -> nativeBuildInputs (host tools) nix.rust.packages.runtime -> buildInputs (link libs) nix.rust.env -> buildRustPackage env Names resolve via the existing dotted-path getPkg. Empty by default, so existing modules are unaffected. Also adds programmatic escape-hatch args rustExtraNativeBuildInputs / rustExtraBuildInputs / rustEnv to mkLogosModule. Verified by a new fixture + flake check (checks.<sys>.rust-native-dep): a Rust module whose build.rs probes zlib via pkg-config builds only with the nix.rust block, and fails without it. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
03ad946f19 |
Builder-driven Rust codegen: no build.rs, trivial flake (#125)
* feat(builder): builder-driven Rust codegen — no build.rs, trivial flake
A Rust cdylib module's module-impl C ABI scaffold and staticlib are now
produced by mkLogosModule itself, driven by metadata.json's codegen.rust —
exactly as the builder already runs the C++ generator. The author writes no
build.rs and the module's flake.nix / CMakeLists.txt shrink to the same shape
a C++ module uses.
metadata:
"codegen": {
"lidl": "rust-lib/<name>.lidl",
"rust": { "crate": "rust-lib", "staticlib": "<libname>" }
}
mkLogosModule, when codegen.rust is present:
- runs logos-lidl-gen --provider <lidl> [--dep ...] to emit the scaffold,
feeding it the SAME resolved concrete + interface deps the C++ generator
gets (concrete -> modules().<dep>; interface -> a bind()-able client);
- injects the scaffold at generated/provider_gen.rs and buildRustPackages
the crate to a staticlib;
- stages the archive into lib/ (preConfigure) and sets
-DLOGOS_MODULE_RUST_STATIC_LIBS so the new LogosModule.cmake block links it
(plain link + protocol re-mention + frameworks/pthread — mirrors the Go
static-archive path).
logos-lidl-gen is read from the MODULE's flakeInputs.logos-rust-sdk, NOT a
builder input: logos-rust-sdk already depends on this builder (for its tests),
so a builder->rust-sdk input edge would be a cycle. The module needs
logos-rust-sdk in its inputs regardless (the crate links the SDK), so reading
it from flakeInputs adds no new edge.
Verified: a minimal codegen.rust module builds with a trivial flake + trivial
CMakeLists (no build.rs), loads under lm, and returns add(5,3)=8 / greet over
a logoscore daemon.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* builder: codegen.rust.author_trait for rust-first modules
A rust-first module declares its contract trait in the crate (the .lidl is
derived from it), so the generated scaffold must NOT also emit the trait.
codegen.rust.author_trait = true makes mkLogosModule pass --no-trait to
logos-lidl-gen --provider, eliminating the last reason such a module needed a
hand-written build.rs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* doctest: migrate the cross-language Rust modules off build.rs
The two cross-language composition doc-tests hand-wrote a build.rs in each
Rust module to run the generator. With codegen.rust (+ author_trait for the
rust-first modules) the builder does that, so drop every build.rs:
- rust_ledger (lidl-first) → codegen.rust
- rust_orchestrator (rust-first) → codegen.rust + author_trait
- rust_auditor (rust-first) → codegen.rust + author_trait
Each module loses its build.rs, its logos-lidl-gen build-dependency, its
buildRustPackage/preConfigure flake plumbing, and its find_library/link
CMakeLists — flake.nix and CMakeLists.txt shrink to the C++ shape (one extra
logos-rust-sdk input for the generator). The dep contracts are now resolved by
the builder from the deps' published .lidl (no manual deps/ copy), so the typed
clients are named by the metadata dependency (modules().cpp_counter_module /
modules().cpp_gateway_module). The rust-first --from-rust .lidl-derivation step
stays — that's authoring, not build.rs.
Verified: the rust-first orchestrator (author trait + a dependency + a typed
event) builds via the builder and introspects correctly (tally/whoami +
tally_changed). Pins logos-rust-sdk to the --no-trait commit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* builder: rust-first derives the .lidl from the trait (codegen.rust.trait)
Replace codegen.rust.author_trait with codegen.rust.trait (the contract trait
name). When set, the builder runs logos-lidl-gen --from-rust over the crate
source to DERIVE the .lidl at build time — exactly as a universal C++ module
derives its contract from the impl header — then generates the scaffold with
--no-trait and publishes the derived .lidl as packages.<sys>.lidl. The .rs file
is the single source of truth: a rust-first module commits no .lidl and runs no
manual derive step.
- new bindings: rustTrait / rustDeriveMode / rustSource, the derivedLidl
derivation, rustLidlPath, and lidlStaging (stages the derived .lidl into the
build tree before the Qt-glue codegen reads codegen.lidl).
- modulePreConfigure.compose gains a preCodegen hook (runs before codegen).
- the published #lidl output uses the derived .lidl in rust-first mode.
Verified: a rust-first module committing ONLY src/lib.rs (with a dependency and
a typed event) builds, introspects (methods + event), and publishes a correct
derived contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* doctest: rust-first modules commit only the trait (codegen.rust.trait)
With the builder deriving the .lidl from the trait, the cross-language doctests
drop the manual 'derive the .lidl' step, the committed .lidl, and the separate
logos-lidl-gen tool build. rust_orchestrator and rust_auditor now declare
codegen.rust.trait and commit only their .rs — single source of truth.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* builder: own the Rust generator + SDK so the module flake matches C++
A Rust module's flake.nix no longer needs a logos-rust-sdk input — it becomes
identical to a C++ module's (just logos-module-builder). The builder now owns
logos-rust-sdk and provides BOTH halves a Rust module needs:
- logos-lidl-gen (the generator it runs), and
- the SDK source the crate links — staged as ../logos-rust-sdk-src so the
crate's path-dep on logos-rust-sdk resolves against the SAME rev the
generator came from (zero generator/runtime skew, the way C++ already gets
both generator and runtime from the builder).
logos-rust-sdk depends BACK on this builder for its own tests, so its
logos-module-builder input (and the other branch-pinned test inputs) are cut
with follows in flake.nix to break the would-be cycle — we only consume its
lidl-gen package + source tree, never its tests. Also exposes #rust-sdk-src so
a module can stage the SDK to generate its Cargo.lock.
Backward compatible: a module that still uses a git dep + a logos-rust-sdk
flake input keeps building (the builder ignores the redundant input; the crate
uses its git dep; the staged SDK goes unused).
Verified: a module with a C++-shaped flake.nix (no logos-rust-sdk) and a
path-dep crate builds, derives its contract, and loads.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* doctest: Rust modules drop the logos-rust-sdk input (C++-shaped flake)
Now that the builder owns logos-rust-sdk and provides both lidl-gen AND the SDK
source, the cross-language doctests' Rust modules carry no logos-rust-sdk input:
their flake.nix is identical to a C++ module's. The crate depends on the SDK as
a path dep on ../logos-rust-sdk-src — staged from the builder's #rust-sdk-src
output (the SAME rev the generator came from, so no skew). The build step stages
it before `cargo generate-lockfile`; the builder re-stages it for the compile.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* builder: infer staticlib from Cargo.toml + drop codegen.lidl for rust-first
Two redundant codegen fields a Rust module had to repeat are now inferred:
- staticlib: read from the crate's Cargo.toml ([lib].name, else [package].name
with - -> _). codegen.rust.staticlib still overrides if present.
- codegen.lidl: no longer needed for a rust-first module. The .lidl is derived
from the trait and staged at generated_code/<name>.lidl, where cdylibCodegen
now reads it (the builder owns the path). Still required for lidl-first
(contract-first) modules, where it names the committed contract.
So a rust-first module's codegen shrinks to `rust: { crate, trait }`.
Verified: a module with `codegen: { rust: { crate, trait } }` (no lidl, no
staticlib) builds, derives its contract, and loads.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* doctest: trim redundant codegen fields from the Rust modules
The builder now infers staticlib from Cargo.toml and (for rust-first) owns the
.lidl path, so:
- rust_orchestrator / rust_auditor (rust-first): codegen shrinks to
`rust: { crate, trait }` — no lidl, no staticlib.
- rust_ledger (lidl-first): keeps its committed `lidl`, drops `staticlib`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
a7008815cb |
feat: Qt-typed context for universal UI plugins (LogosUiPluginContext) (#124)
* feat: Qt-typed context for universal UI plugins (LogosUiPluginContext)
A universal UI plugin (type: ui_qml) is a view, not a module — it derives a
QtRO SimpleSource whose .rep slots are Qt-typed. Mirror logos-plugin-qt's
api-style flip so the `generate` source-layout output drops the std flag for
ui_qml (Qt-typed modules() wrappers), and switch the UI authoring model from
the std-typed LogosModuleContext to the narrow Qt-layer LogosUiPluginContext:
- lib/mkLogosModule.nix — ui_qml api-style flag flip (mirror of buildPlugin.nix)
- lib/modulePreConfigure.nix — UI codegen comment
- templates/ui-qml-backend — backend derives LogosUiPluginContext
- skills/{create-ui-module,SKILL}, README, docs/quick-reference — Qt-typed
modules() + LogosUiPluginContext
- doctests/ui-typed-backend — backend derives LogosUiPluginContext, Qt-typed
event callback (int, not int64_t)
Depends on logos-co/logos-qt-sdk#3 (LogosUiPluginContext) and
logos-co/logos-plugin-qt#14 (api-style). Part of a 4-repo change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: bump logos-qt-sdk + logos-plugin-qt to merged master
Pin the flake.lock to the merged LogosUiPluginContext (logos-qt-sdk) and the
ui_qml api-style flip (logos-plugin-qt) so this PR's template/builder changes
resolve against deps that actually provide them:
- logos-qt-sdk → 872db45 (logos_ui_plugin_context.h + UI glue)
- logos-plugin-qt → f33f264 (ui_qml api-style qt)
- logos-plugin-core → f33f264
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
233a9b6e3a |
feat: add a generate output to module-builder modules (#123)
* feat: add a `generate` output to module-builder modules Wire selectedBackend.generate (logos-plugin-qt) into mkLogosModule and the shared buildCppPlugin pipeline (used by mkLogosQmlModule), exposing a `generate` package on every C++ module and every UI module with a C++ backend. `nix build .#generate` runs all the build's code generators and emits the module source + a fully-populated generated_code/ in result/ — a ready-to-build codebase. Same backend args as the default plugin build, so the emitted tree matches a real build. QML-only modules have no generators, so they get no generate output. Stacks on logos-plugin-qt's generate mode; the flake.lock bump lands with it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump logos-plugin-qt to merged master (generate mode) plugin-qt#13 landed; relock logos-plugin-qt + logos-plugin-core to the merged master (dca52b9) so the `.#generate` output resolves for standalone consumers of this repo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a646725066 |
feat(universal): route the universal interface through the cdylib mechanism (#122)
Core universal modules are now header-first cdylibs: universalCodegen derives the LIDL from the impl header then runs the cdylib backend (uniform Qt glue + Qt-free C-ABI export wrapper) instead of the direct --from-header Qt glue. The author still writes only the impl class; its TUs are Qt-free and outbound modules().<dep> calls go through the lp_* C ABI. UI universal backends (type: ui_qml) keep the Qt/std path. No module source changes — proves source + load/call ABI compatibility. Adds a headers-lp variant per module (introspect the built plugin with --api-style lp) so a core universal module can copy a Qt-free typed wrapper for a LEGACY (no-.lidl) dependency. Pins cpp-sdk + plugin-qt/core to master (universal-lp + lp generator). Proven fleet-wide via the workspace tests-and-doctests gate (ws test --all + all doctests green). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
24cec3569b |
Cdylib module interface: uniform Qt glue over the common module-impl C ABI (#119)
* Wire the Qt-split SDK stack + stamp the protocol version into modules
- New inputs logos-protocol and logos-qt-sdk, injected into both build
pipelines (mkLogosModule + buildCppPlugin): modules now link
logos-qt-sdk (which chains logos-protocol) while the Qt-free
logos-cpp-sdk provides the std headers + code generator.
- cmake/LogosModule.cmake synced with logos-plugin-qt's (it had
drifted): three-root resolution (LOGOS_CPP_SDK_ROOT /
LOGOS_QT_SDK_ROOT / LOGOS_PROTOCOL_ROOT), links
logos-qt-sdk::logos_qt_sdk + logos-cpp-sdk::logos_headers.
- Protocol-version stamp: the logos-protocol semver (parsed from
logos_protocol.h) is injected into each module's metadata.json before
moc embeds it (modulePreConfigure.stampProtocolVersion, jq). liblogos
reads it pre-load to decide compatibility; modules from older
builders lack the field and load as legacy.
Builder-based modules need only a flake.lock bump — no metadata.json or
source changes.
* fix: rebase LogosModule.cmake qt-split patch onto master's copy
The previous commit synced this file from logos-plugin-qt's lineage, which
silently dropped master-only blocks the universal-module path depends on:
the generated_code/ glob (compiles the generated glue + provider dispatch
into the plugin), the metadata.json configure_file for AUTOMOC, the
LOGOS_API_STYLE selection, static-archive external libs, and the Go/cgo
whole-archive block. Universal modules built without any Qt-plugin glue
(no QTMETADATA) and capability_module dlopen-failed on a missing impl
vtable, killing the token flow and with it all cross-module IPC.
This restores master's file and re-applies only the qt-split changes:
logos_module_context.h detection for the Qt-free base SDK, qt-sdk/protocol
root resolution, qt-sdk source-layout sources, the split include dirs, and
exported-target linking (logos-qt-sdk::logos_qt_sdk / logos-protocol /
logos-cpp-sdk::logos_headers).
Verified: logos-test-modules--tests check passes end-to-end with this
builder (universal modules load, capability token flow works, IPC green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* lock: pin extraction-chain branch revs for standalone CI
Temporary — drop when the chain PRs merge (re-lock against masters).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tests): thread qt-sdk/protocol roots into mkLogosModuleTests
logos-test-framework's LogosTest.cmake now resolves three SDK roots
(qt split); the test builder passes -DLOGOS_QT_SDK_ROOT /
-DLOGOS_PROTOCOL_ROOT and the packages on buildInputs. flake.lock pins
logos-test-framework to its qt-split branch (logos-co/logos-test-framework#4)
— temporary, drop when the chain merges.
Verified: the test-framework-integration check passes locally with these
changes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: forward logos-protocol/logos-qt-sdk to the inner mkLogosModuleTests import
mkLogosModuleTests.nix gained required logos-protocol/logos-qt-sdk args
in
|
||
|
|
33bcd1c5d6 |
Qt split: inject logos-qt-sdk + logos-protocol, stamp logos_protocol_version into module metadata (#113)
* Wire the Qt-split SDK stack + stamp the protocol version into modules
- New inputs logos-protocol and logos-qt-sdk, injected into both build
pipelines (mkLogosModule + buildCppPlugin): modules now link
logos-qt-sdk (which chains logos-protocol) while the Qt-free
logos-cpp-sdk provides the std headers + code generator.
- cmake/LogosModule.cmake synced with logos-plugin-qt's (it had
drifted): three-root resolution (LOGOS_CPP_SDK_ROOT /
LOGOS_QT_SDK_ROOT / LOGOS_PROTOCOL_ROOT), links
logos-qt-sdk::logos_qt_sdk + logos-cpp-sdk::logos_headers.
- Protocol-version stamp: the logos-protocol semver (parsed from
logos_protocol.h) is injected into each module's metadata.json before
moc embeds it (modulePreConfigure.stampProtocolVersion, jq). liblogos
reads it pre-load to decide compatibility; modules from older
builders lack the field and load as legacy.
Builder-based modules need only a flake.lock bump — no metadata.json or
source changes.
* fix: rebase LogosModule.cmake qt-split patch onto master's copy
The previous commit synced this file from logos-plugin-qt's lineage, which
silently dropped master-only blocks the universal-module path depends on:
the generated_code/ glob (compiles the generated glue + provider dispatch
into the plugin), the metadata.json configure_file for AUTOMOC, the
LOGOS_API_STYLE selection, static-archive external libs, and the Go/cgo
whole-archive block. Universal modules built without any Qt-plugin glue
(no QTMETADATA) and capability_module dlopen-failed on a missing impl
vtable, killing the token flow and with it all cross-module IPC.
This restores master's file and re-applies only the qt-split changes:
logos_module_context.h detection for the Qt-free base SDK, qt-sdk/protocol
root resolution, qt-sdk source-layout sources, the split include dirs, and
exported-target linking (logos-qt-sdk::logos_qt_sdk / logos-protocol /
logos-cpp-sdk::logos_headers).
Verified: logos-test-modules--tests check passes end-to-end with this
builder (universal modules load, capability token flow works, IPC green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* lock: pin extraction-chain branch revs for standalone CI
Temporary — drop when the chain PRs merge (re-lock against masters).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tests): thread qt-sdk/protocol roots into mkLogosModuleTests
logos-test-framework's LogosTest.cmake now resolves three SDK roots
(qt split); the test builder passes -DLOGOS_QT_SDK_ROOT /
-DLOGOS_PROTOCOL_ROOT and the packages on buildInputs. flake.lock pins
logos-test-framework to its qt-split branch (logos-co/logos-test-framework#4)
— temporary, drop when the chain merges.
Verified: the test-framework-integration check passes locally with these
changes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: forward logos-protocol/logos-qt-sdk to the inner mkLogosModuleTests import
mkLogosModuleTests.nix gained required logos-protocol/logos-qt-sdk args
in
|
||
|
|
aa5b5d354d |
External-library doc-tests + per-platform vendored binaries (#112)
* feat: per-platform vendored binaries in mkExternalLib A vendored external library could only ship one platform's binary: two platforms sharing an extension (linux x86_64 vs aarch64, both libfoo.so) collide in lib/. Commit each platform's binary under lib/<nix-system>/ (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin) and the build selects the subdir matching pkgs.stdenv.hostPlatform.system. Contained to mkExternalLib.nix (new src arg + a selection branch before the null fallback); src threaded through mkLogosModule.nix and buildCppPlugin.nix. The selected binary flows through the existing flake-input staging path, so no CMake or logos-plugin-qt change. Flat single-platform vendoring is unchanged. Documented in docs/external-libraries.md (Nix system strings, distinct from .lgx variant labels; raw nix-develop+cmake does not descend into the subdirs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(doctests): executable external-library doc-tests + Pages publish Add four YAML doc-tests under doctests/ — the logos-tutorial / logos-doctest approach — each scaffolding a real universal module that wraps the same tiny libgreet a different way, building it against the commit under test, loading it in a logoscore daemon, and asserting `call greet_module hello` returns "hello from libgreet": 1. wrap-external-lib-1-source source compiled into the plugin 2. wrap-external-lib-2-prebuilt-binaries prebuilt binary vendored per-platform 3. wrap-external-lib-3-external-source external source built with `make` 4. wrap-external-lib-4-nix-flake library from an external Nix flake .github/workflows/doctests.yml runs them via `nix run github:logos-co/logos-doctest` with `--release-for logos-module-builder=<sha>` (matrix ubuntu/macos), and publishes the two-column HTML report per-ref/per-os to gh-pages with a PR comment — mirroring logos-cpp-sdk's doctests.yml. Skipped on forks. All four pass locally on aarch64-darwin against the PR commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: fail loudly when a configured external library is missing logos_module() used to emit message(WARNING ...) and keep going when a configured external library could not be found at build time — on macOS -undefined dynamic_lookup then let the plugin link anyway, producing a silently broken module. Turn the three not-found paths (EXTERNAL_LIBS, go_build static archives, LINK_TARGETS) into message(FATAL_ERROR ...) so a missing/failed external dependency aborts the build with an actionable message instead of a silent warning. Guarded by test-static-extlib.nix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(doctests): use portable builds (lgx-portable, cli-portable, bundle-dir) Switch the run/package steps in all four external-library doc-tests to the portable distribution path — the form real users ship: portable LGX (`.#lgx-portable`), portable package manager (`#cli-portable`), and the self-contained logoscore bundle (`#cli-bundle-dir`, binary at bin/logoscore). Validated locally on aarch64-darwin: case 1 21/21 and case 2 (per-platform vendored binary) 19/19, each ending in `call greet_module hello -> "result":"hello from libgreet"`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
598ca36983 |
feat: consume concrete deps via LIDL (no dep plugin build) + publish lidl output (#110)
* feat: consume concrete deps via LIDL; publish a cheap lidl output
Building/packaging a module no longer builds its (universal) dependency
modules — only `#run` (the standalone app) still bundles dep plugins.
- mkLogosModule: new packages.<sys>.lidl output — a cheap codegen-only
derivation that runs `logos-cpp-generator --header-to-lidl` over the universal
impl header (no Qt/plugin compile), publishing the module's interface LIDL.
- mkLogosModule: per-dependency classification. A dep that exposes a `lidl`
output (or has a dependency_overrides entry) is LIDL-based → passed to the
backend as `staticDeps` (generated via --dep, no dep plugin build). Deps
without a `lidl` output take the TRANSITIONAL header-copy fallback
(`legacyHeaderDepNames` → resolvedModuleDeps), identical to today; isolated
and banner-commented for removal once all modules publish LIDL.
- parseMetadata: normalize `dependencies` to name strings (accept object
entries) and add optional `dependency_overrides` ({ file, input?, impl_class? })
to force a specific LIDL/header source per dep.
- buildCppPlugin (QML C++ backend): same classification + staticDeps threading,
so UI modules also stop building their universal deps.
Requires logos-cpp-sdk#77 (--header-to-lidl / --dep) and logos-plugin-qt#9
(staticDeps → --dep). A follow-up flake.lock bump pins those once merged
(mirrors the interface-deps plugin-core bump, #109); local builds use
`ws ... --auto-local`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* address review: guard depLidlOf for non-flake deps; add parse-metadata tests
- depLidlOf now explicitly guards `packages`/`<system>` so a raw-derivation
dependency input returns null (→ transitional header-copy fallback) rather
than risking a throw. Applied in mkLogosModule.nix and buildCppPlugin.nix.
- test-parse-metadata.nix: cover `dependencies` object-entry normalization and
`dependency_overrides` parsing/validation (file required; .h needs impl_class).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump logos-cpp-sdk + logos-plugin-qt/core to the deps-via-LIDL backends
Pin the merged --header-to-lidl/--dep generator (logos-cpp-sdk#77) and the
staticDeps-aware Qt backend (logos-plugin-qt#9) so this module builder's LIDL
dependency consumption works against published inputs (not just --auto-local).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
260c755b38 |
feat: dependency interfaces — parse + resolve interface_dependencies (#108)
* feat: parse and resolve interface_dependencies
Support dependency interfaces declared in metadata.json:
- parseMetadata: new top-level interface_dependencies array
({ name, file, impl_class?, input? }); errors on a .h entry missing
impl_class.
- mkLogosModule: resolve each entry to a definition-file path — local
(${src}/file) or cross-repo (${flakeInputs.<input>}/file, mirroring how
concrete dependencies resolve to flake inputs) — and thread interfaceDeps
to the backend's buildPlugin, only when present so existing modules are
unaffected.
Requires logos-cpp-sdk#74 (the --interface generator flag) and
logos-plugin-qt (which passes the flags through).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* address review: clearer errors for malformed/missing interface deps
- parseMetadata: reject a non-object interface_dependencies entry with a
clear message instead of the low-level "is not an attribute set" error
that e.file would raise on a bare string/list element.
- mkLogosModule: when an entry's `input` names a flake input that wasn't
passed, throw a targeted error mentioning interface_dependencies and the
missing input name, rather than the generic "attribute missing".
(Copilot review, PR #108.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* bump dependencies
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
b3f1d658f0 |
Extend universal modules with module context (#96)
* pick API style in logos-cpp-generator * bump cpp-sdk |
||
|
|
875d8ea85e | fix common issues found with new modules | ||
|
|
3621564419 |
fix common issues found with new modules
fix common issues found with new modules fix common issues found with new modules |
||
|
|
7fc859bb00 | Fix Nix tests | ||
|
|
8b0aeacc74 | add logos test framework | ||
|
|
0ee2af9724 |
move non-qt specific from logos-plugin-qt back into logos-module-builder
move non-qt specific from logos-plugin-qt back into logos-module-builder move non-qt specific from logos-plugin-qt back into logos-module-builder |
||
|
|
53170c7006 | support different external lib outputs for portable builds (#30) | ||
|
|
abeb007d4c | use nix-bundle-logos-module-install (#28) | ||
|
|
48f86a6abf | extract qt plugin specific logic into its own library to allow easy swapping with different plugin tech | ||
|
|
3f3966a173 | auto load module dependencies for the standalone app | ||
|
|
7d78dcf302 | chore: add logos-bundle-lgx dependecy to module-builder so that it will be available by default | ||
|
|
2f82594548 |
feat: improvements in the builder
1. embed the lgx bundler into the flake.nix so directly calling nix build .#lgx gives us a lgx pakcage out of the box 2. Remove metada.json file in case cpp libraries |
||
|
|
6437f546c1 | fix: issues in builder | ||
|
|
aff10448b2 |
feat: consolidate module config into metadata.json as single source of truth
Remove module.yaml and use metadata.json exclusively for both runtime and Nix build configuration. The nix block in metadata.json replaces module.yaml for build-time settings. Updates all docs, skills, templates, lib/*.nix files, and the waku-module-migrated example. |
||
|
|
b714ccbc6e | fix: create new nix derivation for logos-standalone-app | ||
|
|
f1a8d74aef | use logos-module for interface instead of liblogos | ||
|
|
fbacdfe3fc | make fixes needed for logos-test-modules | ||
|
|
c5ebea8e9f |
various fixes to get logos-chat-module to work
various fixes to get logos-chat-module to work test test test test test test test test |
||
|
|
ddf6b3961e | test module builder |