ACAV f0ba4b7c9529
Abstract Syntax Tree (AST) visualization tool for C, C++, and Objective-C
Loading...
Searching...
No Matches
MakeAstRunner.cpp
1/*$!{
2* Aurora Clang AST Viewer (ACAV)
3*
4* Copyright (c) 2026 Min Liu
5* Copyright (c) 2026 Michael David Adams
6*
7* SPDX-License-Identifier: GPL-2.0-or-later
8*
9* This program is free software; you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by
11* the Free Software Foundation; either version 2 of the License, or
12* (at your option) any later version.
13*
14* This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details.
18*
19* You should have received a copy of the GNU General Public License along
20* with this program; if not, see <https://www.gnu.org/licenses/>.
21}$!*/
22
23#include "core/MakeAstRunner.h"
25#include <QCoreApplication>
26#include <QDebug>
27#include <QFileInfo>
28
29namespace acav {
30
31MakeAstRunner::MakeAstRunner(QObject *parent)
32 : QObject(parent), process_(new QProcess(this)) {
33 connect(process_, &QProcess::finished, this,
34 &MakeAstRunner::onProcessFinished);
35 connect(process_, &QProcess::errorOccurred, this,
36 &MakeAstRunner::onProcessError);
37 connect(process_, &QProcess::readyReadStandardOutput, this,
38 &MakeAstRunner::onProcessStdOut);
39 connect(process_, &QProcess::readyReadStandardError, this,
40 &MakeAstRunner::onProcessStdErr);
41}
42
43MakeAstRunner::~MakeAstRunner() {
44 if (process_->state() != QProcess::NotRunning) {
45 process_->kill();
46 process_->waitForFinished();
47 }
48}
49
50void MakeAstRunner::run(const QString &compilationDatabasePath,
51 const QString &sourceFilePath,
52 const QString &outputFilePath,
53 const QString &makeAstBinary) {
54 if (isRunning()) {
55 emit error("make-ast is already running");
56 return;
57 }
58
59 sourceFilePath_ = sourceFilePath;
60 outputFilePath_ = outputFilePath;
61
62 // Determine the binary path
63 QString binaryPath = makeAstBinary;
64 if (binaryPath.isEmpty()) {
65 // Try to find make-ast in the same directory as the app
66 QString appDir = QCoreApplication::applicationDirPath();
67 binaryPath = appDir + "/make-ast";
68 }
69
70 QFileInfo sourceInfo(sourceFilePath);
71 emit progress("Generating AST for " + sourceInfo.fileName() + "...");
72 elapsed_.restart();
73
74 // Build command arguments
75 QStringList arguments;
76 arguments << "--compilation-database" << compilationDatabasePath;
77 arguments << "--source" << sourceFilePath;
78 arguments << "--output" << outputFilePath;
79 if (!clangResourceDir_.isEmpty()) {
80 arguments << "--clang-resource-dir" << clangResourceDir_;
81 }
82
83 qDebug() << "Running:" << binaryPath << arguments.join(" ");
84
85 // Start the process
86 process_->start(binaryPath, arguments);
87}
88
90 return process_->state() != QProcess::NotRunning;
91}
92
94 if (process_) {
95 process_->terminate();
96 }
97}
98
100 if (process_) {
101 process_->kill();
102 }
103}
104
106 if (process_) {
107 return process_->waitForFinished(msecs);
108 }
109 return true; // No process to wait for
110}
111
112void MakeAstRunner::onProcessFinished(int exitCode,
113 QProcess::ExitStatus exitStatus) {
114 drainProcessOutput(process_, pendingStdout_, pendingStderr_, "make-ast",
115 [this](const LogEntry &entry) { emit logMessage(entry); });
116
117 if (exitStatus != QProcess::NormalExit) {
118 emit error("make-ast process crashed");
119 return;
120 }
121
122 if (exitCode != 0) {
123 emit error(QString("make-ast failed with exit code %1").arg(exitCode));
124 return;
125 }
126
127 // Success
128 QFileInfo sourceInfo(sourceFilePath_);
129 const double seconds = elapsed_.isValid()
130 ? static_cast<double>(elapsed_.elapsed()) / 1000.0
131 : 0.0;
132 emit progress(QString("make-ast: %1s (%2)")
133 .arg(QString::number(seconds, 'f', 2))
134 .arg(sourceInfo.fileName()));
135 emit astReady(outputFilePath_);
136}
137
138void MakeAstRunner::onProcessError(QProcess::ProcessError error) {
139 drainProcessOutput(process_, pendingStdout_, pendingStderr_, "make-ast",
140 [this](const LogEntry &entry) { emit logMessage(entry); });
141
142 QString systemError = process_->errorString();
143 QString errorMessage;
144 switch (error) {
145 case QProcess::FailedToStart:
146 errorMessage = QString("Failed to start make-ast: %1. Make sure it's in the "
147 "same directory as the app or in PATH.").arg(systemError);
148 break;
149 case QProcess::Crashed:
150 errorMessage = QString("make-ast crashed: %1").arg(systemError);
151 break;
152 case QProcess::Timedout:
153 errorMessage = QString("make-ast timed out: %1").arg(systemError);
154 break;
155 case QProcess::ReadError:
156 errorMessage = QString("Error reading from make-ast: %1").arg(systemError);
157 break;
158 case QProcess::WriteError:
159 errorMessage = QString("Error writing to make-ast: %1").arg(systemError);
160 break;
161 default:
162 errorMessage = QString("Unknown error running make-ast: %1").arg(systemError);
163 break;
164 }
165
166 emit this->error(errorMessage);
167 emitErrorLog("make-ast", errorMessage,
168 [this](const LogEntry &entry) { emit logMessage(entry); });
169}
170
171void MakeAstRunner::onProcessStdOut() {
172 emitParsedOutput(pendingStdout_,
173 QString::fromUtf8(process_->readAllStandardOutput()),
174 "make-ast", false,
175 [this](const LogEntry &entry) { emit logMessage(entry); });
176}
177
178void MakeAstRunner::onProcessStdErr() {
179 emitParsedOutput(pendingStderr_,
180 QString::fromUtf8(process_->readAllStandardError()),
181 "make-ast", true,
182 [this](const LogEntry &entry) { emit logMessage(entry); });
183}
184
185} // namespace acav
Qt runner for make-ast tool execution.
Helpers for draining process output into log entries.
bool waitForFinished(int msecs=30000)
Waits for the process to finish.
void run(const QString &compilationDatabasePath, const QString &sourceFilePath, const QString &outputFilePath, const QString &makeAstBinary=QString())
Run make-ast tool for specified source file.
void error(const QString &errorMessage)
Emitted when an error occurs.
void terminate()
Attempts to terminate the process gracefully Sends SIGTERM on Unix/macOS, WM_CLOSE on Windows.
void kill()
Kills the process immediately Sends SIGKILL on Unix/macOS, forceful termination on Windows.
bool isRunning() const
Check if tool is currently running.
void progress(const QString &message)
Emitted with progress updates.