2014-09-25 11:11:58 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2014 Digia Plc
|
|
|
|
|
** All rights reserved.
|
|
|
|
|
** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
|
|
|
|
|
**
|
|
|
|
|
** This file is part of the Qt Enterprise LicenseChecker Add-on.
|
|
|
|
|
**
|
|
|
|
|
** Licensees holding valid Qt Enterprise licenses may use this file in
|
|
|
|
|
** accordance with the Qt Enterprise License Agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Digia.
|
|
|
|
|
**
|
|
|
|
|
** If you have questions regarding the use of this file, please use
|
|
|
|
|
** contact form at http://qt.digia.com <http://qt.digia.com/>
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "clangstaticanalyzerrunner.h"
|
|
|
|
|
|
|
|
|
|
#include "clangstaticanalyzerconstants.h"
|
|
|
|
|
|
2014-10-31 13:54:49 +01:00
|
|
|
#include <utils/synchronousprocess.h>
|
|
|
|
|
|
2014-09-25 11:11:58 +02:00
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
#include <QTemporaryFile>
|
|
|
|
|
|
2014-10-20 13:08:49 +02:00
|
|
|
static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner")
|
2014-09-25 11:11:58 +02:00
|
|
|
|
|
|
|
|
static QString generalProcessError()
|
|
|
|
|
{
|
|
|
|
|
return QObject::tr("An error occurred with the clang static analyzer process.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString finishedDueToCrash()
|
|
|
|
|
{
|
|
|
|
|
return QObject::tr("Clang static analyzer crashed.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QStringList constructCommandLineArguments(const QString &filePath,
|
|
|
|
|
const QString &logFile,
|
2014-10-23 15:31:35 +02:00
|
|
|
const QStringList &options)
|
2014-09-25 11:11:58 +02:00
|
|
|
{
|
|
|
|
|
QStringList arguments = QStringList()
|
|
|
|
|
<< QLatin1String("--analyze")
|
|
|
|
|
<< QLatin1String("-o")
|
|
|
|
|
<< logFile
|
|
|
|
|
;
|
2014-10-23 15:31:35 +02:00
|
|
|
arguments += options;
|
2014-09-25 11:11:58 +02:00
|
|
|
arguments << filePath;
|
|
|
|
|
return arguments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace ClangStaticAnalyzer {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
QString finishedWithBadExitCode(int exitCode)
|
|
|
|
|
{
|
|
|
|
|
return QObject::tr("Clang static analyzer finished with exit code: %1.").arg(exitCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClangStaticAnalyzerRunner::ClangStaticAnalyzerRunner(const QString &clangExecutable,
|
|
|
|
|
const QString &clangLogFileDir,
|
|
|
|
|
QObject *parent)
|
|
|
|
|
: QObject(parent)
|
|
|
|
|
, m_clangExecutable(clangExecutable)
|
|
|
|
|
, m_clangLogFileDir(clangLogFileDir)
|
|
|
|
|
{
|
|
|
|
|
QTC_CHECK(!m_clangExecutable.isEmpty());
|
|
|
|
|
QTC_CHECK(!m_clangLogFileDir.isEmpty());
|
|
|
|
|
|
|
|
|
|
m_process.setProcessChannelMode(QProcess::MergedChannels);
|
|
|
|
|
connect(&m_process, &QProcess::started,
|
|
|
|
|
this, &ClangStaticAnalyzerRunner::onProcessStarted);
|
|
|
|
|
connect(&m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
|
|
|
|
this, &ClangStaticAnalyzerRunner::onProcessFinished);
|
|
|
|
|
connect(&m_process, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
|
|
|
|
|
this, &ClangStaticAnalyzerRunner::onProcessError);
|
|
|
|
|
connect(&m_process, &QProcess::readyRead,
|
|
|
|
|
this, &ClangStaticAnalyzerRunner::onProcessOutput);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClangStaticAnalyzerRunner::~ClangStaticAnalyzerRunner()
|
|
|
|
|
{
|
2014-10-31 13:54:49 +01:00
|
|
|
Utils::SynchronousProcess::stopProcess(m_process);
|
2014-09-25 11:11:58 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-23 15:31:35 +02:00
|
|
|
bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList &compilerOptions)
|
2014-09-25 11:11:58 +02:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
|
2014-10-23 15:31:35 +02:00
|
|
|
QTC_CHECK(!compilerOptions.contains(QLatin1String("-o")));
|
|
|
|
|
QTC_CHECK(!compilerOptions.contains(filePath));
|
2014-09-25 11:11:58 +02:00
|
|
|
|
2014-10-30 17:35:52 +01:00
|
|
|
m_filePath = filePath;
|
2014-09-25 11:11:58 +02:00
|
|
|
m_processOutput.clear();
|
|
|
|
|
|
|
|
|
|
m_logFile = createLogFile(filePath);
|
|
|
|
|
QTC_ASSERT(!m_logFile.isEmpty(), return false);
|
|
|
|
|
const QStringList arguments = constructCommandLineArguments(filePath, m_logFile,
|
2014-10-23 15:31:35 +02:00
|
|
|
compilerOptions);
|
2014-10-28 09:52:52 +01:00
|
|
|
m_commandLine = (QStringList(m_clangExecutable) + arguments).join(QLatin1String("\" \""));
|
2014-09-25 11:11:58 +02:00
|
|
|
|
|
|
|
|
qCDebug(LOG) << "Starting" << m_commandLine;
|
|
|
|
|
m_process.start(m_clangExecutable, arguments);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-30 17:35:52 +01:00
|
|
|
QString ClangStaticAnalyzerRunner::filePath() const
|
|
|
|
|
{
|
|
|
|
|
return m_filePath;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 11:11:58 +02:00
|
|
|
void ClangStaticAnalyzerRunner::onProcessStarted()
|
|
|
|
|
{
|
|
|
|
|
emit started();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
|
|
|
|
{
|
|
|
|
|
if (exitStatus == QProcess::NormalExit) {
|
|
|
|
|
if (exitCode == 0)
|
|
|
|
|
emit finishedWithSuccess(m_logFile);
|
|
|
|
|
else
|
|
|
|
|
emit finishedWithFailure(finishedWithBadExitCode(exitCode), processCommandlineAndOutput());
|
|
|
|
|
} else { // == QProcess::CrashExit
|
|
|
|
|
emit finishedWithFailure(finishedDueToCrash(), processCommandlineAndOutput());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunner::onProcessError(QProcess::ProcessError error)
|
|
|
|
|
{
|
|
|
|
|
if (error == QProcess::Crashed)
|
|
|
|
|
return; // handled by slot of finished()
|
|
|
|
|
|
|
|
|
|
emit finishedWithFailure(generalProcessError(), processCommandlineAndOutput());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunner::onProcessOutput()
|
|
|
|
|
{
|
|
|
|
|
m_processOutput.append(m_process.readAll());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString ClangStaticAnalyzerRunner::createLogFile(const QString &filePath) const
|
|
|
|
|
{
|
|
|
|
|
const QString fileName = QFileInfo(filePath).fileName();
|
|
|
|
|
const QString fileTemplate = m_clangLogFileDir
|
|
|
|
|
+ QLatin1String("/report-") + fileName + QLatin1String("-XXXXXX.plist");
|
|
|
|
|
|
|
|
|
|
QTemporaryFile temporaryFile;
|
|
|
|
|
temporaryFile.setAutoRemove(false);
|
|
|
|
|
temporaryFile.setFileTemplate(fileTemplate);
|
|
|
|
|
if (temporaryFile.open()) {
|
|
|
|
|
temporaryFile.close();
|
|
|
|
|
return temporaryFile.fileName();
|
|
|
|
|
}
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString ClangStaticAnalyzerRunner::processCommandlineAndOutput() const
|
|
|
|
|
{
|
|
|
|
|
return QObject::tr("Command line: \"%1\"\n"
|
2014-10-30 17:35:52 +01:00
|
|
|
"Process Error: %2\n"
|
|
|
|
|
"Output:\n%3")
|
2014-09-25 11:11:58 +02:00
|
|
|
.arg(m_commandLine,
|
|
|
|
|
QString::number(m_process.error()),
|
|
|
|
|
QString::fromLocal8Bit(m_processOutput));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ClangStaticAnalyzer
|