ClangStaticAnalyzer: Avoid use of DummyRunConfigurations

This moves the explicit build step into a dependent runWorker,
making the whole setup more aligned with a normal tool run.

Change-Id: Icd01b2e82f7f95774434d2ede04d08a5311b64e7
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
hjk
2017-09-19 18:39:43 +02:00
parent b70637de41
commit f3afac128b
6 changed files with 163 additions and 221 deletions

View File

@@ -130,21 +130,6 @@ bool ClangStaticAnalyzerPlugin::initialize(const QStringList &arguments, QString
addAutoReleasedObject(new ClangStaticAnalyzerTool);
addAutoReleasedObject(new ClangStaticAnalyzerOptionsPage);
auto constraint = [](RunConfiguration *runConfiguration) {
Target *target = runConfiguration->target();
QTC_ASSERT(target, return false);
Project *project = target->project();
QTC_ASSERT(project, return false);
const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
return project->projectLanguages().contains(cxx)
&& ToolChainKitInformation::toolChain(target->kit(), cxx);
};
RunControl::registerWorker<ClangStaticAnalyzerToolRunner>
(Constants::CLANGSTATICANALYZER_RUN_MODE, constraint, /*priority*/ -1);
return true;
}

View File

@@ -35,6 +35,7 @@
#include <clangcodemodel/clangutils.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
@@ -46,8 +47,10 @@
#include <projectexplorer/abi.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
@@ -55,6 +58,7 @@
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/hostosinfo.h>
#include <utils/temporarydirectory.h>
@@ -63,71 +67,74 @@
using namespace CppTools;
using namespace ProjectExplorer;
using namespace Utils;
static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runcontrol")
namespace ClangStaticAnalyzer {
namespace Internal {
ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runControl)
: RunWorker(runControl)
class ProjectBuilder : public RunWorker
{
setDisplayName("ClangStaticAnalyzerRunner");
runControl->setDisplayName(tr("Clang Static Analyzer"));
runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
setSupportsReRunning(false);
RunConfiguration *runConfiguration = runControl->runConfiguration();
auto tool = ClangStaticAnalyzerTool::instance();
tool->stopAction()->disconnect();
connect(tool->stopAction(), &QAction::triggered, runControl, [&] {
initiateStop();
appendMessage(tr("Clang Static Analyzer stopped by user."),
Utils::NormalMessageFormat);
});
tool->handleWorkerStart(this);
ProjectInfo projectInfoBeforeBuild = tool->projectInfoBeforeBuild();
QTC_ASSERT(projectInfoBeforeBuild.isValid(), return);
QTC_ASSERT(runConfiguration, return);
Target * const target = runConfiguration->target();
QTC_ASSERT(target, return);
Project * const project = target->project();
QTC_ASSERT(project, return);
// so pass on the updated Project Info unless no configuration change
// (defines/includes/files) happened.
const CppTools::ProjectInfo projectInfoAfterBuild
= CppTools::CppModelManager::instance()->projectInfo(project);
if (projectInfoAfterBuild.configurationOrFilesChanged(projectInfoBeforeBuild)) {
// If it's more than a release/debug build configuration change, e.g.
// a version control checkout, files might be not valid C++ anymore
// or even gone, so better stop here.
tool->resetCursorAndProjectInfoBeforeBuild();
reportFailure(tr(
"The project configuration changed since the start of the Clang Static Analyzer. "
"Please re-run with current configuration."));
return;
public:
ProjectBuilder(RunControl *runControl, Project *project)
: RunWorker(runControl), m_project(project)
{
setDisplayName("ProjectBuilder");
}
// Some projects provides CompilerCallData once a build is finished,
QTC_ASSERT(!projectInfoAfterBuild.configurationOrFilesChanged(projectInfoBeforeBuild),
return);
bool success() const { return m_success; }
m_projectInfo = projectInfoAfterBuild;
private:
void start() final
{
Target *target = m_project->activeTarget();
QTC_ASSERT(target, reportFailure(); return);
BuildConfiguration *buildConfiguration = target->activeBuildConfiguration();
QTC_ASSERT(buildConfiguration, return);
m_environment = buildConfiguration->environment();
BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown;
if (const BuildConfiguration *buildConfig = target->activeBuildConfiguration())
buildType = buildConfig->buildType();
ToolChain *toolChain = ToolChainKitInformation::toolChain(target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID);
QTC_ASSERT(toolChain, return);
m_targetTriple = toolChain->originalTargetTriple();
m_toolChainType = toolChain->typeId();
}
if (buildType == BuildConfiguration::Release) {
const QString wrongMode = ClangStaticAnalyzerTool::tr("Release");
const QString toolName = ClangStaticAnalyzerTool::tr("Clang Static Analyzer");
const QString title = ClangStaticAnalyzerTool::tr("Run %1 in %2 Mode?").arg(toolName)
.arg(wrongMode);
const QString message = ClangStaticAnalyzerTool::tr(
"<html><head/><body>"
"<p>You are trying to run the tool \"%1\" on an application in %2 mode. The tool is "
"designed to be used in Debug mode since enabled assertions can reduce the number of "
"false positives.</p>"
"<p>Do you want to continue and run the tool in %2 mode?</p>"
"</body></html>")
.arg(toolName).arg(wrongMode);
if (CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(),
title, message, Core::ICore::settings(),
"ClangStaticAnalyzerCorrectModeWarning") != QDialogButtonBox::Yes)
{
reportFailure();
return;
}
}
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
this, &ProjectBuilder::onBuildFinished, Qt::QueuedConnection);
ProjectExplorerPlugin::buildProject(m_project);
}
void onBuildFinished(bool success)
{
disconnect(BuildManager::instance(), &BuildManager::buildQueueFinished,
this, &ProjectBuilder::onBuildFinished);
m_success = success;
reportDone();
}
private:
QPointer<Project> m_project;
bool m_success = false;
};
static void prependWordWidthArgumentIfNotIncluded(QStringList *arguments,
ProjectPart::ToolChainWordWidth wordWidth)
@@ -390,16 +397,47 @@ static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
return debug;
}
ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runControl, Target *target)
: RunWorker(runControl), m_target(target)
{
setDisplayName("ClangStaticAnalyzerRunner");
setSupportsReRunning(false);
m_projectBuilder = new ProjectBuilder(runControl, target->project());
addStartDependency(m_projectBuilder);
m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(target->project());
BuildConfiguration *buildConfiguration = target->activeBuildConfiguration();
QTC_ASSERT(buildConfiguration, return);
m_environment = buildConfiguration->environment();
ToolChain *toolChain = ToolChainKitInformation::toolChain(target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID);
QTC_ASSERT(toolChain, return);
m_targetTriple = toolChain->originalTargetTriple();
m_toolChainType = toolChain->typeId();
}
void ClangStaticAnalyzerToolRunner::start()
{
m_success = false;
ClangStaticAnalyzerTool::instance()->onEngineIsStarting();
m_success = m_projectBuilder->success();
if (!m_success) {
reportFailure();
return;
}
connect(runControl(), &RunControl::stopped, this, [this] {
ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success);
});
m_projectInfo = CppTools::CppModelManager::instance()->projectInfo(m_target->project());
// Some projects provides CompilerCallData once a build is finished,
if (m_projectInfo.configurationOrFilesChanged(m_projectInfoBeforeBuild)) {
// If it's more than a release/debug build configuration change, e.g.
// a version control checkout, files might be not valid C++ anymore
// or even gone, so better stop here.
reportFailure(tr("The project configuration changed since the start of "
"the Clang Static Analyzer. Please re-run with current configuration."));
return;
}
QTC_ASSERT(m_projectInfo.isValid(), reportFailure(); return);
const Utils::FileName projectFile = m_projectInfo.project()->projectFilePath();
appendMessage(tr("Running Clang Static Analyzer on %1").arg(projectFile.toUserOutput()),
Utils::NormalMessageFormat);
@@ -502,7 +540,7 @@ void ClangStaticAnalyzerToolRunner::stop()
m_runners.clear();
m_unitsToProcess.clear();
m_progress.reportFinished();
ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success);
//ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success);
reportStopped();
}

