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(