Files
logos-plugin-qt/lib/default.nix
T
Dario Gabriel LipicarandClaude Opus 5 3d7e3e68b3 feat: the view templates exist once, and this repo is where
The four LogosView*.in templates had two byte-identical copies:

  logos-module-builder/cmake/                         (LIVE — LogosModule.cmake)
  logos-plugin-qt/tests/rep-file-plugin/cmake/        (fixture — rep-file-plugin)

Nothing compared them. This is the fifth defect of that exact shape in this
refactor, and it was created by the fix for the fourth: when this repo's
duplicate LogosModule.cmake was deleted, the templates it read were MOVED into
the fixture rather than removed, because the fixture still needed them.

It needed them because it cannot reach where they went. The edge runs
logos-module-builder -> logos-plugin-qt, one way, so a fixture inside this repo
can never consume logos-module-builder's copy, and this repo cannot re-export
it either. "Own them in the builder" is not reachable; a copy in the fixture is
what "own them in the builder" degrades into.

So ownership follows the direction that works. The templates are this repo's:

  * cmake/ holds the one copy, with cmake/README.md stating the rule and the
    argument for it.
  * packages.<sys>.logos-view-templates publishes them as a nameable output.
  * lib.buildPlugin / lib.generate set LOGOS_VIEW_TEMPLATE_DIR (cmake flag and
    env var) on every plugin build, so logos-module-builder's LogosModule.cmake
    receives the directory without either repo growing a new input.
  * devShellInputs exports it too, so a hand-run cmake in a module dev shell
    resolves it the same way.

LogosModule.cmake itself does NOT come back here. That file is the builder's
build-system contract and stays there; only the Qt-specific templates it
instantiates are published from this side of the edge.

The fixture now takes the directory from the harness and has nothing to fall
back to — a missing LOGOS_VIEW_TEMPLATE_DIR is a FATAL_ERROR, not a quiet
second copy. Proof it reads the owned file and not a leftover: changing the IID
in cmake/LogosViewReplicaFactory.h.in makes rep-file-plugin fail its
"IID found in binary" assertion.

rep-file-plugin is also added to CI. It is the check that instantiates these
templates and it was never listed in the workflow, so the templates had no CI
coverage at all — part of why a duplicate could sit in the fixture unnoticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:07:05 -03:00

195 lines
8.1 KiB
Nix

