From 0ad68c252ea3db2af1fde4b638d9dfa732eb37f0 Mon Sep 17 00:00:00 2001 From: osmaczko <33099791+osmaczko@users.noreply.github.com> Date: Fri, 31 Jul 2026 01:17:20 +0200 Subject: [PATCH] fix: read dependency entries declared in object form The manifest schema lets a dependency entry be an object carrying the name alongside the constraints an installer resolves it by, but every reader took the element as a plain string and skipped what came back empty, so an object entry disappeared: the module it names was left out of the generated LogosModules aggregate, and every call through it failed to compile. The rule lives in one place now, since the copies of it are how the gap spread. It ships in share/lidl-frontend alongside the parser that includes it, which consumers compile from there. --- .../experimental/impl_header_parser.cpp | 9 ++++-- cpp-generator/legacy/main.cpp | 30 ++++++++++--------- cpp-generator/metadata_dependencies.h | 24 +++++++++++++++ nix/bin.nix | 1 + .../fixtures/object_deps_metadata.json | 15 ++++++++++ .../experimental/test_impl_header_parser.cpp | 19 ++++++++++++ 6 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 cpp-generator/metadata_dependencies.h create mode 100644 tests/experimental/fixtures/object_deps_metadata.json diff --git a/cpp-generator/experimental/impl_header_parser.cpp b/cpp-generator/experimental/impl_header_parser.cpp index d129016..b52d3e3 100644 --- a/cpp-generator/experimental/impl_header_parser.cpp +++ b/cpp-generator/experimental/impl_header_parser.cpp @@ -1,5 +1,7 @@ #include "impl_header_parser.h" +#include "metadata_dependencies.h" + #include #include #include @@ -393,8 +395,11 @@ ImplParseResult parseImplHeader(const QString& headerPath, result.module.description = obj.value("description").toString().toStdString(); result.module.category = obj.value("category").toString().toStdString(); QJsonArray deps = obj.value("dependencies").toArray(); - for (const QJsonValue& v : deps) - result.module.depends.push_back(v.toString().toStdString()); + for (const QJsonValue& v : deps) { + const QString depName = dependencyName(v); + if (!depName.isEmpty()) + result.module.depends.push_back(depName.toStdString()); + } // Read events declared in metadata.json QJsonArray events = obj.value("events").toArray(); diff --git a/cpp-generator/legacy/main.cpp b/cpp-generator/legacy/main.cpp index 1e0ec16..de0b366 100644 --- a/cpp-generator/legacy/main.cpp +++ b/cpp-generator/legacy/main.cpp @@ -16,6 +16,7 @@ #include #include "logos_provider_interface.h" #include "generator_lib.h" +#include "metadata_dependencies.h" #include "../experimental/lidl_compat.h" #include "../experimental/impl_header_parser.h" #include "../experimental/lidl_emit_common.h" // lidlTypeToQt — the one Qt type mapper @@ -464,15 +465,16 @@ static bool writeUmbrellaHeaderFromDeps(const QString& genDirPath, const QJsonAr s << " LogosModules()"; bool first = true; for (const QJsonValue& v : deps) { - if (!v.isString()) continue; + const QString depName = dependencyName(v); + if (depName.isEmpty()) continue; s << (first ? " : " : ",\n "); first = false; - s << v.toString() << "(\"" << originName << "\")"; + s << depName << "(\"" << originName << "\")"; } s << " {}\n"; for (const QJsonValue& v : deps) { - if (!v.isString()) continue; - const QString depName = v.toString(); + const QString depName = dependencyName(v); + if (depName.isEmpty()) continue; s << " " << toPascalCase(depName) << " " << depName << ";\n"; } // Interface dependencies: bound at runtime. The bound wrapper is a @@ -531,8 +533,8 @@ static bool writeUmbrellaHeaderFromDeps(const QString& genDirPath, const QJsonAr s << "#include \"logos_api.h\"\n"; s << "#include \"logos_api_client.h\"\n\n"; for (const QJsonValue& v : deps) { - if (!v.isString()) continue; - QString depName = v.toString(); + const QString depName = dependencyName(v); + if (depName.isEmpty()) continue; s << "#include \"" << depName << "_api.h\"\n"; } for (const QString& ifaceName : interfaceNames) { @@ -543,15 +545,15 @@ static bool writeUmbrellaHeaderFromDeps(const QString& genDirPath, const QJsonAr s << "struct LogosModules {\n"; s << " explicit LogosModules(LogosAPI* api) : api(api)"; for (const QJsonValue& v : deps) { - if (!v.isString()) continue; - QString depName = v.toString(); + const QString depName = dependencyName(v); + if (depName.isEmpty()) continue; s << ", \n " << depName << "(api)"; } s << " {}\n"; s << " LogosAPI* api;\n"; for (const QJsonValue& v : deps) { - if (!v.isString()) continue; - QString depName = v.toString(); + const QString depName = dependencyName(v); + if (depName.isEmpty()) continue; QString className = toPascalCase(depName); s << " " << className << " " << depName << ";\n"; } @@ -621,8 +623,8 @@ static bool writeUmbrellaSourceFromDeps(const QString& genDirPath, const QJsonAr QTextStream s(&content); s << "#include \"logos_sdk.h\"\n\n"; for (const QJsonValue& v : deps) { - if (!v.isString()) continue; - QString depName = v.toString(); + const QString depName = dependencyName(v); + if (depName.isEmpty()) continue; s << "#include \"" << depName << "_api.cpp\"\n"; } for (const QString& ifaceName : interfaceNames) { @@ -1174,8 +1176,8 @@ int legacy_main(int argc, char* argv[]) int overallStatus = 0; for (const QJsonValue& v : deps) { - if (!v.isString()) continue; - const QString depName = v.toString(); + const QString depName = dependencyName(v); + if (depName.isEmpty()) continue; const QString pluginFileName = depName + "_plugin" + suffix; const QString pluginPath = moduleDir.filePath(pluginFileName); if (!QFileInfo::exists(pluginPath)) { diff --git a/cpp-generator/metadata_dependencies.h b/cpp-generator/metadata_dependencies.h new file mode 100644 index 0000000..91781e3 --- /dev/null +++ b/cpp-generator/metadata_dependencies.h @@ -0,0 +1,24 @@ +#ifndef METADATA_DEPENDENCIES_H +#define METADATA_DEPENDENCIES_H + +#include +#include +#include + +/// The module named by one `metadata.json` `dependencies[]` element. +/// +/// An element is either a bare name or an object holding that name alongside +/// the constraints an installer resolves it by (version range, signer DID); +/// generation needs the name only. Empty for an element that names nothing. +inline QString dependencyName(const QJsonValue& entry) +{ + if (entry.isString()) { + return entry.toString(); + } + if (entry.isObject()) { + return entry.toObject().value("name").toString(); + } + return QString(); +} + +#endif // METADATA_DEPENDENCIES_H diff --git a/nix/bin.nix b/nix/bin.nix index 526700a..8397422 100644 --- a/nix/bin.nix +++ b/nix/bin.nix @@ -46,6 +46,7 @@ pkgs.stdenv.mkDerivation { cp cpp-generator/experimental/lidl_compat.h \ cpp-generator/experimental/impl_header_parser.h cpp-generator/experimental/impl_header_parser.cpp \ cpp-generator/experimental/lidl_emit_common.h cpp-generator/experimental/lidl_emit_common.cpp \ + cpp-generator/metadata_dependencies.h \ $out/share/lidl-frontend/ runHook postInstall diff --git a/tests/experimental/fixtures/object_deps_metadata.json b/tests/experimental/fixtures/object_deps_metadata.json new file mode 100644 index 0000000..b151b18 --- /dev/null +++ b/tests/experimental/fixtures/object_deps_metadata.json @@ -0,0 +1,15 @@ +{ + "name": "sample_module", + "version": "1.2.3", + "description": "A sample module whose dependencies carry resolution constraints", + "author": "Test", + "type": "core", + "category": "testing", + "main": "sample_module_plugin", + "dependencies": [ + "dep_a", + { "name": "dep_b", "version": "=1.2.3" }, + { "name": "dep_c", "version": "^2.0", "signer": "did:jwk:abc" }, + {} + ] +} diff --git a/tests/experimental/test_impl_header_parser.cpp b/tests/experimental/test_impl_header_parser.cpp index bdfca02..d405afc 100644 --- a/tests/experimental/test_impl_header_parser.cpp +++ b/tests/experimental/test_impl_header_parser.cpp @@ -64,6 +64,25 @@ TEST_F(ImplHeaderParserTest, ParsesSampleImpl) EXPECT_GE(r.module.methods.size(), 10); } +// A dependency entry may carry the constraints an installer resolves it by, +// and generation still needs the name. Read as a plain string, an object entry +// came back empty and the module it names vanished from the generated +// LogosModules aggregate, so every call through it failed to compile. +TEST_F(ImplHeaderParserTest, ReadsDependenciesDeclaredInObjectForm) +{ + auto r = parseImplHeader( + fixturesDir() + "/sample_impl.h", + "SampleModuleImpl", + fixturesDir() + "/object_deps_metadata.json", + err); + ASSERT_FALSE(r.hasError()) << r.error.toStdString(); + + ASSERT_EQ(r.module.depends.size(), 3); + EXPECT_EQ(r.module.depends[0], "dep_a"); + EXPECT_EQ(r.module.depends[1], "dep_b"); + EXPECT_EQ(r.module.depends[2], "dep_c"); +} + TEST_F(ImplHeaderParserTest, MethodTypes) { auto r = parseImplHeader(