mirror of
https://github.com/logos-co/logos-basecamp.git
synced 2026-08-27 06:41:14 +00:00
Measured on the built artefact: main_ui.dylib DEFINES zero and IMPORTS zero of
TokenManager, StoreRegistry, LogosAPI, LogosAPIClient and logos_core_*, out of
3410 symbols read. It links Qt and nothing else from this workspace, which
nix/symbol-gate.nix enforces across the in-process image set.
Getting there needed the last non-Qt types off the boundary: the model
properties cross as QAbstractItemModel*, catalogInstallStageChanged carries an
int rather than InstallStage::Value, and the two prebuilt AppsFilterProxy
instances are declared in QML instead of owned by MainUIBackend. That last one
removes a real inversion -- PackageCoordinator called setRequiredPackages() on
a proxy the host held a pointer to; it now emits requiredPackagesResolved() and
QML binds to the republished property.
Window resolves the plugin, qobject_casts it to IShellView, checks
hostAbiVersion() against IShellHost_abi, and calls createShell(IShellHost*).
No error-label fallback widget: that degraded to something that looked like a
working app with an empty window.
Filter proxies read role constants off host-side models and InstallEnums is
used by nine host files, so app/interfaces/ gains the contract headers both
sides compile against -- the models inherit the role structs, leaving every
AppsModel::NameRole call site unchanged. The plugin's include path is
app/interfaces only, so including a host header does not compile.
Four things only running it finds:
* Logos::DesignSystem may be linked by exactly ONE image -- both linked it
and the app aborted with "Cannot add multiple registrations for
Logos.Icons"; QML module registration is process-global
* qmltyperegistrar emits no #include for a SOURCES header given as an
absolute path outside the project
* each qt_add_qml_module is its own target and inherits no include dirs
* AUTOMOC pairs header<->cpp by same-basename-same-DIRECTORY, which the
split breaks
tst_AppManagerView.qml grows five tests for the QML binding, checked with a
negative control: breaking one assertion fails qml-tests, so they run.
709 lines
40 KiB
Nix
709 lines
40 KiB
Nix
{
|
|
description = "Logos Basecamp - Qt application with UI plugins";
|
|
|
|
inputs = {
|
|
logos-nix.url = "github:logos-co/logos-nix";
|
|
# Follow the same nixpkgs as logos-nix
|
|
nixpkgs.follows = "logos-nix/nixpkgs";
|
|
# Unpinned: feat/sdk-codegen-b3-d11 merged (logos-cpp-sdk#138), so the B3/B4
|
|
# SDK split and the generator entry points the module inputs below call are
|
|
# on master.
|
|
logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk";
|
|
logos-cpp-sdk.inputs.logos-protocol.follows = "logos-protocol";
|
|
# Unpinned: feat/per-client-token-store merged (logos-protocol#59), so
|
|
# TokenManager::forIdentity/isolateIdentity — which logos-qt-host calls — are
|
|
# on master, along with the host-services C ABI.
|
|
logos-protocol = {
|
|
url = "github:logos-co/logos-protocol";
|
|
inputs.logos-nix.follows = "logos-nix";
|
|
};
|
|
# The Qt HOST RUNTIME — LogosAPI, LogosAPIProvider, the LogosProviderBase
|
|
# macros and the legacy QMetaObject adapter — that LogosBasecamp links. It
|
|
# lived in logos-qt-sdk until the host split moved it here.
|
|
#
|
|
# NOTE: the runtime is no longer the only thing basecamp takes from
|
|
# logos-qt-sdk — see the logos-qt-sdk input below. Taking the runtime from
|
|
# BOTH would put two copies of the same 392 symbols on the link, which is
|
|
# why that input is headers-only and links nothing.
|
|
#
|
|
# Unpinned: feat/b4-qt-host-windows-target merged (logos-plugin-qt#19), so
|
|
# logos-qt-host is on master for every target. One master rev means one
|
|
# logos-qt-host in the closure, which is what the rev pin was protecting.
|
|
logos-plugin-qt = {
|
|
url = "github:logos-co/logos-plugin-qt";
|
|
inputs.logos-nix.follows = "logos-nix";
|
|
inputs.logos-protocol.follows = "logos-protocol";
|
|
inputs.logos-module.follows = "logos-module";
|
|
};
|
|
# logos-qt-sdk, HEADERS ONLY. It owns the Qt<->lp SEAM HEADERS
|
|
# (logos_qt_lp_bridge.h, logos_qt_wire.h) that are NOT part of the host
|
|
# runtime, and basecamp compiles a wrapper that opens with
|
|
# `#include "logos_qt_lp_bridge.h"`:
|
|
#
|
|
# nix/app.nix copies ${logosPackageManagerModule}/include/* into
|
|
# app/generated/, and since B5 the builder emits that Qt-typed dependency
|
|
# wrapper as a VENEER over the lp path. app/generated/package_manager_api.cpp
|
|
# therefore needs the seam header, and logos-qt-host does not ship it.
|
|
#
|
|
# This is the same reason logos-test-modules and logos-test-framework keep
|
|
# the input; only pure HOSTS (logos-standalone-app, logos-view-module-runtime)
|
|
# can drop it, because they load module plugins rather than compiling a
|
|
# module's dependency wrapper.
|
|
#
|
|
# Nothing links a logos-qt-sdk archive here — after B2a it is an INTERFACE
|
|
# library, and app/CMakeLists.txt consumes only its include directory.
|
|
#
|
|
# 8a06b870 is the tip of feat/sdk-codegen-b3-d11 and is the same rev
|
|
# logos-test-framework and the workspace pin; a different rev would put a
|
|
# second logos-qt-sdk in the closure.
|
|
# Unpinned: feat/sdk-codegen-b3-d11 merged (logos-qt-sdk#33). This pin was
|
|
# kept byte-identical to logos-test-framework's and the workspace's by hand,
|
|
# because NOTHING here makes logos-qt-sdk follow — and the convention had
|
|
# already drifted (logos-module-builder pinned aca2951, not 8a06b870), which
|
|
# is exactly the second-logos-qt-sdk-in-the-closure this guarded against.
|
|
# Tracking master puts every consumer on one rev structurally.
|
|
logos-qt-sdk = {
|
|
url = "github:logos-co/logos-qt-sdk";
|
|
inputs.logos-nix.follows = "logos-nix";
|
|
inputs.logos-protocol.follows = "logos-protocol";
|
|
inputs.logos-cpp-sdk.follows = "logos-cpp-sdk";
|
|
inputs.logos-plugin-qt.follows = "logos-plugin-qt";
|
|
};
|
|
# win_dll_search.h (included unconditionally by app/PluginLoader.cpp) arrives
|
|
# in logos-module's MODULE_LIB_HEADERS from fcaaf78 "fix(windows): resolve a
|
|
# plugin's private DLLs from its own directory". An older rev fails the build
|
|
# on EVERY platform, not just Windows — the header is installed unguarded.
|
|
logos-module.url = "github:logos-co/logos-module";
|
|
# The Qt-plugin module loader: the QtPluginFormatLoader that liblogos_core
|
|
# links AND the logos_host binary this app ships (nix/app.nix copies it out
|
|
# of ${logosLiblogos}/bin, which is just liblogos re-exporting this
|
|
# package's bin/ — see logos-liblogos/nix/bin.nix).
|
|
#
|
|
# Declared HERE, as a top-level input, purely so `logos-liblogos.inputs.
|
|
# default-module-loader` below has something to follow. liblogos's own lock
|
|
# pins e648735, the commit BEFORE the host-services grant, and that is the
|
|
# one hop of this chain basecamp was missing: 06134bfc is what teaches
|
|
# logos_host the `--host-services` flag, stamps it as the `hostServices`
|
|
# property on the LogosAPI object (right after loadModule()'s name check, so
|
|
# the identity the grant is bound to is already verified), and holds the
|
|
# host-side name-bound policy in QtPluginFormatLoader::buildArguments.
|
|
#
|
|
# Without it, capability_module at 07dba1f asks for `token_registry` and
|
|
# never receives it, so it fails CLOSED and every gated call becomes
|
|
# "ModuleProxy: rejecting unauthorized call". The module half of the chain
|
|
# is already in place: the plugin's generated glue reads that property and
|
|
# calls logos_module_grant_host_services -> lp_grant_host_services in its
|
|
# OWN image (verified with nm on the built plugin, not with strings).
|
|
#
|
|
# logos-protocol MUST follow, exactly as liblogos declares it for this same
|
|
# input: this loader's own lock pins protocol 03842db, which predates
|
|
# lp_grant_host_services entirely, and the emitted glue's
|
|
# `#if LOGOS_PROTOCOL_VERSION_MINOR >= 3` guard would compile away to
|
|
# nothing against it. c8bab12 is 0.4. logos-cpp-sdk / logos-qt-sdk are
|
|
# deliberately NOT followed: the loader's own lock already resolves them to
|
|
# e3744fb / c6be61d, which is exactly what liblogos's follows resolve to, so
|
|
# leaving them alone keeps this build byte-identical to liblogos's own
|
|
# except for the loader rev itself.
|
|
#
|
|
# Rev-pinned rather than tracking the branch: 06134bfc is the tip of
|
|
# feat/host-services-grant. Drop the rev once that merges, at which point
|
|
# liblogos's own lock can carry it and this input can go away.
|
|
logos-module-loader-qt = {
|
|
url = "github:logos-co/logos-module-loader-qt";
|
|
inputs.logos-protocol.follows = "logos-protocol";
|
|
};
|
|
# Rev-pinned: f2a15ef3 is the tip of fix/b4-align-protocol-with-qt-host —
|
|
# the liblogos aligned with the split host runtime and the per-client token
|
|
# store. liblogos_core.dll's export list (the other half of the Windows
|
|
# one-copy fix) lands there, not on master.
|
|
logos-liblogos = {
|
|
url = "github:logos-co/logos-liblogos";
|
|
inputs.default-module-loader.follows = "logos-module-loader-qt";
|
|
};
|
|
logos-package-manager.url = "github:logos-co/logos-package-manager";
|
|
logos-package-manager-module.url = "github:logos-co/logos-package-manager-module";
|
|
logos-package-downloader-module.url = "github:logos-co/logos-package-downloader-module";
|
|
# Rev-pinned: 07dba1f is the tip of feat/universal-capability, built against
|
|
# the new module-builder. Master's capability_module targets the pre-split
|
|
# runtime and would load a second host copy into this process.
|
|
#
|
|
# This was walked back to 0cb33fb (the legacy hand-written Qt plugin) for one
|
|
# reason: 07dba1f is a UNIVERSAL module that asks the host for the
|
|
# `token_registry` / `token_delivery` services and fails CLOSED when they do
|
|
# not arrive, and the loader pinned here could not grant them. That is fixed
|
|
# above, at logos-module-loader-qt — so the pin returns to the rev the
|
|
# comment always described.
|
|
logos-capability-module.url = "github:logos-co/logos-capability-module";
|
|
logos-package.url = "github:logos-co/logos-package";
|
|
# Rev-pinned: c932e1c is the tip of feat/universal-view-plugin — the
|
|
# generated view plugin. This UI is loaded in-process by the app, so it must
|
|
# come from the same generation as the host runtime above.
|
|
# Rev-pinned: f135195 is the tip of feat/universal-view-plugin — the
|
|
# generated view plugin. This UI is loaded in-process by the app, so it must
|
|
# come from the same generation as the host runtime above.
|
|
#
|
|
# f135195 is that branch WITH master merged in. The previous value, c932e1c,
|
|
# published packages for the four native systems only, so the cross build
|
|
# failed with "logos-module-builder: dependency 'package_manager' publishes
|
|
# no packages for x86_64-windows". Master fixed that (#68, a pure lock bump)
|
|
# but does not carry the view plugin, so tracking master here would trade a
|
|
# build failure for a runtime mismatch that still looks green. The merge
|
|
# gives both.
|
|
logos-package-manager-ui.url = "github:logos-co/logos-package-manager-ui";
|
|
logos-design-system.url = "github:logos-co/logos-design-system";
|
|
# Unpinned: logos-view-module-runtime#25 merged, so master carries the move
|
|
# onto the split host and no longer rev-pins logos-plugin-qt itself. That
|
|
# matters here beyond tidiness: a runtime naming a different plugin-qt puts a
|
|
# second logos-qt-host in the closure.
|
|
logos-view-module-runtime = {
|
|
url = "github:logos-co/logos-view-module-runtime";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.logos-cpp-sdk.follows = "logos-cpp-sdk";
|
|
};
|
|
nix-bundle-logos-module-install.url = "github:logos-co/nix-bundle-logos-module-install";
|
|
nix-bundle-dir.url = "github:logos-co/nix-bundle-dir";
|
|
logos-qt-mcp.url = "github:logos-co/logos-qt-mcp";
|
|
nix-bundle-appimage.url = "github:logos-co/nix-bundle-appimage";
|
|
nix-bundle-macos-app = {
|
|
url = "github:logos-co/nix-bundle-macos-app";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.nix-bundle-dir.follows = "nix-bundle-dir";
|
|
};
|
|
};
|
|
|
|
nixConfig = {
|
|
extra-substituters = [ "https://cache.nix.logos.co/public" ];
|
|
extra-trusted-public-keys = [ "public:l4HrXgL4nw246+LBh2SOJyhz64BoGegOYLheT/iIAPU=" ];
|
|
};
|
|
|
|
# logos-module-loader-qt is in the formals only because Nix passes EVERY
|
|
# declared input to this function and this set has no ellipsis. Nothing here
|
|
# uses it directly — it exists so logos-liblogos.inputs.default-module-loader
|
|
# has a top-level input to follow.
|
|
outputs = { self, nixpkgs, logos-nix, logos-cpp-sdk, logos-protocol, logos-plugin-qt, logos-qt-sdk, logos-module, logos-module-loader-qt, logos-liblogos, logos-package-manager, logos-package-manager-module, logos-package-downloader-module, logos-capability-module, logos-package, logos-package-manager-ui, logos-design-system, logos-view-module-runtime, logos-qt-mcp, nix-bundle-logos-module-install, nix-bundle-dir, nix-bundle-appimage, nix-bundle-macos-app }:
|
|
let
|
|
systems = [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ];
|
|
# Build info (version + commit hashes) baked into the app binary so
|
|
# the Dashboard can render it. Commits come from the flake inputs'
|
|
# locked revs; self's rev is "dirty" when the checkout has uncommitted
|
|
# changes or is overridden via a path input.
|
|
revOf = input: input.rev or input.dirtyRev or "dirty";
|
|
buildInfo = {
|
|
# VERSION is only present on release branches. On master (pre-release
|
|
# CI builds) there is no VERSION file, so fall back to a
|
|
# "pre-release-{sha7}" string derived from self.rev — available on
|
|
# every clean CI checkout. Dirty local builds lack self.rev and get
|
|
# an empty string, which hides the badge (intentional for dev).
|
|
version = if builtins.pathExists ./VERSION
|
|
then nixpkgs.lib.removeSuffix "\n" (builtins.readFile ./VERSION)
|
|
else if (self ? rev) then "pre-release-${builtins.substring 0 7 self.rev}" else "";
|
|
commits = [
|
|
{ name = "logos-basecamp"; commit = revOf self; }
|
|
{ name = "logos-nix"; commit = revOf logos-nix; }
|
|
{ name = "logos-cpp-sdk"; commit = revOf logos-cpp-sdk; }
|
|
{ name = "logos-module"; commit = revOf logos-module; }
|
|
{ name = "logos-liblogos"; commit = revOf logos-liblogos; }
|
|
{ name = "logos-package-manager"; commit = revOf logos-package-manager; }
|
|
{ name = "logos-package-manager-module"; commit = revOf logos-package-manager-module; }
|
|
{ name = "logos-package-downloader-module"; commit = revOf logos-package-downloader-module; }
|
|
{ name = "logos-capability-module"; commit = revOf logos-capability-module; }
|
|
{ name = "logos-package"; commit = revOf logos-package; }
|
|
{ name = "logos-package-manager-ui"; commit = revOf logos-package-manager-ui; }
|
|
{ name = "logos-design-system"; commit = revOf logos-design-system; }
|
|
{ name = "logos-view-module-runtime"; commit = revOf logos-view-module-runtime; }
|
|
{ name = "logos-qt-mcp"; commit = revOf logos-qt-mcp; }
|
|
{ name = "nix-bundle-logos-module-install"; commit = revOf nix-bundle-logos-module-install; }
|
|
{ name = "nix-bundle-dir"; commit = revOf nix-bundle-dir; }
|
|
{ name = "nix-bundle-appimage"; commit = revOf nix-bundle-appimage; }
|
|
{ name = "nix-bundle-macos-app"; commit = revOf nix-bundle-macos-app; }
|
|
];
|
|
};
|
|
# The BUILD platform for a given target. Bundlers and code generators RUN
|
|
# during the build, so on the x86_64-windows cross target they must come
|
|
# from the build system -- taking them from packages.x86_64-windows would
|
|
# hand the builder a PE it cannot execute.
|
|
buildSystemFor = target:
|
|
if target == "x86_64-windows" then "x86_64-linux" else target;
|
|
|
|
# forAllSystems, plus the "x86_64-windows" pseudo-system. A cross
|
|
# derivation's `system` attr is its BUILD platform, so the Windows
|
|
# attributes evaluate anywhere and realise on x86_64-linux. Keying it as a
|
|
# system rather than a package-name suffix is what lets the 34
|
|
# `dep.packages.${system}.x` interpolations below stay untouched.
|
|
forAllSystems = f: logos-nix.lib.forAllTargets ({ system, pkgs }:
|
|
let buildSystem = buildSystemFor system; in f {
|
|
inherit system pkgs;
|
|
logosSdk = logos-cpp-sdk.packages.${system}.default;
|
|
# The SAME output carries BOTH the target headers/CMake package AND the
|
|
# logos-cpp-generator binary, so this is a SPLIT, not a swap: keep
|
|
# logosSdk for -DLOGOS_CPP_SDK_ROOT and use logosSdkBuild wherever the
|
|
# generator must RUN. Getting it backwards succeeds on native and, under
|
|
# cross, puts a PE on the builder's PATH -- the symptom is
|
|
# "logos-cpp-generator: command not found".
|
|
logosSdkBuild = logos-cpp-sdk.packages.${buildSystem}.default;
|
|
logosProtocolPkg = logos-protocol.packages.${system}.default;
|
|
logosQtHost = logos-plugin-qt.packages.${system}.logos-qt-host;
|
|
# HEADERS ONLY -- the Qt<->lp seam headers the generated dependency
|
|
# wrapper in app/generated/ includes. Nothing links this.
|
|
logosQtSdk = logos-qt-sdk.packages.${system}.default;
|
|
logosModule = logos-module.packages.${system}.default;
|
|
logosLiblogos = logos-liblogos.packages.${system}.default;
|
|
logosPackageManagerLibrary = logos-package-manager.packages.${system}.lib;
|
|
logosPackageManagerModule = logos-package-manager-module.packages.${system}.default;
|
|
logosPackageManagerModuleLib = logos-package-manager-module.packages.${system}.lib;
|
|
logosPackageDownloaderModule = logos-package-downloader-module.packages.${system}.default;
|
|
logosPackageDownloaderModuleLib = logos-package-downloader-module.packages.${system}.lib;
|
|
logosLiblogosPortable = logos-liblogos.packages.${system}.portable;
|
|
logosPackageManagerModuleLibPortable = logos-package-manager-module.packages.${system}.lib-portable;
|
|
logosCapabilityModule = logos-capability-module.packages.${system}.default;
|
|
logosPackageLib = logos-package.packages.${system}.lib;
|
|
# Headers-only output (include/ with logos/semver.hpp + semver/, no
|
|
# library). The app's AppsModel includes the shared semver comparator;
|
|
# it links nothing from lgx, so the headers output keeps liblgx out of
|
|
# the app entirely.
|
|
logosPackageHeaders = logos-package.packages.${system}.headers;
|
|
logosPackageManagerUI = logos-package-manager-ui.packages.${system}.default;
|
|
logosDesignSystem = logos-design-system.packages.${system}.default;
|
|
logosViewModuleRuntime = logos-view-module-runtime.packages.${system}.default;
|
|
# logos-qt-mcp is the QML inspector used by the UI test harness. It has
|
|
# no Windows target and is not needed to RUN the app -- nix/app.nix
|
|
# already takes `logosQtMcp ? null` and gates the inspector on it -- so
|
|
# Windows builds simply go without it. The inspector-dependent outputs
|
|
# (integration-test, shutdown-test, mcp-server) are correspondingly
|
|
# absent from the Windows package set; see the `packages` block.
|
|
logosQtMcp =
|
|
if system == "x86_64-windows" then null
|
|
else logos-qt-mcp.packages.${system}.default;
|
|
logosCppSdkSrc = logos-cpp-sdk.outPath;
|
|
logosLiblogosSrc = logos-liblogos.outPath;
|
|
logosPackageManagerModuleSrc = logos-package-manager-module.outPath;
|
|
logosCapabilityModuleSrc = logos-capability-module.outPath;
|
|
# Bundlers run ON the builder, so they are keyed by buildSystem, not by
|
|
# the target. nix-bundle-dir in particular is ELF/Mach-O only (its
|
|
# bundle.sh branches `file -b` -> Mach-O | ELF with no PE case), so on
|
|
# Windows it must not be invoked at all -- see nix/app.nix.
|
|
# Keyed by the TARGET, not buildSystem: the install bundler now does its
|
|
# own host/target split internally -- it takes lgpm from the build
|
|
# system (it runs there) and the .lgx bundler from the target (which
|
|
# decides the variant name and library extension). Keying the whole
|
|
# thing by buildSystem made it label a Windows package "linux-amd64"
|
|
# and look for a .so payload that was really a .dll.
|
|
installDev = nix-bundle-logos-module-install.bundlers.${system}.dev;
|
|
installPortable = nix-bundle-logos-module-install.bundlers.${system}.portable;
|
|
dirBundler = nix-bundle-dir.bundlers.${buildSystem}.qtApp;
|
|
});
|
|
in
|
|
{
|
|
packages = forAllSystems ({ pkgs, system, logosSdk, logosSdkBuild, logosProtocolPkg, logosQtHost, logosQtSdk, logosModule, logosLiblogos, logosLiblogosPortable, logosPackageManagerLibrary, logosPackageManagerModule, logosPackageManagerModuleLib, logosPackageManagerModuleLibPortable, logosPackageDownloaderModule, logosPackageDownloaderModuleLib, logosPackageLib, logosPackageHeaders, logosPackageManagerUI, logosCapabilityModule, logosDesignSystem, logosViewModuleRuntime, logosQtMcp, installDev, installPortable, dirBundler, ... }:
|
|
let
|
|
# Common configuration
|
|
common = import ./nix/default.nix {
|
|
inherit pkgs logosSdk logosProtocolPkg logosQtHost logosModule logosLiblogos;
|
|
};
|
|
src = ./.;
|
|
|
|
# Basecamp's own UI shell: a privilege-free plugin that links Qt and
|
|
# nothing else from this workspace, which nix/symbol-gate.nix enforces
|
|
# across the in-process image set. It builds from the SAME `src` as
|
|
# the app; its CMakeLists lives in src/ and can only see
|
|
# app/interfaces/, so it cannot include a host header even by accident.
|
|
mainUIPlugin = import ./nix/main-ui.nix {
|
|
inherit pkgs common src logosDesignSystem;
|
|
};
|
|
|
|
packageManagerUIPlugin = logosPackageManagerUI;
|
|
|
|
# Pre-installed modules/plugins (bundle + lgpm install in one step).
|
|
# Dev build: raw derivation (depends on /nix/store at runtime).
|
|
# Distributed build: portable self-contained bundle. Off Windows that
|
|
# portability comes from nix-bundle-dir; on Windows nix-bundle-lgx
|
|
# takes its own `mkWindowsPayload` path instead and does not call
|
|
# nix-bundle-dir at all. That is nix-bundle-lgx's business, not this
|
|
# flake's -- a MODULE must not carry the Qt/OpenSSL/runtime DLLs the
|
|
# host already ships in bin/, so the PE path needs a hostLibs strip
|
|
# that nix-bundle-dir does not have yet. Unrelated to binBundleDir
|
|
# below, which is the APP and therefore is the thing that ships them.
|
|
installedDev = map installDev [
|
|
logosPackageManagerModuleLib
|
|
logosPackageDownloaderModuleLib
|
|
logosCapabilityModule
|
|
packageManagerUIPlugin
|
|
];
|
|
installedDistributed = map installPortable [
|
|
logosPackageManagerModuleLibPortable
|
|
logosPackageDownloaderModuleLib
|
|
logosCapabilityModule
|
|
packageManagerUIPlugin
|
|
];
|
|
|
|
# App package (development build)
|
|
app = import ./nix/app.nix {
|
|
inherit pkgs common src logosModule logosLiblogos logosSdk logosProtocolPkg logosQtHost logosQtSdk logosDesignSystem logosViewModuleRuntime logosPackageManagerModule logosPackageDownloaderModule logosPackageHeaders buildInfo logosSdkBuild;
|
|
inherit logosQtMcp mainUIPlugin;
|
|
installedModules = installedDev;
|
|
};
|
|
|
|
# App package (distributed build for DMG/AppImage)
|
|
# Uses portable-compiled liblogos for portable variant selection
|
|
appDistributed = import ./nix/app.nix {
|
|
inherit pkgs common src logosModule logosSdk logosProtocolPkg logosQtHost logosQtSdk logosDesignSystem logosViewModuleRuntime logosPackageManagerModule logosPackageDownloaderModule logosPackageHeaders buildInfo logosSdkBuild;
|
|
inherit mainUIPlugin;
|
|
logosLiblogos = logosLiblogosPortable;
|
|
installedModules = installedDistributed;
|
|
portable = true;
|
|
enableInspector = false;
|
|
};
|
|
|
|
# Distributed build with inspector enabled (for macOS integration tests)
|
|
appDistributedWithInspector = import ./nix/app.nix {
|
|
inherit pkgs common src logosModule logosSdk logosProtocolPkg logosQtHost logosQtSdk logosDesignSystem logosViewModuleRuntime logosPackageManagerModule logosPackageDownloaderModule logosPackageHeaders buildInfo logosSdkBuild;
|
|
inherit logosQtMcp mainUIPlugin;
|
|
logosLiblogos = logosLiblogosPortable;
|
|
installedModules = installedDistributed;
|
|
portable = true;
|
|
enableInspector = true;
|
|
};
|
|
|
|
# macOS app for testing (distributed build with inspector enabled)
|
|
macosAppTest = if pkgs.stdenv.isDarwin then
|
|
nix-bundle-macos-app.lib.${system}.mkMacOSApp {
|
|
drv = appDistributedWithInspector;
|
|
name = "LogosBasecamp";
|
|
bundle = dirBundler appDistributedWithInspector;
|
|
icon = ./app/macos/logos.icns;
|
|
infoPlist = ./app/macos/Info.plist.in;
|
|
entitlements = ./app/macos/LogosBasecamp.entitlements;
|
|
}
|
|
else null;
|
|
|
|
macosApp = if pkgs.stdenv.isDarwin then
|
|
nix-bundle-macos-app.lib.${system}.mkMacOSApp {
|
|
drv = appDistributed;
|
|
name = "LogosBasecamp";
|
|
bundle = dirBundler appDistributed;
|
|
icon = ./app/macos/logos.icns;
|
|
infoPlist = ./app/macos/Info.plist.in;
|
|
entitlements = ./app/macos/LogosBasecamp.entitlements;
|
|
}
|
|
else null;
|
|
|
|
# Linux AppImage (only for Linux)
|
|
appImage = if pkgs.stdenv.isLinux then
|
|
import ./nix/appimage.nix {
|
|
inherit pkgs src;
|
|
app = appDistributed;
|
|
version = common.version;
|
|
}
|
|
else null;
|
|
|
|
# Self-contained directory bundle: appDistributed modules expect host Qt
|
|
# via @rpath; qtApp copies Qt frameworks into lib/ and rewrites the binary.
|
|
# (appDistributed alone is an intermediate used by AppImage / .app wrappers.)
|
|
withMainProgram = drv: drv.overrideAttrs (old: {
|
|
meta = (old.meta or {}) // {
|
|
mainProgram = "LogosBasecamp";
|
|
};
|
|
});
|
|
# dirBundler on EVERY platform, Windows included.
|
|
#
|
|
# The `winBundler = drv: drv` / `bundleFor` bypass that used to sit
|
|
# here was placed on an explicit condition: "the real fix is a PE
|
|
# branch in nix-bundle-dir's bundle.sh that skips relocation and keeps
|
|
# Qt staging; when that lands, DELETE bundleFor". It has landed, and
|
|
# this flake's root `nix-bundle-dir` input already resolves to it
|
|
# (f843b8ec, which is `main`), so the condition is met and the bypass
|
|
# is gone.
|
|
#
|
|
# Why it was never a working alternative: nix-bundle-dir does two
|
|
# separable jobs. (a) RELOCATION -- rewriting rpaths / install names
|
|
# so binaries stop pointing into /nix/store. (b) Qt STAGING -- the Qt
|
|
# plugin scan, the QML module scan and qt.conf generation; that half
|
|
# is FORMAT-AGNOSTIC and Windows needs it exactly as much as anywhere
|
|
# else, because Qt plugins and QML module DLLs are LoadLibrary'd and
|
|
# nothing in the import table reveals them.
|
|
#
|
|
# The bypass was argued for on the grounds that a PE needs none of
|
|
# (a). Only the rpath REWRITING half of that is true, and the
|
|
# measurement below is what corrects it: the un-bundled tree reaches
|
|
# a third of its bin/ through 12 SYMLINKS into /nix/store, which no
|
|
# amount of "PE imports are base names" makes portable. Phase 1's
|
|
# `cp -aL` is what dereferences them, and skipping the bundler
|
|
# skipped that as surely as it skipped (b).
|
|
#
|
|
# Measured, not assumed. Both trees were realised on x86_64-linux
|
|
# from ONE tree -- same appDistributed derivation, the only variable
|
|
# being whether dirBundler is applied -- and compared entry by entry:
|
|
#
|
|
# bypass(drv:drv) dirBundler
|
|
# entries 85 1740
|
|
# regular files 59 1657
|
|
# symlinks 12 0
|
|
# bytes 259 MB 479 MB
|
|
# *.dll in bin/ 33 88
|
|
# bin/qt.conf MISSING present
|
|
# lib/qt-6/…/platforms/qwindows.dll
|
|
# MISSING present
|
|
# lib/qt-6 (plugins + qml) MISSING 1533 files
|
|
# nix closure refs 12 1
|
|
#
|
|
# Those 12 symlinks are the part that matters most, and they are why
|
|
# "the bypass at least shipped the app" was never true. bin/Qt6Core
|
|
# .dll, Qt6Gui.dll, Qt6Widgets.dll, Qt6Network.dll, Qt6RemoteObjects
|
|
# .dll, libssl/libcrypto, libpng16, libzstd, libb2, pcre2 and
|
|
# double-conversion were SYMLINKS into /nix/store. Copy that tree to
|
|
# a Windows box -- the entire point of a portable bundle -- and every
|
|
# one of them dangles: 0xC0000135, no output, before main(). The
|
|
# bundled tree resolves all 12 into real files and has no symlink
|
|
# left. Its single remaining nix reference is an inert /nix string
|
|
# embedded in a PE's data, which imports nothing.
|
|
#
|
|
# 55 DLLs exist only in the bundled bin/: the Qt Quick / Controls /
|
|
# Labs set the fixpoint sweep pulls in once the QML modules are
|
|
# staged, plus libcurl and its TLS/HTTP2 chain mirrored beside the
|
|
# package_downloader module that imports them.
|
|
#
|
|
# The one thing the bundler does NOT carry over: README.txt and
|
|
# share/ (a .desktop file and a hicolor icon, 9 paths). That is not a
|
|
# Windows regression -- bundle.sh Phase 1 copies bin/, lib/ and
|
|
# extraDirs on EVERY platform, so the shipping Linux and macOS
|
|
# bundles have never had them either; the bypass "kept" them only by
|
|
# doing nothing at all. Both are dead weight off-store anyway:
|
|
# README.txt is a build-info file whose every line is a /nix/store
|
|
# path, and a .desktop file does nothing on Windows.
|
|
#
|
|
# Getting here also required three additions to the app's
|
|
# passthru.extraClosurePaths (see nix/app.nix) -- qtdeclarative,
|
|
# libjpeg.bin, sqlite.bin. Each was a build the bundler FAILED,
|
|
# naming the missing DLL and the plugin that imported it, rather than
|
|
# shipping a tree that dies before main(). That is the behaviour the
|
|
# bypass was hiding.
|
|
#
|
|
# NOT demonstrated, stated plainly:
|
|
#
|
|
# * None of this has been RUN on Windows, by this change or by CI,
|
|
# which has never executed a Windows binary. Every claim above is
|
|
# build-time and tree-shape only.
|
|
# * The two trees measured came from a harness that drops ONE entry
|
|
# from installedDistributed -- packageManagerUIPlugin -- because
|
|
# logos-package-manager-ui does not cross-compile at the rev this
|
|
# flake pins: its generated logos_sdk.h includes
|
|
# package_manager_api.h, which is not produced for the Windows
|
|
# target. That is pre-existing and independent of this change; the
|
|
# identical derivation fails when built straight from the pinned
|
|
# rev with no basecamp involved. Everything above concerns Qt
|
|
# staging and the DLL closure, which that one plugin does not
|
|
# participate in -- but the numbers are from a bundle missing it.
|
|
# * For the same reason `nix build .#packages.x86_64-windows.*`
|
|
# cannot succeed on this branch as pinned. A second, unrelated
|
|
# blocker sits in front of it at EVAL time: line ~119's
|
|
# logos-package-downloader-module has no x86_64-windows target, so
|
|
# the attribute cannot even be evaluated. Both blockers predate
|
|
# this change and are identical with the bypass in place.
|
|
binBundleDir = withMainProgram (dirBundler appDistributed);
|
|
binBundleDirInspector = withMainProgram (dirBundler appDistributedWithInspector);
|
|
in
|
|
{
|
|
# Individual outputs.
|
|
main-ui-plugin = mainUIPlugin;
|
|
package-manager-ui-plugin = packageManagerUIPlugin;
|
|
app = app;
|
|
|
|
# Self-contained flat directory (bin/ + lib/ with Qt).
|
|
# Run: nix run .#bin-bundle-dir
|
|
bin-bundle-dir = binBundleDir;
|
|
|
|
# Test-only twin of bin-bundle-dir WITH the QML inspector compiled in,
|
|
# so logos-qt-mcp can connect and drive the UI headlessly. Identical to
|
|
# the shipping bundle in every other respect.
|
|
#
|
|
# The inspector is a compile-time feature and is deliberately OFF in the
|
|
# shipping bin-bundle-dir / appimage / macos outputs — we do NOT ship
|
|
# the inspector in release builds. This output exists purely so the
|
|
# package-manager doc-test can install and exercise modules through
|
|
# the real bundled UI; it is not a release artifact.
|
|
# Build: nix build .#bin-bundle-dir-inspector
|
|
bin-bundle-dir-inspector = binBundleDirInspector;
|
|
|
|
# QML Inspector MCP server: nix build .#mcp-server -o result-mcp
|
|
mcp-server = logos-qt-mcp.packages.${system}.mcp-server;
|
|
|
|
# Full logos-qt-mcp package (includes test-framework, mcp-server, qt-plugin)
|
|
# Use: nix build .#logos-qt-mcp -o result-mcp
|
|
# Then: LOGOS_QT_MCP=./result-mcp node tests/ui-tests.mjs --ci ./result/bin/LogosBasecamp
|
|
logos-qt-mcp = logosQtMcp;
|
|
|
|
# Smoke test (also exposed as a package so it can be built standalone)
|
|
smoke-test = import ./nix/smoke-test.nix { inherit pkgs; appPkg = app; };
|
|
|
|
# One-runtime symbol gate. Asserts the logos C++ runtime (TokenManager,
|
|
# StoreRegistry, LogosAPI, LogosAPIClient) is DEFINED exactly once across
|
|
# the images that share one process — in liblogos_core, the single
|
|
# provider. A second definition is a second TokenManager and every
|
|
# cross-module call is refused at runtime with no build diagnostic.
|
|
# Build: nix build .#symbol-gate
|
|
symbol-gate = import ./nix/symbol-gate.nix { inherit pkgs; appPkg = app; };
|
|
|
|
# Negative control for the above. Plants a REAL duplicate runtime where
|
|
# an in-process consumer goes and asserts the gate REJECTS it. Ship both
|
|
# or neither: an absence assertion that has never been seen to fail is
|
|
# indistinguishable from a broken one.
|
|
# Build: nix build .#symbol-gate-negative
|
|
symbol-gate-negative = import ./nix/symbol-gate.nix {
|
|
inherit pkgs; appPkg = app; negativeControl = true;
|
|
};
|
|
|
|
# ui_qml sandbox-escape regression test (F-008). Focused C++ unit test:
|
|
# builds a real malicious QML plugin and asserts the production sandbox
|
|
# refuses to load it. Build: nix build .#sandbox-test
|
|
sandbox-test = import ./nix/sandbox-test.nix { inherit pkgs src; };
|
|
|
|
# Pure-model unit tests (AppsModel install-status logic, etc.). Same
|
|
# shape as sandbox-test — standalone QtTest project, no app launch,
|
|
# no IPC. Build: nix build .#unit-tests
|
|
unit-tests = import ./nix/unit-tests.nix {
|
|
inherit pkgs src logosPackageHeaders;
|
|
};
|
|
|
|
# QML component tests (Qt Quick Test)
|
|
qml-tests = import ./nix/qml-tests.nix { inherit pkgs src logosPackageHeaders; };
|
|
|
|
# Coverage report for the unit-test suite: same targets as
|
|
# .#unit-tests, compiled with --coverage and reported via gcovr.
|
|
# Report-only for now (failUnderLine = 0) — raise the threshold as
|
|
# the test plan phases land to make it a gate.
|
|
# Build: nix build .#coverage -L && open result/coverage.html
|
|
coverage = import ./nix/coverage.nix {
|
|
inherit pkgs src logosPackageHeaders;
|
|
failUnderLine = 0;
|
|
};
|
|
|
|
# Integration test (UI tests via Qt Inspector)
|
|
integration-test = import ./nix/integration-test.nix { inherit pkgs src logosQtMcp; appPkg = app; };
|
|
|
|
# Host-services grant guard. Asserts that a NON-"core" identity
|
|
# (ui-host running package_manager_ui) actually completes a
|
|
# capability-gated call chain — i.e. that capability_module really
|
|
# received its token_registry/token_delivery grant from the loader
|
|
# basecamp pins, rather than failing closed. See
|
|
# nix/host-services-test.nix and tests/host-services-assert.mjs.
|
|
# Build: nix build .#host-services-test
|
|
host-services-test = import ./nix/host-services-test.nix {
|
|
inherit pkgs src logosQtMcp; appPkg = app;
|
|
};
|
|
|
|
# Shutdown tests (SIGTERM, SIGINT, Ctrl+Q / ⌘Q). Spawns a fresh
|
|
# app per case and asserts orderly exit (code 0).
|
|
shutdown-test = import ./nix/shutdown-test.nix { inherit pkgs src logosQtMcp; appPkg = app; };
|
|
|
|
# Default package
|
|
default = app;
|
|
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
|
|
bin-appimage = nix-bundle-appimage.lib.${system}.mkAppImage {
|
|
drv = appDistributed;
|
|
name = "logos-basecamp";
|
|
bundle = dirBundler appDistributed;
|
|
desktopFile = ./assets/logos-basecamp.desktop;
|
|
icon = ./app/icons/logos.png;
|
|
};
|
|
} // pkgs.lib.optionalAttrs pkgs.stdenv.isDarwin {
|
|
bin-macos-app = macosApp;
|
|
smoke-test-bundle = import ./nix/smoke-test.nix {
|
|
inherit pkgs;
|
|
appPkg = macosApp;
|
|
appBin = "${macosApp}/LogosBasecamp.app/Contents/MacOS/LogosBasecamp";
|
|
};
|
|
integration-test-bundle = import ./nix/integration-test.nix {
|
|
inherit pkgs src;
|
|
appPkg = macosAppTest;
|
|
inherit logosQtMcp;
|
|
appBin = "${macosAppTest}/LogosBasecamp.app/Contents/MacOS/LogosBasecamp";
|
|
};
|
|
host-services-test-bundle = import ./nix/host-services-test.nix {
|
|
inherit pkgs src;
|
|
appPkg = macosAppTest;
|
|
inherit logosQtMcp;
|
|
appBin = "${macosAppTest}/LogosBasecamp.app/Contents/MacOS/LogosBasecamp";
|
|
};
|
|
}
|
|
);
|
|
|
|
# nix run . → dev build (depends on /nix/store at runtime)
|
|
# nix run .#bin-bundle-dir → self-contained bundle (Qt frameworks in lib/)
|
|
apps = forAllSystems ({ system, ... }: {
|
|
default = {
|
|
type = "app";
|
|
program = "${self.packages.${system}.app}/bin/LogosBasecamp";
|
|
};
|
|
bin-bundle-dir = {
|
|
type = "app";
|
|
program = "${self.packages.${system}.bin-bundle-dir}/bin/LogosBasecamp";
|
|
};
|
|
});
|
|
|
|
checks = forAllSystems ({ pkgs, system, ... }: {
|
|
smoke-test = self.packages.${system}.smoke-test;
|
|
sandbox-test = self.packages.${system}.sandbox-test;
|
|
unit-tests = self.packages.${system}.unit-tests;
|
|
qml-tests = self.packages.${system}.qml-tests;
|
|
integration-test = self.packages.${system}.integration-test;
|
|
shutdown-test = self.packages.${system}.shutdown-test;
|
|
host-services-test = self.packages.${system}.host-services-test;
|
|
symbol-gate = self.packages.${system}.symbol-gate;
|
|
symbol-gate-negative = self.packages.${system}.symbol-gate-negative;
|
|
});
|
|
|
|
devShells = forAllSystems ({ pkgs, logosSdk, logosProtocolPkg, logosQtHost, logosModule, logosLiblogos, logosPackageManagerLibrary, logosPackageManagerModule, logosCapabilityModule, logosPackageLib, logosDesignSystem, logosCppSdkSrc, logosLiblogosSrc, logosPackageManagerModuleSrc, logosCapabilityModuleSrc, ... }: {
|
|
default = pkgs.mkShell {
|
|
nativeBuildInputs = [
|
|
pkgs.cmake
|
|
pkgs.ninja
|
|
pkgs.pkg-config
|
|
];
|
|
buildInputs = [
|
|
pkgs.qt6.qtbase
|
|
pkgs.qt6.qtremoteobjects
|
|
pkgs.zstd
|
|
pkgs.krb5
|
|
pkgs.abseil-cpp
|
|
];
|
|
|
|
shellHook = ''
|
|
# Nix package paths (pre-built for host system)
|
|
export LOGOS_CPP_SDK_ROOT="${logosSdk}"
|
|
export LOGOS_PROTOCOL_ROOT="${logosProtocolPkg}"
|
|
export LOGOS_QT_HOST_ROOT="${logosQtHost}"
|
|
export LOGOS_MODULE_ROOT="${logosModule}"
|
|
export LOGOS_LIBLOGOS_ROOT="${logosLiblogos}"
|
|
export LOGOS_PACKAGE_MANAGER_ROOT="${logosPackageManagerLibrary}"
|
|
export LOGOS_CAPABILITY_MODULE_ROOT="${logosCapabilityModule}"
|
|
export LGX_ROOT="${logosPackageLib}"
|
|
export LOGOS_DESIGN_SYSTEM_ROOT="${logosDesignSystem}"
|
|
|
|
# Source paths for iOS builds (from flake inputs)
|
|
export LOGOS_CPP_SDK_SRC="${logosCppSdkSrc}"
|
|
export LOGOS_LIBLOGOS_SRC="${logosLiblogosSrc}"
|
|
export LOGOS_PACKAGE_MANAGER_MODULE_SRC="${logosPackageManagerModuleSrc}"
|
|
export LOGOS_CAPABILITY_MODULE_SRC="${logosCapabilityModuleSrc}"
|
|
|
|
echo "Logos Basecamp development environment"
|
|
echo ""
|
|
echo "Nix packages (host builds):"
|
|
echo " LOGOS_CPP_SDK_ROOT: $LOGOS_CPP_SDK_ROOT"
|
|
echo " LOGOS_MODULE_ROOT: $LOGOS_MODULE_ROOT"
|
|
echo " LOGOS_LIBLOGOS_ROOT: $LOGOS_LIBLOGOS_ROOT"
|
|
echo " LOGOS_PACKAGE_MANAGER_ROOT: $LOGOS_PACKAGE_MANAGER_ROOT"
|
|
echo " LOGOS_CAPABILITY_MODULE_ROOT: $LOGOS_CAPABILITY_MODULE_ROOT"
|
|
echo " LGX_ROOT: $LGX_ROOT"
|
|
echo " LOGOS_DESIGN_SYSTEM_ROOT: $LOGOS_DESIGN_SYSTEM_ROOT"
|
|
echo ""
|
|
echo "Source paths (for iOS builds):"
|
|
echo " LOGOS_CPP_SDK_SRC: $LOGOS_CPP_SDK_SRC"
|
|
echo " LOGOS_LIBLOGOS_SRC: $LOGOS_LIBLOGOS_SRC"
|
|
echo " LOGOS_PACKAGE_MANAGER_MODULE_SRC: $LOGOS_PACKAGE_MANAGER_MODULE_SRC"
|
|
echo " LOGOS_CAPABILITY_MODULE_SRC: $LOGOS_CAPABILITY_MODULE_SRC"
|
|
'';
|
|
};
|
|
});
|
|
};
|
|
}
|