2#include <JitCpp/JitPlatform.hpp>
4#include <score/tools/File.hpp>
9#include <QJsonDocument>
11#include <QRegularExpression>
12#include <QStringBuilder>
21 QJsonObject addon_info;
25 std::string unity_cpp;
27 std::vector<std::pair<QString, QString>> files;
28 std::vector<std::string> flags;
34static void loadBasicAddon(
const QString& addon,
AddonData& data)
36 std::string cpp_files;
37 std::vector<std::pair<QString, QString>> files;
41 QDir::Filter::Files | QDir::Filter::NoDotAndDotDot,
42 QDirIterator::Subdirectories};
46 if(QFile f(it.next()); f.open(QIODevice::ReadOnly))
49 if(fi.suffix() ==
"cpp")
51 data.unity_cpp.append(
"#include \"" + it.filePath().toStdString() +
"\"\n");
54 data.files.push_back({fi.filePath(), f.readAll()});
59static void loadCMakeAddon(
const QString& addon, AddonData& data, QString cm)
65 static const QRegularExpression project_re{
66 R
"_(project\s*\(\s*([A-Za-z_][A-Za-z_0-9]*))_"};
67 if(
auto m = project_re.match(cm); m.hasMatch())
68 data.target = m.captured(1);
71 static const QRegularExpression space{R
"_(\s+)_"};
72 static const QRegularExpression sourceFiles{
73 R
"_(add_library\(\s*[[:graph:]]+([a-zA-Z0-9_.\/\n ]*)\))_"};
74 static const QRegularExpression definitions{
75 R
"_(target_compile_definitions\(\s*[[:graph:]]+\s*[[:graph:]]+([a-zA-Z0-9_"= ]+)\))_"};
76 static const QRegularExpression includes{
77 R
"_(target_include_directories\(\s*[[:graph:]]+\s*[[:graph:]]+([a-zA-Z0-9_\/ ]+)\))_"};
78 static const QRegularExpression avnd{
79 R
"_((?:avnd_score_plugin_add|avnd_addon_object)\(([a-zA-Z0-9_\/.\n" -]+)\))_"};
80 static const QRegularExpression avnd_finalize{
81 R
"_((?:avnd_score_plugin_finalize|avnd_addon_finalize)\(([a-zA-Z0-9_\/.\n" -]+)\))_"};
83 auto avnds = avnd.globalMatch(cm);
84 auto avnd_finalizes = avnd_finalize.globalMatch(cm);
87 auto files = sourceFiles.globalMatch(cm);
88 auto defs = definitions.globalMatch(cm);
89 auto incs = includes.globalMatch(cm);
90 while(files.hasNext())
92 auto m = files.next();
93 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
94 for(
const QString& file : res)
96 QString filename = QString{R
"_(%1/%2)_"}.arg(addon).arg(file);
100 if(!QFile::exists(filename))
103 QString path = QString{R
"_(#include "%1/%2"
107 data.unity_cpp.append(path.toStdString());
110 f.open(QIODevice::ReadOnly);
111 data.files.push_back({filename, score::readFileAsQString(f)});
114 while(defs.hasNext())
116 auto m = defs.next();
117 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
118 for(
const QString& define : res)
120 data.flags.push_back(QString{R
"_(-D%1)_"}.arg(define).toStdString());
124 while(incs.hasNext())
126 auto m = incs.next();
127 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
129 for(
const QString& path : res)
131 data.flags.push_back(QString{R
"_(-I%1/%2)_"}.arg(addon).arg(path).toStdString());
141 QString avnd_namespace;
148 std::vector<source_file> files;
151 struct avnd_plugin_group
156 std::vector<avnd_plugin> plugins;
159 std::vector<avnd_plugin_group> groups;
160 qDebug() <<
"Found avnd finalize? " << avnd_finalizes.hasNext();
161 while(avnd_finalizes.hasNext())
163 auto m = avnd_finalizes.next();
164 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
166 avnd_plugin_group plug;
167 for(
auto it = res.begin(); it != res.end(); ++it)
169 if(*it ==
"BASE_TARGET" || *it ==
"NAME")
174 plug.base_target = *it;
178 else if(*it ==
"PLUGIN_VERSION" || *it ==
"VERSION")
184 else if(*it ==
"PLUGIN_UUID" || *it ==
"UUID")
192 if(!plug.base_target.isEmpty())
193 groups.push_back(std::move(plug));
196 while(avnds.hasNext())
198 auto m = avnds.next();
199 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
201 auto unquote = [](QString s) {
205 for(
auto it = res.begin(); it != res.end(); ++it)
207 if(*it ==
"BASE_TARGET" || *it ==
"BASE")
211 plug.base_target = *it;
213 else if(*it ==
"SOURCES")
216 while(it != res.end()
217 && QFile::exists(QString{
"%1/%2"}.arg(addon).arg(unquote(*it))))
219 const QString file = unquote(*it);
220 avnd_plugin::source_file sf;
221 sf.filename = QString{
"%1/%2"}.arg(addon).arg(file);
222 sf.path = QString{
"#include \"%1/%2\"\n"}.arg(addon).arg(file);
224 QFile f{sf.filename};
225 if(f.open(QIODevice::ReadOnly))
227 sf.file = score::readFileAsQString(f);
228 data.files.push_back({sf.filename, sf.file});
229 data.unity_cpp.append(sf.path.toStdString());
230 plug.files.push_back(std::move(sf));
237 else if(*it ==
"MAIN_CLASS" || *it ==
"CLASS")
241 plug.main_class = *it;
243 else if(*it ==
"TARGET" || *it ==
"C_NAME")
249 else if(*it ==
"NAMESPACE")
253 plug.avnd_namespace = *it;
257 if(plug.files.empty())
259 qDebug() <<
"no files found";
263 for(
auto& gp : groups)
265 if(plug.base_target == gp.base_target)
266 gp.plugins.push_back(std::move(plug));
270 auto sdk_location = locateSDKWithFallback();
271 auto qsdk = QString::fromStdString(sdk_location.path);
272 QString prototypes_folders;
273 if(sdk_location.sdk_kind == located_sdk::official && sdk_location.deploying)
275 prototypes_folders = qsdk +
"/lib/cmake/score";
280 = QString::fromUtf8(SCORE_ROOT_SOURCE_DIR) +
"/src/plugins/score-plugin-avnd/";
283 for(
auto& gp : groups)
285 auto avnd_plugin_version = gp.version.toUtf8();
286 auto avnd_plugin_uuid = gp.uuid.toUtf8();
287 avnd_plugin_uuid.removeIf([](
char c) {
return c ==
'"'; });
288 auto avnd_base_target = gp.base_target.toUtf8();
290 QFile proto_file = QFile(prototypes_folders +
"/prototype.cpp.in");
291 proto_file.open(QIODevice::ReadOnly);
292 auto proto = proto_file.readAll();
294 QFile cpp_proto_file = QFile(prototypes_folders +
"/plugin_prototype.cpp.in");
295 cpp_proto_file.open(QIODevice::ReadOnly);
296 auto cpp_proto = cpp_proto_file.readAll();
298 QFile hpp_proto_file = QFile(prototypes_folders +
"/plugin_prototype.hpp.in");
299 hpp_proto_file.open(QIODevice::ReadOnly);
300 auto hpp_proto = hpp_proto_file.readAll();
302 QByteArray cpp_proto_replaced = cpp_proto;
303 cpp_proto_replaced.replace(R
"_(#include "@AVND_BASE_TARGET@.hpp")_", "");
304 QByteArray hpp_proto_replaced = hpp_proto;
306 QString avnd_additional_classes;
307 QString avnd_custom_factories;
309 for(
auto& plug : gp.plugins)
311 QByteArray proto_replaced = proto;
312 proto_replaced.replace(R
"_(#cmakedefine AVND_REFLECTION_HELPERS)_", "");
313 auto avnd_main_file = plug.files[0].filename.toUtf8();
314 auto avnd_qualified = (plug.avnd_namespace +
"::" + plug.main_class).toUtf8();
315 proto_replaced.replace(
"@AVND_MAIN_FILE@", avnd_main_file);
316 proto_replaced.replace(
"@AVND_QUALIFIED@", avnd_qualified);
317 proto_replaced.replace(
"@AVND_BASE_TARGET@", avnd_base_target);
318 if(!plug.avnd_namespace.isEmpty())
320 avnd_additional_classes.append(QString(
"namespace %1 { struct %2; }\n")
321 .arg(plug.avnd_namespace)
322 .arg(plug.main_class)
324 avnd_custom_factories.append(
325 QString(
"::oscr::custom_factories<%1>(fx, ctx, key); \n")
331 avnd_additional_classes.append(
332 QString(
"struct %1; \n").arg(plug.main_class).toUtf8());
333 avnd_custom_factories.append(
334 QString(
"::oscr::custom_factories<%1>(fx, ctx, key); \n")
335 .arg(plug.main_class)
338 protos.append(proto_replaced);
340 for(QByteArray& f : {std::ref(cpp_proto_replaced), std::ref(hpp_proto_replaced)})
342 f.replace(
"@AVND_PLUGIN_VERSION@", avnd_plugin_version);
343 f.replace(
"@AVND_PLUGIN_UUID@", avnd_plugin_uuid);
344 f.replace(
"@AVND_BASE_TARGET@", avnd_base_target);
345 f.replace(
"@AVND_ADDITIONAL_CLASSES@", avnd_additional_classes.toUtf8());
346 f.replace(
"@AVND_CUSTOM_FACTORIES@", avnd_custom_factories.toUtf8());
349 data.unity_cpp.append(hpp_proto_replaced);
350 data.unity_cpp.append(protos);
351 data.unity_cpp.append(cpp_proto_replaced);
353 qDebug().noquote().nospace() <<
"===============================================\n"
354 << data.unity_cpp.data();
358static AddonData loadAddon(
const QString& addon)
361 if(QFile f(addon + QDir::separator() +
"addon.json"); f.open(QIODevice::ReadOnly))
363 qDebug() <<
"Loading addon info from: " << f.fileName();
364 f.setTextModeEnabled(
true);
365 data.addon_info = QJsonDocument::fromJson(f.readAll()).object();
368 if(QFile f(addon + QDir::separator() +
"CMakeLists.txt"); f.open(QIODevice::ReadOnly))
370 qDebug() <<
"Loading CMake-based add-on: " << f.fileName();
372 f.setTextModeEnabled(
true);
373 loadCMakeAddon(addon, data, f.readAll());
377 qDebug() <<
"Loading non-CMake-based add-on";
378 loadBasicAddon(addon, data);
386static void generateCommandFiles(
387 const QString& output,
const QString& addon_name,
const QString& addon_path,
388 const std::vector<std::pair<QString, QString>>& files)
390 QRegularExpression decl(
391 "SCORE_COMMAND_DECL\\([A-Za-z_0-9,:<>\r\n\t "
392 "]*\\(\\)[A-Za-z_0-9,\"':<>\r\n\t ]*\\)");
393 QRegularExpression decl_t(
"SCORE_COMMAND_DECL_T\\([A-Za-z_0-9,:<>\r\n\t ]*\\)");
397 for(
const auto& f : files)
400 auto res = decl.globalMatch(f.second);
403 auto match = res.next();
404 if(
auto txt = match.capturedTexts(); !txt.empty())
406 if(
auto split = txt[0].split(
","); split.size() > 1)
408 auto filename = f.first;
409 filename.remove(addon_path +
"/");
410 includes +=
"#include <" + filename +
">\n";
411 commands += split[1] +
",\n";
417 commands.remove(commands.length() - 2, 2);
418 commands.push_back(
"\n");
419 QDir{}.mkpath(output);
422 QString out_name = addon_name;
423 out_name.replace(
"-",
"_");
425 QFile cmd_f{output +
"/" + out_name +
"_commands_files.hpp"};
426 cmd_f.open(QIODevice::WriteOnly);
427 cmd_f.write(includes.toUtf8());
431 QFile cmd_f{output +
"/" + out_name +
"_commands.hpp"};
432 cmd_f.open(QIODevice::WriteOnly);
433 cmd_f.write(commands.toUtf8());
439static void generateExportFile(
440 const QString& addon_files_path,
const QString& addon_name,
441 const QByteArray& addon_export)
443 QFile export_file = QFile{addon_files_path +
"/" + addon_name +
"_export.h"};
444 export_file.open(QIODevice::WriteOnly);
445 QByteArray export_data{
446 "#ifndef " + addon_export +
"_EXPORT_H\n"
447 "#define " + addon_export +
"_EXPORT_H\n"
448 "#define " + addon_export +
"_EXPORT __attribute__((visibility(\"default\")))\n"
449 "#define " + addon_export +
"_DEPRECATED [[deprecated]]\n"
452 export_file.write(export_data);
458static QString generateAddonFiles(
459 QString addon_name,
const QString& addon,
460 const std::vector<std::pair<QString, QString>>& files)
462 addon_name.replace(
"-",
"_");
463 QByteArray addon_export = addon_name.toUpper().toUtf8();
465 QString addon_files_path = QDir::tempPath() +
"/score-tmp-build/" + addon_name;
466 QDir{}.mkpath(addon_files_path);
467 generateExportFile(addon_files_path, addon_name, addon_export);
468 generateCommandFiles(addon_files_path, addon_name, addon, files);
469 return addon_files_path;
Definition MetadataGenerator.hpp:20
QString target
Definition MetadataGenerator.hpp:24