1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "runner.h"
6
7#include "debuggersignals.h"
8#include "debuggerglobals.h"
9#include "base/abstractmenu.h"
10#include "services/language/languageservice.h"
11#include "services/project/projectservice.h"
12#include "services/builder/builderservice.h"
13#include "services/window/windowservice.h"
14
15#include <QMenu>
16#include <QTextBlock>
17
18using namespace dpfservice;
19
20class RunnerPrivate
21{
22 friend class Runner;
23 QString currentBuildUuid;
24 QString currentOpenedFilePath;
25 QSharedPointer<QAction> runAction;
26 bool isRunning = false;
27};
28
29Runner::Runner(QObject *parent)
30 : QObject(parent)
31 , d(new RunnerPrivate())
32{
33 connect(debuggerSignals, &DebuggerSignals::receivedEvent, this, &Runner::handleEvents);
34
35 d->runAction.reset(new QAction(MWMDA_RUNNING));
36 ActionManager::getInstance()->registerAction(d->runAction.get(), "Debug.Running",
37 MWMDA_RUNNING, QKeySequence(Qt::Modifier::CTRL | Qt::Key::Key_F5),
38 ":/resource/images/run.png");
39 connect(d->runAction.get(), &QAction::triggered, this, &Runner::run);
40 WindowService *service = dpfGetService(WindowService);
41 service->addToolBarActionItem(tr("Running"), d->runAction.get(), "Debug.Start");
42}
43
44void Runner::run()
45{
46 LanguageService *service = dpfGetService(LanguageService);
47 if (service) {
48 auto generator = service->create<LanguageGenerator>(getActiveProjectInfo().kitName());
49 if (generator) {
50 if (generator->isNeedBuild()) {
51 d->currentBuildUuid = generator->build(getActiveProjectInfo().workspaceFolder());
52 } else {
53 running();
54 }
55 }
56 }
57}
58
59void Runner::handleEvents(const dpf::Event &event)
60{
61 QString topic = event.topic();
62 QString data = event.data().toString();
63 if (topic == T_BUILDER) {
64 if (data == D_BUILD_STATE) {
65 int state = event.property(P_STATE).toInt();
66 BuildCommandInfo commandInfo = qvariant_cast<BuildCommandInfo>(event.property(P_ORIGINCMD));
67 if (commandInfo.uuid == d->currentBuildUuid) {
68 int buildSuccess = 0;
69 if (state == buildSuccess) {
70 running();
71 }
72 }
73 }
74 } else if (event.data() == editor.switchedFile.name) {
75 QString filePath = event.property(editor.switchedFile.pKeys[0]).toString();
76 if (d->currentOpenedFilePath != filePath) {
77 d->currentOpenedFilePath = filePath;
78 }
79 } else if (event.data() == editor.openedFile.name) {
80 QString filePath = event.property(editor.switchedFile.pKeys[0]).toString();
81 d->currentOpenedFilePath = filePath;
82 } else if (event.data() == editor.closedFile.name) {
83 QString filePath = event.property(editor.switchedFile.pKeys[0]).toString();
84 if (d->currentOpenedFilePath == filePath) {
85 d->currentOpenedFilePath.clear();
86 }
87 }
88}
89
90void Runner::running()
91{
92 if(d->isRunning) {
93 outputMsg("The program is running, please try again later!", OutputPane::OutputFormat::ErrorMessage);
94 return;
95 }
96
97 editor.switchContext(tr("&Application Output"));
98
99 LanguageService *service = dpfGetService(LanguageService);
100 if (service) {
101 auto generator = service->create<LanguageGenerator>(getActiveProjectInfo().kitName());
102 if (generator) {
103 dpfservice::ProjectInfo activeProjInfo = dpfGetService(ProjectService)->getActiveProjectInfo();
104 RunCommandInfo args = generator->getRunArguments(activeProjInfo, d->currentOpenedFilePath);
105 QtConcurrent::run([=](){
106 execCommand(args);
107 });
108 }
109 }
110}
111
112bool Runner::execCommand(const RunCommandInfo &info)
113{
114 bool ret = false;
115 QString retMsg = tr("Error: execute command error! The reason is unknown.\n");
116 QProcess process;
117 process.setWorkingDirectory(info.workingDir);
118
119 QString startMsg = tr("Start execute command: \"%1\" \"%2\" in workspace \"%3\".\n")
120 .arg(info.program, info.arguments.join(" "), info.workingDir);
121 outputMsg(startMsg, OutputPane::OutputFormat::NormalMessage);
122
123 connect(&process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
124 [&](int exitcode, QProcess::ExitStatus exitStatus) {
125 if (0 == exitcode && exitStatus == QProcess::ExitStatus::NormalExit) {
126 ret = true;
127 retMsg = tr("The process \"%1\" exited normally.\n").arg(process.program());
128 } else if (exitStatus == QProcess::NormalExit) {
129 ret = false;
130 retMsg = tr("The process \"%1\" exited with code %2.\n")
131 .arg(process.program(), QString::number(exitcode));
132 } else {
133 ret = false;
134 retMsg = tr("The process \"%1\" crashed.\n")
135 .arg(process.program());
136 }
137 });
138
139 connect(&process, &QProcess::readyReadStandardOutput, [&]() {
140 process.setReadChannel(QProcess::StandardOutput);
141 while (process.canReadLine()) {
142 QString line = QString::fromUtf8(process.readLine());
143 outputMsg(line, OutputPane::OutputFormat::StdOut);
144 }
145 });
146
147 connect(&process, &QProcess::readyReadStandardError, [&]() {
148 process.setReadChannel(QProcess::StandardError);
149 while (process.canReadLine()) {
150 QString line = QString::fromUtf8(process.readLine());
151 outputMsg(line, OutputPane::OutputFormat::StdErr);
152 }
153 });
154
155 process.start(info.program, info.arguments);
156 process.waitForFinished(-1);
157
158 outputMsg(retMsg, ret ? OutputPane::OutputFormat::NormalMessage : OutputPane::OutputFormat::StdErr);
159
160 QString endMsg = tr("Execute command finished.\n");
161 outputMsg(endMsg, OutputPane::OutputFormat::NormalMessage);
162
163 return ret;
164}
165
166void Runner::outputMsg(const QString &content, OutputPane::OutputFormat format)
167{
168 QMetaObject::invokeMethod(this, "synOutputMsg", Q_ARG(QString, content), Q_ARG(OutputPane::OutputFormat, format));
169}
170
171ProjectInfo Runner::getActiveProjectInfo() const
172{
173 return dpfGetService(ProjectService)->getActiveProjectInfo();
174}
175
176void Runner::synOutputMsg(const QString &content, OutputPane::OutputFormat format)
177{
178 auto outputPane = OutputPane::instance();
179 QString outputContent = content;
180 if (format == OutputPane::OutputFormat::NormalMessage) {
181 QTextDocument *doc = outputPane->document();
182 QTextBlock tb = doc->lastBlock();
183 QString lastLineText = tb.text();
184 QString prefix = "\n";
185 if (lastLineText.isEmpty()) {
186 prefix = "";
187 }
188 QDateTime curDatetime = QDateTime::currentDateTime();
189 QString time = curDatetime.toString("hh:mm:ss");
190 outputContent = prefix + time + ":" + content + "\n";
191 }
192 OutputPane::AppendMode mode = OutputPane::AppendMode::Normal;
193 outputPane->appendText(outputContent, format, mode);
194}
195
196