mirror of
https://github.com/logos-co/logos-plugin-qt.git
synced 2026-08-27 17:01:11 +00:00
* feat(consumer): logos::admitConsumer — one home for admitting a non-module Two hosts hand-rolled the same three steps independently — isolate an identity, mint a credential, register it with capability_module — and the duplication had already produced a bug: basecamp did the registration inside the has-a-backend branch, below an early return, so pure-QML plugins registered nothing. It survived only because those calls went out on the host's ambient ring, where every token already existed and the handshake was never reached. Remove the ambient ring (logos-protocol #71) and that becomes a hard failure. So the operation gets one home. admitConsumer isolates, mints, registers and installs the credential as a single step, and hands back a ConsumerIdentity. Both hosts lose their private copies: basecamp -16 net lines, standalone-app -14. It lives here rather than in logos-liblogos, which is where it was first proposed. Both hand-rolled sites call LogosAPI, which is this repo's, and the operation needs a capability_module client to register through — going via liblogos's C API would add a hop for hosts already holding the Qt object. logos-liblogos needs no change at all. Named for what it does: a CONSUMER is admitted, not registered as a module. It is never published to the registry, never callable, never in --modules-dir. Two guards, because the sharp edges here are silent ones: * adoptConsumerCredential REFUSES an isolated store. It exists for a co-process adopting its parent's credential into its own process ring, where the store IS that ring. Pointed at an in-process private store it would install whatever it was handed — including the host anchor — straight past adoptCredentialFor's refusal, putting the elevation #71 removes one call away again. * A #error when logos-protocol is newer than the contract this file implements. The wave order is mandatory and was unenforced: bump the protocol alone and every isolated identity gets an empty store, while nothing fails to build (every MINOR guard in the fleet is >=) and the integration tests stay green (the ui-host half keeps working off its stdin credential). The bound fired on its first build — this change ships protocol 0.7 and it was set to 6 — and was proven non-inert by going red at 0.8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deps): relock logos-protocol onto master, which now carries #71 admitConsumer calls TokenManager::adoptCredentialFor and adoptCredential. Both arrived with logos-protocol#71 ("a private store is created EMPTY"), so the lock had to move past it — this branch still pinned 6c24fcb1, which is 0.6.0 and has neither: logos_consumer.cpp:89: error: 'adoptCredentialFor' is not a member of 'TokenManager' logos_consumer.cpp:155: error: 'class TokenManager' has no member named 'adoptCredential' Now b37a2e9f, protocol master with #71 merged. This is the wave order the header documents, seen from the other side: the empty-store change lands in protocol, and everything that makes it survivable lands here. The guard in logos_consumer.h fires when this repo is BEHIND the protocol; this commit is the ordinary case of catching up to it. Verified on x86_64-linux with the relocked input: consumer-admission PASS qt-host PASS caller-contract PASS glue-compiles PASS Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
229 lines
9.6 KiB
CMake
229 lines
9.6 KiB
CMake
cmake_minimum_required(VERSION 3.14)
|
|
project(LogosQtHost)
|
|
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
set(CMAKE_AUTOMOC ON)
|
|
|
|
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core RemoteObjects)
|
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core RemoteObjects)
|
|
|
|
# The protocol layer — transports, consumer core, token manager, the abstract
|
|
# LogosProviderObject interface and the lp_* C ABI. This library is the Qt
|
|
# HOST runtime on top of it: the object a Qt plugin is handed (LogosAPI), the
|
|
# provider side that publishes it (LogosAPIProvider), the provider base the
|
|
# generated <name>_cdylib_glue.cpp derives from (LogosProviderBase), and the
|
|
# QtProviderObject adapter that wraps a legacy Q_INVOKABLE QObject plugin.
|
|
# (Hand-written provider classes — `interface: "provider"` — no longer exist.)
|
|
if(NOT DEFINED LOGOS_PROTOCOL_ROOT)
|
|
if(DEFINED ENV{LOGOS_PROTOCOL_ROOT})
|
|
set(LOGOS_PROTOCOL_ROOT "$ENV{LOGOS_PROTOCOL_ROOT}")
|
|
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../logos-protocol/cpp/logos_protocol.h")
|
|
set(LOGOS_PROTOCOL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../logos-protocol")
|
|
endif()
|
|
endif()
|
|
if(EXISTS "${LOGOS_PROTOCOL_ROOT}/lib/cmake/logos-protocol")
|
|
# Installed protocol package → proper layered link.
|
|
find_package(logos-protocol REQUIRED
|
|
PATHS "${LOGOS_PROTOCOL_ROOT}/lib/cmake/logos-protocol" NO_DEFAULT_PATH)
|
|
set(LP_TARGET logos-protocol::logos_protocol)
|
|
set(LP_SHARED_TARGET logos-protocol::logos_protocol_shared)
|
|
elseif(EXISTS "${LOGOS_PROTOCOL_ROOT}/cpp/CMakeLists.txt")
|
|
# Source checkout → build it as a subproject (dev convenience).
|
|
add_subdirectory("${LOGOS_PROTOCOL_ROOT}/cpp"
|
|
"${CMAKE_BINARY_DIR}/logos-protocol-build")
|
|
set(LP_TARGET logos_protocol)
|
|
set(LP_SHARED_TARGET logos_protocol_shared)
|
|
else()
|
|
message(FATAL_ERROR "logos-protocol not found. Set LOGOS_PROTOCOL_ROOT to an "
|
|
"installed logos-protocol prefix or a source checkout.")
|
|
endif()
|
|
|
|
set(QT_HOST_SOURCES
|
|
logos_api.cpp
|
|
logos_api.h
|
|
logos_api_provider.cpp
|
|
logos_api_provider.h
|
|
logos_provider_object.cpp
|
|
logos_provider_object.h
|
|
qt_provider_object.cpp
|
|
qt_provider_object.h
|
|
# Not part of the host runtime proper, but qt_provider_object.cpp's
|
|
# QMetaObject dispatch decodes every incoming argument through
|
|
# logos::qtArgDecode. Carrying the legacy adapter means carrying this.
|
|
logos_qt_arg_decode.cpp
|
|
logos_qt_arg_decode.h
|
|
# The host side of the module teardown hook. Not a transport concern, but
|
|
# the surface it reaches a plugin by ("aboutToUnload" / "unloadFinished")
|
|
# is EMITTED by this repo's qt-host-generator, so consumer and emitter
|
|
# stay under one test -- see tests/test-unload-contract.nix.
|
|
logos_plugin_unload.cpp
|
|
logos_plugin_unload.h
|
|
# Admitting a NON-MODULE consumer (a QML view, an in-process widget plugin,
|
|
# a co-process view host) to a running system: isolate, mint, register with
|
|
# capability_module, adopt. Host-only, and here rather than in liblogos
|
|
# because both hand-rolled sites -- and ui-host, which does not link
|
|
# liblogos at all -- reach the system through THIS library's LogosAPI.
|
|
logos_consumer.cpp
|
|
logos_consumer.h
|
|
)
|
|
|
|
add_library(logos_qt_host STATIC ${QT_HOST_SOURCES})
|
|
|
|
target_link_libraries(logos_qt_host PUBLIC
|
|
${LP_TARGET}
|
|
Qt${QT_VERSION_MAJOR}::Core
|
|
Qt${QT_VERSION_MAJOR}::RemoteObjects
|
|
)
|
|
|
|
target_include_directories(logos_qt_host PUBLIC
|
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
$<INSTALL_INTERFACE:include>
|
|
)
|
|
|
|
set_target_properties(logos_qt_host PROPERTIES
|
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
|
)
|
|
|
|
install(TARGETS logos_qt_host
|
|
EXPORT logos-qt-hostTargets
|
|
ARCHIVE DESTINATION lib
|
|
LIBRARY DESTINATION lib
|
|
RUNTIME DESTINATION bin
|
|
INCLUDES DESTINATION include
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# logos_qt_host_shared — the Qt host runtime as a SHARED library.
|
|
#
|
|
# WHY. The types in here (LogosAPI) and in logos-protocol (TokenManager,
|
|
# LogosAPIClient, the per-identity StoreRegistry) must exist EXACTLY ONCE per
|
|
# process. Every image that links a static archive gets its own copy of every
|
|
# function-local static inside it, so the host writes a capability token into
|
|
# one store and another in-process image reads an empty one -- with no build
|
|
# diagnostic. Linking ONE shared library is what replaces the whole-archive +
|
|
# generated-.def scheme in which liblogos_core absorbed both archives and
|
|
# re-exported them.
|
|
#
|
|
# THE STATIC ARCHIVE STAYS, and not transitionally. Module plugins and ui_qml
|
|
# backends run in their OWN processes, so their own copy is the CORRECT
|
|
# per-process singleton; staying static also keeps a .lgx self-contained (a .lgx
|
|
# records an empty nix closure, so a shared library would not travel with it).
|
|
# In-process images link the shared one; out-of-process images link the archive.
|
|
if(NOT TARGET ${LP_SHARED_TARGET})
|
|
# Hard failure rather than "skip the shared target", because every failure
|
|
# mode here is silent: a consumer that wanted the shared runtime would fall
|
|
# back to the archive, link cleanly, and reintroduce the duplicate singleton
|
|
# at runtime.
|
|
message(FATAL_ERROR
|
|
"${LP_SHARED_TARGET} is not a target, so logos_qt_host_shared cannot be "
|
|
"built. It requires a logos-protocol that EXPORTS its shared library "
|
|
"(logos-protocol#65). Check that LOGOS_PROTOCOL_ROOT points at a "
|
|
"protocol build new enough to provide it.")
|
|
endif()
|
|
|
|
add_library(logos_qt_host_shared SHARED ${QT_HOST_SOURCES})
|
|
|
|
# PUBLIC-links the SHARED protocol, NOT ${LP_TARGET}.
|
|
#
|
|
# This single line is the whole correctness of this target. Linking the static
|
|
# archive here would embed a second copy of TokenManager INSIDE
|
|
# liblogos_qt_host.{so,dylib,dll} -- the exact bug this migration removes, one
|
|
# layer down, and invisible to a consumer-side symbol gate because the duplicate
|
|
# would live in a library the gate treats as a provider.
|
|
target_link_libraries(logos_qt_host_shared PUBLIC
|
|
${LP_SHARED_TARGET}
|
|
Qt${QT_VERSION_MAJOR}::Core
|
|
Qt${QT_VERSION_MAJOR}::RemoteObjects
|
|
)
|
|
|
|
# LogosAPI is EXPORTED from this library (LOGOS_QT_HOST_API), while TokenManager
|
|
# and LogosAPIClient -- owned by logos-protocol -- must be IMPORTED from
|
|
# liblogos_protocol. That is why they are two macros and not one: a single macro
|
|
# cannot say "export" and "import" in the same translation unit.
|
|
#
|
|
# LOGOS_QT_HOST_BUILDING_SHARED is tested FIRST in logos_shared_api.h, so
|
|
# LogosAPI still resolves to dllexport here even with LOGOS_SHARED_USE_DLL set.
|
|
target_compile_definitions(logos_qt_host_shared PRIVATE LOGOS_QT_HOST_BUILDING_SHARED)
|
|
if(WIN32)
|
|
target_compile_definitions(logos_qt_host_shared PRIVATE LOGOS_SHARED_USE_DLL)
|
|
endif()
|
|
|
|
target_include_directories(logos_qt_host_shared PUBLIC
|
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
$<INSTALL_INTERFACE:include>
|
|
)
|
|
|
|
# Same basename as the archive; the LIBRARY install lands it in $out/lib beside
|
|
# liblogos_qt_host.a.
|
|
set_target_properties(logos_qt_host_shared PROPERTIES
|
|
OUTPUT_NAME logos_qt_host
|
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
|
)
|
|
|
|
# ARCHIVE DESTINATION is load-bearing on Windows and inert elsewhere: a shared
|
|
# library's IMPORT library (.dll.a) is the ARCHIVE artifact while the DLL is
|
|
# RUNTIME, so omitting it installs no import library and a consumer gets an
|
|
# imported target whose IMPORTED_IMPLIB does not exist. Invisible on ELF/Mach-O.
|
|
install(TARGETS logos_qt_host_shared
|
|
EXPORT logos-qt-hostTargets
|
|
ARCHIVE DESTINATION lib
|
|
LIBRARY DESTINATION lib
|
|
RUNTIME DESTINATION bin
|
|
INCLUDES DESTINATION include
|
|
)
|
|
|
|
install(EXPORT logos-qt-hostTargets
|
|
FILE logos-qt-hostTargets.cmake
|
|
NAMESPACE logos-qt-host::
|
|
DESTINATION lib/cmake/logos-qt-host
|
|
)
|
|
|
|
include(CMakePackageConfigHelpers)
|
|
configure_package_config_file(
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/logos-qt-hostConfig.cmake.in"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/logos-qt-hostConfig.cmake"
|
|
INSTALL_DESTINATION lib/cmake/logos-qt-host
|
|
)
|
|
write_basic_package_version_file(
|
|
"${CMAKE_CURRENT_BINARY_DIR}/logos-qt-hostConfigVersion.cmake"
|
|
VERSION 0.1.0
|
|
COMPATIBILITY SameMajorVersion
|
|
)
|
|
install(FILES
|
|
"${CMAKE_CURRENT_BINARY_DIR}/logos-qt-hostConfig.cmake"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/logos-qt-hostConfigVersion.cmake"
|
|
DESTINATION lib/cmake/logos-qt-host
|
|
)
|
|
|
|
set(QT_HOST_PUBLIC_HEADERS
|
|
logos_api.h
|
|
logos_api_provider.h
|
|
logos_provider_object.h
|
|
qt_provider_object.h
|
|
logos_qt_arg_decode.h
|
|
logos_plugin_unload.h
|
|
logos_consumer.h
|
|
)
|
|
|
|
# Headers keep their historical names so existing `#include "logos_api.h"`
|
|
# lines resolve unchanged once consumers add this prefix's include dir.
|
|
install(FILES ${QT_HOST_PUBLIC_HEADERS} DESTINATION include)
|
|
|
|
# Legacy Qt plugin interface (PluginInterface / initLogos(LogosAPI*)) —
|
|
# installed at include/core/ exactly where logos-cpp-sdk, and then
|
|
# logos-qt-sdk, shipped it. Consumers put include/core on the include path.
|
|
install(FILES
|
|
${CMAKE_CURRENT_SOURCE_DIR}/../core/interface.h
|
|
DESTINATION include/core
|
|
)
|
|
|
|
# interface.h reaches LogosAPI as `#include "../cpp/logos_api.h"`, i.e. it
|
|
# resolves RELATIVE TO ITSELF, so from include/core/ it needs include/cpp/ to
|
|
# exist. logos-qt-sdk satisfied that by shipping a second, source-export copy
|
|
# of the headers from a separate derivation; here the same mirror is part of
|
|
# the one install, which keeps the prefix self-consistent on its own. The flat
|
|
# copy above stays the one consumers include by name — this exists only so the
|
|
# relative shape inside interface.h keeps resolving, unchanged.
|
|
install(FILES ${QT_HOST_PUBLIC_HEADERS} DESTINATION include/cpp)
|