mirror of
https://github.com/logos-co/logos-protocol.git
synced 2026-08-30 21:41:10 +00:00
docs-shared-runtime-scheme
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
480f40ff63 |
feat(abi): publish the module-impl export list as data, for backends to check against (#66)
* feat(abi): publish the module-impl export list as data, for backends to check against
logos-protocol DECLARES the module-impl C ABI; every language backend
(logos-cpp-sdk, logos-rust-sdk, and the Nim path now in flight) must
DEFINE every entry. Those are independent facts, and the gap between them
has shipped twice — grant_host_services at 0.3, the teardown pair at 0.5.
Each time it surfaced three repos downstream as an "undefined symbol" at
dlopen, on Linux only, and each time the runtime still reported the module
as LOADED, so what anyone actually saw was other modules timing out on a
replica that never appeared.
Both breakages happened at PERFECT version agreement between the caller
and the module. Version agreement is necessary and not sufficient: it says
nothing about which symbols a given backend's emitter happens to write.
So derive the list once, here, in the repo that owns the ABI, and ship it
as a build output:
packages.<sys>.module-impl-abi
exports.txt — the declared names
version — the protocol version that header belongs to
bin/logos-module-impl-diff — the assertion, and the explanation
Two properties follow from putting it here rather than in each backend.
There is ONE parser to keep working, rather than one regex per language
that can each silently stop matching. And the list is version-correct with
no version arithmetic anywhere: the header is itself versioned — at 0.4 it
declared eight exports, at 0.5 it declares ten — so "what this protocol
requires" is just "what this header declares". A backend pinning 0.4 reads
eight and is right to define eight. No @since tags, no MINOR comparisons,
nothing for a backend to get wrong.
The extractor parses LOGICAL declarations rather than lines (a reflowed
header must not silently drop one) and refuses to emit a list it is unsure
of: under-reporting is the dangerous direction, because a short list makes
every consumer's diff pass over an ABI nobody checked. The floor it checks
against is asserted rather than derived, so a broken parse cannot satisfy
it. logos-protocol failing to build is the right consequence of
logos-protocol being unable to state its own ABI.
checks.<sys>.module-impl-abi-tests proves all of that can still fail: empty
header, renamed macro, a founding export removed, an empty defined-set, and
a reflowed declaration — eleven cases, each a way this could have decayed
into a green check over nothing.
Also corrects the compatibility note above logos_module_about_to_unload.
It argued the pair was safe because "the glue is generated alongside the
module". That does not follow, and is the reasoning the 0.5 break rested on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* ci: run the ABI manifest check — `nix build .#tests` never reaches `checks`
The step added here is not incidental. `nix build '.#tests'` builds the
PACKAGE; nothing in this workflow evaluated the `checks` attrset at all, so
the manifest self-test added in the previous commit would have sat there
green-by-absence — which is precisely the failure mode it exists to catch.
`ws test` is not a substitute either: it evaluates exactly one check per
repo (scripts/ws truncates the checks JSON at the first comma), so a green
`ws test logos-protocol` says nothing about whether this ran.
builtins.currentSystem rather than a literal, so one line is correct on both
matrix runners.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(abi): the helper must not depend on the consumer's PATH — and the suite must notice
Two bugs, and the second is the interesting one.
1. The installed helper carried `#!/usr/bin/env bash`. Consumers execute it
from inside their own nix builds, whose PATH is whatever THEIR
nativeBuildInputs provide. It resolved on macOS and not in the Linux
sandbox, so the helper simply did not run there. patchShebangs pins an
absolute interpreter.
2. The self-test did not notice, and the reason is worth keeping. expect_fail
accepted ANY non-zero exit as a correct refusal — but a script that cannot
be executed exits 126/127, so all five refusal cases reported PASS while
proving nothing at all. Only the two POSITIVE cases failed, which is the
only reason this surfaced.
That is precisely the failure this whole change exists to prevent, one
level up: a check that reports green over something it never examined. So
expect_fail now asserts a deliberate refusal and rejects 126/127 by name.
Caught by CI on ubuntu-latest while macOS was green — the same
platform-asymmetry that let the original ABI break through.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
03842db5c1 |
feat(windows): named pipes, an explicit lp_* ABI, and a cross target (#58)
* feat(windows): port logos_socket_paths and add a cross target logos_socket_paths.cpp is the only POSIX-bound file in logos-protocol. All of it is unix-domain-socket machinery, and on Windows the local transport is named pipes (QLocalServer maps a name to \\.\pipe\<name>), where none of the assumptions hold: a pipe has no inode to lstat/chown/chmod -- access comes from a security descriptor set at CreateNamedPipe time -- and a pipe cannot outlive its last handle, so a hard-killed process leaves nothing behind. isSocketDead and reapStaleSockets are therefore not merely unimplemented on Windows, they are vacuous: the state they detect cannot arise. Both return the fail-closed answer (false / 0), matching the documented contract that an endpoint is never reported dead unless certain. applySocketPerms deliberately does NOT no-op. With no policy requested it returns true, as on POSIX. But when LOGOS_SOCKET_GROUP or LOGOS_SOCKET_MODE *are* set it fails with an explanatory error, because silently returning true would leave the endpoint more permissive than the operator asked for -- the one direction this file is careful never to go (cf. the chgrp-then-chmod ordering in the POSIX branch). Granting a pipe to a group needs a DACL plus a group->SID resolver; until that exists, refuse loudly. Also gates qt6.wrapQtAppsNoGuiHook behind !isWindows and sets dontWrapQtApps. Both halves are required: the hook does not even evaluate for a mingw host, it would be inert anyway (wrap-qt-apps-hook.sh skips anything that is not ELF or Mach-O), and qtbase's setup hook hard-errors in qtPreHook unless dontWrapQtApps is set. Header contract updated per function. POSIX branch unchanged and still compiles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: make the Boost.System component optional, not required find_package(Boost REQUIRED COMPONENTS system) hard-fails on Boost 1.89: Could not find a package configuration file provided by "boost_system" Boost.System has been header-only for years, and 1.89 finally dropped the compiled boost_system library, so no boost_systemConfig.cmake is installed at all. The COMPONENTS request was not gratuitous though -- on 1.87 the Boost::system imported target is only exported when the component is asked for, which is what the previous comment recorded. So ask optionally and fall back to Boost::headers, which supplies the same header-only error_code either way. The choice is by BOOST VERSION, not by platform: this is not a Windows quirk, it simply surfaced first there because the Windows target pins a newer nixpkgs (Boost 1.89) than the native one (Boost 1.87). Verified both ways -- native aarch64-darwin still selects Boost::system: -- Boost.System target: Boost::system (Boost 1.87.0) and the build completes unchanged. Also adds QT_HOST_PATH / QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH for the Windows target. Qt6RemoteObjectsDependencies.cmake declares set(__qt_RemoteObjects_tool_deps "Qt6RemoteObjectsTools;6.11.1") and Qt6RemoteObjectsTools holds repc, which must RUN on the build machine -- so under cross it lives in the build-platform Qt, not the mingw one. Without these, find_package reports the thoroughly misleading "Expected Config file at <qtbase>/lib/cmake/Qt6RemoteObjects ... does NOT exist": the TARGET config is found fine; it is the HOST tool package that is missing. Every Qt-consuming repo will need this, so it should be hoisted into logos-nix rather than repeated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: declare the lp_* C ABI explicitly instead of relying on auto-export Adds LP_API (__declspec(dllexport) when building the shared library, default visibility elsewhere) to the 21 lp_* entry points, and defines LOGOS_PROTOCOL_BUILDING_SHARED for the shared target only, so the static archive leaves LP_API empty and its consumers need no import library. This is NOT a bug fix, contrary to what the concern in the Windows plan suggested. Measured on the cross-built DLL, before and after: before: export table 0x2ece (11982 symbols), lp_* present: 21 after: export table 0x15 ( 21 symbols), lp_* present: 21 GNU ld's PE auto-export was already exporting lp_* -- along with roughly twelve thousand other symbols. The worry was that logos_module_impl.h's __declspec(dllexport) would disable auto-export image-wide and silently drop lp_*; it does not, because no translation unit in logos_protocol includes that header (it is listed in PROTOCOL_SOURCES for IDE visibility only). What this does buy is worth having anyway: the exported surface is now the ABI we actually declare rather than whatever happens to have external linkage, it stops being contingent on auto-export staying enabled -- which the very next TU to gain a dllexport would silently end -- and it drops ~12k incidental symbols from the export table. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: relax the Boost.System requirement in the EXPORTED cmake config too The previous commit fixed cpp/CMakeLists.txt but left logos-protocolConfig.cmake.in still doing find_dependency(Boost REQUIRED COMPONENTS system) so logos-protocol itself built fine on Boost 1.89 while every CONSUMER of its installed CMake package failed at configure time -- caught by logos-qt-sdk, which is the first downstream repo to be cross-built. Worth noting as a general trap: a package can be internally consistent and still ship a broken contract, because the exported config is a separate artifact from the build. Anything changed in one has to be checked in the other. Verified both directions: the Windows cross builds of logos-cpp-sdk and logos-qt-sdk now succeed, and a native aarch64-darwin logos-qt-sdk build -- which consumes this same config against Boost 1.87 -- still succeeds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(windows): mark the types that must exist once per process PE has no symbol interposition. ELF and Mach-O interpose across the whole image set, so when liblogos_core exports TokenManager::instance() every other image binds to that one definition and the function-local `static TokenManager instance;` is genuinely a singleton. On Windows every image that links liblogos_protocol.a / liblogos_qt_sdk.a statically gets its own copy of the code and therefore its own statics -- measured: NINE images in the Basecamp payload each defined TokenManager::instance()::instance. The host saved a capability token into its copy, the UI plugin read its own empty copy, and every cross-module call was refused (29 "ModuleProxy: rejecting unauthorized call"). LOGOS_SHARED_API marks the affected types. It expands to __declspec(dllimport) only for a consumer that opts in with LOGOS_SHARED_USE_DLL, and to nothing everywhere else -- off Windows, and inside logos-protocol/logos-qt-sdk/liblogos_core themselves, so the static archives compile byte-identically to before. The dllimport is the load-bearing half, not the export: it rewrites the reference to go through __imp_, so the plain symbol is never undefined and GNU ld never pulls the archive member that would redefine it. Without it the link still succeeds, binds to the archive, and gives no diagnostic at all. logos_shared_api.h records both wrong answers -- export everything (collides with the static archive over LogosAPI) and export nothing (today's silent per-image statics) -- so neither gets reinvented. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(windows): let checks and devShells take the arg forAllSystems now passes The cross-target commit added `inherit system;` to forAllSystems so the Windows arm could tell which target it was building, but left `checks` and `devShells` on the strict `({ pkgs }: ...)` pattern. A Nix attrset pattern without `...` is exact, so both stopped evaluating: error: function 'anonymous lambda' called with unexpected argument 'system' on EVERY platform, not just Windows -- `nix flake check` and `ws develop logos-protocol` are dead on this branch while they work on master. `packages` was unaffected because it goes through forAllTargets, which is why nothing caught it. Measured, same worktree, before and after: before: checks.aarch64-darwin -> the error above at flake.nix:52 after: checks.aarch64-darwin -> [ "tests" ] devShells.aarch64-darwin.default.name -> "nix-shell" packages -> [ aarch64-darwin aarch64-linux x86_64-darwin x86_64-linux x86_64-windows ] * chore(deps): re-pin logos-nix to the merged Windows overlay The cross overlay landed in logos-nix#2. This branch was locked to a pre-merge rev, which has no `lib.forAllTargets` and no `lib.mkWindowsPkgs`, so it could not evaluate standalone -- only against the unmerged branch. Level 2 of the Windows chain; L1 (logos-nix) is merged. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1384dc8bec | Add flake + nix packaging scaffold |