From 1afcf46f4dfd3b51297c07b07c4932cc33accb59 Mon Sep 17 00:00:00 2001 From: Dario Gabriel Lipicar Date: Tue, 18 Aug 2026 16:46:09 -0300 Subject: [PATCH] refactor(cpp-generator): merge legacy/ into the generator, dropping its dead half MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `legacy/` was never a library with an API surface. Two files, 475 lines, exactly one exported symbol — `int legacy_main(int, char**)` — with every other definition `static`, compiled INTO logos-cpp-generator and reached by fallthrough at the end of main(). So there was nothing to keep separate: it is one mode of this binary, and it now lives beside the others as plugin_introspect.{cpp,h} behind `runPluginIntrospectMode()`, named for what it does. Deleting it was never an option — that premise was checked and refused earlier. `--general-only` alone has ~10 live callers across 7 repos including the central module path (buildPlugin.nix:206,211, buildHeaders.nix:221, LogosModule.cmake:423). The mode is load-bearing; only its packaging was wrong. 110 lines go with the move, all genuinely unreferenced: * cppStringEscape — zero callers anywhere. * writeUmbrellaHeader / writeUmbrellaSource and the `if (!moduleOnly)` block that called them. This is the real prize: a SECOND, directory-SCRAPING implementation of logos_sdk.{h,cpp}, unreachable in practice because generate-module-headers.sh:60 always passes --module-only. generator_lib's deps-driven makeUmbrella*FromDeps is now the only umbrella emitter, so the two cannot drift. * a dead `QJsonDocument doc(methods);` and its commented-out use. With the block gone, `--module-only` suppresses nothing, so the parameter and its plumbing go too. The FLAG stays tolerated rather than rejected, because generate-module-headers.sh passes it unconditionally — a comment at the old parse site says so. Verified: #default builds, and both checks pass — `generator-cli` (which exercises the CLI surface, including --general-only) and `tests`. Co-Authored-By: Claude Opus 5 --- cpp-generator/CMakeLists.txt | 2 +- cpp-generator/generator_lib.h | 2 +- cpp-generator/legacy/legacy_main.h | 6 - cpp-generator/main.cpp | 18 +-- .../main.cpp => plugin_introspect.cpp} | 136 ++---------------- cpp-generator/plugin_introspect.h | 13 ++ 6 files changed, 37 insertions(+), 140 deletions(-) delete mode 100644 cpp-generator/legacy/legacy_main.h rename cpp-generator/{legacy/main.cpp => plugin_introspect.cpp} (74%) create mode 100644 cpp-generator/plugin_introspect.h diff --git a/cpp-generator/CMakeLists.txt b/cpp-generator/CMakeLists.txt index e4a820f..916324b 100644 --- a/cpp-generator/CMakeLists.txt +++ b/cpp-generator/CMakeLists.txt @@ -22,7 +22,7 @@ add_executable(logos-cpp-generator main.cpp generator_lib.cpp lidl_to_json.cpp - legacy/main.cpp + plugin_introspect.cpp experimental/lidl_emit_common.cpp experimental/lidl_gen_client.cpp experimental/lidl_gen_cdylib.cpp diff --git a/cpp-generator/generator_lib.h b/cpp-generator/generator_lib.h index b8ffda9..3f1d6e1 100644 --- a/cpp-generator/generator_lib.h +++ b/cpp-generator/generator_lib.h @@ -33,7 +33,7 @@ enum class ApiStyle { Qt, Lp }; // refuses, in which case `outStyle` is untouched and the caller must exit 1. // // Lives here, next to the enum, because BOTH CLI entry points need it: the -// umbrella mode in main.cpp and legacy_main's plugin path. Two copies of this +// umbrella mode in main.cpp and the plugin-introspection path. Two copies of this // table is exactly how the surfaces drift apart. // // `std` was a third surface (std types over a QVariant/LogosAPIClient body). diff --git a/cpp-generator/legacy/legacy_main.h b/cpp-generator/legacy/legacy_main.h deleted file mode 100644 index 30d4b81..0000000 --- a/cpp-generator/legacy/legacy_main.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef LEGACY_MAIN_H -#define LEGACY_MAIN_H - -int legacy_main(int argc, char* argv[]); - -#endif // LEGACY_MAIN_H diff --git a/cpp-generator/main.cpp b/cpp-generator/main.cpp index 2f9cd62..2ec217d 100644 --- a/cpp-generator/main.cpp +++ b/cpp-generator/main.cpp @@ -1,4 +1,4 @@ -#include "legacy/legacy_main.h" +#include "plugin_introspect.h" #include "generator_lib.h" #include "lidl_to_json.h" #include "experimental/lidl_gen_client.h" @@ -26,11 +26,11 @@ // its interface dependencies, and the per-dependency / per-interface wrappers // those aggregate. // -// This is NOT a legacy mode, despite having lived in `legacy/main.cpp` until +// This is NOT a legacy mode, despite having lived in `plugin_introspect.cpp` until // now: `LogosModuleContext::modules()` returns `LogosModules&`, so every // `interface: "universal"` module that calls a declared dependency goes // through it, and LogosModule.cmake runs it for every module build. Only the -// QPluginLoader-introspection path in `legacy/main.cpp` is legacy. +// QPluginLoader-introspection path in `plugin_introspect.cpp` is legacy. // // `--general-only` is kept as an exact alias — it is what LogosModule.cmake, // buildPlugin.nix and buildHeaders.nix all pass today — so there is ONE @@ -217,7 +217,7 @@ static int runUmbrellaMode(const QStringList& args, const QString& progName, outputDir = stripAt(args.at(outDirIdx + 1)); } - // `--api-style qt|lp` — the one parser, shared with legacy_main's plugin + // `--api-style qt|lp` — the one parser, shared with runPluginIntrospectMode's plugin // path (generator_lib.h, next to the ApiStyle enum). ApiStyle apiStyle = ApiStyle::Qt; if (!parseApiStyleFlag(args, apiStyle, err)) return 1; @@ -428,7 +428,7 @@ static int runUmbrellaMode(const QStringList& args, const QString& progName, int main(int argc, char* argv[]) { // Check for --lidl / --from-header / --header-to-lidl mode before - // initializing QCoreApplication, since legacy_main creates its own. + // initializing QCoreApplication, since runPluginIntrospectMode creates its own. bool hasLidl = false; bool hasFromHeader = false; bool hasHeaderToLidl = false; @@ -446,8 +446,8 @@ int main(int argc, char* argv[]) } // Umbrella mode. `--general-only` routes here too — ONE implementation, - // no second copy in legacy/main.cpp to drift — but only in the shape - // legacy_main ever honoured it: inside the `--metadata` branch. Without + // no second copy in plugin_introspect.cpp to drift — but only in the shape + // runPluginIntrospectMode ever honoured it: inside the `--metadata` branch. Without // `--metadata` the flag was never a mode at all (it fell through to the // plugin path and reported the flag itself as a missing plugin file), so // that case still falls through, unchanged. @@ -471,7 +471,7 @@ int main(int argc, char* argv[]) const QStringList args = app.arguments(); // Strip a leading '@' from path arguments — some build drivers pass - // `@/abs/path`. Matches the legacy_main path handling. + // `@/abs/path`. Matches the runPluginIntrospectMode path handling. auto stripAt = [](QString p) { if (p.startsWith('@')) p.remove(0, 1); return p; }; const int idx = args.indexOf("--header-to-lidl"); @@ -769,5 +769,5 @@ int main(int argc, char* argv[]) return lidlGenerateClientStubs(lidlPath, outputDir, moduleOnly, out, err); } - return legacy_main(argc, argv); + return runPluginIntrospectMode(argc, argv); } diff --git a/cpp-generator/legacy/main.cpp b/cpp-generator/plugin_introspect.cpp similarity index 74% rename from cpp-generator/legacy/main.cpp rename to cpp-generator/plugin_introspect.cpp index 00a9d86..1d4a04f 100644 --- a/cpp-generator/legacy/main.cpp +++ b/cpp-generator/plugin_introspect.cpp @@ -14,20 +14,10 @@ #include #include #include "logos_provider_interface.h" -#include "../generator_lib.h" -#include "../metadata_dependencies.h" -#include "../experimental/lidl_compat.h" -#include "../lidl_to_json.h" // ModuleDecl -> the JSON surface generator_lib consumes - -// Escape a string for safe embedding inside a generated C++ string literal. -static QString cppStringEscape(const QString& s) -{ - QString out = s; - out.replace('\\', "\\\\"); - out.replace('"', "\\\""); - out.replace('\n', "\\n"); - return out; -} +#include "generator_lib.h" +#include "metadata_dependencies.h" +#include "experimental/lidl_compat.h" +#include "lidl_to_json.h" // ModuleDecl -> the JSON surface generator_lib consumes // Load events from a `.lidl` sidecar shipped alongside a module's // pre-built headers. Returns a JSON array of @@ -114,97 +104,7 @@ static QJsonArray enumerateMethods(QObject* moduleInstance) // makeSource -> generator_lib.h/cpp -static bool writeUmbrellaHeader(const QString& genDirPath, QTextStream& err) -{ - // Generate logos_sdk.h: include every per-module wrapper header in - // the gen dir and aggregate them into a flat `LogosModules` struct. - // The wrappers may be Qt-typed or std-typed (lp) depending on the - // --api-style picked for this build; the umbrella shape doesn't - // change because either flavor produces the same accessor name - // (``) on the same class name (``). - // - // `core_manager_api.h` (if present in the gen dir from an older - // run) is intentionally filtered out — universal modules access - // only the deps they explicitly declared in `metadata.json# - // dependencies`. Apps that need to manage the core use the C API - // in liblogos directly, not the typed `LogosModules` aggregate. - QDir genDir(genDirPath); - QStringList headers = genDir.entryList(QStringList() << "*_api.h", QDir::Files | QDir::Readable); - headers.removeAll(QStringLiteral("core_manager_api.h")); - - QString content; - QTextStream s(&content); - s << "#pragma once\n"; - s << "#include \"logos_api.h\"\n"; - s << "#include \"logos_api_client.h\"\n\n"; - for (const QString& h : headers) s << "#include \"" << h << "\"\n"; - s << "\n"; - - s << "struct LogosModules {\n"; - s << " explicit LogosModules(LogosAPI* api) : api(api)"; - for (const QString& h : headers) { - QString base = h; - base.chop(QString("_api.h").size()); - s << ", \n " << base << "(api)"; - } - s << " {}\n"; - s << " LogosAPI* api;\n"; - for (const QString& h : headers) { - QString base = h; - base.chop(QString("_api.h").size()); - QString className = toPascalCase(base); - s << " " << className << " " << base << ";\n"; - } - s << "};\n"; - - QFile outFile(genDir.filePath("logos_sdk.h")); - if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { - err << "Failed to write umbrella header: " << outFile.fileName() << "\n"; - return false; - } - outFile.write(content.toUtf8()); - outFile.close(); - return true; -} - -static bool writeUmbrellaSource(const QString& genDirPath, QTextStream& err) -{ - // Generate logos_sdk.cpp: one #include per per-module wrapper - // `.cpp` in the gen dir. There's now exactly one wrapper file per - // module (Qt or std, picked at generation time), so no de-dup or - // twin-file filtering is needed. - // - // `core_manager_api.cpp` (if present from an older run) is - // filtered out — the umbrella header no longer declares - // `CoreManager core_manager;` so including its definitions would - // produce dead code. - QDir genDir(genDirPath); - QStringList sources = genDir.entryList(QStringList() << "*_api.cpp", QDir::Files | QDir::Readable); - sources.removeAll(QStringLiteral("core_manager_api.cpp")); - - QString content; - QTextStream s(&content); - s << "#include \"logos_sdk.h\"\n\n"; - for (const QString& c : sources) s << "#include \"" << c << "\"\n"; - s << "\n"; - - QFile outFile(genDir.filePath("logos_sdk.cpp")); - if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { - err << "Failed to write umbrella source: " << outFile.fileName() << "\n"; - return false; - } - outFile.write(content.toUtf8()); - outFile.close(); - return true; -} - -// The deps-driven umbrella writers (writeUmbrellaHeaderFromDeps / -// writeUmbrellaSourceFromDeps) moved to ../main.cpp's umbrella mode, which is -// now the only caller of makeUmbrellaHeaderFromDeps / makeUmbrellaSourceFromDeps. -// The two directory-SCRAPING writers above stay: they belong to -// generateFromPlugin (QPluginLoader introspection) and die with it. - -static int generateFromPlugin(const QString& pluginInputPath, const QString& outputDir, bool moduleOnly, ApiStyle apiStyle, const QJsonArray& events, QTextStream& out, QTextStream& err, const QJsonArray& records = {}) +static int generateFromPlugin(const QString& pluginInputPath, const QString& outputDir, ApiStyle apiStyle, const QJsonArray& events, QTextStream& out, QTextStream& err, const QJsonArray& records = {}) { QFileInfo fi(pluginInputPath); if (!fi.exists()) { @@ -297,19 +197,6 @@ static int generateFromPlugin(const QString& pluginInputPath, const QString& out f.close(); } - if (!moduleOnly) { - if (!writeUmbrellaHeader(genDirPath, err)) { - loader.unload(); - return 7; - } - if (!writeUmbrellaSource(genDirPath, err)) { - loader.unload(); - return 8; - } - } - - QJsonDocument doc(methods); - // out << doc.toJson(QJsonDocument::Indented) << "\n"; out << "Generated: " << QDir(genDirPath).filePath(headerRel) << " and " << QDir(genDirPath).filePath(sourceRel) << "\n"; out.flush(); @@ -317,7 +204,7 @@ static int generateFromPlugin(const QString& pluginInputPath, const QString& out return 0; } -int legacy_main(int argc, char* argv[]) +int runPluginIntrospectMode(int argc, char* argv[]) { QCoreApplication app(argc, argv); @@ -336,12 +223,15 @@ int legacy_main(int argc, char* argv[]) } } - // Parse --module-only option - bool moduleOnly = args.contains("--module-only"); + // `--module-only` is accepted and ignored. The only thing it ever + // suppressed was the directory-scraping umbrella pair above, which is gone + // — generator_lib's deps-driven makeUmbrella*FromDeps is the sole umbrella + // emitter now. generate-module-headers.sh:60 always passes the flag, so it + // stays tolerated rather than rejected. // `--general-only` (the umbrella) is NOT handled here any more: ../main.cpp // intercepts it, together with its new `--umbrella` spelling, and runs the - // one non-legacy implementation. It can only reach legacy_main when it was + // one non-legacy implementation. It can only reach runPluginIntrospectMode when it was // passed WITHOUT --metadata, which was never a mode — the plugin path // below reports it as a missing plugin file, exactly as before. @@ -471,5 +361,5 @@ int legacy_main(int argc, char* argv[]) } QString argPath = args.at(1); - return generateFromPlugin(argPath, outputDir, moduleOnly, apiStyle, eventsFromSidecar, out, err, recordsFromSidecar); + return generateFromPlugin(argPath, outputDir, apiStyle, eventsFromSidecar, out, err, recordsFromSidecar); } diff --git a/cpp-generator/plugin_introspect.h b/cpp-generator/plugin_introspect.h new file mode 100644 index 0000000..d3010d6 --- /dev/null +++ b/cpp-generator/plugin_introspect.h @@ -0,0 +1,13 @@ +#ifndef PLUGIN_INTROSPECT_H +#define PLUGIN_INTROSPECT_H + +// The QPluginLoader-introspection mode: given a BUILT plugin, walk its +// QMetaObject and emit a consumer wrapper for it. main() falls through to this +// when its own modes do not claim the arguments. +// +// This lived in `legacy/main.cpp` behind `legacy_main()`. The directory was not +// a legacy library — it held exactly one exported symbol and one reachable +// mode — so it was merged here rather than kept as a parallel implementation. +int runPluginIntrospectMode(int argc, char* argv[]); + +#endif // PLUGIN_INTROSPECT_H