#include "lidl_gen_provider.h" #include "lidl_gen_client.h" // lidlToPascalCase, lidlTypeToQt #include "lidl_parser.h" #include "lidl_serializer.h" #include "lidl_validator.h" #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // Type mapping: LIDL → C++ std types // --------------------------------------------------------------------------- bool lidlIsStdConvertible(const TypeExpr& te) { if (te.kind == TypeExpr::Primitive) { return te.name == "tstr" || te.name == "bstr" || te.name == "int" || te.name == "uint" || te.name == "float64" || te.name == "bool"; } if (te.kind == TypeExpr::Array && te.elements.size() == 1) { const TypeExpr& elem = te.elements[0]; if (elem.kind == TypeExpr::Primitive) { return elem.name == "tstr" || elem.name == "bstr" || elem.name == "int" || elem.name == "uint" || elem.name == "float64" || elem.name == "bool"; } } return false; } QString lidlTypeToStd(const TypeExpr& te) { if (te.kind == TypeExpr::Primitive) { if (te.name == "tstr") return "std::string"; if (te.name == "bstr") return "std::vector"; if (te.name == "int") return "int64_t"; if (te.name == "uint") return "uint64_t"; if (te.name == "float64") return "double"; if (te.name == "bool") return "bool"; if (te.name == "result") return "LogosResult"; if (te.name == "any") return "QVariant"; return "QVariant"; } if (te.kind == TypeExpr::Array && te.elements.size() == 1) { const TypeExpr& elem = te.elements[0]; if (elem.kind == TypeExpr::Primitive) { if (elem.name == "tstr") return "std::vector"; if (elem.name == "bstr") return "std::vector>"; if (elem.name == "int") return "std::vector"; if (elem.name == "uint") return "std::vector"; if (elem.name == "float64") return "std::vector"; if (elem.name == "bool") return "std::vector"; } return "QVariantList"; } if (te.kind == TypeExpr::Map) return "QVariantMap"; if (te.kind == TypeExpr::Optional) return "QVariant"; if (te.kind == TypeExpr::Named) return "QVariant"; return "QVariant"; } // --------------------------------------------------------------------------- // Conversion helpers: Qt type ↔ std type // --------------------------------------------------------------------------- static QString qtParamToStd(const TypeExpr& te, const QString& paramName) { if (!lidlIsStdConvertible(te)) return paramName; if (te.kind == TypeExpr::Primitive) { if (te.name == "tstr") return paramName + ".toStdString()"; if (te.name == "bstr") return "std::vector(" + paramName + ".begin(), " + paramName + ".end())"; if (te.name == "int") return "static_cast(" + paramName + ")"; if (te.name == "uint") return "static_cast(" + paramName + ")"; return paramName; } if (te.kind == TypeExpr::Array && te.elements.size() == 1) { const TypeExpr& elem = te.elements[0]; if (elem.kind == TypeExpr::Primitive && elem.name == "tstr") return "lidlToStdStringVector(" + paramName + ")"; return "lidlToStdVector_" + elem.name + "(" + paramName + ")"; } return paramName; } static QString stdReturnToQt(const TypeExpr& te, const QString& varName) { if (!lidlIsStdConvertible(te)) return varName; if (te.kind == TypeExpr::Primitive) { if (te.name == "tstr") return "QString::fromStdString(" + varName + ")"; if (te.name == "bstr") return "QByteArray(reinterpret_cast(" + varName + ".data()), static_cast(" + varName + ".size()))"; if (te.name == "int") return "static_cast(" + varName + ")"; if (te.name == "uint") return "static_cast(" + varName + ")"; return varName; } if (te.kind == TypeExpr::Array && te.elements.size() == 1) { const TypeExpr& elem = te.elements[0]; if (elem.kind == TypeExpr::Primitive && elem.name == "tstr") return "lidlToQStringList(" + varName + ")"; return "lidlToQVariantList_" + elem.name + "(" + varName + ")"; } return varName; } static QString variantToQtArg(const TypeExpr& te, int argIdx) { QString a = "args.at(" + QString::number(argIdx) + ")"; QString qt = lidlTypeToQt(te); if (qt == "QString") return a + ".toString()"; if (qt == "QByteArray") return a + ".toByteArray()"; if (qt == "int") return a + ".toInt()"; if (qt == "double") return a + ".toDouble()"; if (qt == "bool") return a + ".toBool()"; if (qt == "QStringList") return a + ".toStringList()"; if (qt == "QVariantList") return a + ".toList()"; if (qt == "QVariantMap") return a + ".toMap()"; if (qt == "LogosResult") return a + ".value()"; return a; } // --------------------------------------------------------------------------- // Provider header generation // --------------------------------------------------------------------------- QString lidlMakeProviderHeader(const ModuleDecl& module, const QString& implClass, const QString& implHeader) { QString className = lidlToPascalCase(module.name); QString providerObjectClass = className + "ProviderObject"; QString pluginClass = className + "Plugin"; QString h; QTextStream s(&h); s << "// AUTO-GENERATED by logos-cpp-generator -- do not edit\n"; s << "#pragma once\n\n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n\n"; s << "#include \"logos_provider_object.h\"\n"; s << "#include \"interface.h\"\n"; s << "#include \"logos_types.h\"\n"; // The generated provider always emits an `onInit(LogosAPI*) override` // that — when the impl class inherits from LogosModuleContext — // copies the three runtime-injected properties (modulePath, // instanceId, instancePersistencePath) into the context base AND // builds a per-module `LogosModules` aggregate so impls can call // other modules without ever touching the raw `LogosAPI`. Both // dispatch paths sit behind SFINAE'd helpers in // logos_module_context.h, so modules that don't inherit // LogosModuleContext compile unchanged (the helpers resolve to // no-op overloads). s << "#include \"logos_api.h\"\n"; s << "#include \"logos_module_context.h\"\n"; // logos_sdk.h is generated alongside this header by the codegen's // umbrella pass (lidl_gen_client.cpp / legacy/main.cpp) and defines // the per-module `LogosModules` struct from the module's // metadata.json#dependencies list. Always present in the build's // generated_code/ directory; included unconditionally so the // onInit override below can construct an instance. s << "#include \"logos_sdk.h\"\n"; s << "#include \n"; s << "#include \n\n"; s << "#include \"" << implHeader << "\"\n\n"; // Conversion helpers (only if needed) bool needsStringVecHelper = false; for (const MethodDecl& md : module.methods) { for (const ParamDecl& pd : md.params) { if (pd.type.kind == TypeExpr::Array && pd.type.elements.size() == 1 && pd.type.elements[0].kind == TypeExpr::Primitive && pd.type.elements[0].name == "tstr") needsStringVecHelper = true; } if (md.returnType.kind == TypeExpr::Array && md.returnType.elements.size() == 1 && md.returnType.elements[0].kind == TypeExpr::Primitive && md.returnType.elements[0].name == "tstr") needsStringVecHelper = true; } if (needsStringVecHelper) { s << "namespace {\n"; s << "inline QStringList lidlToQStringList(const std::vector& v) {\n"; s << " QStringList result;\n"; s << " result.reserve(static_cast(v.size()));\n"; s << " for (const auto& s : v)\n"; s << " result.append(QString::fromStdString(s));\n"; s << " return result;\n"; s << "}\n\n"; s << "inline std::vector lidlToStdStringVector(const QStringList& list) {\n"; s << " std::vector result;\n"; s << " result.reserve(static_cast(list.size()));\n"; s << " for (const auto& s : list)\n"; s << " result.push_back(s.toStdString());\n"; s << " return result;\n"; s << "}\n"; s << "} // anonymous namespace\n\n"; } // Emit nlohmannToQVariant helper if any method returns LogosMap / LogosList / StdLogosResult bool needsNlohmannHelper = false; bool needsResultHelper = false; for (const MethodDecl& md : module.methods) { if (md.jsonReturn) needsNlohmannHelper = true; if (md.resultReturn) { needsNlohmannHelper = true; needsResultHelper = true; } } if (needsNlohmannHelper) { s << "#include \n\n"; s << "namespace {\n"; s << "inline QVariant nlohmannToQVariant(const nlohmann::json& j) {\n"; s << " if (j.is_null()) return QVariant();\n"; s << " if (j.is_boolean()) return QVariant(j.get());\n"; s << " if (j.is_number_integer()) return QVariant(static_cast(j.get()));\n"; s << " if (j.is_number_unsigned()) return QVariant(static_cast(j.get()));\n"; s << " if (j.is_number_float()) return QVariant(j.get());\n"; s << " if (j.is_string()) return QVariant(QString::fromStdString(j.get()));\n"; s << " if (j.is_array()) {\n"; s << " QVariantList list;\n"; s << " list.reserve(static_cast(j.size()));\n"; s << " for (const auto& elem : j)\n"; s << " list.append(nlohmannToQVariant(elem));\n"; s << " return QVariant(list);\n"; s << " }\n"; s << " if (j.is_object()) {\n"; s << " QVariantMap map;\n"; s << " for (auto it = j.begin(); it != j.end(); ++it)\n"; s << " map.insert(QString::fromStdString(it.key()), nlohmannToQVariant(it.value()));\n"; s << " return QVariant(map);\n"; s << " }\n"; s << " return QVariant();\n"; s << "}\n"; if (needsResultHelper) { s << "\n"; s << "#include \"logos_result.h\"\n"; s << "inline LogosResult stdResultToQt(const StdLogosResult& r) {\n"; s << " LogosResult qr;\n"; s << " qr.success = r.success;\n"; s << " qr.value = nlohmannToQVariant(r.value);\n"; s << " qr.error = r.error.empty() ? QVariant() : QVariant(QString::fromStdString(r.error));\n"; s << " return qr;\n"; s << "}\n"; } s << "} // anonymous namespace\n\n"; } // --- ProviderObject class --- s << "class " << providerObjectClass << " : public LogosProviderBase {\n"; s << " LOGOS_PROVIDER(" << providerObjectClass << ", \"" << module.name << "\", \"" << (module.version.isEmpty() ? "0.0.0" : module.version) << "\")\n\n"; s << "public:\n"; for (const MethodDecl& md : module.methods) { QString qtRet = lidlTypeToQt(md.returnType); bool retConvertible = lidlIsStdConvertible(md.returnType); s << " " << qtRet << " " << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { QString qt = lidlTypeToQt(md.params[i].type); if (qt == "QString" || qt == "QByteArray" || qt == "QStringList" || qt == "QVariantList" || qt == "QVariantMap" || qt == "LogosResult") s << "const " << qt << "& " << md.params[i].name; else s << qt << " " << md.params[i].name; if (i + 1 < md.params.size()) s << ", "; } s << ") {\n"; if (qtRet == "void") { s << " m_impl." << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { s << qtParamToStd(md.params[i].type, md.params[i].name); if (i + 1 < md.params.size()) s << ", "; } s << ");\n"; } else if (md.jsonReturn) { // LogosMap / LogosList: impl returns nlohmann::json, convert to Qt type s << " auto _result = m_impl." << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { s << qtParamToStd(md.params[i].type, md.params[i].name); if (i + 1 < md.params.size()) s << ", "; } s << ");\n"; if (qtRet == "QVariantMap") s << " return nlohmannToQVariant(_result).toMap();\n"; else s << " return nlohmannToQVariant(_result).toList();\n"; } else if (md.resultReturn) { // StdLogosResult: impl returns pure-C++ result, convert to Qt LogosResult s << " auto _result = m_impl." << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { s << qtParamToStd(md.params[i].type, md.params[i].name); if (i + 1 < md.params.size()) s << ", "; } s << ");\n"; s << " return stdResultToQt(_result);\n"; } else if (retConvertible) { s << " auto _result = m_impl." << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { s << qtParamToStd(md.params[i].type, md.params[i].name); if (i + 1 < md.params.size()) s << ", "; } s << ");\n"; s << " return " << stdReturnToQt(md.returnType, "_result") << ";\n"; } else { s << " return m_impl." << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { s << qtParamToStd(md.params[i].type, md.params[i].name); if (i + 1 < md.params.size()) s << ", "; } s << ");\n"; } s << " }\n\n"; } // Always emit an `onInit` override that: // 1. copies the three runtime-injected LogosAPI properties // (modulePath, instanceId, instancePersistencePath) into the // impl when it opts in by inheriting LogosModuleContext; // 2. builds a `LogosModules` aggregate (auto-generated by the // codegen's umbrella pass — `generated_code/logos_sdk.h`) // from the LogosAPI and threads its pointer into the same // context base, giving the impl typed access to its // declared dependencies via `modules()....`. // // Both wire-ups go through SFINAE'd helpers // (`_logos_codegen_::maybeSet*`) so impls that don't inherit // LogosModuleContext compile unchanged and pay zero runtime cost // (the no-op overloads inline away). The full LogosAPI is never // exposed to user code. // // `m_logosModules` is owned by the provider object, lifetime // matched to it — the impl is a member of this same provider // (`m_impl`), so the pointer the context holds stays valid for // the impl's entire lifetime. s << "protected:\n"; s << " void onInit(LogosAPI* api) override {\n"; s << " if (!api) return;\n"; s << " _logos_codegen_::maybeSetContext(m_impl,\n"; s << " api->property(\"modulePath\").toString().toStdString(),\n"; s << " api->property(\"instanceId\").toString().toStdString(),\n"; s << " api->property(\"instancePersistencePath\").toString().toStdString());\n"; s << " m_logosModules = std::make_unique(api);\n"; s << " _logos_codegen_::maybeSetLogosModules(m_impl, m_logosModules.get());\n"; // Wire the impl's `logos_events:` declarations to LogosProviderBase's // emitEvent. Codegen-emitted `_events.cpp` bodies call // `this->emitEventImpl_(name, &args)`; the lambda below casts the // void* back to QVariantList and routes through the existing wire. s << " _logos_codegen_::maybeSetEmitEvent(m_impl,\n"; s << " [this](const std::string& name, void* args) {\n"; s << " emitEvent(QString::fromStdString(name),\n"; s << " *static_cast(args));\n"; s << " });\n"; s << " }\n\n"; if (!module.events.isEmpty()) { s << "protected:\n"; for (const EventDecl& ed : module.events) { QString methodName = "emit" + lidlToPascalCase(ed.name); s << " void " << methodName << "("; for (int i = 0; i < ed.params.size(); ++i) { QString qt = lidlTypeToQt(ed.params[i].type); if (qt == "QString" || qt == "QByteArray" || qt == "QStringList" || qt == "QVariantList" || qt == "QVariantMap" || qt == "LogosResult") s << "const " << qt << "& " << ed.params[i].name; else s << qt << " " << ed.params[i].name; if (i + 1 < ed.params.size()) s << ", "; } s << ") {\n"; s << " emitEvent(\"" << ed.name << "\", QVariantList{"; for (int i = 0; i < ed.params.size(); ++i) { s << "QVariant::fromValue(" << ed.params[i].name << ")"; if (i + 1 < ed.params.size()) s << ", "; } s << "});\n"; s << " }\n\n"; } } s << "private:\n"; // Built by onInit; lives for the provider's full lifetime. The impl // (declared next, so destroyed first — reverse-of-construction // order) holds only a non-owning pointer to it via the // LogosModuleContext base. Using unique_ptr instead of a direct // member so the build doesn't require LogosModules to be // default-constructible (it isn't — it takes a LogosAPI*). s << " std::unique_ptr m_logosModules;\n"; s << " " << implClass << " m_impl;\n"; s << "};\n\n"; // --- Plugin/Loader class --- s << "class " << pluginClass << " : public QObject, public PluginInterface, public LogosProviderPlugin {\n"; s << " Q_OBJECT\n"; s << " Q_PLUGIN_METADATA(IID LogosProviderPlugin_iid FILE \"metadata.json\")\n"; s << " Q_INTERFACES(PluginInterface LogosProviderPlugin)\n\n"; s << "public:\n"; s << " QString name() const override { return QStringLiteral(\"" << module.name << "\"); }\n"; s << " QString version() const override { return QStringLiteral(\"" << (module.version.isEmpty() ? "0.0.0" : module.version) << "\"); }\n"; s << " LogosProviderObject* createProviderObject() override {\n"; s << " return new " << providerObjectClass << "();\n"; s << " }\n"; s << "};\n"; return h; } // --------------------------------------------------------------------------- // Dispatch source generation // --------------------------------------------------------------------------- QString lidlMakeProviderDispatch(const ModuleDecl& module) { QString className = lidlToPascalCase(module.name); QString providerObjectClass = className + "ProviderObject"; QString c; QTextStream s(&c); s << "// AUTO-GENERATED by logos-cpp-generator -- do not edit\n"; s << "#include \"" << module.name << "_qt_glue.h\"\n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \"logos_types.h\"\n\n"; // --- callMethod --- s << "QVariant " << providerObjectClass << "::callMethod(const QString& methodName, const QVariantList& args)\n{\n"; for (const MethodDecl& md : module.methods) { QString qtRet = lidlTypeToQt(md.returnType); s << " if (methodName == \"" << md.name << "\") {\n"; if (qtRet == "void") { s << " " << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { s << variantToQtArg(md.params[i].type, i); if (i + 1 < md.params.size()) s << ", "; } s << ");\n"; s << " return QVariant(true);\n"; } else { s << " return QVariant::fromValue(" << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { s << variantToQtArg(md.params[i].type, i); if (i + 1 < md.params.size()) s << ", "; } s << "));\n"; } s << " }\n"; } s << " qWarning() << \"" << providerObjectClass << "::callMethod: unknown method:\" << methodName;\n"; s << " return QVariant();\n"; s << "}\n\n"; // --- getMethods --- s << "QJsonArray " << providerObjectClass << "::getMethods()\n{\n"; s << " QJsonArray methods;\n"; for (const MethodDecl& md : module.methods) { QString qtRet = lidlTypeToQt(md.returnType); s << " {\n"; s << " QJsonObject obj;\n"; s << " obj[\"type\"] = QStringLiteral(\"method\");\n"; s << " obj[\"name\"] = QStringLiteral(\"" << md.name << "\");\n"; s << " obj[\"returnType\"] = QStringLiteral(\"" << qtRet << "\");\n"; s << " obj[\"isInvokable\"] = true;\n"; if (!md.description.isEmpty()) { QString escDesc = md.description; escDesc.replace('\\', "\\\\"); escDesc.replace('"', "\\\""); escDesc.replace('\n', "\\n"); s << " obj[\"description\"] = QStringLiteral(\"" << escDesc << "\");\n"; } QString sig = md.name + "("; for (int i = 0; i < md.params.size(); ++i) { sig += lidlTypeToQt(md.params[i].type); if (i + 1 < md.params.size()) sig += ","; } sig += ")"; s << " obj[\"signature\"] = QStringLiteral(\"" << sig << "\");\n"; if (!md.params.isEmpty()) { s << " QJsonArray params;\n"; for (int i = 0; i < md.params.size(); ++i) { s << " params.append(QJsonObject{{\"type\", QStringLiteral(\"" << lidlTypeToQt(md.params[i].type) << "\")}, {\"name\", QStringLiteral(\"" << md.params[i].name << "\")}});\n"; } s << " obj[\"parameters\"] = params;\n"; } s << " methods.append(obj);\n"; s << " }\n"; } // Events are appended to the SAME interface list, tagged type "event" (and // with no returnType/isInvokable — they are void/fire-and-forget). Folding // them into getMethods() instead of adding a getEvents() vtable slot keeps // LogosProviderObject's vtable layout stable, so old/new hosts and modules // stay binary-compatible. Callers split the list back out by "type" (see // ModuleProxy::getPluginMethods/getPluginEvents/getPluginInterface). for (const EventDecl& ed : module.events) { s << " {\n"; s << " QJsonObject obj;\n"; s << " obj[\"type\"] = QStringLiteral(\"event\");\n"; s << " obj[\"name\"] = QStringLiteral(\"" << ed.name << "\");\n"; if (!ed.description.isEmpty()) { QString escDesc = ed.description; escDesc.replace('\\', "\\\\"); escDesc.replace('"', "\\\""); escDesc.replace('\n', "\\n"); s << " obj[\"description\"] = QStringLiteral(\"" << escDesc << "\");\n"; } QString sig = ed.name + "("; for (int i = 0; i < ed.params.size(); ++i) { sig += lidlTypeToQt(ed.params[i].type); if (i + 1 < ed.params.size()) sig += ","; } sig += ")"; s << " obj[\"signature\"] = QStringLiteral(\"" << sig << "\");\n"; if (!ed.params.isEmpty()) { s << " QJsonArray params;\n"; for (int i = 0; i < ed.params.size(); ++i) { s << " params.append(QJsonObject{{\"type\", QStringLiteral(\"" << lidlTypeToQt(ed.params[i].type) << "\")}, {\"name\", QStringLiteral(\"" << ed.params[i].name << "\")}});\n"; } s << " obj[\"parameters\"] = params;\n"; } s << " methods.append(obj);\n"; s << " }\n"; } s << " return methods;\n"; s << "}\n"; return c; } // --------------------------------------------------------------------------- // Events source generation — Qt-MOC-style bodies for `logos_events:` decls // --------------------------------------------------------------------------- // // The impl header declares typed event prototypes in a `logos_events:` // section. The compiler sees them as ordinary public-method declarations // (the macro expands to `public:`). This generator emits the matching // definitions in a sidecar `_events.cpp` — exactly the role that // `moc_*.cpp` plays for Qt's `signals:`. Each body marshals typed args // into a `QVariantList` and calls `LogosModuleContext::emitEventImpl_`, // which the provider's onInit wired to LogosProviderBase::emitEvent (and // onward over QRO). // Returns a C++ expression of static type QVariant for the given // std-typed parameter. Mirrors the type-mapping table in lidlTypeToStd. static QString stdParamToQVariantExpr(const TypeExpr& te, const QString& pn) { if (te.kind == TypeExpr::Primitive) { if (te.name == "tstr") return "QVariant(QString::fromStdString(" + pn + "))"; if (te.name == "bstr") return "QVariant(QByteArray(reinterpret_cast(" + pn + ".data()), static_cast(" + pn + ".size())))"; if (te.name == "int") return "QVariant(static_cast(" + pn + "))"; if (te.name == "uint") return "QVariant(static_cast(" + pn + "))"; if (te.name == "float64") return "QVariant(" + pn + ")"; if (te.name == "bool") return "QVariant(" + pn + ")"; } if (te.kind == TypeExpr::Array && te.elements.size() == 1 && te.elements[0].kind == TypeExpr::Primitive && te.elements[0].name == "tstr") { // std::vector -> QStringList wrapped in QVariant return "QVariant([&](){ QStringList _l; _l.reserve(static_cast(" + pn + ".size())); for (const auto& _e : " + pn + ") _l.append(QString::fromStdString(_e)); return _l; }())"; } // Fallback — let QVariant::fromValue figure it out (works for primitives // and any QMetaType-registered user type). return "QVariant::fromValue(" + pn + ")"; } QString lidlMakeEventsSource(const ModuleDecl& module, const QString& implClass, const QString& implHeader) { QString c; QTextStream s(&c); s << "// AUTO-GENERATED by logos-cpp-generator -- do not edit\n"; s << "//\n"; s << "// Bodies for `logos_events:` methods declared in " << implHeader << ".\n"; s << "// Each call marshals typed args into a QVariantList and routes\n"; s << "// them through LogosModuleContext::emitEventImpl_, which the\n"; s << "// generated provider wires to LogosProviderBase::emitEvent.\n"; s << "#include \"" << implHeader << "\"\n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n"; s << "#include \n\n"; for (const EventDecl& ed : module.events) { // Signature — mirrors the prototype the impl declared. s << "void " << implClass << "::" << ed.name << "("; for (int i = 0; i < ed.params.size(); ++i) { QString stdType = lidlTypeToStd(ed.params[i].type); const TypeExpr& te = ed.params[i].type; // Pass-by-const-ref for non-trivial std types; by-value for // primitives. Mirrors the existing method-signature shape. bool byRef = (te.kind == TypeExpr::Array) || (te.kind == TypeExpr::Primitive && (te.name == "tstr" || te.name == "bstr")); if (byRef) s << "const " << stdType << "& " << ed.params[i].name; else s << stdType << " " << ed.params[i].name; if (i + 1 < ed.params.size()) s << ", "; } s << ") {\n"; s << " QVariantList _args{"; for (int i = 0; i < ed.params.size(); ++i) { s << stdParamToQVariantExpr(ed.params[i].type, ed.params[i].name); if (i + 1 < ed.params.size()) s << ", "; } s << "};\n"; s << " this->emitEventImpl_(\"" << ed.name << "\", &_args);\n"; s << "}\n\n"; } return c; } // --------------------------------------------------------------------------- // Full pipeline (from .lidl file) // --------------------------------------------------------------------------- int lidlGenerateProviderGlue(const QString& lidlPath, const QString& implClass, const QString& implHeader, const QString& outputDir, QTextStream& out, QTextStream& err) { QFileInfo fi(lidlPath); if (!fi.exists()) { err << "LIDL file does not exist: " << lidlPath << "\n"; return 2; } QFile file(fi.canonicalFilePath().isEmpty() ? fi.absoluteFilePath() : fi.canonicalFilePath()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { err << "Failed to open LIDL file: " << lidlPath << "\n"; return 3; } QString source = QString::fromUtf8(file.readAll()); file.close(); LidlParseResult pr = lidlParse(source); if (pr.hasError()) { err << lidlPath << ":" << pr.errorLine << ":" << pr.errorColumn << ": " << pr.error << "\n"; return 4; } LidlValidationResult vr = lidlValidate(pr.module); if (vr.hasErrors()) { for (const QString& e : vr.errors) err << lidlPath << ": " << e << "\n"; return 5; } const ModuleDecl& mod = pr.module; QString genDirPath = outputDir.isEmpty() ? QDir::current().filePath("generated") : outputDir; QDir().mkpath(genDirPath); QString glueHeaderAbs = QDir(genDirPath).filePath(mod.name + "_qt_glue.h"); { QFile f(glueHeaderAbs); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { err << "Failed to write glue header: " << glueHeaderAbs << "\n"; return 6; } f.write(lidlMakeProviderHeader(mod, implClass, implHeader).toUtf8()); } QString dispatchAbs = QDir(genDirPath).filePath(mod.name + "_dispatch.cpp"); { QFile f(dispatchAbs); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { err << "Failed to write dispatch source: " << dispatchAbs << "\n"; return 7; } f.write(lidlMakeProviderDispatch(mod).toUtf8()); } out << "Generated: " << glueHeaderAbs << "\n"; out << "Generated: " << dispatchAbs << "\n"; // Events bodies + LIDL sidecar: emitted whenever the module declares // any events. The sidecar gets shipped in the dep's headers-* output // by buildPlugin.nix's installPhase so consumer-side codegen can // discover events without reintrospecting the .dylib. if (!mod.events.isEmpty()) { QString eventsAbs = QDir(genDirPath).filePath(mod.name + "_events.cpp"); { QFile f(eventsAbs); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { err << "Failed to write events source: " << eventsAbs << "\n"; return 8; } f.write(lidlMakeEventsSource(mod, implClass, implHeader).toUtf8()); } out << "Generated: " << eventsAbs << "\n"; QString lidlAbs = QDir(genDirPath).filePath(mod.name + ".lidl"); { QFile f(lidlAbs); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { err << "Failed to write LIDL sidecar: " << lidlAbs << "\n"; return 9; } f.write(lidlSerialize(mod).toUtf8()); } out << "Generated: " << lidlAbs << "\n"; } out.flush(); return 0; }