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\(([a-zA-Z0-9_\/.\n ]+)\))_"};
67 static const QRegularExpression avnd_finalize{
68 R
"_(avnd_score_plugin_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);
85 QString path = QString{R"_(#include "%1/%2"
89 data.unity_cpp.append(path.toStdString());
92 f.open(QIODevice::ReadOnly);
93 data.files.push_back({filename, score::readFileAsQString(f)});
99 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
100 for(
const QString& define : res)
102 data.flags.push_back(QString{R
"_(-D%1)_"}.arg(define).toStdString());
106 while(incs.hasNext())
108 auto m = incs.next();
109 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
111 for(
const QString& path : res)
113 data.flags.push_back(QString{R
"_(-I%1/%2)_"}.arg(addon).arg(path).toStdString());
123 QString avnd_namespace;
130 std::vector<source_file> files;
133 struct avnd_plugin_group
138 std::vector<avnd_plugin> plugins;
141 std::vector<avnd_plugin_group> groups;
142 qDebug() <<
"Found avnd finalize? " << avnd_finalizes.hasNext();
143 while(avnd_finalizes.hasNext())
145 auto m = avnd_finalizes.next();
146 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
148 avnd_plugin_group plug;
149 for(
auto it = res.begin(); it != res.end(); ++it)
151 if(*it ==
"BASE_TARGET")
155 plug.base_target = *it;
157 else if(*it ==
"PLUGIN_VERSION")
163 else if(*it ==
"PLUGIN_UUID")
171 if(!plug.base_target.isEmpty())
172 groups.push_back(std::move(plug));
175 while(avnds.hasNext())
177 auto m = avnds.next();
178 auto res = m.captured(1).replace(
'\n',
' ').split(space, Qt::SkipEmptyParts);
180 for(
auto it = res.begin(); it != res.end(); ++it)
182 if(*it ==
"BASE_TARGET")
186 plug.base_target = *it;
188 else if(*it ==
"SOURCES")
191 while(it != res.end() && QFile::exists(QString{
"%1/%2"}.arg(addon).arg(*it)))
193 avnd_plugin::source_file sf;
194 sf.filename = QString{
"%1/%2"}.arg(addon).arg(*it);
195 sf.path = QString{
"#include \"%1/%2\"\n"}.arg(addon).arg(*it);
197 QFile f{sf.filename};
198 if(f.open(QIODevice::ReadOnly))
200 sf.file = score::readFileAsQString(f);
201 data.files.push_back({sf.filename, sf.file});
202 data.unity_cpp.append(sf.path.toStdString());
203 plug.files.push_back(std::move(sf));
210 else if(*it ==
"MAIN_CLASS")
214 plug.main_class = *it;
216 else if(*it ==
"TARGET")
222 else if(*it ==
"NAMESPACE")
226 plug.avnd_namespace = *it;
230 if(plug.files.empty())
232 qDebug() <<
"no files found";
236 for(
auto& gp : groups)
238 if(plug.base_target == gp.base_target)
239 gp.plugins.push_back(std::move(plug));
243 auto sdk_location = locateSDKWithFallback();
244 auto qsdk = QString::fromStdString(sdk_location.path);
245 QString prototypes_folders;
246 if(sdk_location.sdk_kind == located_sdk::official && sdk_location.deploying)
248 prototypes_folders = qsdk +
"/lib/cmake/score";
253 = QString::fromUtf8(SCORE_ROOT_SOURCE_DIR) +
"/src/plugins/score-plugin-avnd/";
256 for(
auto& gp : groups)
258 auto avnd_plugin_version = gp.version.toUtf8();
259 auto avnd_plugin_uuid = gp.uuid.toUtf8();
260 avnd_plugin_uuid.removeIf([](
char c) {
return c ==
'"'; });
261 auto avnd_base_target = gp.base_target.toUtf8();
263 QFile proto_file = QFile(prototypes_folders +
"/prototype.cpp.in");
264 proto_file.open(QIODevice::ReadOnly);
265 auto proto = proto_file.readAll();
267 QFile cpp_proto_file = QFile(prototypes_folders +
"/plugin_prototype.cpp.in");
268 cpp_proto_file.open(QIODevice::ReadOnly);
269 auto cpp_proto = cpp_proto_file.readAll();
271 QFile hpp_proto_file = QFile(prototypes_folders +
"/plugin_prototype.hpp.in");
272 hpp_proto_file.open(QIODevice::ReadOnly);
273 auto hpp_proto = hpp_proto_file.readAll();
275 QByteArray cpp_proto_replaced = cpp_proto;
276 cpp_proto_replaced.replace(R
"_(#include "@AVND_BASE_TARGET@.hpp")_", "");
277 QByteArray hpp_proto_replaced = hpp_proto;
279 QString avnd_additional_classes;
280 QString avnd_custom_factories;
282 for(
auto& plug : gp.plugins)
284 QByteArray proto_replaced = proto;
285 proto_replaced.replace(R
"_(#cmakedefine AVND_REFLECTION_HELPERS)_", "");
286 auto avnd_main_file = plug.files[0].filename.toUtf8();
287 auto avnd_qualified = (plug.avnd_namespace +
"::" + plug.main_class).toUtf8();
288 proto_replaced.replace(
"@AVND_MAIN_FILE@", avnd_main_file);
289 proto_replaced.replace(
"@AVND_QUALIFIED@", avnd_qualified);
290 proto_replaced.replace(
"@AVND_BASE_TARGET@", avnd_base_target);
291 if(!plug.avnd_namespace.isEmpty())
293 avnd_additional_classes.append(QString(
"namespace %1 { struct %2; }\n")
294 .arg(plug.avnd_namespace)
295 .arg(plug.main_class)
297 avnd_custom_factories.append(
298 QString(
"::oscr::custom_factories<%1>(fx, ctx, key); \n")
304 avnd_additional_classes.append(
305 QString(
"struct %1; \n").arg(plug.main_class).toUtf8());
306 avnd_custom_factories.append(
307 QString(
"::oscr::custom_factories<%1>(fx, ctx, key); \n")
308 .arg(plug.main_class)
311 protos.append(proto_replaced);
313 for(QByteArray& f : {std::ref(cpp_proto_replaced), std::ref(hpp_proto_replaced)})
315 f.replace(
"@AVND_PLUGIN_VERSION@", avnd_plugin_version);
316 f.replace(
"@AVND_PLUGIN_UUID@", avnd_plugin_uuid);
317 f.replace(
"@AVND_BASE_TARGET@", avnd_base_target);
318 f.replace(
"@AVND_ADDITIONAL_CLASSES@", avnd_additional_classes.toUtf8());
319 f.replace(
"@AVND_CUSTOM_FACTORIES@", avnd_custom_factories.toUtf8());
322 data.unity_cpp.append(hpp_proto_replaced);
323 data.unity_cpp.append(protos);
324 data.unity_cpp.append(cpp_proto_replaced);
326 qDebug().noquote().nospace() <<
"===============================================\n"
327 << data.unity_cpp.data();
331static AddonData loadAddon(
const QString& addon)
334 if(QFile f(addon + QDir::separator() +
"addon.json"); f.open(QIODevice::ReadOnly))
336 qDebug() <<
"Loading addon info from: " << f.fileName();
337 f.setTextModeEnabled(
true);
338 data.addon_info = QJsonDocument::fromJson(f.readAll()).object();
341 if(QFile f(addon + QDir::separator() +
"CMakeLists.txt"); f.open(QIODevice::ReadOnly))
343 qDebug() <<
"Loading CMake-based add-on: " << f.fileName();
345 f.setTextModeEnabled(
true);
346 loadCMakeAddon(addon, data, f.readAll());
350 qDebug() <<
"Loading non-CMake-based add-on";
351 loadBasicAddon(addon, data);
359static void generateCommandFiles(
360 const QString& output,
const QString& addon_path,
361 const std::vector<std::pair<QString, QString>>& files)
363 QRegularExpression decl(
364 "SCORE_COMMAND_DECL\\([A-Za-z_0-9,:<>\r\n\t "
365 "]*\\(\\)[A-Za-z_0-9,\"':<>\r\n\t ]*\\)");
366 QRegularExpression decl_t(
"SCORE_COMMAND_DECL_T\\([A-Za-z_0-9,:<>\r\n\t ]*\\)");
370 for(
const auto& f : files)
373 auto res = decl.globalMatch(f.second);
376 auto match = res.next();
377 if(
auto txt = match.capturedTexts(); !txt.empty())
379 if(
auto split = txt[0].split(
","); split.size() > 1)
381 auto filename = f.first;
382 filename.remove(addon_path +
"/");
383 includes +=
"#include <" + filename +
">\n";
384 commands += split[1] +
",\n";
390 commands.remove(commands.length() - 2, 2);
391 commands.push_back(
"\n");
392 QDir{}.mkpath(output);
393 auto out_name = QFileInfo{addon_path}.fileName().replace(
"-",
"_");
395 QFile cmd_f{output +
"/" + out_name +
"_commands_files.hpp"};
396 cmd_f.open(QIODevice::WriteOnly);
397 cmd_f.write(includes.toUtf8());
401 QFile cmd_f{output +
"/" + out_name +
"_commands.hpp"};
402 cmd_f.open(QIODevice::WriteOnly);
403 cmd_f.write(commands.toUtf8());
409static void generateExportFile(
410 const QString& addon_files_path,
const QString& addon_name,
411 const QByteArray& addon_export)
413 QFile export_file = QFile{addon_files_path +
"/" + addon_name +
"_export.h"};
414 export_file.open(QIODevice::WriteOnly);
415 QByteArray export_data{
416 "#ifndef " + addon_export +
"_EXPORT_H\n"
417 "#define " + addon_export +
"_EXPORT_H\n"
418 "#define " + addon_export +
"_EXPORT __attribute__((visibility(\"default\")))\n"
419 "#define " + addon_export +
"_DEPRECATED [[deprecated]]\n"
422 export_file.write(export_data);
428static QString generateAddonFiles(
429 QString addon_name,
const QString& addon,
430 const std::vector<std::pair<QString, QString>>& files)
432 addon_name.replace(
"-",
"_");
433 QByteArray addon_export = addon_name.toUpper().toUtf8();
435 QString addon_files_path = QDir::tempPath() +
"/score-tmp-build/" + addon_name;
436 QDir{}.mkpath(addon_files_path);
437 generateExportFile(addon_files_path, addon_name, addon_export);
438 generateCommandFiles(addon_files_path, addon, files);
439 return addon_files_path;
Definition MetadataGenerator.hpp:20