diff options
Diffstat (limited to 'src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp')
-rw-r--r-- | src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp new file mode 100644 index 0000000000..00e0672d3b --- /dev/null +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** 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 "clangstaticanalyzerrunner.h" + +#include "clangstaticanalyzerconstants.h" + +#include <utils/synchronousprocess.h> + +#include <QDebug> +#include <QDir> +#include <QFileInfo> +#include <QLoggingCategory> +#include <QTemporaryFile> + +static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner") + +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, + const QStringList &options) +{ + QStringList arguments = QStringList() + << QLatin1String("--analyze") + << QLatin1String("-o") + << logFile + ; + arguments += options; + arguments << QDir::toNativeSeparators(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, + const Utils::Environment &environment, + 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); + 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, &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() +{ + Utils::SynchronousProcess::stopProcess(m_process); +} + +bool ClangStaticAnalyzerRunner::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(filePath, m_logFile, + compilerOptions); + m_commandLine = (QStringList(m_clangExecutable) + arguments).join(QLatin1String("\" \"")); + + qCDebug(LOG) << "Starting" << m_commandLine; + m_process.start(m_clangExecutable, arguments); + return true; +} + +QString ClangStaticAnalyzerRunner::filePath() const +{ + return m_filePath; +} + +void ClangStaticAnalyzerRunner::onProcessStarted() +{ + emit started(); +} + +void ClangStaticAnalyzerRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (exitStatus == QProcess::NormalExit) { + if (exitCode == 0) + emit finishedWithSuccess(actualLogFile()); + 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" + "Process Error: %2\n" + "Output:\n%3") + .arg(m_commandLine, + QString::number(m_process.error()), + QString::fromLocal8Bit(m_processOutput)); +} + +QString ClangStaticAnalyzerRunner::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 ClangStaticAnalyzer |