1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "gradleprojectgenerator.h" |
6 | #include "gradleasynparse.h" |
7 | #include "properties/gradleconfigpropertywidget.h" |
8 | |
9 | #include "common/dialog/propertiesdialog.h" |
10 | #include "services/window/windowservice.h" |
11 | #include "services/builder/builderservice.h" |
12 | #include "services/option/optionmanager.h" |
13 | #include "properties/gradleconfigutil.h" |
14 | #include "common/util/custompaths.h" |
15 | #include "common/util/environment.h" |
16 | #include "common/supportfile/dapconfig.h" |
17 | #include "services/project/projectservice.h" |
18 | |
19 | #include <QtConcurrent> |
20 | #include <QtXml> |
21 | #include <QFileIconProvider> |
22 | |
23 | using namespace gradleConfig; |
24 | using namespace dpfservice; |
25 | enum_def(GradleShellKey, QString) |
26 | { |
27 | enum_exp ScriptName = "gradlew" ; |
28 | enum_exp ScriptArg_Task = "tasks" ; |
29 | }; |
30 | |
31 | enum_def(, QString) |
32 | { |
33 | enum_exp = "Build tasks" ; |
34 | enum_exp = "Build Setup tasks" ; |
35 | enum_exp = "Documentation tasks" ; |
36 | enum_exp = "Help tasks" ; |
37 | enum_exp = "Verification tasks" ; |
38 | }; |
39 | |
40 | struct GradleTasksHelp |
41 | { |
42 | QString argsName; |
43 | QString argsHelp; |
44 | }; |
45 | |
46 | typedef QList<GradleTasksHelp> GradleTasksHelps; |
47 | typedef QHash<QString, GradleTasksHelps> GradleTasks; |
48 | |
49 | class GradleProjectGeneratorPrivate |
50 | { |
51 | friend class GradleProjectGenerator; |
52 | QStandardItem* configureRootItem {nullptr}; |
53 | QMenu * {nullptr}; |
54 | QProcess * {nullptr}; |
55 | QHash<QStandardItem*, GradleAsynParse*> projectParses {}; |
56 | private: |
57 | |
58 | GradleTasks parseTasks(const QByteArray &data) |
59 | { |
60 | GradleTasks result; |
61 | QTextStream textStream(data); |
62 | while (!textStream.atEnd()) { |
63 | qInfo() << textStream.readLine(); |
64 | } |
65 | return result; |
66 | } |
67 | |
68 | GradleTasks getTasks(QString sourceFolder, |
69 | QString shellName = GradleShellKey::get()->ScriptName) |
70 | { |
71 | GradleTasks result; |
72 | QString program = sourceFolder + QDir::separator() +shellName; |
73 | QStringList args = {GradleShellKey::get()->ScriptArg_Task}; |
74 | ProcessUtil::execute(program, args, sourceFolder, [&](const QByteArray &data){ |
75 | result = parseTasks(data); |
76 | }); |
77 | return result; |
78 | } |
79 | }; |
80 | |
81 | GradleProjectGenerator::GradleProjectGenerator() |
82 | : d(new GradleProjectGeneratorPrivate()) |
83 | { |
84 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>" ); |
85 | using namespace dpfservice; |
86 | auto &ctx = dpfInstance.serviceContext(); |
87 | ProjectService *projectService = ctx.service<ProjectService>(ProjectService::name()); |
88 | if (!projectService) { |
89 | qCritical() << "Failed, not found service : projectService" ; |
90 | abort(); |
91 | } |
92 | } |
93 | |
94 | GradleProjectGenerator::~GradleProjectGenerator() |
95 | { |
96 | qInfo() << __FUNCTION__; |
97 | |
98 | if (d) |
99 | delete d; |
100 | } |
101 | |
102 | QStringList GradleProjectGenerator::supportLanguages() |
103 | { |
104 | return {dpfservice::MWMFA_JAVA}; |
105 | } |
106 | |
107 | QStringList GradleProjectGenerator::supportFileNames() |
108 | { |
109 | return {"build.gradle" , "settings.gradle" }; |
110 | } |
111 | |
112 | QDialog *GradleProjectGenerator::configureWidget(const QString &language, |
113 | const QString &projectPath) |
114 | { |
115 | // get config result. |
116 | ProjectInfo projectInfo; |
117 | projectInfo.setLanguage(language); |
118 | projectInfo.setKitName(GradleProjectGenerator::toolKitName()); |
119 | projectInfo.setWorkspaceFolder(projectPath); |
120 | |
121 | // refresh config. |
122 | restoreRuntimeCfg(projectInfo); |
123 | configure(projectInfo); |
124 | |
125 | return nullptr; |
126 | } |
127 | |
128 | bool GradleProjectGenerator::configure(const dpfservice::ProjectInfo &info) |
129 | { |
130 | dpfservice::ProjectGenerator::configure(info); |
131 | |
132 | auto root = createRootItem(info); |
133 | using namespace dpfservice; |
134 | auto &ctx = dpfInstance.serviceContext(); |
135 | ProjectService *projectService = ctx.service<ProjectService>(ProjectService::name()); |
136 | if (projectService && root) { |
137 | projectService->projectView.addRootItem(root); |
138 | projectService->projectView.expandedDepth(root, 1); |
139 | } |
140 | |
141 | dpfservice::ProjectGenerator::configure(info); |
142 | |
143 | return true; |
144 | } |
145 | |
146 | QStandardItem *GradleProjectGenerator::createRootItem(const dpfservice::ProjectInfo &info) |
147 | { |
148 | using namespace dpfservice; |
149 | QStandardItem * rootItem = ProjectGenerator::createRootItem(info); |
150 | dpfservice::ProjectInfo::set(rootItem, info); |
151 | d->projectParses[rootItem] = new GradleAsynParse(); |
152 | QObject::connect(d->projectParses[rootItem], &GradleAsynParse::itemsModified, |
153 | this, &GradleProjectGenerator::doProjectChildsModified); |
154 | QMetaObject::invokeMethod(d->projectParses[rootItem], "parseProject" , |
155 | Q_ARG(const dpfservice::ProjectInfo &, info)); |
156 | return rootItem; |
157 | } |
158 | |
159 | void GradleProjectGenerator::removeRootItem(QStandardItem *root) |
160 | { |
161 | if (!root) |
162 | return; |
163 | auto parser = d->projectParses[root]; |
164 | |
165 | while (root->hasChildren()) { |
166 | root->takeRow(0); |
167 | } |
168 | d->projectParses.remove(root); |
169 | |
170 | delete root; |
171 | |
172 | if (parser) |
173 | delete parser; |
174 | } |
175 | |
176 | QMenu *GradleProjectGenerator::createItemMenu(const QStandardItem *item) |
177 | { |
178 | if (item->parent()) |
179 | return nullptr; |
180 | |
181 | using namespace dpfservice; |
182 | ProjectInfo info = ProjectInfo::get(item); |
183 | if (!QFileInfo(info.workspaceFolder()).exists()) |
184 | return nullptr; |
185 | |
186 | QString program = info.workspaceFolder() + QDir::separator() |
187 | + GradleShellKey::get()->ScriptName; |
188 | QStringList args = {GradleShellKey::get()->ScriptArg_Task}; |
189 | QMenu * = new QMenu(); |
190 | if (!d->gradleMenu) { |
191 | d->gradleMenu = new QMenu("Gradle" ); |
192 | doGradleGeneratMenu(program, args, info.workspaceFolder()); // asyn |
193 | } |
194 | menu->addMenu(d->gradleMenu); |
195 | |
196 | QStandardItem *itemTemp = const_cast<QStandardItem *>(item); |
197 | if (!itemTemp) |
198 | return d->gradleMenu; |
199 | |
200 | QAction *action = new QAction(tr("Properties" )); |
201 | menu->addAction(action); |
202 | QObject::connect(action, &QAction::triggered, [=](){ |
203 | actionProperties(info, itemTemp); |
204 | }); |
205 | |
206 | return menu; |
207 | } |
208 | |
209 | void GradleProjectGenerator::doProjectChildsModified(const QList<QStandardItem *> &items) |
210 | { |
211 | auto rootItem = d->projectParses.key(qobject_cast<GradleAsynParse*>(sender())); |
212 | if (rootItem) { |
213 | while (rootItem->hasChildren()) { |
214 | rootItem->takeRow(0); |
215 | } |
216 | rootItem->appendRows(items); |
217 | } |
218 | } |
219 | |
220 | void GradleProjectGenerator::(const QString &program, |
221 | const QStringList &args, |
222 | const QString &workdir) |
223 | { |
224 | struct { |
225 | QString operator ()(int count) |
226 | { |
227 | QString result; |
228 | for(int i= 0; i < count; i++) { |
229 | result += "-" ; |
230 | } |
231 | return result; |
232 | } |
233 | }taskChildSplit; |
234 | |
235 | if (!d->menuGenProcess) { |
236 | d->menuGenProcess = new QProcess(); |
237 | |
238 | QObject::connect(d->menuGenProcess, &QProcess::errorOccurred, |
239 | [=](QProcess::ProcessError error){ |
240 | qCritical() << "program: " << d->menuGenProcess->program() |
241 | << "aruments: " << d->menuGenProcess->arguments() |
242 | << "workdir: " << d->menuGenProcess->workingDirectory() |
243 | << "error" << error |
244 | << "errorString" << d->menuGenProcess->errorString(); |
245 | }); |
246 | |
247 | QObject::connect(d->menuGenProcess, |
248 | QOverload<int, QProcess::ExitStatus> |
249 | ::of(&QProcess::finished), |
250 | [&](int, QProcess::ExitStatus) |
251 | { |
252 | if (d->menuGenProcess->canReadLine() && d->gradleMenu) { |
253 | QTextStream stream(d->menuGenProcess->readAll()); |
254 | while (!stream.atEnd()) { |
255 | QString taskBegin = stream.readLine(); |
256 | QMenu * = nullptr; |
257 | if (GradleMenuKey::contains(taskBegin)) { |
258 | menu = new QMenu(taskBegin); |
259 | menu->setToolTipsVisible(true); |
260 | } |
261 | |
262 | if (menu) { |
263 | QString taskEnd = stream.readLine(); |
264 | while (taskEnd != "" ) { |
265 | taskEnd = stream.readLine(); |
266 | if (taskChildSplit(taskBegin.size()) |
267 | == taskEnd) { |
268 | continue; |
269 | } |
270 | QStringList taskChild = taskEnd.split(" - " ); |
271 | if (taskChild.size() == 2) { |
272 | QAction *action = new QAction(taskChild[0]); |
273 | qInfo() << taskChild[0] << taskChild[1]; |
274 | action->setToolTip(taskChild[1]); |
275 | action->setProperty("kitName" , GradleProjectGenerator::toolKitName()); |
276 | #ifdef __WIN32__ |
277 | action->setPriority("program" , "./gradlew.bat" ); |
278 | #elif __linux__ || __apple__ |
279 | action->setProperty("program" , OptionManager::getInstance()->getGradleToolPath()); |
280 | #endif |
281 | action->setProperty("arguments" , QStringList({"task" , taskChild[0]})); |
282 | action->setProperty("workDir" , d->menuGenProcess->workingDirectory()); |
283 | QObject::connect(action, &QAction::triggered, this, |
284 | &GradleProjectGenerator::doGradleTaskActionTriggered, |
285 | Qt::UniqueConnection); |
286 | menu->addAction(action); |
287 | } |
288 | } |
289 | d->gradleMenu->addMenu(menu); |
290 | } // menu is create |
291 | } // while readline |
292 | } // if can read line |
293 | }); |
294 | d->menuGenProcess->setProgram(program); |
295 | d->menuGenProcess->setArguments(args); |
296 | d->menuGenProcess->setWorkingDirectory(workdir); |
297 | d->menuGenProcess->start(); |
298 | } |
299 | } |
300 | |
301 | void GradleProjectGenerator::() |
302 | { |
303 | if (d->gradleMenu) { |
304 | for (auto &action : d->gradleMenu->actions()) { |
305 | d->gradleMenu->removeAction(action); |
306 | } |
307 | } |
308 | } |
309 | |
310 | void GradleProjectGenerator::doGradleTaskActionTriggered() |
311 | { |
312 | QAction *action = qobject_cast<QAction*>(sender()); |
313 | if (action) { |
314 | auto &ctx = dpfInstance.serviceContext(); |
315 | auto builderService = ctx.service<dpfservice::BuilderService>(dpfservice::BuilderService::name()); |
316 | if (builderService) { |
317 | BuildCommandInfo commandInfo; |
318 | commandInfo.kitName = action->property("kitName" ).toString(); |
319 | commandInfo.program = action->property("program" ).toString(); |
320 | commandInfo.arguments = action->property("arguments" ).toStringList(); |
321 | commandInfo.workingDir = action->property("workDir" ).toString(); |
322 | builderService->interface.builderCommand({commandInfo}, false); |
323 | } |
324 | } |
325 | } |
326 | |
327 | void GradleProjectGenerator::actionProperties(const dpfservice::ProjectInfo &info, QStandardItem *item) |
328 | { |
329 | PropertiesDialog dlg; |
330 | GradleConfigPropertyWidget *property = new GradleConfigPropertyWidget(info, item); |
331 | dlg.insertPropertyPanel("Config" , property); |
332 | dlg.exec(); |
333 | } |
334 | |
335 | void GradleProjectGenerator::restoreRuntimeCfg(dpfservice::ProjectInfo &projectInfo) |
336 | { |
337 | // get project config. |
338 | ConfigureParam *cfgParams = ConfigUtil::instance()->getConfigureParamPointer(); |
339 | ConfigUtil::instance()->readConfig(ConfigUtil::instance()->getConfigPath(projectInfo.workspaceFolder()), *cfgParams); |
340 | |
341 | // get global config. |
342 | QString arch = ProcessUtil::localPlatform(); |
343 | QString dapSupportFilePath = support_file::DapSupportConfig::globalPath(); |
344 | QString configHomePath = env::pkg::native::path();//CustomPaths::user(CustomPaths::Configures) + QDir::separator(); |
345 | support_file::JavaDapPluginConfig javaDapPluginConfig; |
346 | bool ret = support_file::DapSupportConfig::readFromSupportFile(dapSupportFilePath, arch, javaDapPluginConfig, configHomePath); |
347 | if (!ret) { |
348 | qDebug("Read dapconfig.support failed, please check the file and retry." ); |
349 | return; |
350 | } |
351 | |
352 | // use global config when project config is null. |
353 | auto setPropertyParams = [&](QString &projectProperty, const QString &key, const QString &globalPropery){ |
354 | if (projectProperty.isEmpty()) { |
355 | // use global propery. |
356 | projectInfo.setProperty(key, globalPropery); |
357 | projectProperty = globalPropery; |
358 | } else { |
359 | // use project propery. |
360 | projectInfo.setProperty(key, projectProperty); |
361 | } |
362 | }; |
363 | setPropertyParams(cfgParams->jrePath, kJrePath, javaDapPluginConfig.jrePath); |
364 | setPropertyParams(cfgParams->jreExecute, kJreExecute, javaDapPluginConfig.jreExecute); |
365 | setPropertyParams(cfgParams->launchConfigPath, kLaunchConfigPath, javaDapPluginConfig.launchConfigPath); |
366 | setPropertyParams(cfgParams->launchPackageFile, kLaunchPackageFile, javaDapPluginConfig.launchPackageFile); |
367 | setPropertyParams(cfgParams->dapPackageFile, kDapPackageFile, javaDapPluginConfig.dapPackageFile); |
368 | projectInfo.setDetailInformation(cfgParams->detailInfo); |
369 | } |
370 | |