2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2018-01-17 15:08:30 +01:00
|
|
|
|
|
|
|
|
#include "clangtoolruncontrol.h"
|
|
|
|
|
|
|
|
|
|
#include "clangtool.h"
|
2023-01-10 17:51:34 +01:00
|
|
|
#include "clangtoolrunner.h"
|
2023-01-19 18:42:01 +01:00
|
|
|
#include "clangtoolstr.h"
|
2020-05-29 15:46:26 +02:00
|
|
|
#include "executableinfo.h"
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2023-01-11 23:48:53 +01:00
|
|
|
#include <coreplugin/progressmanager/taskprogress.h>
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
#include <cppeditor/cppmodelmanager.h>
|
2018-01-17 15:08:30 +01:00
|
|
|
|
|
|
|
|
#include <projectexplorer/buildconfiguration.h>
|
|
|
|
|
#include <projectexplorer/buildmanager.h>
|
|
|
|
|
#include <projectexplorer/kitinformation.h>
|
|
|
|
|
#include <projectexplorer/project.h>
|
|
|
|
|
#include <projectexplorer/projectexplorer.h>
|
|
|
|
|
#include <projectexplorer/target.h>
|
|
|
|
|
#include <projectexplorer/toolchain.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
2023-05-03 17:05:35 +02:00
|
|
|
#include <utils/process.h>
|
2020-02-28 15:09:35 +01:00
|
|
|
#include <utils/stringutils.h>
|
2018-01-17 15:08:30 +01:00
|
|
|
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
using namespace CppEditor;
|
2018-01-17 15:08:30 +01:00
|
|
|
using namespace ProjectExplorer;
|
2023-05-10 19:54:52 +02:00
|
|
|
using namespace Tasking;
|
2018-01-17 15:08:30 +01:00
|
|
|
using namespace Utils;
|
|
|
|
|
|
2018-10-12 09:33:30 +03:00
|
|
|
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol", QtWarningMsg)
|
2018-01-17 15:08:30 +01:00
|
|
|
|
|
|
|
|
namespace ClangTools {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2018-05-02 16:13:01 +02:00
|
|
|
class ProjectBuilder : public RunWorker
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-08-29 15:00:03 +02:00
|
|
|
ProjectBuilder(RunControl *runControl)
|
|
|
|
|
: RunWorker(runControl)
|
2018-05-02 16:13:01 +02:00
|
|
|
{
|
2018-08-21 08:28:27 +02:00
|
|
|
setId("ProjectBuilder");
|
2018-05-02 16:13:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool success() const { return m_success; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void start() final
|
|
|
|
|
{
|
2019-08-27 11:47:45 +02:00
|
|
|
Target *target = runControl()->target();
|
2018-05-02 16:13:01 +02:00
|
|
|
QTC_ASSERT(target, reportFailure(); return);
|
|
|
|
|
|
|
|
|
|
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
|
|
|
|
|
this, &ProjectBuilder::onBuildFinished, Qt::QueuedConnection);
|
2023-04-18 15:24:48 +02:00
|
|
|
if (!BuildManager::isBuilding(target))
|
|
|
|
|
BuildManager::buildProjectWithDependencies(target->project());
|
2018-05-02 16:13:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onBuildFinished(bool success)
|
|
|
|
|
{
|
|
|
|
|
disconnect(BuildManager::instance(), &BuildManager::buildQueueFinished,
|
|
|
|
|
this, &ProjectBuilder::onBuildFinished);
|
|
|
|
|
m_success = success;
|
|
|
|
|
reportDone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool m_success = false;
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-17 15:08:30 +01:00
|
|
|
static QDebug operator<<(QDebug debug, const Utils::Environment &environment)
|
|
|
|
|
{
|
2019-06-12 08:30:33 +02:00
|
|
|
for (const QString &entry : environment.toStringList())
|
2018-01-17 15:08:30 +01:00
|
|
|
debug << "\n " << entry;
|
|
|
|
|
return debug;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
|
|
|
|
|
{
|
2019-06-12 08:30:33 +02:00
|
|
|
for (const AnalyzeUnit &unit : analyzeUnits)
|
2018-01-17 15:08:30 +01:00
|
|
|
debug << "\n " << unit.file;
|
|
|
|
|
return debug;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 14:49:35 +01:00
|
|
|
ClangToolRunWorker::ClangToolRunWorker(ClangTool *tool, RunControl *runControl,
|
2019-09-13 10:49:14 +02:00
|
|
|
const RunSettings &runSettings,
|
2021-08-30 10:58:08 +02:00
|
|
|
const CppEditor::ClangDiagnosticConfig &diagnosticConfig,
|
2019-08-23 15:25:57 +02:00
|
|
|
const FileInfos &fileInfos,
|
2019-10-28 16:25:07 +01:00
|
|
|
bool buildBeforeAnalysis)
|
2018-04-30 15:26:36 +02:00
|
|
|
: RunWorker(runControl)
|
2022-12-07 14:49:35 +01:00
|
|
|
, m_tool(tool)
|
2019-10-31 10:19:51 +01:00
|
|
|
, m_runSettings(runSettings)
|
2019-09-25 15:46:15 +02:00
|
|
|
, m_diagnosticConfig(diagnosticConfig)
|
2018-05-02 14:51:05 +02:00
|
|
|
, m_fileInfos(fileInfos)
|
2019-09-13 10:49:14 +02:00
|
|
|
, m_temporaryDir("clangtools-XXXXXX")
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2022-08-24 14:53:40 +02:00
|
|
|
m_temporaryDir.setAutoRemove(qtcEnvironmentVariable("QTC_CLANG_DONT_DELETE_OUTPUT_FILES")
|
|
|
|
|
!= "1");
|
2019-08-29 15:00:03 +02:00
|
|
|
setId("ClangTidyClazyRunner");
|
|
|
|
|
setSupportsReRunning(false);
|
2019-08-23 15:25:57 +02:00
|
|
|
|
2019-10-28 16:25:07 +01:00
|
|
|
if (buildBeforeAnalysis) {
|
2019-08-29 15:00:03 +02:00
|
|
|
m_projectBuilder = new ProjectBuilder(runControl);
|
|
|
|
|
addStartDependency(m_projectBuilder);
|
|
|
|
|
}
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2019-08-29 15:00:03 +02:00
|
|
|
Target *target = runControl->target();
|
2021-08-30 10:58:08 +02:00
|
|
|
m_projectInfoBeforeBuild = CppEditor::CppModelManager::instance()->projectInfo(target->project());
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2019-08-27 11:47:45 +02:00
|
|
|
BuildConfiguration *buildConfiguration = target->activeBuildConfiguration();
|
2018-01-17 15:08:30 +01:00
|
|
|
QTC_ASSERT(buildConfiguration, return);
|
|
|
|
|
m_environment = buildConfiguration->environment();
|
|
|
|
|
|
2020-02-18 18:25:26 +01:00
|
|
|
ToolChain *toolChain = ToolChainKitAspect::cxxToolChain(target->kit());
|
2018-01-17 15:08:30 +01:00
|
|
|
QTC_ASSERT(toolChain, return);
|
|
|
|
|
m_targetTriple = toolChain->originalTargetTriple();
|
|
|
|
|
m_toolChainType = toolChain->typeId();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-11 23:48:53 +01:00
|
|
|
ClangToolRunWorker::~ClangToolRunWorker() = default;
|
2019-08-29 15:00:03 +02:00
|
|
|
|
2019-08-27 11:28:48 +02:00
|
|
|
void ClangToolRunWorker::start()
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2019-08-30 16:15:43 +02:00
|
|
|
ProjectExplorerPlugin::saveModifiedFiles();
|
2019-02-08 13:04:57 +01:00
|
|
|
|
2019-09-13 10:49:14 +02:00
|
|
|
if (m_projectBuilder && !m_projectBuilder->success()) {
|
2019-10-28 16:25:07 +01:00
|
|
|
emit buildFailed();
|
2023-01-19 18:42:01 +01:00
|
|
|
reportFailure(Tr::tr("Failed to build the project."));
|
2019-09-13 10:49:14 +02:00
|
|
|
return;
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 14:49:35 +01:00
|
|
|
const QString &toolName = m_tool->name();
|
2019-08-27 11:47:45 +02:00
|
|
|
Project *project = runControl()->project();
|
2021-08-30 10:58:08 +02:00
|
|
|
m_projectInfo = CppEditor::CppModelManager::instance()->projectInfo(project);
|
2021-05-07 16:10:07 +02:00
|
|
|
if (!m_projectInfo) {
|
2023-01-19 18:42:01 +01:00
|
|
|
reportFailure(Tr::tr("No code model data available for project."));
|
2021-05-07 16:10:07 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2019-08-27 11:47:45 +02:00
|
|
|
m_projectFiles = Utils::toSet(project->files(Project::AllFiles));
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2019-10-28 16:25:07 +01:00
|
|
|
// Project changed in the mean time?
|
2021-05-07 16:10:07 +02:00
|
|
|
if (m_projectInfo->configurationOrFilesChanged(*m_projectInfoBeforeBuild)) {
|
2018-01-17 15:08:30 +01:00
|
|
|
// 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.
|
2023-01-19 18:42:01 +01:00
|
|
|
reportFailure(Tr::tr("The project configuration changed since the start of "
|
|
|
|
|
"the %1. Please re-run with current configuration.")
|
2019-10-28 16:25:07 +01:00
|
|
|
.arg(toolName));
|
|
|
|
|
emit startFailed();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create log dir
|
|
|
|
|
if (!m_temporaryDir.isValid()) {
|
|
|
|
|
reportFailure(
|
2023-01-19 18:42:01 +01:00
|
|
|
Tr::tr("Failed to create temporary directory: %1.").arg(m_temporaryDir.errorString()));
|
2019-10-28 16:25:07 +01:00
|
|
|
emit startFailed();
|
2018-01-17 15:08:30 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 16:10:07 +02:00
|
|
|
const Utils::FilePath projectFile = m_projectInfo->projectFilePath();
|
2023-01-19 18:42:01 +01:00
|
|
|
appendMessage(Tr::tr("Running %1 on %2 with configuration \"%3\".")
|
2023-01-10 23:47:52 +01:00
|
|
|
.arg(toolName, projectFile.toUserOutput(), m_diagnosticConfig.displayName()),
|
2018-01-17 15:08:30 +01:00
|
|
|
Utils::NormalMessageFormat);
|
|
|
|
|
|
2023-03-13 15:02:03 +01:00
|
|
|
const ClangToolType tool = m_tool == ClangTidyTool::instance() ? ClangToolType::Tidy
|
|
|
|
|
: ClangToolType::Clazy;
|
|
|
|
|
const FilePath executable = toolExecutable(tool);
|
|
|
|
|
const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable);
|
2023-01-11 15:32:46 +01:00
|
|
|
|
2023-03-13 15:02:03 +01:00
|
|
|
// Collect files
|
2023-01-11 15:32:46 +01:00
|
|
|
AnalyzeUnits unitsToProcess;
|
|
|
|
|
for (const FileInfo &fileInfo : m_fileInfos)
|
|
|
|
|
unitsToProcess.append({fileInfo, includeDir, clangVersion});
|
|
|
|
|
|
2023-03-13 15:02:03 +01:00
|
|
|
qCDebug(LOG) << Q_FUNC_INFO << executable << includeDir << clangVersion;
|
2018-01-17 15:08:30 +01:00
|
|
|
qCDebug(LOG) << "Files to process:" << unitsToProcess;
|
2023-01-11 23:48:53 +01:00
|
|
|
qCDebug(LOG) << "Environment:" << m_environment;
|
2019-08-01 14:50:59 +02:00
|
|
|
|
|
|
|
|
m_filesAnalyzed.clear();
|
|
|
|
|
m_filesNotAnalyzed.clear();
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2023-01-11 23:48:53 +01:00
|
|
|
QList<TaskItem> tasks{ParallelLimit(qMax(1, m_runSettings.parallelJobs()))};
|
|
|
|
|
for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) {
|
2023-04-11 12:49:21 +02:00
|
|
|
if (!m_diagnosticConfig.isEnabled(tool)
|
|
|
|
|
&& !m_runSettings.hasConfigFileForSourceFile(unit.file)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const AnalyzeInputData input{tool, m_runSettings, m_diagnosticConfig, m_temporaryDir.path(),
|
2023-01-11 23:48:53 +01:00
|
|
|
m_environment, unit};
|
|
|
|
|
const auto setupHandler = [this, unit, tool] {
|
2023-02-07 18:22:32 +01:00
|
|
|
const QString filePath = unit.file.toUserOutput();
|
2023-01-19 18:42:01 +01:00
|
|
|
appendMessage(Tr::tr("Analyzing \"%1\" [%2].").arg(filePath, clangToolName(tool)),
|
2023-01-11 23:48:53 +01:00
|
|
|
Utils::StdOutFormat);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); };
|
|
|
|
|
tasks.append(clangToolTask(input, setupHandler, outputHandler));
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-11 23:48:53 +01:00
|
|
|
m_taskTree.reset(new TaskTree(tasks));
|
|
|
|
|
connect(m_taskTree.get(), &TaskTree::done, this, &ClangToolRunWorker::finalize);
|
|
|
|
|
connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &ClangToolRunWorker::finalize);
|
|
|
|
|
auto progress = new Core::TaskProgress(m_taskTree.get());
|
2023-01-19 18:42:01 +01:00
|
|
|
progress->setDisplayName(Tr::tr("Analyzing"));
|
2018-01-17 15:08:30 +01:00
|
|
|
reportStarted();
|
2019-11-29 09:24:05 +01:00
|
|
|
m_elapsed.start();
|
2023-01-11 23:48:53 +01:00
|
|
|
m_taskTree->start();
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-27 11:28:48 +02:00
|
|
|
void ClangToolRunWorker::stop()
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2023-01-11 23:48:53 +01:00
|
|
|
m_taskTree.reset();
|
2019-07-09 12:42:07 +02:00
|
|
|
m_projectFiles.clear();
|
2018-01-17 15:08:30 +01:00
|
|
|
|
|
|
|
|
reportStopped();
|
2019-11-29 09:24:05 +01:00
|
|
|
|
|
|
|
|
// Print elapsed time since start
|
2020-02-28 15:09:35 +01:00
|
|
|
const QString elapsedTime = Utils::formatElapsedTime(m_elapsed.elapsed());
|
|
|
|
|
appendMessage(elapsedTime, NormalMessageFormat);
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-11 15:32:46 +01:00
|
|
|
void ClangToolRunWorker::onDone(const AnalyzeOutputData &output)
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2019-10-28 16:25:07 +01:00
|
|
|
emit runnerFinished();
|
|
|
|
|
|
2023-01-11 15:32:46 +01:00
|
|
|
if (!output.success) {
|
|
|
|
|
qCDebug(LOG).noquote() << "onRunnerFinishedWithFailure:" << output.errorMessage << '\n'
|
|
|
|
|
<< output.errorDetails;
|
|
|
|
|
m_filesAnalyzed.remove(output.fileToAnalyze);
|
|
|
|
|
m_filesNotAnalyzed.insert(output.fileToAnalyze);
|
|
|
|
|
|
2023-01-19 18:42:01 +01:00
|
|
|
const QString message = Tr::tr("Failed to analyze \"%1\": %2")
|
2023-02-07 18:22:32 +01:00
|
|
|
.arg(output.fileToAnalyze.toUserOutput(), output.errorMessage);
|
2023-01-11 15:32:46 +01:00
|
|
|
appendMessage(message, Utils::StdErrFormat);
|
|
|
|
|
appendMessage(output.errorDetails, Utils::StdErrFormat);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << output.outputFilePath;
|
|
|
|
|
|
2018-01-17 15:08:30 +01:00
|
|
|
QString errorMessage;
|
2023-01-11 15:32:46 +01:00
|
|
|
const Diagnostics diagnostics = m_tool->read(output.outputFilePath, m_projectFiles,
|
|
|
|
|
&errorMessage);
|
2018-05-31 15:20:35 +02:00
|
|
|
|
2018-01-17 15:08:30 +01:00
|
|
|
if (!errorMessage.isEmpty()) {
|
2023-01-11 15:32:46 +01:00
|
|
|
m_filesAnalyzed.remove(output.fileToAnalyze);
|
|
|
|
|
m_filesNotAnalyzed.insert(output.fileToAnalyze);
|
2018-01-17 15:08:30 +01:00
|
|
|
qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
|
2023-02-07 18:22:32 +01:00
|
|
|
appendMessage(Tr::tr("Failed to analyze \"%1\": %2")
|
|
|
|
|
.arg(output.fileToAnalyze.toUserOutput(), errorMessage),
|
2018-01-17 15:08:30 +01:00
|
|
|
Utils::StdErrFormat);
|
|
|
|
|
} else {
|
2023-01-11 15:32:46 +01:00
|
|
|
if (!m_filesNotAnalyzed.contains(output.fileToAnalyze))
|
|
|
|
|
m_filesAnalyzed.insert(output.fileToAnalyze);
|
2020-11-24 13:06:23 +01:00
|
|
|
if (!diagnostics.isEmpty()) {
|
|
|
|
|
// do not generate marks when we always analyze open files since marks from that
|
|
|
|
|
// analysis should be more up to date
|
|
|
|
|
const bool generateMarks = !m_runSettings.analyzeOpenFiles();
|
2022-12-07 14:49:35 +01:00
|
|
|
m_tool->onNewDiagnosticsAvailable(diagnostics, generateMarks);
|
2020-11-24 13:06:23 +01:00
|
|
|
}
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-27 11:28:48 +02:00
|
|
|
void ClangToolRunWorker::finalize()
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2023-01-11 23:48:53 +01:00
|
|
|
if (m_taskTree)
|
|
|
|
|
m_taskTree.release()->deleteLater();
|
2022-12-07 14:49:35 +01:00
|
|
|
const QString toolName = m_tool->name();
|
2019-08-01 14:50:59 +02:00
|
|
|
if (m_filesNotAnalyzed.size() != 0) {
|
2023-01-19 18:42:01 +01:00
|
|
|
appendMessage(Tr::tr("Error: Failed to analyze %n files.", nullptr, m_filesNotAnalyzed.size()),
|
2019-10-28 16:25:07 +01:00
|
|
|
ErrorMessageFormat);
|
2019-08-27 11:47:45 +02:00
|
|
|
Target *target = runControl()->target();
|
2019-10-25 19:48:14 +02:00
|
|
|
if (target && target->activeBuildConfiguration() && !target->activeBuildConfiguration()->buildDirectory().exists()
|
2019-09-13 10:49:14 +02:00
|
|
|
&& !m_runSettings.buildBeforeAnalysis()) {
|
2019-10-28 16:25:07 +01:00
|
|
|
appendMessage(
|
2023-01-19 18:42:01 +01:00
|
|
|
Tr::tr("Note: You might need to build the project to generate or update source "
|
2019-10-28 16:25:07 +01:00
|
|
|
"files. To build automatically, enable \"Build the project before analysis\"."),
|
|
|
|
|
NormalMessageFormat);
|
2019-06-19 16:11:54 +02:00
|
|
|
}
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-19 18:42:01 +01:00
|
|
|
appendMessage(Tr::tr("%1 finished: "
|
|
|
|
|
"Processed %2 files successfully, %3 failed.")
|
2019-10-28 16:25:07 +01:00
|
|
|
.arg(toolName)
|
|
|
|
|
.arg(m_filesAnalyzed.size())
|
|
|
|
|
.arg(m_filesNotAnalyzed.size()),
|
|
|
|
|
Utils::NormalMessageFormat);
|
|
|
|
|
|
2018-01-17 15:08:30 +01:00
|
|
|
runControl()->initiateStop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ClangTools
|