mirror of
https://github.com/logos-co/logos-view-module.git
synced 2026-08-29 22:11:13 +00:00
A view module is a distinct authoring flavour, not a variation on a core
module: it publishes a .rep rather than a .lidl, and is driven by a QML view in
a ui-host process. Its two halves were split across two repos that own other
things — the plugin-glue generator sat in logos-qt-sdk's qt-generator, and the
replica-factory CMake plus its four templates sat in logos-plugin-qt — so
changing how views are built meant touching the SDK and the Qt plugin backend.
Collected here:
- view-generator/ logos-view-generator, emitting <name>_ui_interface.h and
<name>_ui_glue.{h,cpp} around the user's .rep + *Backend.
Carried over verbatim from logos-qt-sdk's lidl_gen_ui, with
a main() of its own. It never reads LIDL — a view's contract
IS its .rep — so it links Qt Core and nothing else, and
lidlToPascalCase is inlined rather than dragging in a
generator frontend it has no other use for.
- cmake/ LogosViewModule.cmake wrapping logos_replica_factory(), with
the four .in templates it configures. They must stay
siblings: the function resolves them through
CMAKE_CURRENT_FUNCTION_LIST_DIR.
- cpp/ LogosUiPluginContext, the narrow context a *Backend derives.
That makes this repo a leaf — logos-nix for a pinned Qt, nothing else — so it
can be re-pinned independently of the SDK stack.
Verified behaviour-preserving rather than assumed: run against a real in-tree
view module (logos-chat-ui's ChatBackend.rep + metadata.json), the relocated
generator's three files are BYTE-IDENTICAL to logos-qt-generator --backend ui.
Two checks: one drives the generator over a real .rep and asserts the scraped
rep class, the PascalCase plugin stem and the carried version; the other
asserts a .rep declaring no class fails rather than emitting half a plugin.
The originals are left in place for now — additive first, removal second, so
no downstream repo goes red on this commit alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
172 lines
7.2 KiB
CMake
172 lines
7.2 KiB
CMake
# LogosViewModule.cmake — the view-plugin half of a Logos module's build.
|
|
#
|
|
# `logos_replica_factory()` builds the typed QtRO replica factory a QML view
|
|
# loads, plus the per-module LogosViewPlugin base that lets ui-host drive the
|
|
# plugin through a plain qobject_cast instead of QMetaObject reflection. It is
|
|
# the CMake counterpart of this repo's `logos-view-generator`: the generator
|
|
# emits the plugin glue around the user's .rep and *Backend, and this builds
|
|
# the replica side of the same .rep.
|
|
#
|
|
# It lives here, and not in the Qt plugin backend, for the same reason the
|
|
# generator does: a view module is a distinct authoring flavour from a core
|
|
# module, and keeping both halves in one repo means changing how views are
|
|
# built is a one-repo change.
|
|
#
|
|
# Usage — include it and call the public entry point:
|
|
#
|
|
# include("${LOGOS_VIEW_MODULE_ROOT}/cmake/LogosViewModule.cmake")
|
|
# logos_replica_factory(NAME my_view REP_FILE src/MyBackend.rep)
|
|
#
|
|
# The four .in templates are SIBLINGS of this file; the function resolves them
|
|
# through CMAKE_CURRENT_FUNCTION_LIST_DIR, with a CMAKE_CURRENT_LIST_DIR
|
|
# fallback for callers that `include()` it from a different scope. Move this
|
|
# file without the templates and configure_file() fails.
|
|
|
|
# Public entry point. NAME and REP_FILE are required; QML_URI and
|
|
# QML_TYPE_NAME default to Logos.<RepClass> and <RepClass>.
|
|
function(logos_replica_factory)
|
|
cmake_parse_arguments(VIEW "" "NAME;REP_FILE;QML_URI;QML_TYPE_NAME" "" ${ARGN})
|
|
if(NOT VIEW_NAME)
|
|
message(FATAL_ERROR "logos_replica_factory: NAME is required")
|
|
endif()
|
|
if(NOT VIEW_REP_FILE)
|
|
message(FATAL_ERROR "logos_replica_factory: REP_FILE is required")
|
|
endif()
|
|
_logos_module_add_replica_factory("${VIEW_NAME}" "${VIEW_REP_FILE}"
|
|
"${VIEW_QML_URI}" "${VIEW_QML_TYPE_NAME}")
|
|
endfunction()
|
|
|
|
function(_logos_module_add_replica_factory MODULE_NAME REP_FILE QML_URI QML_TYPE_NAME)
|
|
# Need repc replica generation + Qml for qmlRegisterUncreatableMetaObject
|
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core RemoteObjects Qml)
|
|
|
|
# Also attach the source-side repc to the plugin target so the backend has
|
|
# the generated SimpleSource base class available.
|
|
if(QT_VERSION_MAJOR EQUAL 6)
|
|
qt6_add_repc_sources(${MODULE_NAME}_module_plugin ${REP_FILE})
|
|
else()
|
|
qt5_add_repc_sources(${MODULE_NAME}_module_plugin ${REP_FILE})
|
|
endif()
|
|
|
|
# Parse class name out of the .rep (first `class Foo` line).
|
|
set(_REP_FILE_ABS "${REP_FILE}")
|
|
if(NOT IS_ABSOLUTE "${_REP_FILE_ABS}")
|
|
set(_REP_FILE_ABS "${CMAKE_CURRENT_SOURCE_DIR}/${REP_FILE}")
|
|
endif()
|
|
file(READ "${_REP_FILE_ABS}" _REP_CONTENTS)
|
|
string(REGEX MATCH "class[ \t]+([A-Za-z_][A-Za-z0-9_]*)" _ "${_REP_CONTENTS}")
|
|
set(LOGOS_REP_CLASS "${CMAKE_MATCH_1}")
|
|
if(NOT LOGOS_REP_CLASS)
|
|
message(FATAL_ERROR "logos_module: could not parse class name from ${REP_FILE}")
|
|
endif()
|
|
|
|
get_filename_component(LOGOS_REP_BASE "${REP_FILE}" NAME_WE)
|
|
set(LOGOS_FACTORY_CLASS "${LOGOS_REP_CLASS}ReplicaFactoryPlugin")
|
|
|
|
if(NOT QML_URI)
|
|
set(QML_URI "Logos.${LOGOS_REP_CLASS}")
|
|
endif()
|
|
if(NOT QML_TYPE_NAME)
|
|
set(QML_TYPE_NAME "${LOGOS_REP_CLASS}")
|
|
endif()
|
|
set(LOGOS_QML_URI "${QML_URI}")
|
|
set(LOGOS_QML_TYPE_NAME "${QML_TYPE_NAME}")
|
|
|
|
# Locate the factory h/cpp templates (sibling of this .cmake file).
|
|
set(_TEMPLATE_DIR "${CMAKE_CURRENT_FUNCTION_LIST_DIR}")
|
|
if(NOT EXISTS "${_TEMPLATE_DIR}/LogosViewReplicaFactory.h.in")
|
|
set(_TEMPLATE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
|
endif()
|
|
|
|
set(_GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/replica_factory_${MODULE_NAME}")
|
|
file(MAKE_DIRECTORY "${_GEN_DIR}")
|
|
configure_file("${_TEMPLATE_DIR}/LogosViewReplicaFactory.h.in"
|
|
"${_GEN_DIR}/LogosViewReplicaFactory.h" @ONLY)
|
|
configure_file("${_TEMPLATE_DIR}/LogosViewReplicaFactory.cpp.in"
|
|
"${_GEN_DIR}/LogosViewReplicaFactory.cpp" @ONLY)
|
|
|
|
# Generate the per-module LogosViewPlugin base that plugins inherit
|
|
# from. It implements viewObject() + enableRemoting() so ui-host can
|
|
# drive the plugin via a plain qobject_cast<LogosViewPlugin*> instead
|
|
# of QMetaObject::invokeMethod reflection.
|
|
set(_VIEW_PLUGIN_GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/view_plugin_base_${MODULE_NAME}")
|
|
file(MAKE_DIRECTORY "${_VIEW_PLUGIN_GEN_DIR}")
|
|
configure_file("${_TEMPLATE_DIR}/LogosViewPluginBase.h.in"
|
|
"${_VIEW_PLUGIN_GEN_DIR}/LogosViewPluginBase.h" @ONLY)
|
|
configure_file("${_TEMPLATE_DIR}/LogosViewPluginBase.cpp.in"
|
|
"${_VIEW_PLUGIN_GEN_DIR}/LogosViewPluginBase.cpp" @ONLY)
|
|
target_sources(${MODULE_NAME}_module_plugin PRIVATE
|
|
"${_VIEW_PLUGIN_GEN_DIR}/LogosViewPluginBase.h"
|
|
"${_VIEW_PLUGIN_GEN_DIR}/LogosViewPluginBase.cpp"
|
|
)
|
|
target_include_directories(${MODULE_NAME}_module_plugin PRIVATE
|
|
"${_VIEW_PLUGIN_GEN_DIR}"
|
|
)
|
|
target_link_libraries(${MODULE_NAME}_module_plugin PRIVATE
|
|
Qt${QT_VERSION_MAJOR}::RemoteObjects
|
|
)
|
|
|
|
set(_FACTORY_TARGET ${MODULE_NAME}_replica_factory)
|
|
add_library(${_FACTORY_TARGET} SHARED
|
|
"${_GEN_DIR}/LogosViewReplicaFactory.h"
|
|
"${_GEN_DIR}/LogosViewReplicaFactory.cpp"
|
|
)
|
|
|
|
set_target_properties(${_FACTORY_TARGET} PROPERTIES
|
|
AUTOMOC ON
|
|
PREFIX ""
|
|
OUTPUT_NAME "${MODULE_NAME}_replica_factory"
|
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
|
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
|
|
BUILD_WITH_INSTALL_RPATH TRUE
|
|
SKIP_BUILD_RPATH FALSE
|
|
INSTALL_NAME_DIR "@rpath"
|
|
)
|
|
if(APPLE)
|
|
target_link_options(${_FACTORY_TARGET} PRIVATE "-Wl,-headerpad_max_install_names")
|
|
endif()
|
|
|
|
target_include_directories(${_FACTORY_TARGET} PRIVATE
|
|
"${_GEN_DIR}"
|
|
"${CMAKE_CURRENT_BINARY_DIR}"
|
|
)
|
|
if(QT_VERSION_MAJOR EQUAL 6)
|
|
qt6_add_repc_replicas(${_FACTORY_TARGET} ${REP_FILE})
|
|
else()
|
|
qt5_add_repc_replicas(${_FACTORY_TARGET} ${REP_FILE})
|
|
endif()
|
|
|
|
target_link_libraries(${_FACTORY_TARGET} PRIVATE
|
|
Qt${QT_VERSION_MAJOR}::Core
|
|
Qt${QT_VERSION_MAJOR}::RemoteObjects
|
|
Qt${QT_VERSION_MAJOR}::Qml
|
|
)
|
|
|
|
if(APPLE)
|
|
set_target_properties(${_FACTORY_TARGET} PROPERTIES
|
|
INSTALL_RPATH "@loader_path"
|
|
INSTALL_NAME_DIR "@rpath"
|
|
BUILD_WITH_INSTALL_NAME_DIR TRUE
|
|
)
|
|
add_custom_command(TARGET ${_FACTORY_TARGET} POST_BUILD
|
|
COMMAND install_name_tool -id "@rpath/${MODULE_NAME}_replica_factory.dylib"
|
|
$<TARGET_FILE:${_FACTORY_TARGET}>
|
|
COMMENT "Updating library paths for macOS"
|
|
)
|
|
else()
|
|
set_target_properties(${_FACTORY_TARGET} PROPERTIES
|
|
INSTALL_RPATH "$ORIGIN"
|
|
INSTALL_RPATH_USE_LINK_PATH FALSE
|
|
)
|
|
endif()
|
|
|
|
install(TARGETS ${_FACTORY_TARGET}
|
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
|
|
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
|
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
|
|
)
|
|
|
|
message(STATUS "Logos module ${MODULE_NAME}: replica factory plugin from ${REP_FILE} "
|
|
"(class ${LOGOS_REP_CLASS}, QML ${LOGOS_QML_URI}.${LOGOS_QML_TYPE_NAME})")
|
|
endfunction()
|