25#include <QCoreApplication>
29#include <QJsonDocument>
30#include <QJsonParseError>
34QueryDependenciesRunner::QueryDependenciesRunner(QObject *parent)
35 : QObject(parent), process_(new QProcess(this)) {
36 connect(process_, &QProcess::finished,
this,
37 &QueryDependenciesRunner::onProcessFinished);
38 connect(process_, &QProcess::errorOccurred,
this,
39 &QueryDependenciesRunner::onProcessError);
40 connect(process_, &QProcess::readyReadStandardOutput,
this,
41 &QueryDependenciesRunner::onProcessStdOut);
42 connect(process_, &QProcess::readyReadStandardError,
this,
43 &QueryDependenciesRunner::onProcessStdErr);
46QueryDependenciesRunner::~QueryDependenciesRunner() {
47 if (process_->state() != QProcess::NotRunning) {
49 process_->waitForFinished();
54 const QString &outputFilePath,
55 const QString &queryDependenciesBinary) {
57 emit
error(
"Query-dependencies is already running");
61 compilationDatabasePath_ = compilationDatabasePath;
62 outputFilePath_ = outputFilePath;
65 QString binaryPath = queryDependenciesBinary;
66 if (binaryPath.isEmpty()) {
68 QString appDir = QCoreApplication::applicationDirPath();
69 binaryPath = appDir +
"/query-dependencies";
72 emit
progress(
"Running query-dependencies...");
76 QStringList arguments;
77 arguments <<
"--compilation-database" << compilationDatabasePath;
78 arguments <<
"--output" << outputFilePath;
79 if (!clangResourceDir_.isEmpty()) {
80 arguments <<
"--clang-resource-dir" << clangResourceDir_;
83 qDebug() <<
"Running:" << binaryPath << arguments.join(
" ");
85 process_->start(binaryPath, arguments);
89 return process_->state() != QProcess::NotRunning;
92void QueryDependenciesRunner::onProcessFinished(
93 int exitCode, QProcess::ExitStatus exitStatus) {
94 drainProcessOutput(process_, pendingStdout_, pendingStderr_,
96 [
this](
const LogEntry &entry) { emit logMessage(entry); });
98 if (exitStatus != QProcess::NormalExit) {
99 emit error(
"Query-dependencies process crashed");
104 emit error(QString(
"Query-dependencies failed with exit code %1")
110 QFile outputFile(outputFilePath_);
111 if (!outputFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
112 emit error(QString(
"Failed to open dependencies file: %1")
113 .arg(outputFile.errorString()));
117 QByteArray jsonData = outputFile.readAll();
120 QJsonParseError parseError;
121 QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError);
123 if (parseError.error != QJsonParseError::NoError) {
124 emit error(QString(
"Failed to parse query-dependencies JSON output: %1")
125 .arg(parseError.errorString()));
129 if (!doc.isObject()) {
130 emit error(
"Query-dependencies output is not a JSON object");
134 QJsonObject jsonObj = doc.object();
137 if (jsonObj.contains(
"errors") && jsonObj[
"errors"].isArray()) {
138 QJsonArray errorsArray = jsonObj[
"errors"].toArray();
139 QStringList errorMessages;
141 for (
const QJsonValue &errorVal : errorsArray) {
142 if (errorVal.isObject()) {
143 QJsonObject errorObj = errorVal.toObject();
144 QString path = errorObj[
"path"].toString();
145 QString message = errorObj[
"message"].toString();
146 errorMessages.append(QString(
"%1: %2").arg(path, message));
150 emit progress(QString(
"Dependencies loaded with %1 errors")
151 .arg(errorMessages.size()));
152 emit dependenciesReadyWithErrors(jsonObj, errorMessages);
155 const double seconds = elapsed_.isValid()
156 ?
static_cast<double>(elapsed_.elapsed()) / 1000.0
159 QString(
"query-dependencies: %1s")
160 .arg(QString::number(seconds,
'f', 2)));
161 emit dependenciesReady(jsonObj);
165void QueryDependenciesRunner::onProcessError(QProcess::ProcessError error) {
166 drainProcessOutput(process_, pendingStdout_, pendingStderr_,
167 "query-dependencies",
168 [
this](
const LogEntry &entry) { emit logMessage(entry); });
170 QString systemError = process_->errorString();
171 QString errorMessage;
173 case QProcess::FailedToStart:
174 errorMessage = QString(
"Failed to start query-dependencies: %1. Make sure it's in the "
175 "same directory as the app or in PATH.").arg(systemError);
177 case QProcess::Crashed:
178 errorMessage = QString(
"Query-dependencies crashed: %1").arg(systemError);
180 case QProcess::Timedout:
181 errorMessage = QString(
"Query-dependencies timed out: %1").arg(systemError);
183 case QProcess::ReadError:
184 errorMessage = QString(
"Error reading from query-dependencies: %1").arg(systemError);
186 case QProcess::WriteError:
187 errorMessage = QString(
"Error writing to query-dependencies: %1").arg(systemError);
190 errorMessage = QString(
"Unknown error running query-dependencies: %1").arg(systemError);
194 emit this->error(errorMessage);
195 emitErrorLog(
"query-dependencies", errorMessage,
196 [
this](
const LogEntry &entry) { emit logMessage(entry); });
199void QueryDependenciesRunner::onProcessStdOut() {
200 emitParsedOutput(pendingStdout_,
201 QString::fromUtf8(process_->readAllStandardOutput()),
202 "query-dependencies",
false,
203 [
this](
const LogEntry &entry) { emit logMessage(entry); });
206void QueryDependenciesRunner::onProcessStdErr() {
207 emitParsedOutput(pendingStderr_,
208 QString::fromUtf8(process_->readAllStandardError()),
209 "query-dependencies",
true,
210 [
this](
const LogEntry &entry) { emit logMessage(entry); });
Helpers for draining process output into log entries.
Qt runner for query-dependencies tool.
bool isRunning() const
Check if query is currently running.
void progress(const QString &message)
Emitted with progress updates (e.g., "Running query-dependencies...").
void error(const QString &errorMessage)
Emitted when an error occurs.
void run(const QString &compilationDatabasePath, const QString &outputFilePath, const QString &queryDependenciesBinary=QString())
Run query-dependencies tool with the specified compilation database.