mirror of
https://github.com/logos-co/logos-module-builder.git
synced 2026-08-30 19:51:07 +00:00
* feat: consume concrete deps via LIDL; publish a cheap lidl output
Building/packaging a module no longer builds its (universal) dependency
modules — only `#run` (the standalone app) still bundles dep plugins.
- mkLogosModule: new packages.<sys>.lidl output — a cheap codegen-only
derivation that runs `logos-cpp-generator --header-to-lidl` over the universal
impl header (no Qt/plugin compile), publishing the module's interface LIDL.
- mkLogosModule: per-dependency classification. A dep that exposes a `lidl`
output (or has a dependency_overrides entry) is LIDL-based → passed to the
backend as `staticDeps` (generated via --dep, no dep plugin build). Deps
without a `lidl` output take the TRANSITIONAL header-copy fallback
(`legacyHeaderDepNames` → resolvedModuleDeps), identical to today; isolated
and banner-commented for removal once all modules publish LIDL.
- parseMetadata: normalize `dependencies` to name strings (accept object
entries) and add optional `dependency_overrides` ({ file, input?, impl_class? })
to force a specific LIDL/header source per dep.
- buildCppPlugin (QML C++ backend): same classification + staticDeps threading,
so UI modules also stop building their universal deps.
Requires logos-cpp-sdk#77 (--header-to-lidl / --dep) and logos-plugin-qt#9
(staticDeps → --dep). A follow-up flake.lock bump pins those once merged
(mirrors the interface-deps plugin-core bump, #109); local builds use
`ws ... --auto-local`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* address review: guard depLidlOf for non-flake deps; add parse-metadata tests
- depLidlOf now explicitly guards `packages`/`<system>` so a raw-derivation
dependency input returns null (→ transitional header-copy fallback) rather
than risking a throw. Applied in mkLogosModule.nix and buildCppPlugin.nix.
- test-parse-metadata.nix: cover `dependencies` object-entry normalization and
`dependency_overrides` parsing/validation (file required; .h needs impl_class).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump logos-cpp-sdk + logos-plugin-qt/core to the deps-via-LIDL backends
Pin the merged --header-to-lidl/--dep generator (logos-cpp-sdk#77) and the
staticDeps-aware Qt backend (logos-plugin-qt#9) so this module builder's LIDL
dependency consumption works against published inputs (not just --auto-local).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
496 lines
22 KiB
Nix
496 lines
22 KiB
Nix
# Core module builder function
|
|
# This is the main entry point for building Logos modules.
|
|
# Plugin compilation and header generation are delegated to a backend selected
|
|
# by metadata.json "type": core modules use coreBackend, UI modules use uiBackend.
|
|
{ nixpkgs, lib, common, parseMetadata, builderRoot, uiBackend, coreBackend, logos-cpp-sdk, logos-module, logos-test-framework, nix-bundle-lgx, nix-bundle-logos-module-install, logos-standalone-app }:
|
|
|
|
{
|
|
# Required: Path to the module source
|
|
src,
|
|
|
|
# Required: Path to the metadata.json configuration file
|
|
configFile,
|
|
|
|
# Optional: all flake inputs — dependencies in metadata.json are resolved automatically
|
|
flakeInputs ? {},
|
|
|
|
# Optional: Additional flake inputs for external libraries
|
|
externalLibInputs ? {},
|
|
|
|
# Optional: Extra build inputs to add
|
|
extraBuildInputs ? [],
|
|
|
|
# Optional: Extra native build inputs to add
|
|
extraNativeBuildInputs ? [],
|
|
|
|
# Optional: Override any config values
|
|
configOverrides ? {},
|
|
|
|
# Optional: Custom preConfigure hook
|
|
preConfigure ? "",
|
|
|
|
# Optional: Custom postInstall hook
|
|
postInstall ? "",
|
|
|
|
# Optional: override the logos-standalone-app used for `nix run`.
|
|
# By default, UI modules (type = "ui") automatically get apps.default wired up
|
|
# using the standalone app bundled with logos-module-builder.
|
|
logosStandalone ? null,
|
|
|
|
# Optional: Unit test configuration. When provided, a checks.<system>.unit-tests
|
|
# output is automatically generated using logos-test-framework.
|
|
# tests = {
|
|
# dir = ./tests; # Required: directory containing test sources + CMakeLists.txt
|
|
# mockCLibs = []; # Optional: C libraries to mock at link time
|
|
# preConfigure = ""; # Optional: custom preConfigure hook
|
|
# extraBuildInputs = [];
|
|
# extraCmakeFlags = [];
|
|
# };
|
|
tests ? null,
|
|
}:
|
|
|
|
let
|
|
# Parse the module configuration
|
|
rawConfig = parseMetadata.parseModuleConfig (builtins.readFile configFile);
|
|
config = common.recursiveMerge [ rawConfig configOverrides ];
|
|
|
|
# Select backend based on module type: core modules are swappable, UI stays Qt
|
|
selectedBackend =
|
|
if config.type == "core" then coreBackend
|
|
else uiBackend;
|
|
|
|
# Import sub-builders (backend-agnostic)
|
|
mkExternalLib = import ./mkExternalLib.nix { inherit lib common; };
|
|
mkStandaloneApp = import ./mkStandaloneApp.nix;
|
|
modulePreConfigure = import ./modulePreConfigure.nix { inherit lib; };
|
|
|
|
# When this flake ships cmake/LogosModule.cmake, override LOGOS_MODULE_BUILDER_ROOT
|
|
# so the extended macros (generated_code glob, metadata copy, Go static libs) are used.
|
|
# Otherwise let the backend's default take over — it already sets
|
|
# LOGOS_MODULE_BUILDER_ROOT to its own root which has cmake/LogosModule.cmake.
|
|
hasBuilderCmake = builtins.pathExists (builderRoot + "/cmake/LogosModule.cmake");
|
|
|
|
# Helper to get a package from nixpkgs by name
|
|
getPkg = pkgs: name:
|
|
let evaluatedName = builtins.seq name name;
|
|
in if builtins.isString evaluatedName
|
|
then lib.getAttrFromPath (lib.splitString "." evaluatedName) pkgs
|
|
else builtins.throw "getPkg expected string but got ${builtins.typeOf evaluatedName}";
|
|
|
|
forAllSystems = f: lib.genAttrs common.systems (system: f system);
|
|
|
|
# Package outputs
|
|
packages = forAllSystems (system:
|
|
let
|
|
pkgs = import nixpkgs { inherit system; };
|
|
|
|
# ── Concrete dependency classification ─────────────────────────────────
|
|
# A dependency's typed `modules().<dep>` wrapper is generated from its
|
|
# published LIDL contract (`packages.<sys>.lidl`) WITHOUT building the
|
|
# dep's plugin. Deps that don't expose a `lidl` output yet take the
|
|
# TRANSITIONAL header-copy fallback (`legacyHeaderDepNames`), which DOES
|
|
# build them — identical to today's behavior.
|
|
# Returns the dep's published LIDL output, or null if the input isn't a
|
|
# flake exposing packages.<system>.lidl (e.g. a raw-derivation dep, or a
|
|
# module built by a builder that predates this feature) — those fall
|
|
# through to the TRANSITIONAL header-copy path. Guard every level so a
|
|
# non-flake input never throws.
|
|
depLidlOf = name:
|
|
let i = flakeInputs.${name} or null;
|
|
in if i != null && i ? packages && i.packages ? ${system}
|
|
then (i.packages.${system}.lidl or null)
|
|
else null;
|
|
depIsLidl = name: (config.dependency_overrides ? ${name}) || (depLidlOf name != null);
|
|
|
|
# LIDL-based deps → `--dep <name>=<lidl>` for the generator. An override
|
|
# forces a specific definition (.lidl, or .h + impl_class); otherwise we
|
|
# use the dep's published `lidl` output.
|
|
staticDeps = map (name:
|
|
let ov = config.dependency_overrides.${name} or null;
|
|
in if ov != null then {
|
|
inherit name;
|
|
impl_class = ov.impl_class;
|
|
path = if ov.input != null
|
|
then (if flakeInputs ? ${ov.input}
|
|
then "${flakeInputs.${ov.input}}/${ov.file}"
|
|
else throw "dependency_overrides.${name}: flake input '${ov.input}' was not passed to mkLogosModule.")
|
|
else "${src}/${ov.file}";
|
|
} else {
|
|
inherit name;
|
|
impl_class = null;
|
|
path = "${depLidlOf name}/${name}.lidl";
|
|
}
|
|
) (lib.filter depIsLidl config.dependencies);
|
|
|
|
# TRANSITIONAL: header-copy fallback for deps that predate the `lidl`
|
|
# output. These deps ARE built (their headers come from introspecting the
|
|
# compiled plugin). Remove this block — and the `moduleDepIncludes` use in
|
|
# the plugin backends — once every module exposes packages.<sys>.lidl.
|
|
legacyHeaderDepNames = lib.filter (name: !(depIsLidl name)) config.dependencies;
|
|
|
|
# Resolve the fallback deps from inputs. Each entry is exposed
|
|
# as a struct so the plugin builder can pick BOTH the dep's
|
|
# plugin .dylib AND the right header variant for its own
|
|
# --api-style without re-running the codegen at consume time.
|
|
# Backward-compatible fallbacks let older deps (which only
|
|
# expose `default`) still work — they get treated as Qt-typed.
|
|
moduleInputs = lib.filterAttrs (n: _: builtins.elem n legacyHeaderDepNames) flakeInputs;
|
|
resolvedModuleDeps = lib.mapAttrs (_: input:
|
|
let
|
|
ps = input.packages.${system} or null;
|
|
# Pre-version of this refactor: input was the raw flake-output
|
|
# derivation (not a packages set). Preserve that path so an
|
|
# external flake-input dep still works.
|
|
fallback = if input ? packages.${system}.default
|
|
then input.packages.${system}.default else input;
|
|
in
|
|
if ps != null then {
|
|
default = ps.default;
|
|
lib = ps.lib or ps.default;
|
|
headers-qt = ps.headers-qt or ps.include or ps.default;
|
|
headers-std = ps.headers-std or ps.headers-qt or ps.include or ps.default;
|
|
} else {
|
|
default = fallback;
|
|
lib = fallback;
|
|
headers-qt = fallback;
|
|
headers-std = fallback;
|
|
}
|
|
) moduleInputs;
|
|
|
|
# Resolve interface dependencies (method/event contracts) to concrete
|
|
# definition-file paths. A LOCAL interface lives in this repo's `src`;
|
|
# a REMOTE one comes from a flake input named by `input` — mirroring
|
|
# how `dependencies` resolve to flake inputs. We resolve the path here
|
|
# so the generator never touches flake inputs: it just receives
|
|
# `--interface <name>=<path>[=<impl_class>]`. (System-independent, but
|
|
# kept in this scope alongside resolvedModuleDeps for locality.)
|
|
resolvedInterfaceDeps = map (e: {
|
|
inherit (e) name impl_class;
|
|
path = if e.input != null
|
|
then (if flakeInputs ? ${e.input}
|
|
then "${flakeInputs.${e.input}}/${e.file}"
|
|
else throw "interface_dependencies: interface '${e.name}' references flake input '${e.input}', but no such input was passed to mkLogosModule (declare it in flake.nix and pass it via flakeInputs).")
|
|
else "${src}/${e.file}";
|
|
}) config.interface_dependencies;
|
|
|
|
# Resolve a single externalLibInputs entry for a given variant.
|
|
# Supports both simple (bare flake input) and structured ({ input, packages }) formats.
|
|
resolveExtInput = variant: name: value:
|
|
if builtins.isAttrs value && value ? input then
|
|
let
|
|
flakeInput = value.input;
|
|
packages = value.packages or {};
|
|
pkgName = packages.${variant} or packages.default or "default";
|
|
in
|
|
if flakeInput ? packages.${system}.${pkgName}
|
|
then flakeInput.packages.${system}.${pkgName}
|
|
else builtins.throw ''
|
|
External lib "${name}": flake input does not provide packages.${system}.${pkgName}.
|
|
Check the "externalLibInputs" structured entry and ensure the flake input exposes the expected package.
|
|
''
|
|
else
|
|
if value ? packages.${system}.default then value.packages.${system}.default else value;
|
|
|
|
# Whether any external lib input declares per-variant packages
|
|
hasVariants = lib.any (v: builtins.isAttrs v && v ? input && v ? packages)
|
|
(lib.attrValues externalLibInputs);
|
|
|
|
buildPkgs = map (getPkg pkgs) (lib.filter builtins.isString config.nix_packages.build);
|
|
runtimePkgs = map (getPkg pkgs) (lib.filter builtins.isString config.nix_packages.runtime);
|
|
|
|
# Pre-resolve default variant external libs (always needed, avoids
|
|
# duplicate evaluation when hasVariants triggers a second buildVariant).
|
|
defaultResolvedExternalLibs = lib.mapAttrs (resolveExtInput "default") externalLibInputs;
|
|
defaultExternalLibs = mkExternalLib.buildExternalLibs {
|
|
inherit pkgs config;
|
|
externalInputs = defaultResolvedExternalLibs;
|
|
};
|
|
|
|
# Resolve SDK deps for this system — injected into the backend
|
|
logosSdk = logos-cpp-sdk.packages.${system}.default;
|
|
logosModule = logos-module.packages.${system}.default;
|
|
|
|
# Build the plugin for a given external-lib variant ("default" or "portable")
|
|
buildVariant = variant:
|
|
let
|
|
externalLibs =
|
|
if variant == "default" then defaultExternalLibs
|
|
else mkExternalLib.buildExternalLibs {
|
|
inherit pkgs config;
|
|
externalInputs = lib.mapAttrs (resolveExtInput variant) externalLibInputs;
|
|
};
|
|
|
|
userPreConfigure =
|
|
if builtins.isFunction preConfigure
|
|
then preConfigure { inherit externalLibs; }
|
|
else preConfigure;
|
|
|
|
preConfigureStr = modulePreConfigure.compose {
|
|
inherit config externalLibs;
|
|
userPre = userPreConfigure;
|
|
fixDarwin = false;
|
|
# logos-plugin-qt buildPlugin already stages external libs into lib/
|
|
copyExternals = false;
|
|
};
|
|
|
|
goCmakeFlags = lib.optionals (config.go_static_lib_names != []) [
|
|
"-DLOGOS_MODULE_GO_STATIC_LIBS=${lib.concatStringsSep ";" config.go_static_lib_names}"
|
|
];
|
|
|
|
# LOGOS_API_STYLE forwards through to logos-cpp-generator and
|
|
# picks which type surface the generated <Module> client
|
|
# wrappers (and the umbrella LogosModules struct) expose.
|
|
# Universal modules — those whose impl is pure C++ and the
|
|
# codegen wraps it in Qt glue — get std-typed wrappers so
|
|
# they can call other modules without touching Qt at the
|
|
# call site. Every other interface (legacy, provider, or
|
|
# absent) keeps the historical Qt-typed wrappers.
|
|
apiStyleCmakeFlags = lib.optionals (config.interface == "universal") [
|
|
"-DLOGOS_API_STYLE=std"
|
|
];
|
|
# Delegate plugin compilation to the backend.
|
|
# The backend only knows about Qt + logosModule (interface.h).
|
|
# SDK (generator, lib, headers) is injected via extra* args.
|
|
in selectedBackend.buildPlugin ({
|
|
inherit pkgs src config postInstall logosModule;
|
|
preConfigure = preConfigureStr;
|
|
moduleDeps = resolvedModuleDeps;
|
|
inherit externalLibs;
|
|
extraNativeBuildInputs = extraNativeBuildInputs ++ buildPkgs ++ [ logosSdk ];
|
|
extraBuildInputs = extraBuildInputs ++ runtimePkgs;
|
|
extraCmakeFlags = [ "-DLOGOS_CPP_SDK_ROOT=${logosSdk}" ] ++ goCmakeFlags ++ apiStyleCmakeFlags;
|
|
extraEnv = {
|
|
LOGOS_CPP_SDK_ROOT = "${logosSdk}";
|
|
} // lib.optionalAttrs hasBuilderCmake {
|
|
LOGOS_MODULE_BUILDER_ROOT = "${builderRoot}";
|
|
};
|
|
}
|
|
# Only pass interfaceDeps when the module declares any — keeps existing
|
|
# dependency-only modules buildable against a backend that predates the
|
|
# interface-dependencies feature (graceful degradation).
|
|
// lib.optionalAttrs (config.interface_dependencies != []) {
|
|
interfaceDeps = resolvedInterfaceDeps;
|
|
}
|
|
# LIDL-based concrete deps → `--dep` flags (generate from the dep's
|
|
# published LIDL, no dep plugin build). Gated so a backend that predates
|
|
# this feature still builds (such deps then fall through unresolved).
|
|
// lib.optionalAttrs (staticDeps != []) {
|
|
inherit staticDeps;
|
|
});
|
|
|
|
moduleLib = buildVariant "default";
|
|
moduleLibPortable = if hasVariants then buildVariant "portable" else null;
|
|
|
|
# Two header variants per module — one Qt-typed, one std-typed.
|
|
# Each is its own Nix derivation, so a downstream module only
|
|
# realises the one its `--api-style` actually consumes. Default-
|
|
# output (`include`) stays pointed at the Qt variant for
|
|
# backward compatibility with consumers that read `${dep}/include`
|
|
# directly.
|
|
moduleIncludeQt = selectedBackend.buildHeaders {
|
|
inherit pkgs src config logosSdk;
|
|
pluginLib = moduleLib;
|
|
apiStyle = "qt";
|
|
};
|
|
moduleIncludeStd = selectedBackend.buildHeaders {
|
|
inherit pkgs src config logosSdk;
|
|
pluginLib = moduleLib;
|
|
apiStyle = "std";
|
|
};
|
|
|
|
# Publish this module's interface as LIDL — the language-neutral contract
|
|
# a consumer turns into typed `modules().<name>` bindings WITHOUT building
|
|
# this module's plugin (source → LIDL → C++). Cheap: runs only the C++
|
|
# frontend (`--header-to-lidl`) over the impl header; no Qt/plugin compile.
|
|
# Produced for universal modules; the impl header + class come from the
|
|
# same convention `universalCodegen` uses (`codegen.impl_*` or defaults).
|
|
lidlImplClass = config.codegen.impl_class or (modulePreConfigure.defaultImplClassFromName config.name);
|
|
lidlIhRaw = config.codegen.impl_header or "${config.name}_impl.h";
|
|
lidlImplHeaderRel = if lib.hasInfix "/" lidlIhRaw then lidlIhRaw else "src/${lidlIhRaw}";
|
|
moduleLidl =
|
|
if config.interface == "universal"
|
|
then pkgs.runCommand "logos-${config.name}-lidl" {
|
|
nativeBuildInputs = [ logosSdk ];
|
|
} ''
|
|
mkdir -p $out
|
|
logos-cpp-generator --header-to-lidl "${src}/${lidlImplHeaderRel}" \
|
|
--impl-class "${lidlImplClass}" \
|
|
--metadata "${configFile}" \
|
|
-o "$out/${config.name}.lidl"
|
|
''
|
|
else null;
|
|
|
|
# Combined package — copies the Qt-typed headers (backward
|
|
# compat). The `//` merge exposes src + version on the derivation
|
|
# so downstream bundlers (nix-bundle-lgx) can locate metadata.json.
|
|
combined = (pkgs.runCommand "logos-${config.name}-module" {} ''
|
|
mkdir -p $out/lib $out/include
|
|
|
|
# Copy library files (not symlinks)
|
|
if [ -d "${moduleLib}/lib" ]; then
|
|
cp -rL ${moduleLib}/lib/* $out/lib/
|
|
fi
|
|
|
|
# Copy include files (not symlinks) — use find to avoid nullglob issues
|
|
if [ -d "${moduleIncludeQt}/include" ] && [ -n "$(find ${moduleIncludeQt}/include -maxdepth 1 -not -name '.*' -not -path ${moduleIncludeQt}/include -print -quit)" ]; then
|
|
cp -rL ${moduleIncludeQt}/include/* $out/include/
|
|
fi
|
|
'') // { inherit src; version = config.version; };
|
|
|
|
in {
|
|
# Individual outputs (e.g., nix build .#chat-lib)
|
|
"${config.name}-lib" = moduleLib;
|
|
"${config.name}-include" = moduleIncludeQt;
|
|
"${config.name}-headers-qt" = moduleIncludeQt;
|
|
"${config.name}-headers-std" = moduleIncludeStd;
|
|
|
|
# Short aliases (e.g., nix build .#lib)
|
|
lib = moduleLib;
|
|
include = moduleIncludeQt;
|
|
headers-qt = moduleIncludeQt;
|
|
headers-std = moduleIncludeStd;
|
|
|
|
# Default package - combined lib + include (nix build)
|
|
default = combined;
|
|
} // lib.optionalAttrs (moduleLibPortable != null) {
|
|
"${config.name}-lib-portable" = moduleLibPortable;
|
|
lib-portable = moduleLibPortable;
|
|
} // lib.optionalAttrs (moduleLidl != null) {
|
|
# Published LIDL contract — consumers generate bindings from this without
|
|
# building the plugin. Cheap (frontend only). Absent for non-universal
|
|
# modules, so consumers fall back to the header-copy path for those.
|
|
"${config.name}-lidl" = moduleLidl;
|
|
lidl = moduleLidl;
|
|
}
|
|
);
|
|
|
|
# Development shell (delegates to backend for deps)
|
|
devShells = forAllSystems (system:
|
|
let
|
|
pkgs = import nixpkgs { inherit system; };
|
|
logosSdk = logos-cpp-sdk.packages.${system}.default;
|
|
logosModule = logos-module.packages.${system}.default;
|
|
backendShell = selectedBackend.devShellInputs pkgs { inherit logosModule; };
|
|
buildPkgs = map (getPkg pkgs) config.nix_packages.build;
|
|
runtimePkgs = map (getPkg pkgs) config.nix_packages.runtime;
|
|
in {
|
|
default = pkgs.mkShell {
|
|
nativeBuildInputs = backendShell.nativeBuildInputs ++ buildPkgs ++ [ logosSdk ];
|
|
buildInputs = backendShell.buildInputs ++ runtimePkgs;
|
|
shellHook = ''
|
|
${backendShell.shellHook}
|
|
export LOGOS_CPP_SDK_ROOT="${logosSdk}"
|
|
${lib.optionalString hasBuilderCmake ''export LOGOS_MODULE_BUILDER_ROOT="${builderRoot}"''}
|
|
echo "Logos ${config.name} module development environment"
|
|
echo "LOGOS_CPP_SDK_ROOT: $LOGOS_CPP_SDK_ROOT"
|
|
echo "LOGOS_MODULE_ROOT: $LOGOS_MODULE_ROOT"
|
|
echo "LOGOS_MODULE_BUILDER_ROOT: $LOGOS_MODULE_BUILDER_ROOT"
|
|
'';
|
|
};
|
|
}
|
|
);
|
|
|
|
# LGX package outputs (nix-bundle-lgx provided by the builder)
|
|
nixBundleLgx = nix-bundle-lgx;
|
|
|
|
optionalLgx =
|
|
{
|
|
packages = forAllSystems (system:
|
|
let
|
|
bundleLgx = nixBundleLgx.bundlers.${system}.default;
|
|
bundleLgxPortable = nixBundleLgx.bundlers.${system}.portable;
|
|
installDev = nix-bundle-logos-module-install.bundlers.${system}.dev;
|
|
installPortable = nix-bundle-logos-module-install.bundlers.${system}.portable;
|
|
moduleLib = packages.${system}.lib;
|
|
# Use the portable-linked plugin for lgx-portable when available
|
|
moduleLibForPortable =
|
|
packages.${system}.lib-portable or moduleLib;
|
|
in {
|
|
lgx = bundleLgx moduleLib;
|
|
install = installDev moduleLib;
|
|
lgx-portable = bundleLgxPortable moduleLibForPortable;
|
|
install-portable = installPortable moduleLibForPortable;
|
|
}
|
|
);
|
|
};
|
|
|
|
# Resolve the standalone app: explicit override > built-in from module-builder
|
|
resolvedStandalone =
|
|
if logosStandalone != null then logosStandalone
|
|
else if config.type == "ui" then logos-standalone-app
|
|
else null;
|
|
|
|
optionalApps =
|
|
if resolvedStandalone == null then {}
|
|
else {
|
|
apps = forAllSystems (system:
|
|
let
|
|
pkgs = import nixpkgs { inherit system; };
|
|
# Collect all module dependencies (direct + transitive) for bundling
|
|
allDeps = common.collectAllModuleDeps system flakeInputs config.dependencies;
|
|
in {
|
|
default = mkStandaloneApp {
|
|
inherit pkgs;
|
|
standalone = resolvedStandalone.packages.${system}.default;
|
|
plugin = packages.${system}.default;
|
|
metadataFile = configFile;
|
|
dirName = "logos-${config.name}-plugin-dir";
|
|
format = "qt-plugin";
|
|
moduleDeps = allDeps;
|
|
};
|
|
}
|
|
);
|
|
};
|
|
|
|
# Merge LGX outputs into packages
|
|
mergedPackages = lib.mapAttrs (system: sysPkgs:
|
|
sysPkgs // (optionalLgx.packages.${system} or {})
|
|
) packages;
|
|
|
|
# Build unit tests — explicit config wins, otherwise auto-detect tests/CMakeLists.txt
|
|
mkTests = import ./mkLogosModuleTests.nix {
|
|
inherit nixpkgs lib common parseMetadata;
|
|
inherit logos-cpp-sdk;
|
|
logos-test-framework = logos-test-framework;
|
|
};
|
|
|
|
resolvedTests =
|
|
if tests != null then tests
|
|
else if builtins.pathExists (src + "/tests/CMakeLists.txt") then {
|
|
dir = src + "/tests";
|
|
}
|
|
else null;
|
|
|
|
testChecks =
|
|
if resolvedTests == null then {}
|
|
else mkTests {
|
|
inherit src flakeInputs externalLibInputs;
|
|
configFile = configFile;
|
|
testDir = resolvedTests.dir;
|
|
mockCLibs = resolvedTests.mockCLibs or [];
|
|
preConfigure = resolvedTests.preConfigure or preConfigure;
|
|
extraBuildInputs = resolvedTests.extraBuildInputs or [];
|
|
extraCmakeFlags = resolvedTests.extraCmakeFlags or [];
|
|
};
|
|
|
|
optionalTests =
|
|
if testChecks == {} then {}
|
|
else { checks = testChecks; };
|
|
|
|
# Also expose unit-tests as a package so `nix build .#unit-tests` works
|
|
testPackages =
|
|
if testChecks == {} then {}
|
|
else lib.mapAttrs (_system: sysChecks:
|
|
{ unit-tests = sysChecks.unit-tests; }
|
|
) testChecks;
|
|
|
|
finalPackages = lib.mapAttrs (system: sysPkgs:
|
|
sysPkgs // (testPackages.${system} or {})
|
|
) mergedPackages;
|
|
|
|
in {
|
|
packages = finalPackages;
|
|
inherit devShells config;
|
|
metadataJson = builtins.readFile configFile;
|
|
} // optionalApps // optionalTests
|