View File

@@ -36,6 +36,7 @@ namespace ClangStaticAnalyzer {
namespace Internal {
class ClangStaticAnalyzerRunner;
class ProjectBuilder;
class Diagnostic;
struct AnalyzeUnit {
@@ -52,14 +53,15 @@ class ClangStaticAnalyzerToolRunner : public ProjectExplorer::RunWorker
Q_OBJECT
public:
explicit ClangStaticAnalyzerToolRunner(ProjectExplorer::RunControl *runControl);
void start() override;
void stop() override;
ClangStaticAnalyzerToolRunner(ProjectExplorer::RunControl *runControl,
ProjectExplorer::Target *target);
bool success() const { return m_success; } // For testing.
private:
void start() final;
void stop() final;
AnalyzeUnits sortedUnitsToAnalyze();
void analyzeNextFile();
ClangStaticAnalyzerRunner *createRunner();
@@ -74,6 +76,10 @@ private:
void finalize();
private:
QPointer<ProjectExplorer::Target> m_target;
ProjectBuilder *m_projectBuilder;
CppTools::ProjectInfo m_projectInfoBeforeBuild;
CppTools::ProjectInfo m_projectInfo;
QString m_targetTriple;
Core::Id m_toolChainType;

View File

@@ -36,24 +36,19 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <cpptools/cppmodelmanager.h>
#include <debugger/analyzer/analyzermanager.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/target.h>
#include <projectexplorer/session.h>
#include <utils/checkablemessagebox.h>
#include <utils/fancymainwindow.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QDockWidget>
#include <QHBoxLayout>
#include <QLabel>
#include <QListView>
#include <QSortFilterProxyModel>
#include <QToolButton>
@@ -65,23 +60,6 @@ using namespace Utils;
namespace ClangStaticAnalyzer {
namespace Internal {
class DummyRunConfiguration : public RunConfiguration
{
Q_OBJECT
public:
DummyRunConfiguration(Target *parent)
: RunConfiguration(parent)
{
initialize("ClangStaticAnalyzer.DummyRunConfig");
setDefaultDisplayName(tr("Clang Static Analyzer"));
setEnabled(true);
}
private:
QWidget *createConfigurationWidget() override { return 0; }
};
static ClangStaticAnalyzerTool *s_instance;
ClangStaticAnalyzerTool::ClangStaticAnalyzerTool()
@@ -180,90 +158,43 @@ ClangStaticAnalyzerTool *ClangStaticAnalyzerTool::instance()
return s_instance;
}
static bool dontStartAfterHintForDebugMode(Project *project)
{
BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown;
if (project) {
if (const Target *target = project->activeTarget()) {
if (const BuildConfiguration *buildConfig = target->activeBuildConfiguration())
buildType = buildConfig->buildType();
}
}
if (buildType == BuildConfiguration::Release) {
const QString wrongMode = ClangStaticAnalyzerTool::tr("Release");
const QString toolName = ClangStaticAnalyzerTool::tr("Clang Static Analyzer");
const QString title = ClangStaticAnalyzerTool::tr("Run %1 in %2 Mode?").arg(toolName)
.arg(wrongMode);
const QString message = ClangStaticAnalyzerTool::tr(
"<html><head/><body>"
"<p>You are trying to run the tool \"%1\" on an application in %2 mode. The tool is "
"designed to be used in Debug mode since enabled assertions can reduce the number of "
"false positives.</p>"
"<p>Do you want to continue and run the tool in %2 mode?</p>"
"</body></html>")
.arg(toolName).arg(wrongMode);
if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(),
title, message, Core::ICore::settings(),
QLatin1String("ClangStaticAnalyzerCorrectModeWarning")) != QDialogButtonBox::Yes)
return true;
}
return false;
}
void ClangStaticAnalyzerTool::handleWorkerStart(RunWorker *runWorker)
{
RunControl *runControl = runWorker->runControl();
Project *project = runControl->project();
QTC_ASSERT(project, emit finished(false); return);
Debugger::selectPerspective(ClangStaticAnalyzerPerspectiveId);
m_diagnosticModel->clear();
setBusyCursor(true);
m_diagnosticFilterModel->setProject(project);
m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(project);
QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), emit finished(false); return);
m_running = true;
handleStateUpdate();
m_toolBusy = true;
updateRunActions();
}
void ClangStaticAnalyzerTool::startTool()
{
auto runControl = new RunControl(nullptr, Constants::CLANGSTATICANALYZER_RUN_MODE);
runControl->setDisplayName(tr("Clang Static Analyzer"));
runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
Project *project = SessionManager::startupProject();
QTC_ASSERT(project, return);
Target *target = project->activeTarget();
QTC_ASSERT(target, return);
DummyRunConfiguration *& rc = m_runConfigs[target];
if (!rc) {
rc = new DummyRunConfiguration(target);
connect(project, &Project::aboutToRemoveTarget, this,
[this](Target *t) { m_runConfigs.remove(t); });
const auto onProjectRemoved = [this](Project *p) {
foreach (Target * const t, p->targets())
m_runConfigs.remove(t);
};
connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject, this,
onProjectRemoved, Qt::UniqueConnection);
}
if (dontStartAfterHintForDebugMode(project))
return;
ProjectExplorerPlugin::runRunConfiguration(rc, Constants::CLANGSTATICANALYZER_RUN_MODE);
}
auto clangTool = new ClangStaticAnalyzerToolRunner(runControl, project->activeTarget());
CppTools::ProjectInfo ClangStaticAnalyzerTool::projectInfoBeforeBuild() const
{
return m_projectInfoBeforeBuild;
}
m_stopAction->disconnect();
connect(m_stopAction, &QAction::triggered, runControl, [this, runControl] {
runControl->appendMessage(tr("Clang Static Analyzer stopped by user."),
NormalMessageFormat);
runControl->initiateStop();
});
void ClangStaticAnalyzerTool::resetCursorAndProjectInfoBeforeBuild()
{
setBusyCursor(false);
m_projectInfoBeforeBuild = CppTools::ProjectInfo();
connect(runControl, &RunControl::stopped, this, [this, clangTool] {
bool success = clangTool->success();
setToolBusy(false);
m_running = false;
handleStateUpdate();
updateRunActions();
emit finished(success);
});
Debugger::selectPerspective(ClangStaticAnalyzerPerspectiveId);
m_diagnosticModel->clear();
setToolBusy(true);
m_diagnosticFilterModel->setProject(project);
m_running = true;
handleStateUpdate();
updateRunActions();
ProjectExplorerPlugin::startRunControl(runControl);
}
QList<Diagnostic> ClangStaticAnalyzerTool::diagnostics() const
@@ -271,27 +202,12 @@ QList<Diagnostic> ClangStaticAnalyzerTool::diagnostics() const
return m_diagnosticModel->diagnostics();
}
void ClangStaticAnalyzerTool::onEngineIsStarting()
{
QTC_ASSERT(m_diagnosticModel, return);
}
void ClangStaticAnalyzerTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics)
{
QTC_ASSERT(m_diagnosticModel, return);
m_diagnosticModel->addDiagnostics(diagnostics);
}
void ClangStaticAnalyzerTool::onEngineFinished(bool success)
{
resetCursorAndProjectInfoBeforeBuild();
m_running = false;
handleStateUpdate();
emit finished(success);
m_toolBusy = false;
updateRunActions();
}
void ClangStaticAnalyzerTool::updateRunActions()
{
if (m_toolBusy) {
@@ -299,19 +215,27 @@ void ClangStaticAnalyzerTool::updateRunActions()
m_startAction->setToolTip(tr("Clang Static Analyzer is still running."));
m_stopAction->setEnabled(true);
} else {
QString whyNot = tr("Start Clang Static Analyzer.");
bool canRun = ProjectExplorerPlugin::canRunStartupProject(
Constants::CLANGSTATICANALYZER_RUN_MODE, &whyNot);
m_startAction->setToolTip(whyNot);
QString toolTip = tr("Start Clang Static Analyzer.");
Project *project = SessionManager::startupProject();
Target *target = project ? project->activeTarget() : nullptr;
const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
bool canRun = target && project->projectLanguages().contains(cxx)
&& ToolChainKitInformation::toolChain(target->kit(), cxx);
if (!canRun)
toolTip = tr("This is not C++ project");
m_startAction->setToolTip(toolTip);
m_startAction->setEnabled(canRun);
m_stopAction->setEnabled(false);
}
}
void ClangStaticAnalyzerTool::setBusyCursor(bool busy)
void ClangStaticAnalyzerTool::setToolBusy(bool busy)
{
QTC_ASSERT(m_diagnosticView, return);
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
m_diagnosticView->setCursor(cursor);
m_toolBusy = busy;
}
void ClangStaticAnalyzerTool::handleStateUpdate()
@@ -326,8 +250,12 @@ void ClangStaticAnalyzerTool::handleStateUpdate()
m_goBack->setEnabled(issuesVisible > 1);
m_goNext->setEnabled(issuesVisible > 1);
QString message = m_running ? tr("Clang Static Analyzer is running.")
: tr("Clang Static Analyzer finished.");
QString message;
if (m_running)
message = tr("Clang Static Analyzer is running.");
else
message = tr("Clang Static Analyzer finished.");
message += QLatin1Char(' ');
if (issuesFound == 0) {
message += tr("No issues found.");
@@ -340,5 +268,3 @@ void ClangStaticAnalyzerTool::handleStateUpdate()
} // namespace Internal
} // namespace ClangStaticAnalyzer
#include "clangstaticanalyzertool.moc"

