mirror of
https://github.com/logos-co/logos-liblogos.git
synced 2026-08-27 12:51:10 +00:00
* feat(windows): cross-compile logos-liblogos for x86_64-w64-mingw32
Adds the x86_64-windows pseudo-system to `packages` (checks and devShells stay
native). liblogos's own src/ needed NO portability work at all -- verified
exhaustively, not assumed: 17 files, 2304 lines, zero POSIX headers and zero
POSIX APIs. All process, plugin and socket work already lives in
logos-container-subprocess and logos-module-loader-qt, which were ported first.
Two real blockers, one of them silent:
* install(TARGETS logos_core ...) named only LIBRARY and ARCHIVE destinations.
A DLL is a RUNTIME artifact, so CMake SKIPPED IT WITHOUT COMMENT: the build
succeeded and shipped a lib/ containing liblogos_core.dll.a and no DLL at
all -- a link-only package that would have handed logosctl.exe an import
library with nothing behind it. Proven by reverting the fix: rc=0, no DLL.
lib.nix now refuses to produce an output with no loadable logos_core.
* The test suite is genuinely POSIX-only (spawn.h, sys/wait.h, mkdtemp, kill),
and CMake put it in the default `all` target, so it broke the cross build
before anything else could. Tests are now gated behind LOGOS_BUILD_TESTS,
with the gate wrapping the gtest FetchContent fallback too -- otherwise
dropping gtest sends CMake to the network inside the sandbox and it dies on
a misleading "downloading ... failed".
Also: package_manager_lib needed IMPORTED_IMPLIB (mingw links against the
import library, not the DLL), and Qt's host tools come in via
logosQtCrossCmakeFlags.
Verified: liblogos_core.dll is a PE32+ DLL whose export table carries the C
ABI (logos_core_init / _start / _load_module / _get_loaded_modules). The
output also ships logos_host_qt.exe with its 15 runtime DLLs and
capability_module_plugin.dll. Native unchanged: 181 tests, 174 passed, 7
skipped -- the same 7 ProcessManagerTest cases skipped before this change.
* fix(windows): carry the package-manager DLL closure, and type the manifest
lib.nix (Windows only): copy every *.dll / *.dll.a from the package-manager
root rather than just libpackage_manager_lib*, with a loud guard if liblgx.dll
is missing -- an absent runtime DLL otherwise shows up as an executable that
exits with no output at all.
modules.nix: emit "type": "core" in the generated manifest. Native-visible
but strictly additive.
* fix(windows): export only the C API from liblogos_core
liblogos_core.dll exported 13,252 symbols. Eighteen of them were the
logos_core_* C API; the rest were the entire internal C++ surface,
LogosAPI's included. Any consumer linking both liblogos_core and the
qt-sdk static library therefore failed with
multiple definition of `LogosAPI::LogosAPI(QString const&, QObject*)'
...liblogos_qt_sdk.a(logos_api.cpp.obj)
first defined here: ...liblogos_core.dll.a(...)
which is what blocked main_ui from linking.
Root cause: LOGOS_CORE_EXPORT expanded to
__attribute__((visibility("default"))), an ELF concept that mingw-gcc
accepts and silently ignores on PE. Nothing was explicitly exported, so
an earlier fix reached for -Wl,--export-all-symbols to get the C API out
-- correct as far as it went, and the reason the whole C++ surface
leaked with it.
Using __declspec(dllexport)/dllimport on Windows fixes it at the root and
does so twice over: GNU ld disables PE auto-export image-wide as soon as
any symbol is dllexported, so the internal surface stops leaking as a
side effect. Measured: logos_core_* exports unchanged at 18, mangled C++
exports 13,673 -> 452, LogosAPI no longer exported at all.
Two copies of LogosAPI (host and plugin) is the DESIGNED arrangement, not
a regression -- see logos_qt_lp_bridge.h, which notes a Qt plugin links
its own copy of the protocol library so TokenManager::instance() inside
it is deliberately not the host's, and syncTokens is the bridge. That
holds on Unix too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(windows): export the shared runtime from liblogos_core.dll
Makes liblogos_core the single provider of TokenManager, LogosAPI,
LogosAPIClient and the LogosResult stream operators, so the Basecamp
process has ONE of each instead of nine.
The export set comes from a GENERATED .def rather than dllexport in the
headers, because the definitions live in liblogos_protocol.a /
liblogos_qt_sdk.a -- archives also linked by logos_host.exe, ui-host.exe,
every module plugin and every native platform. Annotating them for export
would mean a second, Windows-only, export-annotated build of both
archives kept in sync forever, to solve a Windows-only problem. Exporting
at this link instead leaves those archives compiled byte-identically.
Two details in gen-shared-exports.sh that look like they could be
simplified and cannot:
* It exports the WHOLE archive, not a curated class list. ld picks
archive members by object file for reasons unrelated to our symbols
-- measured here, main_ui referenced std::string's move constructor
and ld satisfied it from logos_api.cpp.obj, dragging LogosAPI,
LogosAPIClient and TokenManager in behind it. Consumers link an empty
archive and take everything from the DLL, which only works if the DLL
really provides everything; a partial export set surfaces as an
undefined reference in a downstream repo, far from the cause.
* It filters COMDAT symbols out. Those come from inline functions and
templates in headers, so every consumer TU emits its own copy
regardless; exporting them makes the import library a strong
definition that collides with that copy.
Measured after: liblogos_core.dll goes from 18 exports to 376, of which
132 are the shared runtime; LogosBasecamp.exe drops 14.3 MB -> 1.08 MB
and main_ui.dll 23.2 MB -> 10.1 MB as the duplicated statics stop being
linked in. Native aarch64-darwin is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Merge origin/master into feat/windows-cross, and re-pin the L1-L7 inputs
master landed 56aa8bb (bump logos package manager); merged clean, no conflicts.
Then the whole chain moves to its merged revs: logos-nix (L1); logos-protocol,
logos-module, logos-container, process-stats (L2); logos-cpp-sdk,
logos-module-loader, default-container=logos-container-subprocess,
logos-package-manager (L3); logos-qt-sdk (L4);
default-module-loader=logos-module-loader-qt (L5); logos-capability-module (L7).
Verified packages.x86_64-windows.default evaluates to a .drv against the merged
set. The native aarch64-darwin build was NOT verified locally: this machine is
currently failing unrelated derivations two different ways -- clang killed with
signal 9 during CMake's trivial compiler probe, and macOS refusing a Nix-store
gtest dylib with "library load denied by system policy" during
gtest_discover_tests. The first was proven environmental rather than
input-related by building the IDENTICAL derivation hash standalone, where it
passed. CI is the authority for the native side.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
3.5 KiB
CMake
95 lines
3.5 KiB
CMake
cmake_minimum_required(VERSION 3.14)
|
|
project(SimplePluginExample LANGUAGES CXX)
|
|
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
|
|
# Find Qt packages
|
|
# Network is needed explicitly by the token-exchange tests (QLocalServer /
|
|
# QLocalSocket); it is also pulled transitively by RemoteObjects in the
|
|
# runtime build, but we declare it here so the test target can link it
|
|
# explicitly without depending on that transitive resolution.
|
|
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network RemoteObjects)
|
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Network RemoteObjects)
|
|
|
|
# Find nlohmann_json
|
|
find_package(nlohmann_json REQUIRED)
|
|
|
|
# OpenSSL needed transitively by logos_sdk (plain-C++ TCP+SSL transport)
|
|
find_package(OpenSSL REQUIRED)
|
|
|
|
# Find spdlog
|
|
find_package(spdlog REQUIRED)
|
|
|
|
# Set output directories
|
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|
|
|
# Add external library paths to build RPATH so test binaries can find them
|
|
if(DEFINED LOGOS_PACKAGE_MANAGER_ROOT)
|
|
list(APPEND CMAKE_BUILD_RPATH "${LOGOS_PACKAGE_MANAGER_ROOT}/lib")
|
|
endif()
|
|
|
|
# Build src first to ensure logos_core is built before modules
|
|
add_subdirectory(src)
|
|
|
|
# Tests. Gated EXPLICITLY rather than left to find_package: the suite is
|
|
# POSIX-only (tests/test_process_stats.cpp uses <spawn.h>, <sys/wait.h>,
|
|
# posix_spawn/waitpid/kill and `environ`; tests/test_subprocess_manager.cpp
|
|
# spawns /bin/sh), none of which mingw-w64 provides. The default build target
|
|
# is `all`, so an ungated tests/ subdirectory would be compiled by every
|
|
# cross build.
|
|
#
|
|
# The gate wraps the GTest lookup too: dropping only `add_subdirectory(tests)`
|
|
# would still leave find_package(GTest) able to miss and fall through to the
|
|
# FetchContent download, which dies in the nix sandbox with a misleading
|
|
# "downloading ... failed".
|
|
option(LOGOS_BUILD_TESTS "Build the logos_core test suite" ON)
|
|
if(WIN32)
|
|
set(LOGOS_BUILD_TESTS OFF)
|
|
endif()
|
|
|
|
if(LOGOS_BUILD_TESTS)
|
|
# Try to find GoogleTest via find_package first (for Nix and system installations)
|
|
find_package(GTest QUIET)
|
|
|
|
if(NOT GTest_FOUND)
|
|
# Fall back to FetchContent if GoogleTest is not found
|
|
message(STATUS "GoogleTest not found via find_package, using FetchContent")
|
|
include(FetchContent)
|
|
FetchContent_Declare(googletest
|
|
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
|
|
)
|
|
FetchContent_MakeAvailable(googletest)
|
|
else()
|
|
message(STATUS "Using system GoogleTest")
|
|
endif()
|
|
|
|
enable_testing()
|
|
include(GoogleTest)
|
|
|
|
# Add tests subdirectory
|
|
add_subdirectory(tests)
|
|
endif()
|
|
|
|
# Install rules. The logos_host_qt binary (and its logos_host symlink) now live
|
|
# in logos-module-loader-qt; liblogos installs only the core library here, and
|
|
# bin.nix re-exports the host binary from that package so frontends are
|
|
# unaffected.
|
|
# RUNTIME DESTINATION matters on Windows: a DLL is a RUNTIME artifact, and with
|
|
# only LIBRARY/ARCHIVE named CMake sends it to the DEFAULT runtime destination
|
|
# (bin) while nix/lib.nix only ever copies ${build}/lib -- which would yield a
|
|
# lib output holding the import library and no DLL, silently. Pointing RUNTIME
|
|
# at lib keeps the output shape identical on all three platforms.
|
|
install(TARGETS logos_core
|
|
LIBRARY DESTINATION lib
|
|
ARCHIVE DESTINATION lib
|
|
RUNTIME DESTINATION lib
|
|
)
|
|
|
|
# Install headers
|
|
install(FILES src/logos_core/logos_core.h
|
|
DESTINATION include
|
|
)
|