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 | |
18 | using namespace dpfservice; |
19 | |
20 | class RunnerPrivate |
21 | { |
22 | friend class Runner; |
23 | QString currentBuildUuid; |
24 | QString currentOpenedFilePath; |
25 | QSharedPointer<QAction> runAction; |
26 | bool isRunning = false; |
27 | }; |
28 | |
29 | Runner::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 | |
44 | void 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 | |
59 | void 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 | |
90 | void 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 | |
112 | bool 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 | |
166 | void 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 | |
171 | ProjectInfo Runner::getActiveProjectInfo() const |
172 | { |
173 | return dpfGetService(ProjectService)->getActiveProjectInfo(); |
174 | } |
175 | |
176 | void 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 | |