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 "clangstaticanalyzerruncontrol.h"
|
|
|
|
|
|
|
|
|
|
#include "clangstaticanalyzerlogfilereader.h"
|
|
|
|
|
#include "clangstaticanalyzerrunner.h"
|
|
|
|
|
#include "clangstaticanalyzersettings.h"
|
|
|
|
|
#include "clangstaticanalyzerutils.h"
|
|
|
|
|
|
|
|
|
|
#include <analyzerbase/analyzermanager.h>
|
|
|
|
|
|
|
|
|
|
#include <clangcodemodel/clangutils.h>
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/progressmanager/futureprogress.h>
|
|
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
|
|
|
|
|
|
|
|
|
#include <cpptools/cppmodelmanager.h>
|
2014-10-16 14:07:47 +02:00
|
|
|
#include <cpptools/cppprojects.h>
|
2014-09-25 11:11:58 +02:00
|
|
|
#include <cpptools/cppprojectfile.h>
|
|
|
|
|
|
|
|
|
|
#include <projectexplorer/project.h>
|
2014-10-24 10:21:32 +02:00
|
|
|
#include <projectexplorer/target.h>
|
2014-09-25 11:11:58 +02:00
|
|
|
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
#include <QTemporaryDir>
|
|
|
|
|
|
|
|
|
|
using namespace CppTools;
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
|
2014-10-20 13:08:49 +02:00
|
|
|
static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runcontrol")
|
2014-09-25 11:11:58 +02:00
|
|
|
|
|
|
|
|
namespace ClangStaticAnalyzer {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(
|
|
|
|
|
const Analyzer::AnalyzerStartParameters &startParams,
|
|
|
|
|
ProjectExplorer::RunConfiguration *runConfiguration)
|
|
|
|
|
: AnalyzerRunControl(startParams, runConfiguration)
|
|
|
|
|
, m_initialFilesToProcessSize(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-23 15:31:35 +02:00
|
|
|
static QList<ClangStaticAnalyzerRunControl::SourceFileConfiguration> calculateFilesToProcess(
|
2014-09-25 11:11:58 +02:00
|
|
|
Project *project)
|
|
|
|
|
{
|
2014-10-23 15:31:35 +02:00
|
|
|
typedef ClangStaticAnalyzerRunControl::SourceFileConfiguration SourceFileConfiguration;
|
|
|
|
|
QTC_ASSERT(project, return QList<SourceFileConfiguration>());
|
2014-09-25 11:11:58 +02:00
|
|
|
ProjectInfo projectInfo = CppModelManager::instance()->projectInfo(project);
|
2014-10-23 15:31:35 +02:00
|
|
|
QTC_ASSERT(projectInfo, return QList<SourceFileConfiguration>());
|
2014-09-25 11:11:58 +02:00
|
|
|
|
2014-10-23 15:31:35 +02:00
|
|
|
QList<SourceFileConfiguration> files;
|
2014-09-25 11:11:58 +02:00
|
|
|
const QList<ProjectPart::Ptr> projectParts = projectInfo.projectParts();
|
|
|
|
|
foreach (const ProjectPart::Ptr &projectPart, projectParts) {
|
|
|
|
|
foreach (const ProjectFile &file, projectPart->files) {
|
|
|
|
|
if (file.path == CppModelManager::configurationFileName())
|
|
|
|
|
continue;
|
|
|
|
|
QTC_CHECK(file.kind != ProjectFile::Unclassified);
|
|
|
|
|
if (ProjectFile::isSource(file.kind))
|
2014-10-23 15:31:35 +02:00
|
|
|
files << SourceFileConfiguration(file, projectPart);
|
2014-09-25 11:11:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return files;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ClangStaticAnalyzerRunControl::startEngine()
|
|
|
|
|
{
|
|
|
|
|
emit starting(this);
|
|
|
|
|
|
2014-10-24 10:21:32 +02:00
|
|
|
RunConfiguration *runConfig = runConfiguration();
|
|
|
|
|
QTC_ASSERT(runConfig, emit finished(); return false);
|
|
|
|
|
Target *target = runConfig->target();
|
|
|
|
|
QTC_ASSERT(target, emit finished(); return false);
|
|
|
|
|
Project *project = target->project();
|
|
|
|
|
QTC_ASSERT(project, emit finished(); return false);
|
2014-09-25 11:11:58 +02:00
|
|
|
|
|
|
|
|
// Check clang executable
|
|
|
|
|
bool isValidClangExecutable;
|
|
|
|
|
const QString executable = clangExecutableFromSettings(&isValidClangExecutable);
|
|
|
|
|
if (!isValidClangExecutable) {
|
|
|
|
|
emit appendMessage(tr("Clang Static Analyzer: Invalid executable \"%1\", stop.\n")
|
|
|
|
|
.arg(executable),
|
|
|
|
|
Utils::ErrorMessageFormat);
|
|
|
|
|
emit finished();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
m_clangExecutable = executable;
|
|
|
|
|
|
|
|
|
|
// Create log dir
|
|
|
|
|
QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
|
|
|
|
|
temporaryDir.setAutoRemove(false);
|
|
|
|
|
if (!temporaryDir.isValid()) {
|
|
|
|
|
emit appendMessage(tr("Clang Static Analyzer: Failed to create temporary dir, stop.\n"),
|
|
|
|
|
Utils::ErrorMessageFormat);
|
|
|
|
|
emit finished();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
m_clangLogFileDir = temporaryDir.path();
|
|
|
|
|
|
|
|
|
|
// Collect files
|
2014-10-24 10:21:32 +02:00
|
|
|
const QList<SourceFileConfiguration> filesToProcess = calculateFilesToProcess(project);
|
2014-10-24 10:24:16 +02:00
|
|
|
qCDebug(LOG) << "Files to process:";
|
2014-10-23 15:31:35 +02:00
|
|
|
foreach (const SourceFileConfiguration &fileConfig, filesToProcess) {
|
2014-10-24 10:24:16 +02:00
|
|
|
qCDebug(LOG) << fileConfig.file.path + QLatin1String(" [")
|
2014-09-25 11:11:58 +02:00
|
|
|
+ fileConfig.projectPart->projectFile + QLatin1Char(']');
|
|
|
|
|
}
|
|
|
|
|
m_filesToProcess = filesToProcess;
|
|
|
|
|
m_initialFilesToProcessSize = m_filesToProcess.count();
|
|
|
|
|
|
|
|
|
|
// Set up progress information
|
|
|
|
|
using namespace Core;
|
|
|
|
|
FutureProgress *futureProgress
|
|
|
|
|
= ProgressManager::addTask(m_progress.future(), tr("Analyzing"), "ClangStaticAnalyzer");
|
|
|
|
|
futureProgress->setKeepOnFinish(FutureProgress::HideOnFinish);
|
|
|
|
|
connect(futureProgress, &FutureProgress::canceled,
|
|
|
|
|
this, &ClangStaticAnalyzerRunControl::onProgressCanceled);
|
|
|
|
|
m_progress.setProgressRange(0, m_initialFilesToProcessSize);
|
|
|
|
|
m_progress.reportStarted();
|
|
|
|
|
|
|
|
|
|
// Start process(es)
|
2014-10-28 09:44:26 +01:00
|
|
|
m_runners.clear();
|
2014-09-25 11:11:58 +02:00
|
|
|
const int parallelRuns = ClangStaticAnalyzerSettings::instance()->simultaneousProcesses();
|
|
|
|
|
QTC_ASSERT(parallelRuns >= 1, emit finished(); return false);
|
2014-10-28 09:44:26 +01:00
|
|
|
while (m_runners.size() < parallelRuns && !m_filesToProcess.isEmpty())
|
2014-09-25 11:11:58 +02:00
|
|
|
analyzeNextFile();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunControl::stopEngine()
|
|
|
|
|
{
|
2014-10-28 09:44:26 +01:00
|
|
|
QSetIterator<ClangStaticAnalyzerRunner *> i(m_runners);
|
|
|
|
|
while (i.hasNext()) {
|
|
|
|
|
ClangStaticAnalyzerRunner *runner = i.next();
|
|
|
|
|
QObject::disconnect(runner, 0, this, 0);
|
|
|
|
|
delete runner;
|
|
|
|
|
}
|
|
|
|
|
m_runners.clear();
|
2014-09-25 11:11:58 +02:00
|
|
|
m_filesToProcess.clear();
|
2014-10-28 09:44:26 +01:00
|
|
|
analyzeNextFile(); // emits finished
|
2014-09-25 11:11:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunControl::analyzeNextFile()
|
|
|
|
|
{
|
|
|
|
|
if (m_progress.isFinished())
|
|
|
|
|
return; // The previous call already reported that we are finished.
|
|
|
|
|
|
|
|
|
|
if (m_filesToProcess.isEmpty()) {
|
2014-10-28 09:44:26 +01:00
|
|
|
if (m_runners.size() == 0) {
|
2014-09-25 11:11:58 +02:00
|
|
|
m_progress.reportFinished();
|
|
|
|
|
emit finished();
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-23 15:31:35 +02:00
|
|
|
const SourceFileConfiguration config = m_filesToProcess.takeFirst();
|
|
|
|
|
const QString filePath = config.file.path;
|
|
|
|
|
const QStringList options = config.createClangOptions();
|
2014-09-25 11:11:58 +02:00
|
|
|
|
|
|
|
|
ClangStaticAnalyzerRunner *runner = createRunner();
|
2014-10-28 09:44:26 +01:00
|
|
|
m_runners.insert(runner);
|
2014-09-25 11:11:58 +02:00
|
|
|
qCDebug(LOG) << "analyzeNextFile:" << filePath;
|
2014-10-23 15:31:35 +02:00
|
|
|
QTC_ASSERT(runner->run(filePath, options), return);
|
2014-09-25 11:11:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClangStaticAnalyzerRunner *ClangStaticAnalyzerRunControl::createRunner()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0);
|
|
|
|
|
QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0);
|
|
|
|
|
|
|
|
|
|
ClangStaticAnalyzerRunner *runner
|
|
|
|
|
= new ClangStaticAnalyzerRunner(m_clangExecutable, m_clangLogFileDir, this);
|
|
|
|
|
connect(runner, &ClangStaticAnalyzerRunner::finishedWithSuccess,
|
|
|
|
|
this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess);
|
|
|
|
|
connect(runner, &ClangStaticAnalyzerRunner::finishedWithFailure,
|
|
|
|
|
this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure);
|
|
|
|
|
return runner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess(const QString &logFilePath)
|
|
|
|
|
{
|
|
|
|
|
qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath;
|
|
|
|
|
handleFinished();
|
|
|
|
|
|
|
|
|
|
QString errorMessage;
|
|
|
|
|
const QList<Diagnostic> diagnostics = LogFileReader::read(logFilePath, &errorMessage);
|
|
|
|
|
if (!errorMessage.isEmpty())
|
|
|
|
|
qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
|
|
|
|
|
if (!diagnostics.isEmpty())
|
|
|
|
|
emit newDiagnosticsAvailable(diagnostics);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure(const QString &errorMessage,
|
|
|
|
|
const QString &errorDetails)
|
|
|
|
|
{
|
|
|
|
|
qCDebug(LOG) << "onRunnerFinishedWithFailure:" << errorMessage << errorDetails;
|
|
|
|
|
handleFinished();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunControl::handleFinished()
|
|
|
|
|
{
|
2014-10-28 09:44:26 +01:00
|
|
|
m_runners.remove(qobject_cast<ClangStaticAnalyzerRunner *>(sender()));
|
2014-09-25 11:11:58 +02:00
|
|
|
updateProgressValue();
|
|
|
|
|
sender()->deleteLater();
|
|
|
|
|
analyzeNextFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunControl::onProgressCanceled()
|
|
|
|
|
{
|
|
|
|
|
Analyzer::AnalyzerManager::stopTool();
|
|
|
|
|
m_progress.reportCanceled();
|
|
|
|
|
m_progress.reportFinished();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangStaticAnalyzerRunControl::updateProgressValue()
|
|
|
|
|
{
|
|
|
|
|
m_progress.setProgressValue(m_initialFilesToProcessSize - m_filesToProcess.size());
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-23 15:31:35 +02:00
|
|
|
QStringList ClangStaticAnalyzerRunControl::SourceFileConfiguration::createClangOptions() const
|
|
|
|
|
{
|
|
|
|
|
QStringList result;
|
|
|
|
|
|
|
|
|
|
const bool objcExt = projectPart->languageExtensions & ProjectPart::ObjectiveCExtensions;
|
|
|
|
|
result += CppTools::CompilerOptionsBuilder::createLanguageOption(file.kind, objcExt);
|
|
|
|
|
result += CppTools::CompilerOptionsBuilder::createOptionsForLanguage(
|
|
|
|
|
projectPart->languageVersion,
|
|
|
|
|
projectPart->languageExtensions);
|
|
|
|
|
result += CppTools::CompilerOptionsBuilder::createDefineOptions(projectPart->toolchainDefines);
|
|
|
|
|
result += CppTools::CompilerOptionsBuilder::createDefineOptions(projectPart->projectDefines);
|
|
|
|
|
result += CppTools::CompilerOptionsBuilder::createHeaderPathOptions(projectPart->headerPaths);
|
|
|
|
|
result += QLatin1String("-fPIC"); // TODO: Remove?
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 11:11:58 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ClangStaticAnalyzer
|