diff --git a/README.md b/README.md index 1ab4ac9..cee5fb8 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,11 @@ logos-cpp-generator --metadata /path/to/metadata.json --module-dir /path/to/modu # Generate only module files (no core manager or umbrella headers) logos-cpp-generator --metadata /path/to/metadata.json --module-dir /path/to/modules --module-only -# Combine all options -logos-cpp-generator --metadata /path/to/metadata.json --module-dir /path/to/modules --output-dir /custom/output --module-only +# Generate only core manager and umbrella files (assumes module files already exist) +logos-cpp-generator --metadata /path/to/metadata.json --general-only + +# Generate general files with custom output directory +logos-cpp-generator --metadata /path/to/metadata.json --general-only --output-dir /custom/output ``` #### Options @@ -134,16 +137,46 @@ logos-cpp-generator --metadata /path/to/metadata.json --module-dir /path/to/modu - Skips generation of `core_manager_api.*` and umbrella headers (`logos_sdk.*`) - Useful when you only need wrapper code for specific modules +**`--general-only`** +- When specified with `--metadata`, generates only the core manager and umbrella SDK files +- Assumes module wrapper files already exist in the output directory +- Generates: `core_manager_api.h`, `core_manager_api.cpp`, `logos_sdk.h`, `logos_sdk.cpp` +- The umbrella headers will include references to all modules listed in the metadata's `dependencies` array +- For each dependency (e.g., `"waku_module"`), it will: + - Include `waku_module_api.h` in the header + - Include `waku_module_api.cpp` in the source + - Create a `WakuModule waku_module;` member in the `LogosModules` struct +- Does not require `--module-dir` since it doesn't process plugins + #### Generated Files -By default (without `--module-only`): +**By default (without `--module-only` or `--general-only`):** - `_api.h` and `_api.cpp` - Wrapper code for each module - `core_manager_api.h` and `core_manager_api.cpp` - Core manager wrapper - `logos_sdk.h` and `logos_sdk.cpp` - Umbrella headers including all modules -With `--module-only`: +**With `--module-only`:** - Only `_api.h` and `_api.cpp` - Wrapper code for the requested module(s) +**With `--general-only`:** +- Only `core_manager_api.h` and `core_manager_api.cpp` - Core manager wrapper +- Only `logos_sdk.h` and `logos_sdk.cpp` - Umbrella headers that reference existing module files + +#### Typical Workflow + +A common workflow is to generate module wrappers separately, then generate the umbrella SDK: + +```bash +# Step 1: Generate individual module wrappers +logos-cpp-generator /path/to/plugin1.dylib --module-only --output-dir ./generated +logos-cpp-generator /path/to/plugin2.dylib --module-only --output-dir ./generated + +# Step 2: Generate core manager and umbrella SDK (references modules from step 1) +logos-cpp-generator --metadata metadata.json --general-only --output-dir ./generated +``` + +This approach gives you fine-grained control over which modules to include and allows rebuilding just the umbrella headers without regenerating all module wrappers. + ### Requirements #### Build Tools diff --git a/cpp-generator/main.cpp b/cpp-generator/main.cpp index 0a1fb17..5f4edd0 100644 --- a/cpp-generator/main.cpp +++ b/cpp-generator/main.cpp @@ -582,6 +582,52 @@ static bool writeUmbrellaHeader(const QString& genDirPath, QTextStream& err) return true; } +static bool writeUmbrellaHeaderFromDeps(const QString& genDirPath, const QJsonArray& deps, QTextStream& err) +{ + // Generate logos-cpp-sdk/cpp/generated/logos_sdk.h based on dependencies list + QDir genDir(genDirPath); + QString content; + QTextStream s(&content); + s << "#pragma once\n"; + s << "#include \"logos_api.h\"\n"; + s << "#include \"logos_api_client.h\"\n\n"; + // Includes + s << "#include \"core_manager_api.h\"\n"; + for (const QJsonValue& v : deps) { + if (!v.isString()) continue; + QString depName = v.toString(); + s << "#include \"" << depName << "_api.h\"\n"; + } + s << "\n"; + // Convenience aggregator exposing module wrappers + s << "struct LogosModules {\n"; + s << " explicit LogosModules(LogosAPI* api) : api(api), \n core_manager(api)"; + for (const QJsonValue& v : deps) { + if (!v.isString()) continue; + QString depName = v.toString(); + s << ", \n " << depName << "(api)"; + } + s << " {}\n"; + s << " LogosAPI* api;\n"; + s << " CoreManager core_manager;\n"; + for (const QJsonValue& v : deps) { + if (!v.isString()) continue; + QString depName = v.toString(); + QString className = toPascalCase(depName); + s << " " << className << " " << depName << ";\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-cpp-sdk/cpp/generated/logos_sdk.cpp that includes all *_api.cpp in this dir @@ -605,6 +651,31 @@ static bool writeUmbrellaSource(const QString& genDirPath, QTextStream& err) return true; } +static bool writeUmbrellaSourceFromDeps(const QString& genDirPath, const QJsonArray& deps, QTextStream& err) +{ + // Generate logos-cpp-sdk/cpp/generated/logos_sdk.cpp based on dependencies list + QDir genDir(genDirPath); + QString content; + QTextStream s(&content); + s << "#include \"logos_sdk.h\"\n\n"; + s << "#include \"core_manager_api.cpp\"\n"; + for (const QJsonValue& v : deps) { + if (!v.isString()) continue; + QString depName = v.toString(); + s << "#include \"" << depName << "_api.cpp\"\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; +} + static int generateFromPlugin(const QString& pluginInputPath, const QString& outputDir, bool moduleOnly, QTextStream& out, QTextStream& err) { QFileInfo fi(pluginInputPath); @@ -720,12 +791,15 @@ int main(int argc, char* argv[]) // Parse --module-only option bool moduleOnly = args.contains("--module-only"); + // Parse --general-only option + bool generalOnly = args.contains("--general-only"); + // Support: extract dependencies from a metadata.json file { const int metaIdx = args.indexOf("--metadata"); if (metaIdx != -1) { if (metaIdx + 1 >= args.size()) { - err << "Usage: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /absolute/path/to/metadata.json [--output-dir /path/to/output] [--module-only]\n"; + err << "Usage: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /absolute/path/to/metadata.json [--output-dir /path/to/output] [--module-only] [--general-only]\n"; return 1; } QString metaPathArg = args.at(metaIdx + 1); @@ -757,11 +831,34 @@ int main(int argc, char* argv[]) const QJsonObject obj = doc.object(); const QJsonArray deps = obj.value("dependencies").toArray(); + // If --general-only provided, generate only core manager and umbrella files + if (generalOnly) { + QString genDirPath = outputDir.isEmpty() ? QDir::current().filePath("logos-cpp-sdk/cpp/generated") : outputDir; + QDir().mkpath(genDirPath); + + // Generate core manager wrapper + if (!ensureCoreManagerWrapper(genDirPath, err)) { + return 9; + } + + // Generate umbrella headers based on dependencies from metadata + if (!writeUmbrellaHeaderFromDeps(genDirPath, deps, err)) { + return 7; + } + if (!writeUmbrellaSourceFromDeps(genDirPath, deps, err)) { + return 8; + } + + out << "Generated core_manager_api.h, core_manager_api.cpp, logos_sdk.h, and logos_sdk.cpp\n"; + out.flush(); + return 0; + } + // If --module-dir provided, generate for each dependency; else print deps const int modDirIdx = args.indexOf("--module-dir"); if (modDirIdx != -1) { if (modDirIdx + 1 >= args.size()) { - err << "Usage: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /path/to/metadata.json --module-dir /path/to/modules_dir [--output-dir /path/to/output] [--module-only]\n"; + err << "Usage: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /path/to/metadata.json --module-dir /path/to/modules_dir [--output-dir /path/to/output] [--module-only] [--general-only]\n"; return 1; } QString moduleDirArg = args.at(modDirIdx + 1); @@ -829,7 +926,8 @@ int main(int argc, char* argv[]) if (args.size() < 2) { err << "Usage: " << QFileInfo(app.applicationFilePath()).fileName() << " /absolute/path/to/plugin [--output-dir /path/to/output] [--module-only]\n"; - err << " or: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /absolute/path/to/metadata.json [--output-dir /path/to/output] [--module-only]\n"; + err << " or: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /absolute/path/to/metadata.json [--output-dir /path/to/output] [--module-only] [--general-only]\n"; + err << " or: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /absolute/path/to/metadata.json --general-only [--output-dir /path/to/output]\n"; return 1; }