# Logos Qt Plugin Backend
#
# Builds Logos modules as Qt 6 plugins. Encapsulates Qt-specific build logic:
# CMake configuration, Qt dependencies, plugin compilation, and macOS fixups.
#
# This backend does NOT know about logos-cpp-sdk. It only knows about:
# - Qt (cmake, ninja, qtbase, qtremoteobjects)
# - logosModule (interface.h — the plugin interface contract)
#
# The logos-cpp-sdk (generator, SDK lib, headers) is added by the caller
# (logos-module-builder) via extraNativeBuildInputs / extraBuildInputs / env.
#
{ nixpkgs, lib }:
let
common = import ./common.nix { inherit lib; };
mkBuildPlugin = import ./buildPlugin.nix { inherit lib common; };
mkBuildHeaders = import ./buildHeaders.nix { inherit lib common; };
in {
# Build a Qt plugin from sources.
# logosModule provides interface.h. Everything else (SDK, generator) comes
# via extraNativeBuildInputs/extraBuildInputs passed by the caller.
# Returns: derivation with lib/{name}_plugin.so
buildPlugin = {
pkgs,
src,
config,
logosModule,
moduleDeps ? {},
interfaceDeps ? [],
staticDeps ? [],
externalLibs ? {},
extraNativeBuildInputs ? [],
extraBuildInputs ? [],
extraCmakeFlags ? [],
extraEnv ? {},
preConfigure ? "",
postInstall ? "",
}:
let
commonArgs = {
pname = "logos-${config.name}-module";
version = config.version;
nativeBuildInputs = common.commonNativeBuildInputs pkgs ++ extraNativeBuildInputs;
buildInputs = common.commonBuildInputs pkgs ++ extraBuildInputs;
cmakeFlags = common.commonCmakeFlags { inherit logosModule; } ++ extraCmakeFlags;
# LOGOS_MODULE_BUILDER_ROOT is NOT defaulted here. This backend used to
# point it at its own root, which shipped a second copy of
# LogosModule.cmake; the caller only overrode it when the module itself
# carried one, so ui_qml plugins silently configured with the backend's
# copy. The CMake module belongs to logos-module-builder, which now always
# passes this in extraEnv. With no default, a caller that forgets gets a
# loud FATAL_ERROR from the module's CMakeLists instead of a build against
# whatever this repo happens to contain.
env = {
LOGOS_MODULE_ROOT = "${logosModule}";
# The LogosView*.in templates logos_module(REP_FILE ...) instantiates.
# They are owned by THIS repo (see ../cmake/README.md) because
# logos-module-builder depends on it and not the reverse — that
# direction is what lets the one copy serve both that repo's
# LogosModule.cmake and this repo's tests/rep-file-plugin fixture.
# Passed as an env var as well as a cmake flag so a module that drives
# cmake itself still resolves it; LogosModule.cmake hard-errors when
# neither is set rather than falling back to a sibling directory.
LOGOS_VIEW_TEMPLATE_DIR = "${common.viewTemplateDir}";
} // extraEnv;
meta = with lib; {
description = config.description;
platforms = platforms.unix ++ platforms.windows;
};
};
in mkBuildPlugin.build {
inherit pkgs src config commonArgs moduleDeps interfaceDeps staticDeps externalLibs preConfigure postInstall;
logosSdk = null; # not used by buildPlugin.nix directly, kept for compat
};
# Generate-only: run the same code generators as buildPlugin but emit a
# ready-to-build source tree (module source + generated_code/) instead of a
# compiled plugin. Same args as buildPlugin so the caller (logos-module-builder)
# can pass the identical inputs and get a tree that matches a real build.
# Returns: derivation whose $out is the snapshotted source tree.
generate = {
pkgs,
src,
config,
logosModule,
moduleDeps ? {},
interfaceDeps ? [],
staticDeps ? [],
externalLibs ? {},
extraNativeBuildInputs ? [],
extraBuildInputs ? [],
extraCmakeFlags ? [],
extraEnv ? {},
preConfigure ? "",
postInstall ? "",
}:
let
commonArgs = {
pname = "logos-${config.name}-module";
version = config.version;
nativeBuildInputs = common.commonNativeBuildInputs pkgs ++ extraNativeBuildInputs;
buildInputs = common.commonBuildInputs pkgs ++ extraBuildInputs;
cmakeFlags = common.commonCmakeFlags { inherit logosModule; } ++ extraCmakeFlags;
# LOGOS_MODULE_BUILDER_ROOT is NOT defaulted here. This backend used to
# point it at its own root, which shipped a second copy of
# LogosModule.cmake; the caller only overrode it when the module itself
# carried one, so ui_qml plugins silently configured with the backend's
# copy. The CMake module belongs to logos-module-builder, which now always
# passes this in extraEnv. With no default, a caller that forgets gets a
# loud FATAL_ERROR from the module's CMakeLists instead of a build against
# whatever this repo happens to contain.
env = {
LOGOS_MODULE_ROOT = "${logosModule}";
# The LogosView*.in templates logos_module(REP_FILE ...) instantiates.
# They are owned by THIS repo (see ../cmake/README.md) because
# logos-module-builder depends on it and not the reverse — that
# direction is what lets the one copy serve both that repo's
# LogosModule.cmake and this repo's tests/rep-file-plugin fixture.
# Passed as an env var as well as a cmake flag so a module that drives
# cmake itself still resolves it; LogosModule.cmake hard-errors when
# neither is set rather than falling back to a sibling directory.
LOGOS_VIEW_TEMPLATE_DIR = "${common.viewTemplateDir}";
} // extraEnv;
meta = with lib; {
description = config.description;
platforms = platforms.unix ++ platforms.windows;
};
};
in mkBuildPlugin.generate {
inherit pkgs src config commonArgs moduleDeps interfaceDeps staticDeps externalLibs preConfigure postInstall;
logosSdk = null; # not used by buildPlugin.nix directly, kept for compat
};
# Generate SDK headers from a compiled plugin.
# `apiStyle` picks which type surface the generated `<Module>` client
# wrapper exposes ("qt" or "lp"); each module is built once per
# style so downstream consumers can pick the variant matching their
# own --api-style without re-running the codegen. Default is "qt"
# — historically the only option, kept for backward compat.
# Returns: derivation with include/*.h
buildHeaders = {
pkgs,
src,
config,
pluginLib,
logosSdk,
apiStyle ? "qt",
# Path to this module's LIDL contract (or null). This is the PRIMARY input
# for the qt surface — a module that publishes a contract has its wrapper
# generated from it and is never introspected. Only a module with no
# contract falls back to reading its compiled plugin.
contractLidl ? null,
# logos-qt-generator (build-platform). Required to take the contract-driven
# qt path; null demotes it to the legacy emitter, loudly.
qtGenerator ? null,
}:
let
commonArgs = {
pname = "logos-${config.name}-module";
version = config.version;
meta = with lib; {
description = config.description;
platforms = platforms.unix ++ platforms.windows;
};
};
in mkBuildHeaders.build {
inherit pkgs src config commonArgs logosSdk apiStyle contractLidl qtGenerator;
lib = pluginLib;
};
# Dev shell dependencies — Qt only.
# SDK env vars, including LOGOS_MODULE_BUILDER_ROOT, are exported by the
# caller (logos-module-builder) — it owns cmake/LogosModule.cmake now.
devShellInputs = pkgs: {
logosModule ? null,
}: {
nativeBuildInputs = common.commonNativeBuildInputs pkgs;
buildInputs = common.commonBuildInputs pkgs;
shellHook = ''
${if logosModule != null then ''export LOGOS_MODULE_ROOT="${logosModule}"'' else ""}
# Same directory the nix build passes in, so a hand-run `cmake` inside a
# module's dev shell resolves the templates the way logos_module() does.
# Without it a ui_qml module would hit LogosModule.cmake's hard error.
export LOGOS_VIEW_TEMPLATE_DIR="${common.viewTemplateDir}"
'';
};
# Backend metadata
name = "qt";
version = "0.1.0";
inherit common;
}