mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
master
27
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
937f17ed01 |
feat(cdylib): define logos_module_accept_inbound_token (#151)
* feat(cdylib): define logos_module_accept_inbound_token logos-protocol only DECLARES the module-impl C ABI; every language backend owes each definition. A missing one links clean and dies at dlopen, on Linux only — macOS links plugins -undefined dynamic_lookup and hides it entirely. Also fixes a misrouted diagnostic in the ABI check. The at-nextmaj MAJOR probe ran before the export diff, so an export that NOTHING defines was reported as "a version guard testing MINOR without MAJOR" — the wrong lesson, sending the reader to fix a guard that is not there. The probe is now gated on the symbol being present at the current MINOR, with a self-test over synthetic sets because both diagnostics are inline shell and otherwise untestable. Requires logos-protocol fix/token-direction-key-namespace (59b27ef). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deps): relock logos-protocol to 42460e5b (0.8.0), which is what makes the ABI check non-vacuous WHAT MOVED logos-protocol 480f40ff (0.5.0) -> 42460e5b (0.8.0) Nothing else in the lock changed. The input tracks a branch rather than a rev, so the update moved it on its own. WHY IT HAD TO This PR's definition of logos_module_accept_inbound_token is guarded on protocol >= 0.8, so at the locked 0.5 the emitter wrote NOTHING and checks.<sys>.module-impl-abi passed with the feature entirely absent. That check diffs the emitted export set against the list the PINNED logos-protocol declares, and 0.5 declares ten exports, none of them the inbound door. The PR was green because the check could not see the thing the PR adds. Measured rather than argued. Deleting the whole emitter block from cpp-generator/experimental/lidl_gen_cdylib.cpp: at 480f40ff (0.5.0) -- GREEN, i.e. the check was vacuous logos-protocol 0.5.0 declares 10 module-impl exports [A: --from-header, header-first] defines all 10 declared module-impl exports. /nix/store/mlqs29va7lx4m49cclni6pf01yg7n2js-logos-cpp-sdk-module-impl-abi-tests at 42460e5b (0.8.0) -- RED, naming the missing export FAIL: [A: --from-header, header-first] does not define every module-impl C ABI export. DECLARED by logos-protocol but NOT DEFINED by this backend: - logos_module_accept_inbound_token (version probe: 7 exports at MINOR=0, 11 at MINOR=8 -- one short) Restoring the block returns the check to green at the SAME store path it had before the deletion, so the red is attributable to the emitter and to nothing else in the tree. CHECKS Every check the flake exposes, built individually on x86_64-linux at the new lock. Substituters restricted to cache.nixos.org because cache.nix.logos.co is returning 502, so these are builds rather than cache hits. checks.x86_64-linux.generator-cli /nix/store/ndimz2vnkrqlms1pl6bi0jhci8snzh82-logos-cpp-sdk-generator-cli-tests checks.x86_64-linux.module-impl-abi /nix/store/dncpnlql0jlk1yhzygnfchcjrxh1ndf7-logos-cpp-sdk-module-impl-abi-tests checks.x86_64-linux.tests /nix/store/za8digicp97vd1aqis3x5ypnbwbnqz5r-logos-cpp-sdk-tests-0.2.0 module-impl-abi now reports, for all four generator configurations (--from-header, --lidl, zero-method, records+events): logos-protocol 0.8.0 declares 12 module-impl exports; resolving generated code at LOGOS_PROTOCOL_VERSION_MINOR=8 version probe: 7 exports at MINOR=0, 12 at MINOR=8 defines all 12 declared module-impl exports. The Darwin checks were not built; no macOS builder was available. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
acea0d24e2 |
feat(codegen): a lossless Qt type mapping, and LIDL types in getMethods (#149)
* feat(codegen): a lossless Qt type mapping — typed containers and optionals
`lidlTypeToQt` answered four different LIDL types with one Qt name. `[uint]`,
`[bstr]`, `[[uint]]` and `[any]` were all QVariantList; `{tstr: uint}` and
`{tstr: any}` were both QVariantMap; every `?T` was a bare QVariant. A Qt
consumer therefore lost, on the SAME contract, types that the std consumer next
door kept — it could not tell `?tstr` from `?uint`, and got no compile-time
check on any element.
The table is now recursive:
[T] QList<qtOf(T)> ([tstr] stays QStringList)
{tstr: V} QMap<QString, qtOf(V)>
?T std::optional<qtOf(T)> (through optionalValueType,
so ??T stays two-state)
any QVariant — KEPT, deliberately
`any` is the one row that must not widen: QVariant is the only Qt type that
holds bytes AND an exact uint64 AND arbitrary nesting at once, so every
narrower spelling would lose what it was chosen to carry. The rule is applied
at the LEAF, so anything whose element type bottoms out at `any` keeps the
QVariant-family spelling at every depth — `[any]` is QVariantList, `[[any]]`
still is, `{tstr: [any]}` is QVariantMap, `?any` is QVariant.
THE TRAP, and why this is not just a rename. A widened name must never reach
QVariant::fromValue / qvariant_cast / logos::qt::toWire as a WHOLE value.
logos-protocol's qvariantToNlohmann matches a CLOSED userType() set:
QList<qulonglong> is in none of it, so it serialises to JSON null. The decode
fails just as quietly — qvariant_cast<QList<qulonglong>> of a QVariantList
yields an EMPTY list. Neither direction warns. So every widened slot is encoded
and decoded by a generator-emitted ELEMENT LOOP, the shape the record cases
already used, and `lidlQtNeedsElementLoop` is the single predicate that decides
which slots need one.
The emitted loops take their source as a lambda PARAMETER, not a body-local
binding. They nest (`[[uint]]`), every level wants the same short names, and a
local — or a range-for over a name the loop itself declares — is then
self-referential: it compiles and reads uninitialised memory. Measured: three
round-trip tests died on SIGTRAP before the argument form.
THE STRING-KEYED EMITTER IS FROZEN, ON PURPOSE. generator_lib is keyed on flat
type NAMES (lidl_to_json flattens the contract before it gets there, because
that emitter also serves the metaobject-introspection path), so it cannot
derive the levels an element loop needs without parsing C++ type names back
into a tree. Every widened spelling is folded back to the name it produced
before (legacyQtBase), which keeps BOTH surfaces it feeds byte-for-byte
unchanged: the legacy Qt consumer, and the Qt-free lp one whose table is
DERIVED from it through mapParamTypeStd. Verified by generating a
28-method contract through both before and after: the diff is empty. The
widened types are spent in the TypeExpr-driven emitters instead
(lidl_gen_client.cpp here, lidl_gen_qt_consumer.cpp in logos-qt-sdk).
Also here, because both are consequences of the table becoming recursive:
* lidlTypeToQt gained a record-name HOOK. A wrapper nests its record structs
in the wrapper class, so a type written outside that scope must qualify
them — and the emitters used to do that by matching the three shapes that
could mention a record on the finished string. `?Point` and
`QList<QList<Point>>` are now spellable, so the qualification happens
during the walk, at the one place that knows a name is a record.
* lidlTypeToLidlText — the LIDL contract spelling of a type. Unused here; the
commit that follows puts getMethods() on it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(cdylib): getMethods publishes the LIDL contract vocabulary, not Qt names
A module's published metadata — `returnType`, `parameters[].type`, `signature`
— answered in Qt type names. Two things wrong with that, and the second is the
one that matters:
* a cdylib module is Qt-FREE. It described itself in the types of a language
it does not use, to readers (`lm`, logoscore's method listing, basecamp's
module inspector) that are showing a human what the module offers.
* it was LOSSY. `[uint]`, `[bstr]` and `[any]` are three different LIDL types
and all three published as the single word QVariantList, so the listing
could not be read back as a contract. That is now `[uint]`, `[bstr]`,
`[any]`; `{tstr: uint}`; `? tstr`; and a record publishes its declared
name.
WHY THIS IS SAFE — checked, not assumed. The historical objection is recorded
in the function this replaces: these strings are read as METATYPES, and
emitting a record's struct name here once made the host SIGSEGV. Nothing in the
current runtime does that. logos-plugin-qt's QtProviderObject dispatches on
`method.returnMetaType()` / `parameterMetaType(i)` — the QMetaObject, never
this JSON — and every remaining reader treats these fields as opaque text:
logos-module's `lm` prints them, logoscore's output.cpp prints them, basecamp's
CoreModuleManager forwards the JSON to QML, and the plain wire's json_mapping
only round-trips them. Nothing anywhere builds a QMetaObject from this
metadata.
The spelling comes from lidlTypeToLidlText, which mirrors logos-lidl's
serializeTypeExpr. It is a COPY, because that function is file-local to
logos-lidl's serializer.cpp and the public headers expose no type printer —
so instead of hoping, the pairing is ASSERTED: the test round-trips each shape
through `lidl::serialize` and reads the type text back out of the emitted
`.lidl`. When logos-lidl exports a printer, delete the copy and call it.
Not fixed by this, and not attempted: the Rust SDK's provider generator has its
own `qt_type_name` writing the same JSON, so the two languages now disagree
about how a module describes itself. That is a cross-repo change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(doctests): the generator round-trip pins the lossless Qt spellings
`cpp-sdk-generator-roundtrip.test.yaml` is a CI gate
(.github/workflows/doctests.yml), and two of its `expect_contains` were
pinned to the type names the Qt consumer produced BEFORE the lossless
mapping:
QStringList labels(const QVariantList& ids
QVariant nearest(const Point& p, QVariant limit
The generator now emits `QList<qulonglong>` and
`std::optional<Point>` / `std::optional<qulonglong>` for those slots, so both
assertions failed. The `nearest` step's `run` grep was pinned the same way
(`QVariant nearest`), so the line it was supposed to assert on was not even
in the output being searched.
Verified by running the spec's own steps against the generator built from
this commit: 10 run-steps, 0 failures. The `[uint]` -> QList<qulonglong> and
`?T` -> std::optional<T> lines were read out of the real
`consumer/sensor_module_api.h` and `geometry/geometry_module_api.h`, not
written from the mapping table.
Prose too, in three places that described the old table: the Flow-3 type
mapping ("other arrays -> QVariantList"), the composite-types intro
("optionals ... stay QVariantMap / QVariant"), and the composite-signature
step. They now say what the mapping actually is — one LIDL type, one C++
spelling, with `any` the single deliberate exception — and `nearest` is
called out as the one signature carrying both halves of the optional
mapping.
`doctests/outputs/cpp-sdk-generator-roundtrip.md` carries the same prose
corrections. That tree is hand-pinned and CI never diffs it, which is
exactly why it must be corrected by hand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(generator): the consumer wrapper comes from the contract, not from getMethods
`logos-cpp-generator <plugin> --module-only` — the invocation
logos-plugin-qt's generate-module-headers.sh makes for every module's lp
wrapper — built that wrapper's whole type surface out of the plugin's
PUBLISHED `getMethods()` metadata. It now builds it out of the module's `.lidl`
contract, the file the same invocation already passes as `--events-from`.
WHY THIS IS A DEFECT AND NOT A PREFERENCE. generator_lib is keyed on flat type
NAMES, and mapParamType / mapReturnType fall back to QVariant for a name they
do not recognise (generator_lib.cpp:142 and :153). So the wrapper's types
depend on the VOCABULARY a module happens to publish its metadata in, and a
vocabulary this emitter has no row for degrades to QVariant — LogosMap on the
lp surface — with no diagnostic at any layer. It is a machine reader of a
listing that every other consumer treats as human-facing text, and it fails
silently.
It was measured, not theorised. 621772a made the cdylib backend publish the
LIDL contract vocabulary (`tstr`, `[uint]`, `result`, `? tstr`) in place of Qt
type names, because that listing is what `lm`, logoscore and basecamp show a
human and Qt names are the wrong answer for a Qt-free module. Every
`interface: "universal"` module's lp wrapper collapsed:
logos-test-modules' `checks.unit-tests-new-api` went PASS -> FAIL, and the
compiler said exactly why —
error: no viable conversion from 'LogosMap' to 'StdLogosResult'
StdLogosResult r = modules().test_basic_module.resultWithMap();
`result` is not a name mapReturnType knows, so it became QVariant, so it became
LogosMap. Bisected to exactly 621772a (5ffd90b passes, dd52d9d fails).
THE FIX IS TO STOP READING THAT VOCABULARY, not to learn a second one.
`int` means a 32-bit Qt int in one table and a 64-bit LIDL integer in the
other, and the reader cannot tell from the string which table it is holding —
a merged table would silently mistype every integer on every module. The
contract has no such ambiguity: it is a TypeExpr tree, and lidl_to_json is the
single place it is flattened. Taking methods from it makes this path emit the
same wrapper as `--general-only --dep <name>=<name>.lidl`, which is what
buildHeaders.nix already runs under cross-compilation and for the entire Qt
surface. Contract-first, on every platform, for every surface.
WHAT CHANGED, exactly:
* loadEventsFromLidl -> loadContractFromLidl. It already parsed the whole
contract and threw the methods away; it now returns them, after the same
lidlCheckRecords + lidlInjectIdentity + noteOptionalPositionalSlots that
main.cpp's --dep path applies. Identity is injected rather than read,
matching the provider side (main.cpp's --backend cdylib), so the two cannot
disagree about name() / version().
* A sidecar that is NAMED BUT MISSING is now refused (exit 2), and an
unreadable or malformed one is fatal (exit 4). Both used to be shrugged off
— which shipped a wrapper with no typed events, and would now ship one with
no typed methods, in the silently-empty shape generate-module-headers.sh
exists to refuse.
* The plugin is STILL LOADED. That load is the dlopen check this path
performs (exit 3 on an SDK/ABI skew) and it is unchanged; what the plugin
says about itself is now compared against the contract instead of believed,
and a divergence — a stale sidecar — is reported by name on stderr. Only
`isInvokable` entries are compared: a cdylib publishes its events into the
same array, tagged `"type": "event"`, and both emitters already skip those.
* A module with NO contract keeps introspection — a handcrafted Qt plugin's
QMetaObject is still the only description of its API that exists, and Qt
type names are the right vocabulary for it — but a listing spelled in the
LIDL vocabulary with no contract to go with it is now REFUSED (exit 7)
instead of silently producing the untyped wrapper. That combination is only
reachable by hand: buildHeaders.nix always passes the flag when the sidecar
exists, and it is the shape the developer guide used to suggest. The two
vocabularies are not distinguishable in general, which is the whole reason
this emitter must read only one — but they do not have to be: the words
they share (`int`, `bool`) are all in the known table and never reach the
fallback, so the check keys on the LIDL half Qt has no word for at all
(`tstr`, `bstr`, `uint`, `float64`, `result`, `any`, and anything starting
`[`, `{` or `?`). No Qt type is spelled that way, so it cannot false-fire;
a false negative is just the old behaviour.
THE ENUMERATION, because two previous ones missed this reader. Searching for
who greps `returnType` is what missed it; the question is what the data FLOWS
INTO. Every consumer of a published getMethods array in the workspace:
MACHINE (one, and it is this one)
logos-cpp-sdk cpp-generator/plugin_introspect.cpp, reached only through
logos-plugin-qt's generate-module-headers.sh / buildHeaders.nix.
HUMAN-READABLE OR OPAQUE PASSTHROUGH (all of them)
logos-module's `lm` (prints; --json re-emits verbatim), logoscore-cli's
client/output.cpp (prints) and core_service_dispatch.cpp (forwards),
logos-logoscore-tui (formats one line per method), logos-module-viewer
(reads the QMetaObject directly, not this JSON), basecamp's
CoreModuleManager / MainUIBackend (hands the JSON string to QML),
logos-protocol's json_mapping.cpp and qvariant_rpc_value.cpp (round-trip
the strings unread).
PRODUCERS, for completeness: lidl_gen_cdylib.cpp (LIDL vocabulary),
logos-plugin-qt's QtProviderObject (Qt names, from the QMetaObject) and
lidl_gen_cdylib_glue.cpp (forwards the cdylib's), logos-rust-sdk's
rustgen_provider.rs (still Qt names — the two languages disagree, as
621772a noted), and logos-protocol's ModuleProxy, which appends derived
name()/version() entries spelled `QString`. None of that reaches a type
decision any more, which is the point of the change.
Build-system paths checked and clear: `<plugin> --module-only` is invoked
from exactly one place in the workspace (generate-module-headers.sh:60);
LogosModule.cmake, buildPlugin.nix and mkLogosModuleTests.nix all use
`--general-only`, which is contract-driven already; the doctests' `--lidl
--module-only` is a different mode entirely.
VERIFIED.
`nix build path:./repos/logos-test-modules#checks.aarch64-darwin.unit-tests-new-api`
with this SDK overridden in (plus the logos-lidl overrides the branch needs at
the qt-sdk and plugin-qt nodes) — 32 passed, 0 failed. The same command against
this branch's HEAD fails to compile, as above. The build log shows the path
taken, per module:
Detected new-API plugin (LogosProviderPlugin), using getMethods() — 43 methods
Using the module's LIDL contract for the method surface — 41 methods
(the plugin's published listing is a description, not a type source)
The refusal, measured by hand against a real LIDL-publishing plugin
(test_basic_module, built from this branch) because no check exercises a
hand-run invocation:
no --events-from -> exit 7, nothing written, the message above naming
8 offending slots
with --events-from -> exit 0, 41 typed methods, 69 `std::string` in the
emitted lp header
a pre-621772a build of the SAME module (Qt-name listing), no --events-from
-> exit 0, still generates, still typed — the refusal does
not fire on the vocabulary this emitter can read
nix/tests-generator-cli.nix gains the two CLI-surface cases this adds: a
`--events-from` naming a file that does not exist is refused with that
sentence, and — the control that makes it mean something — the same command
with a READABLE contract gets past the flag and fails on the plugin instead. No
plugin is needed for either: the contract is loaded before the plugin is
opened.
logos-cpp-sdk's own checks (tests, generator-cli, module-impl-abi): 334 of 334.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
dbe1d63677 |
feat(abi): define logos_module_set_call_caller, gated on protocol 0.6
The definition lands BEFORE the protocol declares the export. logos-protocol only DECLARES the module-impl C ABI and every backend owes the definition; that gap shipped twice, each time as an undefined symbol at dlopen, on Linux only, invisible on macOS. Declaring first would turn this repo, logos-rust-sdk and logos-module-builder red the night the bump merged. Defining first costs nothing: the guard is MAJOR-aware >= 0.6 and the current pin is 0.5, so nothing is emitted today and the ABI check sees declared == defined. This is the case #146 made possible. The next-MAJOR probe resolves the emitter at MAJOR+1, where a >= 6 guard IS true, so the emitted set there is legitimately a SUPERSET of the declared one. The probe used to demand equality and would have rejected this outright. cpp/logos_caller.h carries the LogosCaller type (std-typed, Qt-free) and logos::currentCaller(), reading a thread-local stack the generated export pushes to. Two things the audit corrected, both worth reading: * A present-but-unreadable `instance` is DROPPED and the module still identified. This backend already did that; Rust returned Unknown, and each had a passing test pinning its own answer, so neither suite could see the divergence. The protocol header now states the rule normatively and Rust is aligned to it. * The accessors are explicitly HIDDEN on ELF. The header argued this state must not be unified across images and then relied on being inline to achieve it — which is false: a function-local static in an inline function emits STB_GNU_UNIQUE at default visibility and the loader collapses every image's copy into one, even under RTLD_LOCAL. Measured across two dlopen'd images: default visibility let a push in A be read by B; hidden restored isolation. logos-module-builder sets no visibility anywhere, so real plugins were built the first way. An anonymous namespace would be worse — vague linkage is load-bearing WITHIN an image, since the generated TU pushes and the author's TU reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
43904ef1a3 |
fix(abi-check): the next-major probe asserts a SUBSET, not equality
The probe exists to catch a version guard written on LOGOS_PROTOCOL_VERSION_MINOR alone: at the next major the MINOR resets to 0, the guard goes false, and the surface disappears silently — no link error and no dlopen failure, because the matching calls are guarded the same way and vanish with it. That property is "everything DECLARED is still emitted at major+1", which is a subset test. It was written as `cmp -s`, an equality test, so it ALSO fails when the emitter defines MORE than the protocol currently declares. That is not a defect — it is precisely how a backend lands a new export BEFORE the protocol bump declaring it reaches this repo's lock, which is the only PR order that avoids a window where every backend is red through no fault of its own. The diagnostic underneath already computes the subset direction with `comm -13`, so the two disagreed: the offending case failed with an EMPTY list of offenders under a heading blaming MAJOR-awareness. A guard and its message have to agree, or the failure teaches the wrong lesson. No-op at the current protocol, where declared and at-nextmaj are equal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
51abc407df |
fix(cdylib): guard the conditional exports on MAJOR too, not MINOR alone
The three version guards this emitter writes tested
LOGOS_PROTOCOL_VERSION_MINOR and ignored LOGOS_PROTOCOL_VERSION_MAJOR. At
protocol 1.0.0 the MINOR resets to 0, so all three would have gone false
and the C++ backend would have dropped from ten module-impl exports to
seven — losing grant_host_services (0.3) and the teardown pair (0.5).
What makes this worth catching early is that it is NOT a link error and
NOT a dlopen failure. logos-plugin-qt's glue guards the matching CALLS the
same way, so the definitions and the calls disappear together: everything
builds, everything loads, and modules simply stop having teardown and stop
being grantable. There is no diagnostic anywhere. logos-rust-sdk would
meanwhile keep emitting all ten, because it compares the (major, minor)
tuple — so the two backends would silently disagree.
The arithmetic is emitted EXPANDED rather than behind a function-like
macro, because the generated sources are resolved by unifdef in the check
below, and unifdef evaluates nested integer arithmetic but silently
no-ops on what it cannot parse — which would be a green check over
unresolved text.
checks.<sys>.module-impl-abi gains the probe that would have caught it: it
already resolved the generated sources at the real MINOR and at MINOR=0,
and now also at one MAJOR up, where the full declared export set must
still be emitted. Nothing pins that to 1 in particular — it is "one past
whatever we are on", which is exactly where a MINOR-only guard breaks, and
it is the only probe here that does not resolve at the current major.
Proven to fail: reverting only the guard change turns it red naming all
three affected exports.
FAIL: [A: --from-header, header-first] the export set is not complete
at protocol 1.0.
DECLARED but NOT emitted once the MAJOR advances:
- logos_module_about_to_unload
- logos_module_grant_host_services
- logos_module_set_unload_done_callback
The MAJOR is read from the same logos-protocol output as the MINOR and the
export list, so no version literal enters this check.
logos-plugin-qt carries the same defect in its two glue guards; that is a
separate PR, and until both land the two sides still agree at every
version that exists today.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8554ecd77b |
test(abi): assert the generated cdylib defines every declared module-impl export (#144)
* test(abi): assert the generated cdylib defines every declared module-impl export
logos-protocol DECLARES the module-impl C ABI; this generator emits the
definitions. Those are independent facts, and the gap has shipped twice —
grant_host_services at protocol 0.3, the teardown pair at 0.5 — each time
as an "undefined symbol" at dlopen, on Linux only, with the runtime still
reporting the module as LOADED.
checks.<system>.module-impl-abi generates in all four shapes the emitter
can be reached in — --from-header, --lidl, a zero-method module, and one
with records and events — and diffs what each DEFINES against the list
logos-protocol publishes (packages.<sys>.module-impl-abi, protocol#66).
Both the list and the protocol version come from that one output, so there
is no version arithmetic here and nothing hardcoded.
The interesting part is what makes this NOT vacuous, because the obvious
implementation is. The generated .cpp carries the version guards as TEXT,
so a plain grep counts all ten exports at every protocol version and the
check would pass forever. unifdef resolves them — but unifdef given an
expression it cannot evaluate exits 0 and leaves the text alone, which
looks exactly like "nothing was conditional". So:
* no residual '#if/#else/#endif' may survive the resolution;
* the same pipeline runs twice, at the real MINOR and at MINOR=0, and
the MINOR=0 set must be a STRICT SUBSET of the real one.
That second assertion is the headline guard. It fires if unifdef no-opped,
if the version never reached it, or if the extraction were counting
mentions rather than definitions — without naming a single symbol or
version, so it does not go stale as the ABI grows.
Proven to fail. Commenting out the logos_module_string_free emission turns
it red naming that symbol, while `tests` and `generator-cli` both stay
GREEN — this is the only thing in the repo that notices a deleted export.
Moving the teardown guard from >= 5 to >= 6 turns it red naming both
teardown symbols, with the probe moving 10 -> 8, so the version resolution
is live rather than a name list.
CI runs it: ci.yml only ever built '.#tests' plus three binaries, so the
`checks` attrset was never evaluated at all — generator-cli has never run.
TODO in flake.nix: logos-protocol is rev-pinned to the protocol#66 branch.
Re-point at master once it merges; a rev pin never moves under
`nix flake update`, so leaving it would freeze the declared side.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): track logos-protocol master now that module-impl-abi has landed
logos-protocol#66 merged as 480f40f, so the temporary rev pin can go. A rev
pin never moves under `nix flake update`, so leaving it would freeze the
DECLARED side of this check at 0.5 and quietly stop catching anything a
later protocol adds — the exact silence the check exists to break.
Restores the master-tracking comment the pin displaced, with a line noting
that checks.module-impl-abi now also consumes
packages.<system>.module-impl-abi from this input.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
95d7b3a9c5 |
feat: split the SDK by capability, retire the provider-header path, and harden the cdylib decode (#138)
* feat(generator): remove --provider-header (interface: "provider")
Every provider now goes through the module-impl C ABI, so the LOGOS_METHOD
dispatch path is gone: parseProviderHeader, generateProviderDispatch,
ParsedMethod and the joinDocLines helper only it used (~300 lines), plus the
test that covered it.
`toQVariantConversion` is NOT removed — it is shared with live emitters — and
its test stays.
The flag is REFUSED rather than dropped. Without that, `--provider-header x.h`
falls through to the plugin-path branch, which reads the flag itself as a
plugin path and reports "Plugin file does not exist: --provider-header" — a
missing-file error for what is really a retired mode. It now exits 2 with a
message pointing at interface: "universal".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(sdk): logos_host_services.h — the C++ veneer over the privileged surface
Phase C1. A Qt-free, header-only wrapper over the three trust-root lp_* calls
A3 added, so capability_module can become an ordinary universal module instead
of a hand-written Qt plugin reaching into TokenManager directly.
Deliberately FREE FUNCTIONS, not a LogosModuleContext seam as the plan
sketched. The grant is process-global per IMAGE (host binary and module cdylib
each link their own logos-protocol, so each has its own grant state and its own
TokenManager), so the gate lives in the caller's own image and there is nothing
per-instance to inject; a context seam would imply the privilege is a property
of one impl object, which it is not. It is also markedly cheaper: a seam would
need a new module-impl C ABI export plus lockstep changes in BOTH codegen paths
(the Qt provider glue and the cdylib wrapper).
constantTimeEquals lives here rather than in each caller: the natural spelling
(a == b) leaks the matching-prefix length through timing, and a trust root
comparing tokens with == is the exact bug this file exists to prevent. Ported
from capability_module's own implementation to std::string.
Two things the tests caught that reading had not:
* lp_inform_module_token_to takes SIX arguments (client, auth_token,
origin_module, module_name, token, timeout_ms), not the three I first wrote.
The wrapper now mirrors it exactly, with the protocol's own default-timeout
semantics documented.
* sdk_tests compiles against logos_headers alone, which carries no protocol
include path. It now resolves logos_protocol.h from LOGOS_PROTOCOL_ROOT,
accepting either the source layout (cpp/) or a package layout (include/) and
failing loudly on neither, rather than hard-coding the one in use today.
The suite deliberately does NOT link logos-protocol: the lp_*-calling wrappers
are `inline` and never ODR-used by these tests, so no protocol symbol is
referenced. That is itself the assertion — the veneer must not drag the
protocol library into a header-only consumer. A future test that calls one will
fail to LINK rather than silently pull it in.
Also documents a real gap found while writing it: lp_token_get performs NO
host-service check, so "token_registry" gates ENUMERATION only. The plan claims
that service covers `lp_token_get(any)`; it does not. Flagged at the call site
rather than papered over — if lookup should be gated, the gate belongs in
lp_token_get.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(generator): emit logos_module_grant_host_services in the cdylib exports
Completes the C1 codegen half. A3 declared this entry point in
logos_module_impl.h and documented that the grant MUST cross the C ABI, but the
generator never emitted it, so nothing could open a module's gates.
The body forwards to lp_grant_host_services in the MODULE's own image, which is
the entire point. Verified that premise on a real built module rather than
taking it from the header comment: test_basic_module_cpp_plugin.dylib DEFINES
25 lp_* symbols and imports zero — logos-protocol is statically linked into
each plugin, so the module's gate state really is its own, and a grant recorded
only in the host would leave lp_token_keys() returning null forever. That
failure is silent: null is indistinguishable from an empty token store.
Emitted unconditionally rather than behind a codegen flag. Which modules are
privileged is the host's decision — it pushes nothing to an ordinary module —
and lp_grant_host_services validates the names and fails closed, so a per-module
flag would only add a second place for declaration and capability to disagree.
The comment states the boundary honestly: this is a declaration-and-audit
mechanism, NOT a defence against a hostile module. The cdylib links
logos-protocol, so its own code can call lp_grant_host_services() directly and
self-grant. What the gate buys is that the privilege is explicit, greppable and
off by default. Isolation between modules rests on process separation, the auth
token, and the target's allowedCallers.
Three tests, one per property that could regress independently: the export
exists; its body actually forwards (a stub returning 0 would make every host
push look successful while both gates stayed shut); and it is emitted for an
ordinary module too, not only for privileged ones.
Confirmed in the built artifact: nm on a real universal module lists
_logos_module_grant_host_services alongside the other seven module-impl exports.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(generator): guard the grant export on the protocol MINOR that added it
The emitted logos_module_grant_host_services calls lp_grant_host_services,
which logos-protocol only gained at MINOR 3. A module built against an older
protocol therefore failed to compile in GENERATED code its author never wrote.
Found by giving logos-template-module a standard flake: its own lock resolves
protocol master, and the build died on `use of undeclared identifier`.
Guarded on LOGOS_PROTOCOL_VERSION_MINOR >= 3. A module built against 0.2 has no
grant entry point at all, which is the same fail-closed state as never being
granted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: point logos_async_result.h at the one LogosModule.cmake
The comment named "logos-plugin-qt/cmake/LogosModule.cmake and its
module-builder twin". There is no twin any more: logos-plugin-qt's copy is
deleted and the file exists once, in logos-module-builder.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): rev-pin logos-protocol at c8bab12 (the trust-root surface)
logos_host_services.h is a veneer over lp_token_keys /
lp_inform_module_token_to / lp_grant_host_services, which 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 `tests`
check could not compile against the previously locked 03842db.
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(generator): remove --module-dir, and the two dead files it documented
--module-dir walked a directory of BUILT plugins and generated one consumer
wrapper per dependency by dlopen'ing each and reading its QMetaObject. Every
wrapper now comes from a contract instead -- `--general-only` with one
`--dep <name>=<name>.lidl` per dependency -- which builds no dependency plugin
and, unlike introspection, works under cross-compilation.
It is REFUSED rather than ignored, mirroring --provider-header right below it.
Falling through to the dependency LISTING would have exited 0 having generated
nothing: the exact shape that lets a stale caller look green while shipping a
module with no typed API.
No nix build changes as a result -- the flag had no caller left in any of them,
so a store-path diff would be empty either way and would prove nothing. The
only observable difference is what the binary does when handed the flag, so
that is what the new `generator-cli` check asserts, by EXIT CODE:
OK: control - --metadata alone exits 0 and lists dependencies
OK: --module-dir exits non-zero (status=2)
OK: --module-dir fails with the removal diagnostic
OK: --general-only still emits the umbrella
The control matters: without it a non-zero exit could equally mean the binary
is broken. It runs against an EXISTING modules directory too, because the old
code only errored when that directory was missing.
Also deleted, both genuinely dead:
* cpp/compile.sh -- compiles logos_api.cpp, module_proxy.cpp, token_manager.cpp
and six headers, NONE of which exist in this repo any more (they moved to
logos-protocol / logos-qt-host in the host split). The script cannot run.
* docs/docs.md -- 789 lines with zero inbound references anywhere in the
workspace, documenting --module-dir and a cpp/ layout that is gone.
cpp-generator/compile.sh is KEPT: logos-module-builder's LogosModule.cmake:404
still invokes it (`add_custom_target(cpp_generator_build ...)`) on the
LOGOS_CPP_SDK_IS_SOURCE branch, and it builds into exactly the
LOGOS_DEPS_ROOT/build/cpp-generator path that file then reads.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(generator): the umbrella emitter leaves legacy/, and gets its own mode
`cpp-generator/legacy/` held four things and only one was legacy. The shared
emitter library was misfiled there: `generator_lib.{h,cpp}` is already consumed
by the MODERN `experimental/lidl_gen_client.h` and by all 11 tests under
tests/generator/. `lidl_to_json.{h,cpp}` likewise. Both are now
`cpp-generator/`; `legacy/` is down to `main.cpp` + `legacy_main.h`.
The logos_sdk umbrella (`struct LogosModules`) is not legacy either — it is the
CURRENT typed-dependency surface. `LogosModuleContext::modules()` returns it,
so every universal module that calls a declared dependency goes through it, and
LogosModule.cmake runs `--general-only` for every module build. Yet the only
code that could emit it lived inside the directory the plan wants deleted.
So `cpp-generator/main.cpp` gains `--umbrella`, with `--general-only` routed to
the same implementation and dispatched before the fall-through to legacy_main.
The deps-driven emission needed no rewriting: `makeUmbrella{Header,Source}
FromDeps` were already in generator_lib, and legacy/main.cpp merely wrapped
them in file I/O. -352 lines from legacy/main.cpp (827 -> 475), including the
interface-wrapper helpers that only that branch used.
`--general-only` keeps working identically, because LogosModule.cmake and
logos-basecamp both call it. The alias is guarded on `--metadata`, since
`--general-only` was never a standalone mode — without metadata it fell through
and reported the flag as a missing plugin path, and it still does.
The scraping `writeUmbrellaHeader`/`writeUmbrellaSource` are untouched: they
belong to `generateFromPlugin`, the QPluginLoader introspection path, and die
with it.
Verified byte-identical, which is the whole claim of a relocation. An
adversarial pass built its own pre- and post-change binaries and diffed the
emitted `logos_sdk.{h,cpp}` across 12 real metadata.json files x {qt,lp} x
{--general-only,--umbrella}: 48/48 identical, stdout/stderr/exit included, with
a positive control (qt vs lp) confirming the harness can see a difference. Real
modules then built through logos-module-builder against both binaries with
`diff -r` empty, including the compiled plugin. 266/266 tests pass, and the
pre-change tree also reports 266, so no test was silently dropped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(sdk): split the SDK by capability, and add the host façade
Host, module-consumer and module-provider are three distinct capabilities. The
SDK exposed them as one target, so a program linked all three regardless of
what it was. Each now gets its own INTERFACE target:
::common logos_json.h, logos_result.h
::consumer logos_lp_client.h, logos_async_result.h
::provider logos_module_context.h, logos_host_services.h
::host logos_host_core.h (new)
`logos_headers` stays as an umbrella over all four, so the ~70 existing
consumers are unaffected — logos_module_context.h alone has 66. Migrate to the
narrow targets when touching a repo; additive first, removal second.
NOTE the misnomer the split exposes: `logos_host_services.h` is MODULE-side
despite its name — it is the veneer a privileged module uses for services the
host granted it — so it belongs to ::provider, not ::host. Renaming it touches
9 files across 5 repos, so it is left for a change that can carry that cascade.
── logos_host_core.h ───────────────────────────────────────────────────────
`logos::host::LogosCore`, a plain RAII wrapper over liblogos' logos_core_* C
API, for the four programs that stand up a core (basecamp, logoscore-cli,
standalone-app, module-viewer). They currently open-code the same calls, and
basecamp had already grown a private wrapper for them.
It is deliberately an ORDINARY class — no codegen, no injection seam, no
void*. LogosModuleContext needs `_logosCoreSetContext_`, SFINAE `maybeSet*`
helpers and a void* round-trip because a module impl is user-authored but
FRAMEWORK-instantiated. A host is main(): it constructs this itself. For the
same reason there is no `modules()` here — the host holds its own LogosModules
from its own generated logos_sdk.h, so this header needs no generated type.
What it earns, each tied to a measured hazard:
* OWNERSHIP. liblogos allocates its char**/char* returns with new[], so
`delete[]` is correct and free() is undefined behaviour. That rule lived in
a comment in one repo's .cpp; it is now in one place.
* ORDERING. Three setters must precede logos_core_start(), stated only in
comments in logos_core.h. They are constructor arguments here, so the
illegal order is not expressible.
* SHAPE. logos_core_get_module_stats() takes no module name and returns one
blob for every module; stats(name) does that parse once.
The logos_core_* ABI is re-declared rather than included: logos-liblogos
depends on logos-cpp-sdk, so including its header would invert the graph. Every
host already hand-declares it; this makes it one declaration instead of four.
15 tests, 281/281 suite total. They define the extern "C" ABI themselves and
allocate exactly as liblogos does, so the ownership rules are exercised rather
than asserted; the ordering test records call order and pins start() as last.
Also fixes a real gap: nix/include.nix carries its own header list, separate
from cpp/CMakeLists.txt's install(FILES), so a new header silently did not ship
in the export layout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(generator): a Qt-typed umbrella that needs no LogosAPI
Splits a consumer's TYPE SURFACE from its TRANSPORT. Until now the qt umbrella
was `explicit LogosModules(LogosAPI* api)` while the lp one was default-
constructible, so "Qt types" implicitly meant "has a LogosAPI" — and a cdylib
module, whose provider surface is the std logos_module_impl.h C ABI and which
holds no LogosAPI anywhere, could not have Qt-typed dependency wrappers at all.
Its generated glue emits `new LogosModules()` unconditionally
(lidl_gen_cdylib.cpp:693), so the combination did not merely misbehave, it did
not compile.
That was a codegen choice, not a law: the wrapper bodies already run over lp_*.
`--binding api|origin` selects it, defaulting to `api`. A second enum rather
than a third ApiStyle value, deliberately: ApiStyle names the type surface and
is switched on by six emitters (makeHeader/makeSource/returnTypeFor/
paramTypeFor/toWireFor/fromWireFor); a "Qt types, explicit origin" member would
force all six to answer a transport question whose honest answer is "same as
Qt" every time. ApiStyle::Lp ignores the new axis — lp is origin-bound by
construction — and that is asserted rather than assumed.
The emitted umbrella bakes metadata.json#name as the origin literal:
LogosModules() : test_fullapi_cpp(QStringLiteral("test_fullapi_qtproxy")) {}
FullApi bind_full_api(const QString& moduleName) {
return FullApi(QStringLiteral("test_fullapi_qtproxy"), moduleName); }
Origin is the CONSUMER's own name and target is the dep — origin first in both
bind_ overloads. This is the load-bearing property: LpBridge::forTarget derives
origin from `api->moduleName()`, and reusing it silently gives a consumer the
caller's identity, which has already preserved a privilege escalation once in
this tree. An empty metadata name is refused at the CLI (exit 6, naming the
file) and emits `#error` in the header: a module that cannot state its identity
must not compile, and must never be handed a blank or borrowed one.
Verified additive on 172 real metadata.json x 2 api-styles = 344 runs, all
producing output, byte-identical old binary vs new. Mutation control: swapping
bind_<iface>'s (origin, moduleName) to (moduleName, origin) fails the suite at
MakeUmbrellaTest.QtExplicitOriginStatesTheConsumersOwnNameEverywhere. 281 -> 286
tests.
Framing worth keeping: the origin is SELF-ASSERTED from the module's own
metadata and is not attested by the transport. That is not a regression —
`api->moduleName()` is equally process-stated — but "explicit origin" means the
module names itself, not that the host vouches for the name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(generator): stop pointing callers at a flag that no longer exists
--backend qt is deleted from logos-qt-generator, and this repo was still
signposting it. main.cpp's refusal said "Use it for --backend qt", and the usage
text advertised --lidl … --backend qt and --from-header … --backend qt. Those
now point at nothing — the exact failure the deletion removes, one repo over.
The refusal names the real replacement chain instead: --backend cdylib here,
then logos-qt-host-generator --backend cdylib for Qt-plugin packaging.
docs/project.md's "Provider Generation" section documented three emitters that
no longer exist; rewritten to state the seam and the two-step pipeline.
docs/spec.md's dataflow diagram showed <name>_qt_glue.h / <name>_dispatch.cpp as
outputs; the diagram is corrected and the sections describing that shape are
marked historical rather than deleted, because the onInit wiring they document
still applies to the cdylib glue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(cpp-generator): merge legacy/ into the generator, dropping its dead half
`legacy/` was never a library with an API surface. Two files, 475 lines, exactly
one exported symbol — `int legacy_main(int, char**)` — with every other
definition `static`, compiled INTO logos-cpp-generator and reached by fallthrough
at the end of main(). So there was nothing to keep separate: it is one mode of
this binary, and it now lives beside the others as plugin_introspect.{cpp,h}
behind `runPluginIntrospectMode()`, named for what it does.
Deleting it was never an option — that premise was checked and refused earlier.
`--general-only` alone has ~10 live callers across 7 repos including the central
module path (buildPlugin.nix:206,211, buildHeaders.nix:221,
LogosModule.cmake:423). The mode is load-bearing; only its packaging was wrong.
110 lines go with the move, all genuinely unreferenced:
* cppStringEscape — zero callers anywhere.
* writeUmbrellaHeader / writeUmbrellaSource and the `if (!moduleOnly)` block
that called them. This is the real prize: a SECOND, directory-SCRAPING
implementation of logos_sdk.{h,cpp}, unreachable in practice because
generate-module-headers.sh:60 always passes --module-only. generator_lib's
deps-driven makeUmbrella*FromDeps is now the only umbrella emitter, so the
two cannot drift.
* a dead `QJsonDocument doc(methods);` and its commented-out use.
With the block gone, `--module-only` suppresses nothing, so the parameter and
its plumbing go too. The FLAG stays tolerated rather than rejected, because
generate-module-headers.sh passes it unconditionally — a comment at the old
parse site says so.
Verified: #default builds, and both checks pass — `generator-cli` (which
exercises the CLI surface, including --general-only) and `tests`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(sdk): make the by-name call path a supported API
The dynamic (by-name) invoke path already existed and was already ungated at
every layer — lp_client_create / lp_invoke in the C ABI, logos::LpClient above
it, and the Qt client above that. Nothing checked a host service; there was no
gate to open. What was missing was the ERGONOMICS, which is what turned a
supported capability into something callers reached around the umbrella to get.
Three additive pieces, no gate touched:
1. LogosModuleContext::moduleName() — the module's own registry name, i.e. the
origin it authenticates as. The typed wrappers bake their origin in at
codegen time; a by-name call has to state one, and a wrong origin
authenticates as nobody and fails far from the call site. Set through a NEW
`_logosCoreSetModuleName_`, deliberately not a fourth parameter on
`_logosCoreSetContext_`: every generated provider calls that signature, so
widening it would break each one until regenerated, for a value the
generator knows statically. Set before the context, so moduleName() is live
inside onContextReady().
2. LogosModules::dynamic(target) on the origin-bound umbrella — the untyped
client, with the origin baked in exactly as the typed members' is, and
cached per target because LpClient owns a connection. The typed members over
metadata.json#dependencies stay the ordinary way to call another module;
this is for the cases whose target is a runtime value (a proxy, a router).
3. LpClient::getMethods() over the already-exported lp_get_methods. Invoke
without introspect is guessing — a caller that cannot ask what exists can
only hardcode, and a wrong guess fails at runtime like a typo.
Verified: #default builds, and both checks pass (tests, generator-cli).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cdylib): shape-check [any]/{tstr:any} args, and fix the umbrella's includes
TWO fixes; the second is a regression I introduced two commits ago.
1. jsonArgToStd() returned the raw json for LogosList / LogosMap — "untyped JSON
passes through, as it always has". Arity was checked, type was not, so a
scalar reached a `[any]` parameter untouched. A proxy forwarding it through a
Qt-typed consumer then turned "notalist" into
["n","o","t","a","l","i","s","t"], because qvariant_cast reads a QString as a
sequential container — and the downstream provider saw a well-formed array
with nothing left to refuse. Now emits logos::jsonRequireArray /
jsonRequireObject; the throw lands in the dispatch's existing catch as
{"code":"dispatch_failed"}. Bare `any` stays raw, deliberately: it declares
nothing, so there is nothing to check it against.
Measured on the conformance matrix: closes all 8 failing cells (498/22/12/8
-> 500/18/12/2), and a whole-matrix per-cell diff shows exactly 14 status
changes, every one inside the two hostile cases. The other 518 cells are
byte-identical in status and value. The remaining 2 are the fix working — the
C++ cdylib provider now refuses the same input, which cases.json still pins
as lenient via expect_by_provider; that is a registry edit, and known.json's
Q1b already names this exact outcome as the intended fix.
2. The Lp umbrella emitted `logos::LpClient& dynamic(...)` and a
std::map<..., std::unique_ptr<logos::LpClient>> while <map>, <memory> and
logos_lp_client.h were conditional on interfaceNames. A module WITH
dependencies compiled by accident, because <dep>_api.h drags the header in
transitively. A module with NO dependencies and no interfaces includes
nothing else and failed outright with "no type named 'LpClient' in namespace
'logos'". test_fullapi_cpp is exactly that shape.
I shipped that in
|
||
|
|
e3744fb84c |
feat(windows): add an x86_64-windows cross target (#136)
* feat(windows): add an x86_64-windows cross target - packages now go through forAllTargets, adding the "x86_64-windows" pseudo-system alongside the native ones. - qt6.wrapQtAppsNoGuiHook is dropped for Windows and dontWrapQtApps set on each derivation. Both halves are needed: the hook does not evaluate for a mingw host, it would skip a PE anyway (wrap-qt-apps-hook.sh only handles ELF and Mach-O), and qtbase's setup hook hard-errors in qtPreHook without the flag. - cmakeFlags pick up pkgs.logosQtCrossCmakeFlags, which is empty natively. - meta.platforms widened to include windows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(windows): fail loudly when the generator binary is missing The install test named only the unsuffixed `logos-cpp-generator` and had no else-branch, so a mingw build — which produces `logos-cpp-generator.exe` — copied nothing, succeeded, and shipped an EMPTY $out/bin. The failure then surfaced in whichever consumer tried to run the generator, nowhere near the cause. Probe both names, and exit 1 with a directory listing if neither is there. Verified: the Windows build now installs a real PE, and the native build is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deps): re-pin logos-nix, logos-protocol and logos-lidl to their merged revs L1 (logos-nix) and L2 (logos-protocol, logos-lidl) 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> |
||
|
|
f3369faca4 |
feat(generator): async callers can see the error, sync callers can set a deadline (#132)
The two consumer surfaces had complementary holes:
sync : T foo(params…, logos::CallError* err = nullptr) error yes, timeout NO
async: void fooAsync(params…, cb, Timeout = Timeout()) timeout yes, error NO
so an async caller could not tell a failed remote call from a provider that
legitimately returned 0 / "" / false — the exact ambiguity the sync path's
CallError* was added to resolve — and a sync caller could not say how long it
was willing to wait, even though the transport overload the generator already
calls takes both.
Both fixes are additive:
T foo(params…, logos::CallError* err = nullptr, Timeout timeout = Timeout());
void fooAsync(params…, std::function<void(T)> cb, Timeout timeout = Timeout()); // unchanged
void fooAsyncResult(params…, std::function<void(logos::AsyncResult<T>)> cb,
Timeout timeout = Timeout()); // new
logos::AsyncResult<T> (new, Qt-free, cpp/logos_async_result.h) is {value, error}
plus ok(); AsyncResult<void> carries only the error so every fooAsyncResult has
the same callback shape. The name is distinct rather than an overload because
std::function<void(AsyncResult<T>)> next to std::function<void(T)> is ambiguous
for a generic lambda.
Applied to both emitters that produce this surface — legacy/generator_lib.cpp
(the module-builder path) and experimental/lidl_gen_client.cpp (`--lidl
--module-only`, from a published contract) — since a consumer can reach either
for the same contract.
The Qt-free (ApiStyle::Lp) surface gets the sync timeout (spelled `int
timeout_ms`; `Timeout` lives behind a Qt header) but NOT fooAsyncResult:
logos-protocol's lp_invoke_async hard-codes `cb(1, …)`, so an AsyncResult there
would report ok() on a failed call. Measured, not assumed. See the note in
makeHeaderLp.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
fde0f6fccb |
fix: read dependency entries declared in object form (#123)
* fix: read dependency entries declared in object form The manifest schema lets a dependency entry be an object carrying the name alongside the constraints an installer resolves it by, but every reader took the element as a plain string and skipped what came back empty, so an object entry disappeared: the module it names was left out of the generated LogosModules aggregate, and every call through it failed to compile. The rule lives in one place now, since the copies of it are how the gap spread. It ships in share/lidl-frontend alongside the parser that includes it, which consumers compile from there. * fix: read every dependency entry through one pass over the array The object form reached the umbrella's members and constructor but not its includes: that emitter still read each element as a plain string, so a module declared in object form came out as a member whose type was never included, and the aggregate no longer compiled. It is the Qt-free umbrella, which is what every universal core module and every cdylib module generates, so the form the previous commit set out to support failed there in a new way rather than working. Reading the array element by element is what let one pass disagree with the next, so no reader does that any more: dependencyNames() answers what an array declares, once, and the emitters walk names. That leaves the entry form knowable in exactly one place, and the includes and members of an aggregate can no longer be built from different answers. The umbrella emission moves to generator_lib alongside the per-module wrapper emitters it mirrors, returning the text instead of writing it, so what it generates can be asserted on directly; main.cpp writes what it returns. Output for string-form dependencies is byte-identical in both API styles, with and without interface dependencies. The listing mode (`--metadata` with no `--module-dir`) went the same way — it was the last reader still deciding on its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Dario Gabriel Lipicar <dario@status.im> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1bc101df1f |
feat: cpp-generator consumes logos-lidl; delete embedded frontend (#89)
* feat: cpp-generator consumes logos-lidl; delete embedded frontend The canonical LIDL frontend now lives in logos-lidl. cpp-generator links it and keeps only the C++/Qt-specific parts (impl-header parsing, the gen_client/ gen_cdylib backends, the Qt type-name mapping). - Delete the embedded lidl_lexer/parser/serializer/validator/ast. - Add experimental/lidl_compat.h: brings logos-lidl's std AST into the global scope the backends use (via `using`), a qs() std::string→QString helper, a QTextStream<<std::string overload, and name-compatible shims (lidlParse/ lidlSerialize/lidlValidate) so the emission code keeps compiling. - Re-point impl_header_parser, lidl_gen_client (+ Doxygen /// docs on the generated client methods), lidl_gen_cdylib, lidl_emit_common, and legacy/ main at lidl::ModuleDecl. - CMake: C++17 + find_package(logos-lidl) + link logos-lidl::logos_lidl. - bin.nix: distribute only the shared C++/Qt backend helpers (compat + impl_header_parser + emit_common) under share/lidl-frontend, not the frontend. - tests: drop the 4 frontend test files (covered by logos-lidl now); the backend tests link logos-lidl. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: pin logos-lidl to the C-ABI commit + lock it The logos-lidl input was declared in flake.nix but missing from flake.lock, so override chains that don't reach the nested input (the doctest harness building a scaffolded module) couldn't resolve it. Pin the branch rev and lock it so the component is self-contained. Re-point at master once logos-lidl lands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: re-point logos-lidl to merged master (#5) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
676154070c |
codegen: Qt-free outbound — ApiStyle::Lp typed wrappers over the lp_* C ABI (#88)
* ci: drop doctest chain pins — the qt-split chain is fully merged logoscore-cli and module-builder masters now contain the chain; the temporary --release-for pins (added so stacked-branch CI could resolve compatible cross-repo revs) default back to latest releases. * codegen: Qt-free outbound — ApiStyle::Lp typed wrappers over the lp_* C ABI Adds a third generator flavor (ApiStyle::Lp, --api-style lp) whose typed dependency wrappers + LogosModules umbrella call the logos-protocol C ABI directly via a new header-only logos::LpClient, instead of LogosAPIClient. This lets a module make outbound typed calls and event subscriptions with NO Qt in its translation units — Qt stays confined to the QRO transport (inside logos-protocol) and the generated plugin glue. - cpp/logos_lp_client.h: header-only logos::LpClient (lazy lp_client_create on a baked origin; invoke / invokeAsync / subscribe; std<->nlohmann JSON; CallError out-param) + RAII logos::LpSubscription (unsubscribes on drop) + json<->std helpers. The C++ analog of rust-sdk PluginProxy. - generator: makeHeaderLp/makeSourceLp emit the Lp wrappers; the Lp umbrella drops the LogosAPI ctor and bakes this module name as the lp_client origin (LogosModules() default-constructible). Qt/Std emission is byte-unchanged (dispatch added at the top of makeHeader/makeSource). Verified: generator builds; generated wrappers + umbrella compile to .o with ONLY cpp-sdk + logos-protocol headers + nlohmann (no Qt); cpp-sdk tests pass. * cdylib: wire the Qt-free typed dependency surface (modules()) into the impl When a cdylib module declares dependencies, the generated exports now include the Lp umbrella (logos_sdk.h) and construct LogosModules() + maybeSetLogosModules on the impl just before onContextReady — so the author can call modules().<dep>... and subscribe to dep events from a Qt-free cdylib. Guarded on module.depends so dependency-less cdylib modules are byte-unchanged. The umbrella + dep wrappers themselves are produced by the --general-only --api-style lp generation; feeding the dep .lidl files into that during the module build is the remaining build-system wiring (module-builder + plugin-qt dep resolution). * cdylib: wire modules() unconditionally (deps come from metadata, not the .lidl) The umbrella wiring was guarded on the .lidl module.depends, but a cdylib module declares its dependencies in metadata.json#dependencies — the .lidl contract.depends is typically empty — so modules() was left null and a typed outbound call segfaulted. Always include the generated logos_sdk.h umbrella and maybeSetLogosModules(impl, new LogosModules()) before onContextReady; the overload is a no-op for context-less impls and the umbrella codegen emits an (empty) logos_sdk.h for every cdylib, so this is safe in all cases. * fix(headers): ship logos_lp_client.h in the include/cpp source-export root A cdylib module's generated dep wrapper includes "logos_lp_client.h" and, transitively, "logos_result.h". The wrapper is compiled with the cpp-sdk source-export include root (include/cpp), so logos_lp_client.h must sit beside logos_result.h there — a quoted include resolves siblings relative to the including file's directory. Previously logos_lp_client.h shipped only at the top-level include/ (the CMake-export layout via cpp/ CMakeLists.txt), so it pulled in include/logos_result.h while the impl's logos_module_context.h pulled include/cpp/logos_result.h. Those are two distinct realpaths under the symlinkJoin, so #pragma once could not dedup them and StdLogosResult was redefined. Install every std header into both roots so a single TU only ever sees one logos_result.h. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cdylib): route logos_module_accept_token into the protocol TokenManager The generated module-impl export stored accepted tokens in a process-local std::map (g_tokens) that nothing ever read, so a cdylib module's OUTBOUND lp_client (modules().<dep>...) never saw the capability_module bootstrap token the host delivers at load. The automatic requestModule flow then ran unauthenticated: capability_module rejected requestModule, no per-target token was issued, and the cross-module call was rejected (returning a default-constructed result, e.g. 0). Forward the token into lp_token_save, which writes the same TokenManager::instance() singleton the cdylib's lp_client reads. The capability/token handshake now completes and typed Qt-free outbound calls return real results. Drop the dead g_tokens map + mutex. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(lidl): exclude LogosModuleContext hooks from header-derived contracts --header-to-lidl parses an impl class's public methods. An impl commonly overrides onContextReady() (and could redeclare a context accessor) in its own public section, so the derived LIDL would include onContextReady / modules / modulePath / instanceId / instancePersistencePath. Those are framework plumbing, not API methods — and feeding them to the cdylib backend breaks cdylib-eligibility (e.g. the inherited accessors' Qt-free return-type check), which is exactly what header-first universal modules now hit. Skip the reserved LogosModuleContext names in the parser so both the Qt --from-header path and the cdylib --header-to-lidl path emit clean, API-only contracts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cdylib): support the full std type set in header-derived contracts Routing core universal modules through the cdylib backend surfaced gaps between the cdylib subset and what the std apiStyle handled — a universal module that built under std must also build as a header-first cdylib. - lidl parser: restore the return-shape flags (resultReturn / jsonReturn) from the parsed return TypeExpr, so a header -> .lidl -> cdylib round-trip (the universal path, needed to feed the Qt glue) preserves the semantics the impl-header parser sets from C++ types (StdLogosResult -> result; LogosMap/LogosList -> json). Without this the cdylib codegen/eligibility mis-handled result / map / list returns. - cdylib eligibility + dispatch: `void` is not a lidlBuiltinType, so the parser yields it as a Named "void" (header path uses empty name) — treat both as void in the eligibility check and the dispatch (was relying on lidlTypeToQt=="void", which didn't match Named "void" -> generated an `auto result = <void call>`). - typeSupported: accept `any` (both directions), `void`/`result` (returns), arrays-of-any, and Map ({k:v}/LogosMap) — the Qt-free-via-nlohmann set. Verified: a probe with void / LogosMap / LogosList / StdLogosResult / const returns is cdylib-eligible and dispatches correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(lidl): carry method/event descriptions across the .lidl round-trip Header-first universal modules go header -> .lidl -> cdylib backend. The impl-header parser captures /// and /** */ doc comments into method/event descriptions, but the .lidl serializer emitted only the signature, so the descriptions were dropped — introspection (lm methods / --json, getMethods) then showed no docs (regressing the wrap-external-lib + tutorial doctests). Serialize each method/event's description as a trailing `description "..."` clause (escaped for the string literal; the lexer already decodes \\ \" \n \t) and parse it back in parseMethodDef/parseEventDef. Module description now escaped too. Verified: /// docs survive header -> .lidl -> getMethods. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(lp): bound-interface wrappers are handles over umbrella-owned state The Lp interface (bind_<iface>) wrapper owned its LpClient + RAII subscriptions BY VALUE, so the idiomatic transient handle — modules().bind_calculator(p).fibonacciAsync(...) modules().bind_calculator(p).onVersionReady(...) — tore the client/subscription down when the temporary died, cancelling the async callback and the event subscription. (Sync calls completed before the temporary's destruction, so they worked; the Qt/std flavor works because its handle is thin over a LogosAPI-owned persistent client.) Make the Lp Bound wrapper a THIN, copyable handle over `State { LpClient client; vector<LpSubscription> subs; }` that the LogosModules umbrella OWNS per provider (std::map<provider, unique_ptr<State>>) for the module's lifetime. bind_<iface>(p) creates/looks up the State and returns a handle to it, so a transient handle's async/event registrations outlive it. Concrete (Static) dep wrappers are unchanged — they're already persistent umbrella members, so by-value ownership is fine there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cdylib): lenient bytes-param decode (string / array / tagged) A universal module's bstr (std::vector<uint8_t>) PARAM arrived empty when the caller sent a plain string rather than the tagged {"_bytes": base64url} form — lidlBytesFromJson only accepted the tagged object, so byteArraySize("12345") and byteArraySize(b"\x01..") both saw 0 bytes (the return direction already worked). The std path was lenient (a QString or QByteArray arg both became bytes). Accept all three forms: a plain JSON string (raw UTF-8 bytes), an array of byte values, and the tagged {"_bytes"} form (base64url). Return direction unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cdylib): a number arg to a bytes param decodes as its decimal text byteArraySize("12345") arrives as a JSON number (the logoscore CLI's type auto-detection turns the string "12345" into int 12345), and the Qt path gives QVariant(int)->QByteArray "12345" (5 bytes). The cdylib bstr decode returned 0 for a number. Treat a JSON number as its decimal text bytes (j.dump()), matching the Qt behaviour, so a bare-number arg to a bytes param round-trips identically. Verified: byteArraySize 12345 -> 5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f0fe8cbfeb |
Make the base SDK Qt-free: Qt developer layer moves to logos-qt-sdk (#83)
* Extract the protocol layer into logos-protocol; consume it as a flake input The transport/token/IPC layer (transports incl. QRO + plain TCP/TLS, consumer core LogosAPIClient/LogosAPIConsumer with the capability auto-requestModule flow, ModuleProxy, token manager, QVariant<->JSON conversion, the abstract LogosProviderObject interface) now lives in the logos-protocol repo behind the versioned lp_* C ABI. This SDK keeps the typed C++ developer layer (LogosAPI, provider base classes + Qt provider glue, module context, code generator) and still compiles the protocol sources INTO liblogos_sdk.a from the flake input, so the installed artifact (archive symbols, include/ + include/cpp layouts, cmake config) stays byte-compatible: existing consumers need no changes. Public headers are unchanged; logos_provider_object.h keeps its name and now re-exports the abstract interface from logos_provider_interface.h. Transport/protocol component tests moved to logos-protocol with the code; the remaining sdk/generator/experimental suites are unchanged (432/432 green against the local protocol checkout). * lock: add logos-protocol input * Make the base SDK Qt-free: move the Qt developer layer to logos-qt-sdk LogosAPI, LogosAPIProvider, LogosProviderBase/LOGOS_PROVIDER macros, the QObject provider glue (QtProviderObject) and the legacy PluginInterface (core/interface.h) move to the new logos-qt-sdk repo. The protocol sources are no longer compiled into a monolithic archive — consumers link logos-qt-sdk (which layers on logos-protocol) instead. What remains here is header-only std C++: logos_module_context.h, logos_result.h (StdLogosResult), logos_json.h — exported as the CMake INTERFACE target logos-cpp-sdk::logos_headers — plus the code generator (a build-time tool; its introspection mode now includes logos_provider_interface.h from logos-protocol, where LogosProviderPlugin moved). Mechanically verified Qt-free: the logos-cpp-lib / logos-cpp-include closures contain only nlohmann_json. Tests: 245/245 (module-context std suite + generator + experimental). * fix: accept the installed source-export layout in the protocol-root check The fail-fast only tested <root>/cpp/logos_protocol.h, but the LP_SRC selection right below (and the error message itself) support the installed export layout <root>/include/cpp as well. Pointing LOGOS_PROTOCOL_ROOT at an installed export tripped the FATAL_ERROR before that fallback could apply. Caught by Copilot review on #82. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * lock: pin logos-protocol to the qt-free-split branch head The Qt-free SDK (and the cdylib backend stacked on it) reference LogosProviderPlugin from protocol's logos_provider_interface.h, which lands on feat/qt-free-split — the P1-branch pin no longer compiles standalone. Temporary — drop when the chain PRs merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: pin the logoscore runtime via its {release} placeholder The spec built logoscore-cli at bare master with only the cpp-sdk inputs overridden — master's stack cannot compile against the qt-free SDK, so the suite failed on the chain branches. With the placeholder, CI's --release-for pins expand it to the workspace's logoscore commit (and local runs without a pin still fall back to master, unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: override the nested module builders to {release} too capability_module (via logoscore's lock) and the cloned accounts module resolve module-builder from their own locks — pre-split revs whose LogosModule.cmake still detects the SDK by logos_api.h, which the qt-free SDK no longer ships ('logos-cpp-sdk not found'). Overriding the builder itself to the workspace-pinned chain rev (keeping the nested cpp-sdk override) builds both modules with the split-aware builder. Verified end-to-end locally with the exact doctest command. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: apply the {release} + nested-builder overrides to all three specs The runtime spec got the treatment in 210eea1; the composition and worker-thread specs have the same logoscore/module build commands and failed identically (pre-split builders from the modules' own locks). All executed run: blocks now pin logoscore-cli{release} and override the nested module builders to logos-module-builder{release}; the displayed code_block: variants stay in their generic master form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * codegen: typed wrappers throw on call failure; dispatch catches escapes Generated sync client wrappers call the new err-out invokeRemoteMethod overload and throw logos::LogosCallError when the call fails (e.g. the bound module is missing) — previously the empty QVariant silently degraded to the return type's default and a caller could not tell failure from a legitimate 0 / "". Both generators (legacy + LIDL), both API styles. Async paths unchanged. Generated provider dispatch (universal qt glue + LOGOS_PROVIDER) wraps the method body in a catch-all that logs and returns an invalid QVariant — an escaped exception becomes an ordinary METHOD_FAILED instead of unwinding through Qt event dispatch and killing the module process. * codegen: CallError out-param instead of throwing wrappers Per review, the generated sync wrappers expose the error channel as an optional trailing parameter — add(a, b, &err) — rather than throwing: explicit, stateless, works on temporaries, and existing call sites compile unchanged (they keep default-on-failure, now with a qWarning so failures are visible in the module log). The dispatch catch-all from the previous commit stays: it contains author exceptions, it doesn't introduce any. * glue: fire onContextReady AFTER modules()/event wiring The generated onInit set the context (which fires the impl's onContextReady hook) before constructing the LogosModules aggregate and wiring typed event emission — so an impl doing its documented one-time setup there (typed dependency calls, event subscriptions) dereferenced a null aggregate and crashed the module process (signal 11). Found by the first module to subscribe to a dependency's typed event from onContextReady. Context now goes last. * ci: run workflows on stacked PRs + workflow_dispatch Both workflows filtered pull_request to master-based PRs, so stacked PRs (feat/qt-free-sdk -> feat/extract-logos-protocol, feat/cdylib-authoring -> feat/qt-free-sdk) ran NO checks at all. Drop the base-branch filter for pull_request and add workflow_dispatch for manual runs. Same fix as logos-module-builder 232b8a2. * lock: protocol at the typed-requestModule port (3de5398) * ci: chain pins for the doc-tests (drop at merge) In repo CI only cpp-sdk's {release} is the commit under test — logoscore-cli and module-builder expanded to master, which doesn't link against the chain SDK the specs override in ('Build the CLI with the SDK override' failed on every run since the stacked-PR triggers were enabled). Pin both to the extraction-chain heads; the workspace pipeline is unaffected (it pins every repo itself). * generator: distribute the LIDL frontend for external generators First step of moving ALL Qt glue emission out of this repo into logos-qt-sdk's logos-qt-generator (cpp-sdk's generator keeps only the Qt-free outputs: std typed wrappers, logos_sdk umbrella, cdylib impl-exports, LIDL derivation). - Shared emit helpers (lidlToPascalCase, lidlTypeToQt, lidlTypeToStd, lidlIsStdConvertible) move to a new lidl_emit_common.{h,cpp} unit, used by both generators. - The frontend set (AST, lexer, parser, serializer, validator, impl-header parser, emit-common) is installed under share/lidl-frontend/ — the qt generator compiles these sources in directly, so the two tools share one frontend without a binary ABI. * lock: protocol#3 merged — pin advances to protocol master --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
38bc77e127 |
Consume logos-protocol: the transport/token/IPC layer moves behind the lp_* C ABI (#82)
* Extract the protocol layer into logos-protocol; consume it as a flake input The transport/token/IPC layer (transports incl. QRO + plain TCP/TLS, consumer core LogosAPIClient/LogosAPIConsumer with the capability auto-requestModule flow, ModuleProxy, token manager, QVariant<->JSON conversion, the abstract LogosProviderObject interface) now lives in the logos-protocol repo behind the versioned lp_* C ABI. This SDK keeps the typed C++ developer layer (LogosAPI, provider base classes + Qt provider glue, module context, code generator) and still compiles the protocol sources INTO liblogos_sdk.a from the flake input, so the installed artifact (archive symbols, include/ + include/cpp layouts, cmake config) stays byte-compatible: existing consumers need no changes. Public headers are unchanged; logos_provider_object.h keeps its name and now re-exports the abstract interface from logos_provider_interface.h. Transport/protocol component tests moved to logos-protocol with the code; the remaining sdk/generator/experimental suites are unchanged (432/432 green against the local protocol checkout). * lock: add logos-protocol input * fix: accept the installed source-export layout in the protocol-root check The fail-fast only tested <root>/cpp/logos_protocol.h, but the LP_SRC selection right below (and the error message itself) support the installed export layout <root>/include/cpp as well. Pointing LOGOS_PROTOCOL_ROOT at an installed export tripped the FATAL_ERROR before that fallback could apply. Caught by Copilot review on #82. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * lock: protocol at the typed-requestModule P1 port (1e4bc72) * lock: protocol at master (protocol#2 merged) The extraction is on protocol master now (29afbac); the temporary branch pin is dropped. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4e20614cda |
add universal qt-free provider interface (#67)
add universal qt-free provider interface address review |
||
|
|
25c88f4d48 |
support non-local remote transports (#57)
* support non-local remote transports * fix LogosResult * allow getting client over specific transport * fix ssl * investiage ssl error * pr comments * allow transport set configuration on any module * pr comments * add docs * pr comments * propagate only non-qt dependencies * restore ABI compatibility |
||
|
|
f7c855b110 | add logos result type (#55) | ||
|
|
1468180b25 | add support for new types (#50) | ||
|
|
d633575677 |
add IDL parser & generator (wip) (#33)
* add IDL parser & generator (wip) * fix fixtures issues affecting tests * fix fixtures issues affecting tests |
||
|
|
02baa918a8 |
remove proxy api (moved to logos-module-client) (#30)
Co-authored-by: Logos Workspace <logos@workspace.local> |
||
|
|
21f8fb43d3 |
abstract/move some of proxy api logic to cpp-sdk (#28)
Co-authored-by: Logos Workspace <logos@workspace.local> |
||
|
|
39b0a9acce |
Add tests; CI (#27)
* add tests for cpp-generator and sdk * add tests for new api, module_proxy and factories * add CI tests * add tests for a module definition |
||
|
|
4197ee1830 |
Finish Abstraction & Refactor (Ongoing) - part 1 (#25)
* refactor: abstract connection/transport; and clearly separate qt remote obj and qt local into separate implementations * abstract qt remote registry * add mock implementation; these serves to further test the abstraction but also useful for testing modules later * use LogosObject instead of QObject * abstract provider side * updates to use new api * re-add async api back --------- Co-authored-by: Logos Workspace <logos@workspace.local> |
||
|
|
a4bd66cd6e |
add Logos instance id to registry url (#20)
Allows multiple Logos core instances to run on the same machine by giving appending a unique instance identifier to each registry url. |
||
|
|
4fdf157120 |
feat: LogosResult (#14)
* Add LogosResult * Add documentation for complex types * Add get type util function * Add more shorthand functions * Add bool support * Provide more shorthand functions * Throw exception on bad access * Fix typo in doc |
||
|
|
c754c83f27 |
add support for a 'local' mode that does not use separate processes. required for some platforms such as mobile.
support local mode some refactor some refactor |
||
|
|
b5b0bdb81d | refactor nix file |