Merge pull request #5 from logos-co/move_non_qt_logic

move non-qt specific logic out; add tests
This commit is contained in:
Iuri Matias
2026-04-02 10:28:06 -04:00
committed by GitHub
10 changed files with 241 additions and 105 deletions
+26
View File
@@ -0,0 +1,26 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v15
with:
name: logos-co
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build vanilla Qt plugin test
run: nix build '.#checks.x86_64-linux.vanilla-plugin'
Generated
+2 -60
View File
@@ -1,31 +1,8 @@
{
"nodes": {
"logos-cpp-sdk": {
"inputs": {
"logos-nix": "logos-nix",
"nixpkgs": [
"logos-cpp-sdk",
"logos-nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1774988975,
"narHash": "sha256-wJ7Bv3TL754bZcTL2qilI0JahiVag7fLDzw8Y6Qz0pk=",
"owner": "logos-co",
"repo": "logos-cpp-sdk",
"rev": "d633575677a3d19a8bf1d5ff687398dace532938",
"type": "github"
},
"original": {
"owner": "logos-co",
"repo": "logos-cpp-sdk",
"type": "github"
}
},
"logos-module": {
"inputs": {
"logos-nix": "logos-nix_2",
"logos-nix": "logos-nix",
"nixpkgs": [
"logos-module",
"logos-nix",
@@ -68,24 +45,6 @@
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1773955630,
"narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=",
"owner": "logos-co",
"repo": "logos-nix",
"rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88",
"type": "github"
},
"original": {
"owner": "logos-co",
"repo": "logos-nix",
"type": "github"
}
},
"logos-nix_3": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1774455309,
"narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=",
@@ -132,27 +91,10 @@
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1759036355,
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"logos-cpp-sdk": "logos-cpp-sdk",
"logos-module": "logos-module",
"logos-nix": "logos-nix_3",
"logos-nix": "logos-nix_2",
"nixpkgs": [
"logos-nix",
"nixpkgs"
+38 -13
View File
@@ -3,12 +3,13 @@
inputs = {
logos-nix.url = "github:logos-co/logos-nix";
logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk";
# Only needed for standalone dev/testing and the convenience lib wrapper.
# When used via logos-module-builder, logosModule is injected by the builder.
logos-module.url = "github:logos-co/logos-module";
nixpkgs.follows = "logos-nix/nixpkgs";
};
outputs = { self, nixpkgs, logos-cpp-sdk, logos-module, ... }:
outputs = { self, nixpkgs, logos-module, ... }:
let
systems = [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ];
@@ -17,15 +18,32 @@
pkgs = import nixpkgs { inherit system; };
});
# Import the backend library
backendLib = import ./lib {
inherit nixpkgs logos-cpp-sdk logos-module;
# Raw backend lib — no deps baked in.
# Callers (logos-module-builder) inject logosModule per call.
rawLib = import ./lib {
inherit nixpkgs;
inherit (nixpkgs) lib;
backendRoot = ./.;
};
# Convenience wrapper that pre-fills logosModule from this flake's inputs.
# Used for standalone dev/testing.
wrappedLib = rawLib // {
buildPlugin = args: rawLib.buildPlugin (args // {
logosModule = logos-module.packages.${args.pkgs.system}.default;
});
buildHeaders = args: rawLib.buildHeaders args;
devShellInputs = pkgs: rawLib.devShellInputs pkgs {
logosModule = logos-module.packages.${pkgs.system}.default;
};
};
in {
# Export the backend library for use by logos-module-builder
lib = backendLib;
# Default export: wrapped with logosModule pre-filled
lib = wrappedLib;
# Raw export: no deps — for use by logos-module-builder
rawLib = rawLib;
# Provide the cmake module as a package
packages = forAllSystems ({ pkgs, ... }: {
@@ -36,18 +54,25 @@
default = self.packages.${pkgs.system}.cmake-module;
});
# Tests
checks = forAllSystems ({ pkgs, ... }: {
# Build a vanilla Qt plugin with no Logos SDK deps
vanilla-plugin = import ./tests/test-vanilla-plugin.nix {
inherit pkgs;
backendCommon = rawLib.common;
};
});
# Dev shell for working on the backend itself
devShells = forAllSystems ({ pkgs, ... }:
let
logosSdk = logos-cpp-sdk.packages.${pkgs.system}.default;
logosModule = logos-module.packages.${pkgs.system}.default;
shell = wrappedLib.devShellInputs pkgs;
in {
default = pkgs.mkShell {
nativeBuildInputs = backendLib.common.commonNativeBuildInputs pkgs;
buildInputs = backendLib.common.commonBuildInputs pkgs;
nativeBuildInputs = shell.nativeBuildInputs;
buildInputs = shell.buildInputs;
shellHook = ''
export LOGOS_CPP_SDK_ROOT="${logosSdk}"
export LOGOS_MODULE_ROOT="${logosModule}"
${shell.shellHook}
echo "Logos Qt Plugin Backend development environment"
'';
};
+8 -3
View File
@@ -1,4 +1,8 @@
# Qt-specific utilities for building Logos plugins
#
# This module only knows about Qt. It does NOT reference logos-cpp-sdk.
# The logosModule dependency (interface.h) is the only Logos-specific dep,
# and it's passed in by the caller.
{ lib }:
{
@@ -29,10 +33,11 @@
pkgs.qt6.qtremoteobjects
];
# CMake flags for Qt plugin builds
commonCmakeFlags = { logosSdk, logosModule }: [
# CMake flags for Qt plugin builds.
# Only includes logosModule (for interface.h).
# SDK flags are added by the builder layer, not here.
commonCmakeFlags = { logosModule }: [
"-GNinja"
"-DLOGOS_CPP_SDK_ROOT=${logosSdk}"
"-DLOGOS_MODULE_ROOT=${logosModule}"
];
+30 -29
View File
@@ -1,16 +1,16 @@
# Logos Qt Plugin Backend
#
# This module exports the backend interface that logos-module-builder uses
# to build Qt 6 plugins. It encapsulates all Qt-specific build logic:
# CMake configuration, Qt dependencies, code generation, and header generation.
# Builds Logos modules as Qt 6 plugins. Encapsulates Qt-specific build logic:
# CMake configuration, Qt dependencies, plugin compilation, and macOS fixups.
#
# Backend interface:
# buildPlugin — compile sources + metadata → Qt plugin .so/.dylib
# buildHeaders — introspect compiled plugin → SDK headers for consumers
# devShellInputs — Qt-specific dev shell dependencies
# common — Qt-specific utilities (deps, cmake flags, platform helpers)
# 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)
#
{ nixpkgs, logos-cpp-sdk, logos-module, lib, backendRoot }:
# The logos-cpp-sdk (generator, SDK lib, headers) is added by the caller
# (logos-module-builder) via extraNativeBuildInputs / extraBuildInputs / env.
#
{ nixpkgs, lib, backendRoot }:
let
common = import ./common.nix { inherit lib; };
@@ -19,52 +19,56 @@ let
in {
# Build a Qt plugin from sources.
# Returns: derivation with lib/{name}_plugin.so and include/ (general SDK headers)
# 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 ? {},
externalLibs ? {},
extraNativeBuildInputs ? [],
extraBuildInputs ? [],
extraCmakeFlags ? [],
extraEnv ? {},
preConfigure ? "",
postInstall ? "",
}:
let
logosSdk = logos-cpp-sdk.packages.${pkgs.system}.default;
logosModule = logos-module.packages.${pkgs.system}.default;
commonArgs = {
pname = "logos-${config.name}-module";
version = config.version;
nativeBuildInputs = common.commonNativeBuildInputs pkgs ++ [ logosSdk ] ++ extraNativeBuildInputs;
nativeBuildInputs = common.commonNativeBuildInputs pkgs ++ extraNativeBuildInputs;
buildInputs = common.commonBuildInputs pkgs ++ extraBuildInputs;
cmakeFlags = common.commonCmakeFlags { inherit logosSdk logosModule; };
cmakeFlags = common.commonCmakeFlags { inherit logosModule; } ++ extraCmakeFlags;
env = {
LOGOS_CPP_SDK_ROOT = "${logosSdk}";
LOGOS_MODULE_ROOT = "${logosModule}";
# Keep LOGOS_MODULE_BUILDER_ROOT for backward compatibility with existing CMakeLists.txt
LOGOS_MODULE_BUILDER_ROOT = "${backendRoot}";
};
} // extraEnv;
meta = with lib; {
description = config.description;
platforms = platforms.unix;
};
};
in mkBuildPlugin.build {
inherit pkgs src config commonArgs logosSdk moduleDeps externalLibs preConfigure postInstall;
inherit pkgs src config commonArgs moduleDeps externalLibs preConfigure postInstall;
logosSdk = null; # not used by buildPlugin.nix directly, kept for compat
};
# Generate SDK headers from a compiled plugin.
# Returns: derivation with include/*.h (module-specific API headers for consumers)
# This is a thin wrapper — the actual generator binary and SDK package
# are passed in by the caller.
# Returns: derivation with include/*.h
buildHeaders = {
pkgs,
src,
config,
pluginLib,
logosSdk,
}:
let
logosSdk = logos-cpp-sdk.packages.${pkgs.system}.default;
commonArgs = {
pname = "logos-${config.name}-module";
version = config.version;
@@ -78,18 +82,15 @@ in {
lib = pluginLib;
};
# Dev shell dependencies for modules using this backend.
# Returns: { nativeBuildInputs, buildInputs, shellHook }
devShellInputs = pkgs:
let
logosSdk = logos-cpp-sdk.packages.${pkgs.system}.default;
logosModule = logos-module.packages.${pkgs.system}.default;
in {
# Dev shell dependencies — Qt only.
# SDK env vars are added by the caller (logos-module-builder).
devShellInputs = pkgs: {
logosModule ? null,
}: {
nativeBuildInputs = common.commonNativeBuildInputs pkgs;
buildInputs = common.commonBuildInputs pkgs;
shellHook = ''
export LOGOS_CPP_SDK_ROOT="${logosSdk}"
export LOGOS_MODULE_ROOT="${logosModule}"
${if logosModule != null then ''export LOGOS_MODULE_ROOT="${logosModule}"'' else ""}
export LOGOS_MODULE_BUILDER_ROOT="${backendRoot}"
'';
};
+74
View File
@@ -0,0 +1,74 @@
# Integration test: build a vanilla Qt plugin using only Qt deps (no logos-cpp-sdk).
# Proves the backend can compile a .so without any Logos SDK dependency.
{ pkgs, backendCommon }:
let
pluginSrc = ./vanilla-plugin;
in
pkgs.stdenv.mkDerivation {
pname = "logos-plugin-qt-vanilla-test";
version = "0.0.1";
src = pluginSrc;
nativeBuildInputs = backendCommon.commonNativeBuildInputs pkgs;
buildInputs = backendCommon.commonBuildInputs pkgs;
dontUseCmakeConfigure = true;
buildPhase = ''
runHook preBuild
mkdir -p build
cd build
cmake .. -GNinja
ninja
cd ..
runHook postBuild
'';
doCheck = true;
checkPhase = ''
runHook preCheck
# Verify the plugin .so was created
PLUGIN="build/vanilla_test_plugin.so"
if [ ! -f "$PLUGIN" ]; then
PLUGIN="build/vanilla_test_plugin.dylib"
fi
if [ ! -f "$PLUGIN" ]; then
echo "FAIL: Plugin binary not found"
ls -la build/
exit 1
fi
echo "OK: Plugin binary exists at $PLUGIN"
echo "Size: $(stat -c%s "$PLUGIN" 2>/dev/null || stat -f%z "$PLUGIN") bytes"
# Verify it's a valid shared library
file "$PLUGIN" | grep -q "shared object\|dynamically linked\|Mach-O" || {
echo "FAIL: Plugin is not a valid shared library"
file "$PLUGIN"
exit 1
}
echo "OK: Plugin is a valid shared library"
# Verify Qt plugin metadata is embedded
if strings "$PLUGIN" | grep -q "vanilla_test"; then
echo "OK: Qt plugin metadata found in binary"
else
echo "FAIL: Qt plugin metadata not found in binary"
exit 1
fi
runHook postCheck
'';
installPhase = ''
mkdir -p $out/lib
cp build/vanilla_test_plugin.* $out/lib/ 2>/dev/null || true
echo "Vanilla Qt plugin test passed" > $out/result.txt
'';
}
+28
View File
@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.14)
project(VanillaTestPlugin LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Core)
# Copy metadata.json to build dir so Q_PLUGIN_METADATA can find it
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/metadata.json ${CMAKE_CURRENT_BINARY_DIR}/metadata.json COPYONLY)
add_library(vanilla_test_plugin SHARED
vanilla_test_plugin.h
interface.h
)
set_target_properties(vanilla_test_plugin PROPERTIES
PREFIX ""
OUTPUT_NAME "vanilla_test_plugin"
)
target_include_directories(vanilla_test_plugin PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(vanilla_test_plugin PRIVATE Qt6::Core)
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <QtPlugin>
#include <QString>
class PluginInterface {
public:
virtual ~PluginInterface() = default;
virtual QString name() const = 0;
virtual QString version() const = 0;
};
#define PluginInterface_iid "com.example.PluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
+9
View File
@@ -0,0 +1,9 @@
{
"name": "vanilla_test",
"version": "1.0.0",
"description": "Vanilla Qt plugin for testing the backend",
"type": "core",
"category": "test",
"main": "vanilla_test_plugin",
"dependencies": []
}
@@ -0,0 +1,13 @@
#pragma once
#include <QObject>
#include "interface.h"
class VanillaTestPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "metadata.json")
Q_INTERFACES(PluginInterface)
public:
QString name() const override { return "vanilla_test"; }
QString version() const override { return "1.0.0"; }
Q_INVOKABLE QString hello() { return "world"; }
};