View File

@@ -28,8 +28,6 @@
#include <projectexplorer/runconfiguration.h>
#include <cpptools/projectinfo.h>
#include <QHash>
namespace ClangStaticAnalyzer {
namespace Internal {
@@ -37,7 +35,6 @@ class ClangStaticAnalyzerDiagnosticFilterModel;
class ClangStaticAnalyzerDiagnosticModel;
class ClangStaticAnalyzerDiagnosticView;
class Diagnostic;
class DummyRunConfiguration;
const char ClangStaticAnalyzerPerspectiveId[] = "ClangStaticAnalyzer.Perspective";
const char ClangStaticAnalyzerDockId[] = "ClangStaticAnalyzer.Dock";
@@ -51,32 +48,21 @@ public:
~ClangStaticAnalyzerTool();
static ClangStaticAnalyzerTool *instance();
QAction *stopAction() { return m_stopAction; }
CppTools::ProjectInfo projectInfoBeforeBuild() const;
void resetCursorAndProjectInfoBeforeBuild();
// For testing.
QList<Diagnostic> diagnostics() const;
void startTool();
void handleWorkerStart(ProjectExplorer::RunWorker *runWorker);
void onEngineIsStarting();
void onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
void onEngineFinished(bool success);
signals:
void finished(bool success); // For testing.
private:
void setBusyCursor(bool busy);
void setToolBusy(bool busy);
void handleStateUpdate();
void updateRunActions();
private:
CppTools::ProjectInfo m_projectInfoBeforeBuild;
ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel = nullptr;
ClangStaticAnalyzerDiagnosticFilterModel *m_diagnosticFilterModel = nullptr;
ClangStaticAnalyzerDiagnosticView *m_diagnosticView = nullptr;
@@ -85,7 +71,6 @@ private:
QAction *m_stopAction = nullptr;
QAction *m_goBack = nullptr;
QAction *m_goNext = nullptr;
QHash<ProjectExplorer::Target *, DummyRunConfiguration *> m_runConfigs;
bool m_running = false;
bool m_toolBusy = false;
};

View File

@@ -616,6 +616,8 @@ public:
outputFormatter = runConfiguration->createOutputFormatter();
device = DeviceKitInformation::device(runConfiguration->target()->kit());
project = runConfiguration->target()->project();
} else {
outputFormatter = new OutputFormatter();
}
}