forked from qt-creator/qt-creator
ClangTools: Split generic part from static analyzer tool
To reuse it for other clang-based tools. Change-Id: I6c0d8e9eee543fa08faf3bf93c9fac33e43c6820 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
184
src/plugins/clangtools/clangtoolrunner.cpp
Normal file
184
src/plugins/clangtools/clangtoolrunner.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangtoolrunner.h"
|
||||
|
||||
#include "clangtoolsconstants.h"
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporaryfile.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner")
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
static QString generalProcessError(const QString &name)
|
||||
{
|
||||
return ClangToolRunner::tr("An error occurred with the %1 process.").arg(name);
|
||||
}
|
||||
|
||||
static QString finishedDueToCrash(const QString &name)
|
||||
{
|
||||
return ClangToolRunner::tr("%1 crashed.").arg(name);
|
||||
}
|
||||
|
||||
QString finishedWithBadExitCode(const QString &name, int exitCode)
|
||||
{
|
||||
return ClangToolRunner::tr("%1 finished with exit code: %2.").arg(name).arg(exitCode);
|
||||
}
|
||||
|
||||
ClangToolRunner::ClangToolRunner(const QString &clangExecutable,
|
||||
const QString &clangLogFileDir,
|
||||
const Utils::Environment &environment,
|
||||
const QString &name,
|
||||
QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_clangExecutable(QDir::toNativeSeparators(clangExecutable))
|
||||
, m_clangLogFileDir(clangLogFileDir)
|
||||
, m_name(name)
|
||||
{
|
||||
QTC_CHECK(!m_clangExecutable.isEmpty());
|
||||
QTC_CHECK(!m_clangLogFileDir.isEmpty());
|
||||
|
||||
m_process.setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_process.setProcessEnvironment(environment.toProcessEnvironment());
|
||||
m_process.setWorkingDirectory(m_clangLogFileDir); // Current clang-cl puts log file into working dir.
|
||||
connect(&m_process, &QProcess::started, this, &ClangToolRunner::onProcessStarted);
|
||||
connect(&m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, &ClangToolRunner::onProcessFinished);
|
||||
connect(&m_process, &QProcess::errorOccurred, this, &ClangToolRunner::onProcessError);
|
||||
connect(&m_process, &QProcess::readyRead, this, &ClangToolRunner::onProcessOutput);
|
||||
}
|
||||
|
||||
ClangToolRunner::~ClangToolRunner()
|
||||
{
|
||||
Utils::SynchronousProcess::stopProcess(m_process);
|
||||
}
|
||||
|
||||
bool ClangToolRunner::run(const QString &filePath, const QStringList &compilerOptions)
|
||||
{
|
||||
QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
|
||||
QTC_CHECK(!compilerOptions.contains(QLatin1String("-o")));
|
||||
QTC_CHECK(!compilerOptions.contains(filePath));
|
||||
|
||||
m_filePath = filePath;
|
||||
m_processOutput.clear();
|
||||
|
||||
m_logFile = createLogFile(filePath);
|
||||
QTC_ASSERT(!m_logFile.isEmpty(), return false);
|
||||
const QStringList arguments = constructCommandLineArguments(compilerOptions);
|
||||
m_commandLine = Utils::QtcProcess::joinArgs(QStringList(m_clangExecutable) + arguments);
|
||||
|
||||
qCDebug(LOG).noquote() << "Starting" << m_commandLine;
|
||||
m_process.start(m_clangExecutable, arguments);
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ClangToolRunner::filePath() const
|
||||
{
|
||||
return m_filePath;
|
||||
}
|
||||
|
||||
void ClangToolRunner::onProcessStarted()
|
||||
{
|
||||
emit started();
|
||||
}
|
||||
|
||||
void ClangToolRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
if (exitStatus == QProcess::NormalExit) {
|
||||
if (exitCode == 0) {
|
||||
qCDebug(LOG).noquote() << "Output:\n" << Utils::SynchronousProcess::normalizeNewlines(
|
||||
QString::fromLocal8Bit(m_processOutput));
|
||||
emit finishedWithSuccess(actualLogFile());
|
||||
}
|
||||
else
|
||||
emit finishedWithFailure(finishedWithBadExitCode(m_name, exitCode), processCommandlineAndOutput());
|
||||
} else { // == QProcess::CrashExit
|
||||
emit finishedWithFailure(finishedDueToCrash(m_name), processCommandlineAndOutput());
|
||||
}
|
||||
}
|
||||
|
||||
void ClangToolRunner::onProcessError(QProcess::ProcessError error)
|
||||
{
|
||||
if (error == QProcess::Crashed)
|
||||
return; // handled by slot of finished()
|
||||
|
||||
emit finishedWithFailure(generalProcessError(m_name), processCommandlineAndOutput());
|
||||
}
|
||||
|
||||
void ClangToolRunner::onProcessOutput()
|
||||
{
|
||||
m_processOutput.append(m_process.readAll());
|
||||
}
|
||||
|
||||
QString ClangToolRunner::createLogFile(const QString &filePath) const
|
||||
{
|
||||
const QString fileName = QFileInfo(filePath).fileName();
|
||||
const QString fileTemplate = m_clangLogFileDir
|
||||
+ QLatin1String("/report-") + fileName + QLatin1String("-XXXXXX.plist");
|
||||
|
||||
Utils::TemporaryFile temporaryFile("clangtools");
|
||||
temporaryFile.setAutoRemove(false);
|
||||
temporaryFile.setFileTemplate(fileTemplate);
|
||||
if (temporaryFile.open()) {
|
||||
temporaryFile.close();
|
||||
return temporaryFile.fileName();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString ClangToolRunner::processCommandlineAndOutput() const
|
||||
{
|
||||
return tr("Command line: %1\n"
|
||||
"Process Error: %2\n"
|
||||
"Output:\n%3")
|
||||
.arg(m_commandLine,
|
||||
QString::number(m_process.error()),
|
||||
Utils::SynchronousProcess::normalizeNewlines(
|
||||
QString::fromLocal8Bit(m_processOutput)));
|
||||
}
|
||||
|
||||
QString ClangToolRunner::actualLogFile() const
|
||||
{
|
||||
if (QFileInfo(m_logFile).size() == 0) {
|
||||
// Current clang-cl ignores -o, always putting the log file into the working directory.
|
||||
return m_clangLogFileDir + QLatin1Char('/') + QFileInfo(m_filePath).completeBaseName()
|
||||
+ QLatin1String(".plist");
|
||||
}
|
||||
return m_logFile;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
Reference in New Issue
Block a user