diff options
Diffstat (limited to 'src/plugins/clangtools/clangtoolruncontrol.cpp')
-rw-r--r-- | src/plugins/clangtools/clangtoolruncontrol.cpp | 258 |
1 files changed, 146 insertions, 112 deletions
diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 6467293417..da8e9f053c 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -25,12 +25,12 @@ #include "clangtoolruncontrol.h" +#include "clangtidyclazyrunner.h" +#include "clangtidyclazytool.h" #include "clangtool.h" #include "clangtoolslogfilereader.h" -#include "clangtoolsprojectsettings.h" #include "clangtoolssettings.h" #include "clangtoolsutils.h" -#include "clangtoolrunner.h" #include <debugger/analyzer/analyzerconstants.h> @@ -81,8 +81,7 @@ static QStringList splitArgs(QString &argsString) return result; } -template<size_t Size> -static QStringList extraOptions(const char(&environment)[Size]) +static QStringList extraOptions(const char *environment) { if (!qEnvironmentVariableIsSet(environment)) return QStringList(); @@ -90,7 +89,8 @@ static QStringList extraOptions(const char(&environment)[Size]) return splitArgs(arguments); } -static QStringList extraClangToolsPrependOptions() { +static QStringList extraClangToolsPrependOptions() +{ constexpr char csaPrependOptions[] = "QTC_CLANG_CSA_CMD_PREPEND"; constexpr char toolsPrependOptions[] = "QTC_CLANG_TOOLS_CMD_PREPEND"; static const QStringList options = extraOptions(csaPrependOptions) @@ -100,7 +100,8 @@ static QStringList extraClangToolsPrependOptions() { return options; } -static QStringList extraClangToolsAppendOptions() { +static QStringList extraClangToolsAppendOptions() +{ constexpr char csaAppendOptions[] = "QTC_CLANG_CSA_CMD_APPEND"; constexpr char toolsAppendOptions[] = "QTC_CLANG_TOOLS_CMD_APPEND"; static const QStringList options = extraOptions(csaAppendOptions) @@ -113,29 +114,26 @@ static QStringList extraClangToolsAppendOptions() { namespace ClangTools { namespace Internal { +static ClangTool *tool() +{ + return ClangTidyClazyTool::instance(); +} + class ProjectBuilder : public RunWorker { public: - ProjectBuilder(RunControl *runControl, Project *project, ClangToolRunControl *parent) - : RunWorker(runControl), m_project(project), m_parent(parent) + ProjectBuilder(RunControl *runControl) + : RunWorker(runControl) { setId("ProjectBuilder"); } - void setEnabled(bool enabled) { m_enabled = enabled; } - bool success() const { return m_success; } private: void start() final { - if (!m_enabled) { - ProjectExplorerPlugin::saveModifiedFiles(); - onBuildFinished(true); - return; - } - - Target *target = m_project->activeTarget(); + Target *target = runControl()->target(); QTC_ASSERT(target, reportFailure(); return); BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown; @@ -143,14 +141,14 @@ private: buildType = buildConfig->buildType(); if (buildType == BuildConfiguration::Release) { - const QString wrongMode = ClangToolRunControl::tr("Release"); - const QString toolName = m_parent->tool()->name(); - const QString title = ClangToolRunControl::tr("Run %1 in %2 Mode?").arg(toolName, wrongMode); - const QString problem = ClangToolRunControl::tr( + const QString wrongMode = ClangToolRunWorker::tr("Release"); + const QString toolName = tool()->name(); + const QString title = ClangToolRunWorker::tr("Run %1 in %2 Mode?").arg(toolName, wrongMode); + const QString problem = ClangToolRunWorker::tr( "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.").arg(toolName, wrongMode); - const QString question = ClangToolRunControl::tr( + const QString question = ClangToolRunWorker::tr( "Do you want to continue and run the tool in %1 mode?").arg(wrongMode); const QString message = QString("<html><head/><body>" "<p>%1</p>" @@ -168,7 +166,7 @@ private: connect(BuildManager::instance(), &BuildManager::buildQueueFinished, this, &ProjectBuilder::onBuildFinished, Qt::QueuedConnection); - ProjectExplorerPlugin::buildProject(m_project); + ProjectExplorerPlugin::buildProject(target->project()); } void onBuildFinished(bool success) @@ -180,9 +178,6 @@ private: } private: - QPointer<Project> m_project; - ClangToolRunControl *m_parent; - bool m_enabled = true; bool m_success = false; }; @@ -207,7 +202,7 @@ static AnalyzeUnits toAnalyzeUnits(const FileInfos &fileInfos) return unitsToAnalyze; } -AnalyzeUnits ClangToolRunControl::unitsToAnalyze() +AnalyzeUnits ClangToolRunWorker::unitsToAnalyze() { QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits()); @@ -216,79 +211,82 @@ AnalyzeUnits ClangToolRunControl::unitsToAnalyze() static QDebug operator<<(QDebug debug, const Utils::Environment &environment) { - foreach (const QString &entry, environment.toStringList()) + for (const QString &entry : environment.toStringList()) debug << "\n " << entry; return debug; } static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits) { - foreach (const AnalyzeUnit &unit, analyzeUnits) + for (const AnalyzeUnit &unit : analyzeUnits) debug << "\n " << unit.file; return debug; } -ClangToolRunControl::ClangToolRunControl(RunControl *runControl, - Target *target, - const FileInfos &fileInfos) +ClangToolRunWorker::ClangToolRunWorker(RunControl *runControl, + const ClangDiagnosticConfig &diagnosticConfig, + const FileInfos &fileInfos, + bool preventBuild) : RunWorker(runControl) - , m_projectBuilder(new ProjectBuilder(runControl, target->project(), this)) - , m_clangExecutable(Core::ICore::clangExecutable(CLANG_BINDIR)) , m_temporaryDir("clangtools-XXXXXX") - , m_target(target) + , m_diagnosticConfig(diagnosticConfig) , m_fileInfos(fileInfos) { - addStartDependency(m_projectBuilder); - - ClangToolsProjectSettings *projectSettings = ClangToolsProjectSettingsManager::getSettings( - target->project()); - if (projectSettings->useGlobalSettings()) - m_projectBuilder->setEnabled(ClangToolsSettings::instance()->savedBuildBeforeAnalysis()); - else - m_projectBuilder->setEnabled(projectSettings->buildBeforeAnalysis()); -} - -void ClangToolRunControl::init() -{ + setId("ClangTidyClazyRunner"); setSupportsReRunning(false); - m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo( - m_target->project()); - BuildConfiguration *buildConfiguration = m_target->activeBuildConfiguration(); + if (!preventBuild && ClangToolsSettings::instance()->savedBuildBeforeAnalysis()) { + m_projectBuilder = new ProjectBuilder(runControl); + addStartDependency(m_projectBuilder); + } + + Target *target = runControl->target(); + m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(target->project()); + + BuildConfiguration *buildConfiguration = target->activeBuildConfiguration(); QTC_ASSERT(buildConfiguration, return); m_environment = buildConfiguration->environment(); - ToolChain *toolChain = ToolChainKitAspect::toolChain(m_target->kit(), - ProjectExplorer::Constants::CXX_LANGUAGE_ID); + ToolChain *toolChain = ToolChainKitAspect::toolChain(target->kit(), + ProjectExplorer::Constants::CXX_LANGUAGE_ID); QTC_ASSERT(toolChain, return); m_targetTriple = toolChain->originalTargetTriple(); m_toolChainType = toolChain->typeId(); } -void ClangToolRunControl::start() +QList<RunnerCreator> ClangToolRunWorker::runnerCreators() +{ + QList<RunnerCreator> creators; + + if (m_diagnosticConfig.clangTidyMode() != CppTools::ClangDiagnosticConfig::TidyMode::Disabled) + creators << [this]() { return createRunner<ClangTidyRunner>(); }; + + if (!m_diagnosticConfig.clazyChecks().isEmpty()) { + if (!qEnvironmentVariable("QTC_USE_CLAZY_STANDALONE_PATH").isEmpty()) + creators << [this]() { return createRunner<ClazyStandaloneRunner>(); }; + else + creators << [this]() { return createRunner<ClazyPluginRunner>(); }; + } + + return creators; +} + +void ClangToolRunWorker::start() { TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID); + ProjectExplorerPlugin::saveModifiedFiles(); if (ClangToolsSettings::instance()->savedBuildBeforeAnalysis()) { - QTC_ASSERT(m_projectBuilder, return;); - if (!m_projectBuilder->success()) { + if (m_projectBuilder && !m_projectBuilder->success()) { reportFailure(); return; } } const QString &toolName = tool()->name(); - if (m_clangExecutable.isEmpty()) { - const QString errorMessage = tr("%1: Can't find clang executable, stop.").arg(toolName); - appendMessage(errorMessage, Utils::ErrorMessageFormat); - TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID); - TaskHub::requestPopup(); - reportFailure(); - return; - } - - m_projectInfo = CppTools::CppModelManager::instance()->projectInfo(m_target->project()); - m_projectFiles = Utils::toSet(m_target->project()->files(Project::AllFiles)); + Project *project = runControl()->project(); + m_projectInfo = CppTools::CppModelManager::instance()->projectInfo(project); + m_projectFiles = Utils::toSet(project->files(Project::AllFiles)); // Some projects provides CompilerCallData once a build is finished, if (m_projectInfo.configurationOrFilesChanged(m_projectInfoBeforeBuild)) { @@ -318,10 +316,15 @@ void ClangToolRunControl::start() // Collect files const AnalyzeUnits unitsToProcess = unitsToAnalyze(); qCDebug(LOG) << "Files to process:" << unitsToProcess; - m_unitsToProcess = unitsToProcess; - m_initialFilesToProcessSize = m_unitsToProcess.count(); - m_filesAnalyzed = 0; - m_filesNotAnalyzed = 0; + + m_queue.clear(); + for (const AnalyzeUnit &unit : unitsToProcess) { + for (const RunnerCreator &creator : runnerCreators()) + m_queue << QueueItem{unit, creator}; + } + m_initialQueueSize = m_queue.count(); + m_filesAnalyzed.clear(); + m_filesNotAnalyzed.clear(); // Set up progress information using namespace Core; @@ -331,8 +334,8 @@ void ClangToolRunControl::start() toolName.toStdString().c_str()); futureProgress->setKeepOnFinish(FutureProgress::HideOnFinish); connect(futureProgress, &FutureProgress::canceled, - this, &ClangToolRunControl::onProgressCanceled); - m_progress.setProgressRange(0, m_initialFilesToProcessSize); + this, &ClangToolRunWorker::onProgressCanceled); + m_progress.setProgressRange(0, m_initialQueueSize); m_progress.reportStarted(); // Start process(es) @@ -342,75 +345,91 @@ void ClangToolRunControl::start() QTC_ASSERT(parallelRuns >= 1, reportFailure(); return); m_success = true; - if (m_unitsToProcess.isEmpty()) { + if (m_queue.isEmpty()) { finalize(); return; } reportStarted(); - while (m_runners.size() < parallelRuns && !m_unitsToProcess.isEmpty()) + while (m_runners.size() < parallelRuns && !m_queue.isEmpty()) analyzeNextFile(); } -void ClangToolRunControl::stop() +void ClangToolRunWorker::stop() { - QSetIterator<ClangToolRunner *> i(m_runners); - while (i.hasNext()) { - ClangToolRunner *runner = i.next(); + for (ClangToolRunner *runner : m_runners) { QObject::disconnect(runner, nullptr, this, nullptr); delete runner; } m_projectFiles.clear(); m_runners.clear(); - m_unitsToProcess.clear(); + m_queue.clear(); m_progress.reportFinished(); reportStopped(); } -void ClangToolRunControl::analyzeNextFile() +void ClangToolRunWorker::analyzeNextFile() { if (m_progress.isFinished()) return; // The previous call already reported that we are finished. - if (m_unitsToProcess.isEmpty()) { + if (m_queue.isEmpty()) { if (m_runners.isEmpty()) finalize(); return; } - const AnalyzeUnit unit = m_unitsToProcess.takeFirst(); + const QueueItem queueItem = m_queue.takeFirst(); + const AnalyzeUnit unit = queueItem.unit; qCDebug(LOG) << "analyzeNextFile:" << unit.file; - ClangToolRunner *runner = createRunner(); + ClangToolRunner *runner = queueItem.runnerCreator(); m_runners.insert(runner); + + const QString executable = runner->executable(); + if (!isFileExecutable(executable)) { + const QString errorMessage = tr("%1: Invalid executable \"%2\", stop.") + .arg(runner->name(), executable); + TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID); + TaskHub::requestPopup(); + reportFailure(errorMessage); + stop(); + return; + } + QTC_ASSERT(runner->run(unit.file, unit.arguments), return); - appendMessage(tr("Analyzing \"%1\".").arg( - Utils::FilePath::fromString(unit.file).toUserOutput()), + appendMessage(tr("Analyzing \"%1\" [%2].") + .arg(FilePath::fromString(unit.file).toUserOutput(), runner->name()), Utils::StdOutFormat); } -void ClangToolRunControl::onRunnerFinishedWithSuccess(const QString &filePath) +void ClangToolRunWorker::onRunnerFinishedWithSuccess(const QString &filePath) { - const QString logFilePath = qobject_cast<ClangToolRunner *>(sender())->logFilePath(); - qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath; + auto runner = qobject_cast<ClangToolRunner *>(sender()); + const QString outputFilePath = runner->outputFilePath(); + qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << outputFilePath; QString errorMessage; - const QList<Diagnostic> diagnostics = tool()->read(filePath, - m_projectFiles, - logFilePath, - &errorMessage); - QFile::remove(logFilePath); // Clean-up. + const Diagnostics diagnostics = tool()->read(runner->outputFileFormat(), + outputFilePath, + filePath, + m_projectFiles, + &errorMessage); + QFile::remove(outputFilePath); // Clean-up. if (!errorMessage.isEmpty()) { + m_filesAnalyzed.remove(filePath); + m_filesNotAnalyzed.insert(filePath); qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage; - const QString filePath = qobject_cast<ClangToolRunner *>(sender())->filePath(); + const QString filePath = qobject_cast<ClangToolRunner *>(sender())->fileToAnalyze(); appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage), Utils::StdErrFormat); } else { - ++m_filesAnalyzed; + if (!m_filesNotAnalyzed.contains(filePath)) + m_filesAnalyzed.insert(filePath); if (!diagnostics.isEmpty()) tool()->onNewDiagnosticsAvailable(diagnostics); } @@ -418,30 +437,31 @@ void ClangToolRunControl::onRunnerFinishedWithSuccess(const QString &filePath) handleFinished(); } -void ClangToolRunControl::onRunnerFinishedWithFailure(const QString &errorMessage, +void ClangToolRunWorker::onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails) { qCDebug(LOG).noquote() << "onRunnerFinishedWithFailure:" << errorMessage << '\n' << errorDetails; auto *toolRunner = qobject_cast<ClangToolRunner *>(sender()); - const QString filePath = toolRunner->filePath(); - const QString logFilePath = toolRunner->logFilePath(); + const QString fileToAnalyze = toolRunner->fileToAnalyze(); + const QString outputFilePath = toolRunner->outputFilePath(); // Even in the error case the log file was created, so clean it up here, too. - QFile::remove(logFilePath); + QFile::remove(outputFilePath); - const QString message = tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage); - - ++m_filesNotAnalyzed; + m_filesAnalyzed.remove(fileToAnalyze); + m_filesNotAnalyzed.insert(fileToAnalyze); m_success = false; + + const QString message = tr("Failed to analyze \"%1\": %2").arg(fileToAnalyze, errorMessage); appendMessage(message, Utils::StdErrFormat); appendMessage(errorDetails, Utils::StdErrFormat); TaskHub::addTask(Task::Error, message, Debugger::Constants::ANALYZERTASK_ID); handleFinished(); } -void ClangToolRunControl::handleFinished() +void ClangToolRunWorker::handleFinished() { m_runners.remove(qobject_cast<ClangToolRunner *>(sender())); updateProgressValue(); @@ -449,31 +469,33 @@ void ClangToolRunControl::handleFinished() analyzeNextFile(); } -void ClangToolRunControl::onProgressCanceled() +void ClangToolRunWorker::onProgressCanceled() { m_progress.reportCanceled(); runControl()->initiateStop(); } -void ClangToolRunControl::updateProgressValue() +void ClangToolRunWorker::updateProgressValue() { - m_progress.setProgressValue(m_initialFilesToProcessSize - m_unitsToProcess.size()); + m_progress.setProgressValue(m_initialQueueSize - m_queue.size()); } -void ClangToolRunControl::finalize() +void ClangToolRunWorker::finalize() { const QString toolName = tool()->name(); appendMessage(tr("%1 finished: " "Processed %2 files successfully, %3 failed.") - .arg(toolName).arg(m_filesAnalyzed).arg(m_filesNotAnalyzed), + .arg(toolName) + .arg(m_filesAnalyzed.size()) + .arg(m_filesNotAnalyzed.size()), Utils::NormalMessageFormat); - if (m_filesNotAnalyzed != 0) { + if (m_filesNotAnalyzed.size() != 0) { QString msg = tr("%1: Not all files could be analyzed.").arg(toolName); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); - if (m_target && !m_target->activeBuildConfiguration()->buildDirectory().exists() - && !ClangToolsProjectSettingsManager::getSettings(m_target->project()) - ->buildBeforeAnalysis()) { + Target *target = runControl()->target(); + if (target && !target->activeBuildConfiguration()->buildDirectory().exists() + && !ClangToolsSettings::instance()->savedBuildBeforeAnalysis()) { msg = tr("%1: You might need to build the project to generate or update source " "files. To build automatically, enable \"Build the project before starting " "analysis\".") @@ -488,5 +510,17 @@ void ClangToolRunControl::finalize() runControl()->initiateStop(); } +template<class T> +ClangToolRunner *ClangToolRunWorker::createRunner() +{ + auto runner = new T(m_diagnosticConfig, this); + runner->init(m_temporaryDir.path(), m_environment); + connect(runner, &ClangToolRunner::finishedWithSuccess, + this, &ClangToolRunWorker::onRunnerFinishedWithSuccess); + connect(runner, &ClangToolRunner::finishedWithFailure, + this, &ClangToolRunWorker::onRunnerFinishedWithFailure); + return runner; +} + } // namespace Internal } // namespace ClangTools |