Loading...
Searching...
No Matches
MetadataGenerator.hpp
1#pragma once
2#include <score/tools/File.hpp>
3
4#include <QDir>
5#include <QDirIterator>
6#include <QFile>
7#include <QJsonDocument>
8#include <QJsonObject>
9#include <QRegularExpression>
10#include <QStringBuilder>
11
12#include <vector>
13
14namespace Jit
15{
16
17struct AddonData
18{
19 QJsonObject addon_info;
20 std::string unity_cpp;
21 // Warning ! if ever changing that to QByteArray, look for mapFile usage as right now the file is read with mmap
22 std::vector<std::pair<QString, QString>> files;
23 std::vector<std::string> flags;
24};
25
29static void loadBasicAddon(const QString& addon, AddonData& data)
30{
31 std::string cpp_files;
32 std::vector<std::pair<QString, QString>> files;
33 QDirIterator it{
34 addon,
35 {"*.cpp", "*.hpp"},
36 QDir::Filter::Files | QDir::Filter::NoDotAndDotDot,
37 QDirIterator::Subdirectories};
38
39 while(it.hasNext())
40 {
41 if(QFile f(it.next()); f.open(QIODevice::ReadOnly))
42 {
43 QFileInfo fi{f};
44 if(fi.suffix() == "cpp")
45 {
46 data.unity_cpp.append("#include \"" + it.filePath().toStdString() + "\"\n");
47 }
48
49 data.files.push_back({fi.filePath(), f.readAll()});
50 }
51 }
52}
53
54static void loadCMakeAddon(const QString& addon, AddonData& data, QString cm)
55{
56 static const QRegularExpression space{R"_(\s+)_"};
57 static const QRegularExpression sourceFiles{
58 R"_(add_library\‍(\s*[[:graph:]]+([a-zA-Z0-9_.\/\n ]*)\))_"};
59 static const QRegularExpression definitions{
60 R"_(target_compile_definitions\‍(\s*[[:graph:]]+\s*[[:graph:]]+([a-zA-Z0-9_"= ]+)\))_"};
61 static const QRegularExpression includes{
62 R"_(target_include_directories\‍(\s*[[:graph:]]+\s*[[:graph:]]+([a-zA-Z0-9_\/ ]+)\))_"};
63
64 auto files = sourceFiles.globalMatch(cm);
65 auto defs = definitions.globalMatch(cm);
66 auto incs = includes.globalMatch(cm);
67
68 while(files.hasNext())
69 {
70 auto m = files.next();
71 auto res = m.captured(1).replace('\n', ' ').split(space, Qt::SkipEmptyParts);
72 for(const QString& file : res)
73 {
74 QString filename = QString{R"_(%1/%2)_"}.arg(addon).arg(file);
75
76 QString path = QString{R"_(#include "%1/%2"
77)_"}
78 .arg(addon)
79 .arg(file);
80 data.unity_cpp.append(path.toStdString());
81
82 QFile f{filename};
83 f.open(QIODevice::ReadOnly);
84 data.files.push_back({filename, score::readFileAsQString(f)});
85 }
86 }
87 while(defs.hasNext())
88 {
89 auto m = defs.next();
90 auto res = m.captured(1).replace('\n', ' ').split(space, Qt::SkipEmptyParts);
91 for(const QString& define : res)
92 {
93 data.flags.push_back(QString{R"_(-D%1)_"}.arg(define).toStdString());
94 }
95 }
96
97 while(incs.hasNext())
98 {
99 auto m = incs.next();
100 auto res = m.captured(1).replace('\n', ' ').split(space, Qt::SkipEmptyParts);
101
102 for(const QString& path : res)
103 {
104 data.flags.push_back(QString{R"_(-I%1/%2)_"}.arg(addon).arg(path).toStdString());
105 }
106 }
107}
108
109static AddonData loadAddon(const QString& addon)
110{
111 AddonData data;
112 if(QFile f(addon + QDir::separator() + "addon.json"); f.open(QIODevice::ReadOnly))
113 {
114 qDebug() << "Loading addon info from: " << f.fileName();
115 f.setTextModeEnabled(true);
116 data.addon_info = QJsonDocument::fromJson(f.readAll()).object();
117 }
118
119 if(QFile f(addon + QDir::separator() + "CMakeLists.txt"); f.open(QIODevice::ReadOnly))
120 {
121 qDebug() << "Loading CMake-based add-on: " << f.fileName();
122 // Needed because regex uses \n
123 f.setTextModeEnabled(true);
124 loadCMakeAddon(addon, data, f.readAll());
125 }
126 else
127 {
128 qDebug() << "Loading non-CMake-based add-on";
129 loadBasicAddon(addon, data);
130 }
131
132 return data;
133}
134
137static void generateCommandFiles(
138 const QString& output, const QString& addon_path,
139 const std::vector<std::pair<QString, QString>>& files)
140{
141 QRegularExpression decl(
142 "SCORE_COMMAND_DECL\\([A-Za-z_0-9,:<>\r\n\t "
143 "]*\\(\\)[A-Za-z_0-9,\"':<>\r\n\t ]*\\)");
144 QRegularExpression decl_t("SCORE_COMMAND_DECL_T\\([A-Za-z_0-9,:<>\r\n\t ]*\\)");
145
146 QString includes;
147 QString commands;
148 for(const auto& f : files)
149 {
150 {
151 auto res = decl.globalMatch(f.second);
152 while(res.hasNext())
153 {
154 auto match = res.next();
155 if(auto txt = match.capturedTexts(); !txt.empty())
156 {
157 if(auto split = txt[0].split(","); split.size() > 1)
158 {
159 auto filename = f.first;
160 filename.remove(addon_path + "/");
161 includes += "#include <" + filename + ">\n";
162 commands += split[1] + ",\n";
163 }
164 }
165 }
166 }
167 }
168 commands.remove(commands.length() - 2, 2);
169 commands.push_back("\n");
170 QDir{}.mkpath(output);
171 auto out_name = QFileInfo{addon_path}.fileName().replace("-", "_");
172 {
173 QFile cmd_f{output + "/" + out_name + "_commands_files.hpp"};
174 cmd_f.open(QIODevice::WriteOnly);
175 cmd_f.write(includes.toUtf8());
176 cmd_f.close();
177 }
178 {
179 QFile cmd_f{output + "/" + out_name + "_commands.hpp"};
180 cmd_f.open(QIODevice::WriteOnly);
181 cmd_f.write(commands.toUtf8());
182 cmd_f.close();
183 }
184}
185
187static void generateExportFile(
188 const QString& addon_files_path, const QString& addon_name,
189 const QByteArray& addon_export)
190{
191 QFile export_file = QString{addon_files_path + "/" + addon_name + "_export.h"};
192 export_file.open(QIODevice::WriteOnly);
193 QByteArray export_data{
194 "#ifndef " + addon_export + "_EXPORT_H\n"
195 "#define " + addon_export + "_EXPORT_H\n"
196 "#define " + addon_export + "_EXPORT __attribute__((visibility(\"default\")))\n"
197 "#define " + addon_export + "_DEPRECATED [[deprecated]]\n"
198 "#endif\n"
199 };
200 export_file.write(export_data);
201 export_file.close();
202}
203
206static QString generateAddonFiles(
207 QString addon_name, const QString& addon,
208 const std::vector<std::pair<QString, QString>>& files)
209{
210 addon_name.replace("-", "_");
211 QByteArray addon_export = addon_name.toUpper().toUtf8();
212
213 QString addon_files_path = QDir::tempPath() + "/score-tmp-build/" + addon_name;
214 QDir{}.mkpath(addon_files_path);
215 generateExportFile(addon_files_path, addon_name, addon_export);
216 generateCommandFiles(addon_files_path, addon, files);
217 return addon_files_path;
218}
219
220}