mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
refactor(cpp-generator): merge legacy/ into the generator, dropping its dead half
`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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d27927aba5
commit
1afcf46f4d
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef LEGACY_MAIN_H
|
||||
#define LEGACY_MAIN_H
|
||||
|
||||
int legacy_main(int argc, char* argv[]);
|
||||
|
||||
#endif // LEGACY_MAIN_H
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -14,20 +14,10 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QtGlobal>
|
||||
#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
|
||||
// (`<dep>`) on the same class name (`<Dep>`).
|
||||
//
|
||||
// `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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user