2#include <JitCpp/JitPlatform.hpp>
4#include <score/tools/File.hpp>
9#include <QJsonDocument>
11#include <QRegularExpression>
12#include <QStringBuilder>
21 QJsonObject addon_info;
22 std::string unity_cpp;
24 std::vector<std::pair<QString, QString>> files;
25 std::vector<std::string> flags;
31static void loadBasicAddon(
const QString& addon,
AddonData& data)
33 std::string cpp_files;
34 std::vector<std::pair<QString, QString>> files;
38 QDir::Filter::Files | QDir::Filter::NoDotAndDotDot,
39 QDirIterator::Subdirectories};
43 if(QFile f(it.next()); f.open(QIODevice::ReadOnly))
46 if(fi.suffix() ==
"cpp")
48 data.unity_cpp.append(
"#include \"" + it.filePath().toStdString() +
"\"\n");
51 data.files.push_back({fi.filePath(), f.readAll()});
56static void loadCMakeAddon(
const QString& addon, AddonData& data, QString cm)
58 static const QRegularExpression space{R
"_(\s+)_"};
59 static const QRegularExpression sourceFiles{
60 R
"_(add_library\(\s*[[:graph:]]+([a-zA-Z0-9_.\/\n ]*)\))_"};
61 static const QRegularExpression definitions{
62 R
"_(target_compile_definitions\(\s*[[:graph:]]+\s*[[:graph:]]+([a-zA-Z0-9_"= ]+)\))_"};
63 static const QRegularExpression includes{
64 R
"_(target_include_directories\(\s*[[:graph:]]+\s*[[:graph:]]+([a-zA-Z0-9_\/ ]+)\))_"};
65 static const QRegularExpression avnd{
66 R
"_((?:avnd_score_plugin_add|avnd_addon_object)\(([a-zA-Z0-9_\/.\n" -]+)\))_"};
67 static const QRegularExpression avnd_finalize{
68 R
"_((?:avnd_score_plugin_finalize|avnd_addon_finalize)\(([a-zA-Z0-9_\/.\n" -]+)\))_"};
70 auto avnds = avnd.globalMatch(cm);
71 auto avnd_finalizes = avnd_finalize.globalMatch(cm);
74 auto files = sourceFiles.globalMatch(cm);
75 auto defs = definitions.globalMatch(cm);
76 auto incs = includes.globalMatch(cm);
77 while(files.hasNext())
79 auto m = files.next();
80 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
81 for(
const QString& file : res)
83 QString filename = QString{R
"_(%1/%2)_"}.arg(addon).arg(file);
87 if(!QFile::exists(filename))
90 QString path = QString{R
"_(#include "%1/%2"
94 data.unity_cpp.append(path.toStdString());
97 f.open(QIODevice::ReadOnly);
98 data.files.push_back({filename, score::readFileAsQString(f)});
101 while(defs.hasNext())
103 auto m = defs.next();
104 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
105 for(
const QString& define : res)
107 data.flags.push_back(QString{R
"_(-D%1)_"}.arg(define).toStdString());
111 while(incs.hasNext())
113 auto m = incs.next();
114 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
116 for(
const QString& path : res)
118 data.flags.push_back(QString{R
"_(-I%1/%2)_"}.arg(addon).arg(path).toStdString());
128 QString avnd_namespace;
135 std::vector<source_file> files;
138 struct avnd_plugin_group
143 std::vector<avnd_plugin> plugins;
146 std::vector<avnd_plugin_group> groups;
147 qDebug() <<
"Found avnd finalize? " << avnd_finalizes.hasNext();
148 while(avnd_finalizes.hasNext())
150 auto m = avnd_finalizes.next();
151 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
153 avnd_plugin_group plug;
154 for(
auto it = res.begin(); it != res.end(); ++it)
156 if(*it ==
"BASE_TARGET" || *it ==
"NAME")
160 plug.base_target = *it;
162 else if(*it ==
"PLUGIN_VERSION" || *it ==
"VERSION")
168 else if(*it ==
"PLUGIN_UUID" || *it ==
"UUID")
176 if(!plug.base_target.isEmpty())
177 groups.push_back(std::move(plug));
180 while(avnds.hasNext())
182 auto m = avnds.next();
183 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
185 auto unquote = [](QString s) {
189 for(
auto it = res.begin(); it != res.end(); ++it)
191 if(*it ==
"BASE_TARGET" || *it ==
"BASE")
195 plug.base_target = *it;
197 else if(*it ==
"SOURCES")
200 while(it != res.end()
201 && QFile::exists(QString{
"%1/%2"}.arg(addon).arg(unquote(*it))))
203 const QString file = unquote(*it);
204 avnd_plugin::source_file sf;
205 sf.filename = QString{
"%1/%2"}.arg(addon).arg(file);
206 sf.path = QString{
"#include \"%1/%2\"\n"}.arg(addon).arg(file);
208 QFile f{sf.filename};
209 if(f.open(QIODevice::ReadOnly))
211 sf.file = score::readFileAsQString(f);
212 data.files.push_back({sf.filename, sf.file});
213 data.unity_cpp.append(sf.path.toStdString());
214 plug.files.push_back(std::move(sf));
221 else if(*it ==
"MAIN_CLASS" || *it ==
"CLASS")
225 plug.main_class = *it;
227 else if(*it ==
"TARGET" || *it ==
"C_NAME")
233 else if(*it ==
"NAMESPACE")
237 plug.avnd_namespace = *it;
241 if(plug.files.empty())
243 qDebug() <<
"no files found";
247 for(
auto& gp : groups)
249 if(plug.base_target == gp.base_target)
250 gp.plugins.push_back(std::move(plug));
254 auto sdk_location = locateSDKWithFallback();
255 auto qsdk = QString::fromStdString(sdk_location.path);
256 QString prototypes_folders;
257 if(sdk_location.sdk_kind == located_sdk::official && sdk_location.deploying)
259 prototypes_folders = qsdk +
"/lib/cmake/score";
264 = QString::fromUtf8(SCORE_ROOT_SOURCE_DIR) +
"/src/plugins/score-plugin-avnd/";
267 for(
auto& gp : groups)
269 auto avnd_plugin_version = gp.version.toUtf8();
270 auto avnd_plugin_uuid = gp.uuid.toUtf8();
271 avnd_plugin_uuid.removeIf([](
char c) {
return c ==
'"'; });
272 auto avnd_base_target = gp.base_target.toUtf8();
274 QFile proto_file = QFile(prototypes_folders +
"/prototype.cpp.in");
275 proto_file.open(QIODevice::ReadOnly);
276 auto proto = proto_file.readAll();
278 QFile cpp_proto_file = QFile(prototypes_folders +
"/plugin_prototype.cpp.in");
279 cpp_proto_file.open(QIODevice::ReadOnly);
280 auto cpp_proto = cpp_proto_file.readAll();
282 QFile hpp_proto_file = QFile(prototypes_folders +
"/plugin_prototype.hpp.in");
283 hpp_proto_file.open(QIODevice::ReadOnly);
284 auto hpp_proto = hpp_proto_file.readAll();
286 QByteArray cpp_proto_replaced = cpp_proto;
287 cpp_proto_replaced.replace(R
"_(#include "@AVND_BASE_TARGET@.hpp")_", "");
288 QByteArray hpp_proto_replaced = hpp_proto;
290 QString avnd_additional_classes;
291 QString avnd_custom_factories;
293 for(
auto& plug : gp.plugins)
295 QByteArray proto_replaced = proto;
296 proto_replaced.replace(R
"_(#cmakedefine AVND_REFLECTION_HELPERS)_", "");
297 auto avnd_main_file = plug.files[0].filename.toUtf8();
298 auto avnd_qualified = (plug.avnd_namespace +
"::" + plug.main_class).toUtf8();
299 proto_replaced.replace(
"@AVND_MAIN_FILE@", avnd_main_file);
300 proto_replaced.replace(
"@AVND_QUALIFIED@", avnd_qualified);
301 proto_replaced.replace(
"@AVND_BASE_TARGET@", avnd_base_target);
302 if(!plug.avnd_namespace.isEmpty())
304 avnd_additional_classes.append(QString(
"namespace %1 { struct %2; }\n")
305 .arg(plug.avnd_namespace)
306 .arg(plug.main_class)
308 avnd_custom_factories.append(
309 QString(
"::oscr::custom_factories<%1>(fx, ctx, key); \n")
315 avnd_additional_classes.append(
316 QString(
"struct %1; \n").arg(plug.main_class).toUtf8());
317 avnd_custom_factories.append(
318 QString(
"::oscr::custom_factories<%1>(fx, ctx, key); \n")
319 .arg(plug.main_class)
322 protos.append(proto_replaced);
324 for(QByteArray& f : {std::ref(cpp_proto_replaced), std::ref(hpp_proto_replaced)})
326 f.replace(
"@AVND_PLUGIN_VERSION@", avnd_plugin_version);
327 f.replace(
"@AVND_PLUGIN_UUID@", avnd_plugin_uuid);
328 f.replace(
"@AVND_BASE_TARGET@", avnd_base_target);
329 f.replace(
"@AVND_ADDITIONAL_CLASSES@", avnd_additional_classes.toUtf8());
330 f.replace(
"@AVND_CUSTOM_FACTORIES@", avnd_custom_factories.toUtf8());
333 data.unity_cpp.append(hpp_proto_replaced);
334 data.unity_cpp.append(protos);
335 data.unity_cpp.append(cpp_proto_replaced);
337 qDebug().noquote().nospace() <<
"===============================================\n"
338 << data.unity_cpp.data();
342static AddonData loadAddon(
const QString& addon)
345 if(QFile f(addon + QDir::separator() +
"addon.json"); f.open(QIODevice::ReadOnly))
347 qDebug() <<
"Loading addon info from: " << f.fileName();
348 f.setTextModeEnabled(
true);
349 data.addon_info = QJsonDocument::fromJson(f.readAll()).object();
352 if(QFile f(addon + QDir::separator() +
"CMakeLists.txt"); f.open(QIODevice::ReadOnly))
354 qDebug() <<
"Loading CMake-based add-on: " << f.fileName();
356 f.setTextModeEnabled(
true);
357 loadCMakeAddon(addon, data, f.readAll());
361 qDebug() <<
"Loading non-CMake-based add-on";
362 loadBasicAddon(addon, data);
370static void generateCommandFiles(
371 const QString& output,
const QString& addon_path,
372 const std::vector<std::pair<QString, QString>>& files)
374 QRegularExpression decl(
375 "SCORE_COMMAND_DECL\\([A-Za-z_0-9,:<>\r\n\t "
376 "]*\\(\\)[A-Za-z_0-9,\"':<>\r\n\t ]*\\)");
377 QRegularExpression decl_t(
"SCORE_COMMAND_DECL_T\\([A-Za-z_0-9,:<>\r\n\t ]*\\)");
381 for(
const auto& f : files)
384 auto res = decl.globalMatch(f.second);
387 auto match = res.next();
388 if(
auto txt = match.capturedTexts(); !txt.empty())
390 if(
auto split = txt[0].split(
","); split.size() > 1)
392 auto filename = f.first;
393 filename.remove(addon_path +
"/");
394 includes +=
"#include <" + filename +
">\n";
395 commands += split[1] +
",\n";
401 commands.remove(commands.length() - 2, 2);
402 commands.push_back(
"\n");
403 QDir{}.mkpath(output);
404 auto out_name = QFileInfo{addon_path}.fileName().replace(
"-",
"_");
406 QFile cmd_f{output +
"/" + out_name +
"_commands_files.hpp"};
407 cmd_f.open(QIODevice::WriteOnly);
408 cmd_f.write(includes.toUtf8());
412 QFile cmd_f{output +
"/" + out_name +
"_commands.hpp"};
413 cmd_f.open(QIODevice::WriteOnly);
414 cmd_f.write(commands.toUtf8());
420static void generateExportFile(
421 const QString& addon_files_path,
const QString& addon_name,
422 const QByteArray& addon_export)
424 QFile export_file = QFile{addon_files_path +
"/" + addon_name +
"_export.h"};
425 export_file.open(QIODevice::WriteOnly);
426 QByteArray export_data{
427 "#ifndef " + addon_export +
"_EXPORT_H\n"
428 "#define " + addon_export +
"_EXPORT_H\n"
429 "#define " + addon_export +
"_EXPORT __attribute__((visibility(\"default\")))\n"
430 "#define " + addon_export +
"_DEPRECATED [[deprecated]]\n"
433 export_file.write(export_data);
439static QString generateAddonFiles(
440 QString addon_name,
const QString& addon,
441 const std::vector<std::pair<QString, QString>>& files)
443 addon_name.replace(
"-",
"_");
444 QByteArray addon_export = addon_name.toUpper().toUtf8();
446 QString addon_files_path = QDir::tempPath() +
"/score-tmp-build/" + addon_name;
447 QDir{}.mkpath(addon_files_path);
448 generateExportFile(addon_files_path, addon_name, addon_export);
449 generateCommandFiles(addon_files_path, addon, files);
450 return addon_files_path;
Definition MetadataGenerator.hpp:20