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 "clangtoolrunner.h"
|
|
|
|
|
|
2023-01-10 17:51:34 +01:00
|
|
|
#include "clangtoolsutils.h"
|
|
|
|
|
|
|
|
|
|
#include <cppeditor/clangdiagnosticconfigsmodel.h>
|
|
|
|
|
#include <cppeditor/compileroptionsbuilder.h>
|
|
|
|
|
|
2018-01-17 15:08:30 +01:00
|
|
|
#include <utils/environment.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
2021-05-05 18:21:22 +02:00
|
|
|
#include <utils/qtcprocess.h>
|
2018-01-17 15:08:30 +01:00
|
|
|
#include <utils/temporaryfile.h>
|
|
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
|
2018-10-12 09:33:30 +03:00
|
|
|
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner", QtWarningMsg)
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2023-01-10 17:51:34 +01:00
|
|
|
using namespace CppEditor;
|
2021-04-30 17:10:00 +02:00
|
|
|
using namespace Utils;
|
|
|
|
|
|
2018-01-17 15:08:30 +01:00
|
|
|
namespace ClangTools {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2023-01-10 17:51:34 +01:00
|
|
|
static bool isClMode(const QStringList &options)
|
|
|
|
|
{
|
|
|
|
|
return options.contains("--driver-mode=cl");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QStringList checksArguments(ClangToolType tool,
|
|
|
|
|
const ClangDiagnosticConfig &diagnosticConfig)
|
|
|
|
|
{
|
|
|
|
|
if (tool == ClangToolType::Tidy) {
|
|
|
|
|
const ClangDiagnosticConfig::TidyMode tidyMode = diagnosticConfig.clangTidyMode();
|
|
|
|
|
// The argument "-config={}" stops stating/evaluating the .clang-tidy file.
|
|
|
|
|
if (tidyMode == ClangDiagnosticConfig::TidyMode::UseDefaultChecks)
|
|
|
|
|
return {"-config={}", "-checks=-clang-diagnostic-*"};
|
|
|
|
|
if (tidyMode == ClangDiagnosticConfig::TidyMode::UseCustomChecks)
|
|
|
|
|
return {"-config=" + diagnosticConfig.clangTidyChecksAsJson()};
|
|
|
|
|
return {"--warnings-as-errors=-*", "-checks=-clang-diagnostic-*"};
|
|
|
|
|
}
|
|
|
|
|
const QString clazyChecks = diagnosticConfig.checks(ClangToolType::Clazy);
|
|
|
|
|
if (!clazyChecks.isEmpty())
|
|
|
|
|
return {"-checks=" + diagnosticConfig.checks(ClangToolType::Clazy)};
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QStringList clangArguments(const ClangDiagnosticConfig &diagnosticConfig,
|
|
|
|
|
const QStringList &baseOptions)
|
|
|
|
|
{
|
|
|
|
|
QStringList arguments;
|
|
|
|
|
arguments << ClangDiagnosticConfigsModel::globalDiagnosticOptions()
|
|
|
|
|
<< (isClMode(baseOptions) ? clangArgsForCl(diagnosticConfig.clangOptions())
|
|
|
|
|
: diagnosticConfig.clangOptions())
|
|
|
|
|
<< baseOptions;
|
|
|
|
|
|
|
|
|
|
if (LOG().isDebugEnabled())
|
|
|
|
|
arguments << QLatin1String("-v");
|
|
|
|
|
|
|
|
|
|
return arguments;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-17 15:08:30 +01:00
|
|
|
static QString generalProcessError(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
return ClangToolRunner::tr("An error occurred with the %1 process.").arg(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString finishedDueToCrash(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
return ClangToolRunner::tr("%1 crashed.").arg(name);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 14:25:49 +02:00
|
|
|
static QString finishedWithBadExitCode(const QString &name, int exitCode)
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
|
|
|
|
return ClangToolRunner::tr("%1 finished with exit code: %2.").arg(name).arg(exitCode);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 17:51:34 +01:00
|
|
|
ClangToolRunner::ClangToolRunner(const AnalyzeInputData &input, QObject *parent)
|
2022-06-16 10:34:01 +02:00
|
|
|
: QObject(parent)
|
2023-01-11 01:07:33 +01:00
|
|
|
, m_input(input)
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2023-01-10 17:51:34 +01:00
|
|
|
m_name = input.tool == ClangToolType::Tidy ? tr("Clang-Tidy") : tr("Clazy");
|
|
|
|
|
m_executable = toolExecutable(input.tool);
|
2023-01-11 01:07:33 +01:00
|
|
|
QTC_CHECK(!m_input.outputDirPath.isEmpty());
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2023-01-10 17:51:34 +01:00
|
|
|
m_process.setEnvironment(input.environment);
|
2022-07-13 16:12:37 +02:00
|
|
|
m_process.setUseCtrlCStub(true);
|
2023-01-11 01:07:33 +01:00
|
|
|
m_process.setWorkingDirectory(m_input.outputDirPath); // Current clang-cl puts log file into working dir.
|
2022-06-16 10:34:01 +02:00
|
|
|
connect(&m_process, &QtcProcess::done, this, &ClangToolRunner::onProcessDone);
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-25 06:18:26 +02:00
|
|
|
QStringList ClangToolRunner::mainToolArguments() const
|
|
|
|
|
{
|
|
|
|
|
QStringList result;
|
|
|
|
|
result << "-export-fixes=" + m_outputFilePath;
|
2023-01-11 01:07:33 +01:00
|
|
|
if (!m_input.overlayFilePath.isEmpty() && supportsVFSOverlay())
|
|
|
|
|
result << "--vfsoverlay=" + m_input.overlayFilePath;
|
|
|
|
|
result << QDir::toNativeSeparators(m_input.unit.file);
|
2020-08-25 06:18:26 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ClangToolRunner::supportsVFSOverlay() const
|
|
|
|
|
{
|
2021-08-12 09:19:55 +02:00
|
|
|
static QMap<FilePath, bool> vfsCapabilities;
|
2020-08-25 06:18:26 +02:00
|
|
|
auto it = vfsCapabilities.find(m_executable);
|
|
|
|
|
if (it == vfsCapabilities.end()) {
|
2021-06-22 04:33:47 +02:00
|
|
|
QtcProcess p;
|
2021-08-12 09:19:55 +02:00
|
|
|
p.setCommand({m_executable, {"--help"}});
|
2021-05-17 12:02:42 +02:00
|
|
|
p.runBlocking();
|
2021-05-12 14:25:50 +02:00
|
|
|
it = vfsCapabilities.insert(m_executable, p.allOutput().contains("vfsoverlay"));
|
2020-08-25 06:18:26 +02:00
|
|
|
}
|
|
|
|
|
return it.value();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-02 07:12:33 +02:00
|
|
|
static QString createOutputFilePath(const FilePath &dirPath, const QString &fileToAnalyze)
|
2019-08-29 14:25:49 +02:00
|
|
|
{
|
|
|
|
|
const QString fileName = QFileInfo(fileToAnalyze).fileName();
|
2021-07-02 07:12:33 +02:00
|
|
|
const FilePath fileTemplate = dirPath.pathAppended("report-" + fileName + "-XXXXXX");
|
2019-08-29 14:25:49 +02:00
|
|
|
|
2021-07-02 07:12:33 +02:00
|
|
|
TemporaryFile temporaryFile("clangtools");
|
2019-08-29 14:25:49 +02:00
|
|
|
temporaryFile.setAutoRemove(false);
|
2021-07-02 07:12:33 +02:00
|
|
|
temporaryFile.setFileTemplate(fileTemplate.path());
|
2019-08-29 14:25:49 +02:00
|
|
|
if (temporaryFile.open()) {
|
|
|
|
|
temporaryFile.close();
|
|
|
|
|
return temporaryFile.fileName();
|
|
|
|
|
}
|
2023-01-10 17:51:34 +01:00
|
|
|
return {};
|
2019-08-29 14:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-11 01:07:33 +01:00
|
|
|
bool ClangToolRunner::run()
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2023-01-11 18:47:23 +01:00
|
|
|
QTC_ASSERT(m_executable.isExecutableFile(),
|
|
|
|
|
qWarning() << "Can't start:" << m_executable << "as" << m_name; return false);
|
2023-01-11 01:07:33 +01:00
|
|
|
QTC_CHECK(!m_input.unit.arguments.contains(QLatin1String("-o")));
|
|
|
|
|
QTC_CHECK(!m_input.unit.arguments.contains(m_input.unit.file));
|
|
|
|
|
QTC_ASSERT(FilePath::fromString(m_input.unit.file).exists(), return false);
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2023-01-11 01:07:33 +01:00
|
|
|
m_outputFilePath = createOutputFilePath(m_input.outputDirPath, m_input.unit.file);
|
2019-08-29 14:25:49 +02:00
|
|
|
QTC_ASSERT(!m_outputFilePath.isEmpty(), return false);
|
2023-01-11 11:16:46 +01:00
|
|
|
|
|
|
|
|
const QStringList args = checksArguments(m_input.tool, m_input.config)
|
|
|
|
|
+ mainToolArguments()
|
|
|
|
|
+ QStringList{"--"}
|
|
|
|
|
+ clangArguments(m_input.config, m_input.unit.arguments);
|
|
|
|
|
const CommandLine commandLine = {m_executable, args};
|
2018-01-17 15:08:30 +01:00
|
|
|
|
2023-01-11 01:07:33 +01:00
|
|
|
qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput();
|
|
|
|
|
m_process.setCommand(commandLine);
|
2022-06-16 10:34:01 +02:00
|
|
|
m_process.start();
|
2018-01-17 15:08:30 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-14 09:14:31 +02:00
|
|
|
void ClangToolRunner::onProcessDone()
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
2022-06-16 10:34:01 +02:00
|
|
|
if (m_process.result() == ProcessResult::StartFailed) {
|
2022-04-14 09:14:31 +02:00
|
|
|
emit finishedWithFailure(generalProcessError(m_name), commandlineAndOutput());
|
2022-06-16 10:34:01 +02:00
|
|
|
} else if (m_process.result() == ProcessResult::FinishedWithSuccess) {
|
2022-06-17 14:17:14 +02:00
|
|
|
qCDebug(LOG).noquote() << "Output:\n" << m_process.cleanedStdOut();
|
2023-01-11 01:07:33 +01:00
|
|
|
emit finishedWithSuccess(m_input.unit.file);
|
2022-06-16 10:34:01 +02:00
|
|
|
} else if (m_process.result() == ProcessResult::FinishedWithError) {
|
|
|
|
|
emit finishedWithFailure(finishedWithBadExitCode(m_name, m_process.exitCode()),
|
2021-05-28 16:53:36 +02:00
|
|
|
commandlineAndOutput());
|
2018-01-17 15:08:30 +01:00
|
|
|
} else { // == QProcess::CrashExit
|
2019-08-29 14:25:49 +02:00
|
|
|
emit finishedWithFailure(finishedDueToCrash(m_name), commandlineAndOutput());
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 14:25:49 +02:00
|
|
|
QString ClangToolRunner::commandlineAndOutput() const
|
2018-01-17 15:08:30 +01:00
|
|
|
{
|
|
|
|
|
return tr("Command line: %1\n"
|
2019-08-29 14:25:49 +02:00
|
|
|
"Process Error: %2\n"
|
|
|
|
|
"Output:\n%3")
|
2023-01-11 01:07:33 +01:00
|
|
|
.arg(m_process.commandLine().toUserOutput())
|
2022-06-16 10:34:01 +02:00
|
|
|
.arg(m_process.error())
|
2022-06-17 14:17:14 +02:00
|
|
|
.arg(m_process.cleanedStdOut());
|
2018-01-17 15:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ClangTools
|