forked from qt-creator/qt-creator
Move sources to new location for Qt Creator repository
When moving the files within the merge commit, git for some reason does not recognize the files as moved, so we move them before actually doing the merge. Change-Id: I5a8ba6b027734c8ff38a7f2a5dc51a30a0fc17a8 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
@@ -0,0 +1,511 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangstaticanalyzerruncontrol.h"
|
||||
|
||||
#include "clangstaticanalyzerlogfilereader.h"
|
||||
#include "clangstaticanalyzerrunner.h"
|
||||
#include "clangstaticanalyzersettings.h"
|
||||
#include "clangstaticanalyzerutils.h"
|
||||
|
||||
#include <analyzerbase/analyzermanager.h>
|
||||
#include <analyzerbase/analyzerutils.h>
|
||||
|
||||
#include <clangcodemodel/clangutils.h>
|
||||
|
||||
#include <coreplugin/progressmanager/futureprogress.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
|
||||
#include <cpptools/compileroptionsbuilder.h>
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
#include <cpptools/cppprojectfile.h>
|
||||
#include <cpptools/projectinfo.h>
|
||||
|
||||
#include <projectexplorer/abi.h>
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/runconfiguration.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <projectexplorer/taskhub.h>
|
||||
#include <projectexplorer/toolchain.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
using namespace CppTools;
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runcontrol")
|
||||
|
||||
namespace ClangStaticAnalyzer {
|
||||
namespace Internal {
|
||||
|
||||
ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(
|
||||
RunConfiguration *runConfiguration,
|
||||
Core::Id runMode,
|
||||
const ProjectInfo &projectInfo)
|
||||
: AnalyzerRunControl(runConfiguration, runMode)
|
||||
, m_projectInfo(projectInfo)
|
||||
, m_wordWidth(runConfiguration->abi().wordWidth())
|
||||
, m_initialFilesToProcessSize(0)
|
||||
, m_filesAnalyzed(0)
|
||||
, m_filesNotAnalyzed(0)
|
||||
{
|
||||
Target *target = runConfiguration->target();
|
||||
BuildConfiguration *buildConfiguration = target->activeBuildConfiguration();
|
||||
QTC_ASSERT(buildConfiguration, return);
|
||||
m_environment = buildConfiguration->environment();
|
||||
}
|
||||
|
||||
static void prependWordWidthArgumentIfNotIncluded(QStringList *arguments, unsigned char wordWidth)
|
||||
{
|
||||
QTC_ASSERT(arguments, return);
|
||||
|
||||
const QString m64Argument = QLatin1String("-m64");
|
||||
const QString m32Argument = QLatin1String("-m32");
|
||||
|
||||
const QString argument = wordWidth == 64 ? m64Argument : m32Argument;
|
||||
if (!arguments->contains(argument))
|
||||
arguments->prepend(argument);
|
||||
|
||||
QTC_CHECK(!arguments->contains(m32Argument) || !arguments->contains(m64Argument));
|
||||
}
|
||||
|
||||
// Removes (1) filePath (2) -o <somePath>.
|
||||
// Adds -m64/-m32 argument if not already included.
|
||||
static QStringList tweakedArguments(const QString &filePath,
|
||||
const QStringList &arguments,
|
||||
unsigned char wordWidth)
|
||||
{
|
||||
QStringList newArguments;
|
||||
|
||||
bool skip = false;
|
||||
foreach (const QString &argument, arguments) {
|
||||
if (skip) {
|
||||
skip = false;
|
||||
continue;
|
||||
} else if (argument == QLatin1String("-o")) {
|
||||
skip = true;
|
||||
continue;
|
||||
} else if (QDir::fromNativeSeparators(argument) == filePath) {
|
||||
continue; // TODO: Let it in?
|
||||
}
|
||||
|
||||
newArguments << argument;
|
||||
}
|
||||
QTC_CHECK(skip == false);
|
||||
|
||||
prependWordWidthArgumentIfNotIncluded(&newArguments, wordWidth);
|
||||
|
||||
return newArguments;
|
||||
}
|
||||
|
||||
static QString createLanguageOptionMsvc(ProjectFile::Kind fileKind)
|
||||
{
|
||||
switch (fileKind) {
|
||||
case ProjectFile::CHeader:
|
||||
case ProjectFile::CSource:
|
||||
return QLatin1String("/TC");
|
||||
break;
|
||||
case ProjectFile::CXXHeader:
|
||||
case ProjectFile::CXXSource:
|
||||
return QLatin1String("/TP");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
class ClangStaticAnalyzerOptionsBuilder : public CompilerOptionsBuilder
|
||||
{
|
||||
public:
|
||||
static QStringList build(const CppTools::ProjectPart &projectPart,
|
||||
CppTools::ProjectFile::Kind fileKind,
|
||||
unsigned char wordWidth)
|
||||
{
|
||||
ClangStaticAnalyzerOptionsBuilder optionsBuilder(projectPart);
|
||||
optionsBuilder.addLanguageOption(fileKind);
|
||||
optionsBuilder.addOptionsForLanguage(false);
|
||||
|
||||
// In gcc headers, lots of built-ins are referenced that clang does not understand.
|
||||
// Therefore, prevent the inclusion of the header that references them. Of course, this
|
||||
// will break if code actually requires stuff from there, but that should be the less common
|
||||
// case.
|
||||
const Core::Id type = projectPart.toolchainType;
|
||||
if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID
|
||||
|| type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID)
|
||||
optionsBuilder.addDefine("#define _X86INTRIN_H_INCLUDED\n");
|
||||
|
||||
optionsBuilder.addToolchainAndProjectDefines();
|
||||
optionsBuilder.addHeaderPathOptions();
|
||||
|
||||
if (type == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID)
|
||||
optionsBuilder.add(QLatin1String("/EHsc")); // clang-cl does not understand exceptions
|
||||
else
|
||||
optionsBuilder.add(QLatin1String("-fPIC")); // TODO: Remove?
|
||||
|
||||
QStringList options = optionsBuilder.options();
|
||||
prependWordWidthArgumentIfNotIncluded(&options, wordWidth);
|
||||
return options;
|
||||
}
|
||||
|
||||
private:
|
||||
ClangStaticAnalyzerOptionsBuilder(const CppTools::ProjectPart &projectPart)
|
||||
: CompilerOptionsBuilder(projectPart)
|
||||
, m_isMsvcToolchain(m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID)
|
||||
{
|
||||
}
|
||||
|
||||
void addLanguageOption(ProjectFile::Kind fileKind) override
|
||||
{
|
||||
if (m_isMsvcToolchain)
|
||||
add(createLanguageOptionMsvc(fileKind));
|
||||
else
|
||||
CompilerOptionsBuilder::addLanguageOption(fileKind);
|
||||
}
|
||||
|
||||
void addOptionsForLanguage(bool checkForBorlandExtensions) override
|
||||
{
|
||||
if (m_isMsvcToolchain)
|
||||
return;
|
||||
CompilerOptionsBuilder::addOptionsForLanguage(checkForBorlandExtensions);
|
||||
}
|
||||
|
||||
QString includeOption() const override
|
||||
{
|
||||
if (m_isMsvcToolchain)
|
||||
return QLatin1String("/I");
|
||||
return CompilerOptionsBuilder::includeOption();
|
||||
}
|
||||
|
||||
QString defineOption() const override
|
||||
{
|
||||
if (m_isMsvcToolchain)
|
||||
return QLatin1String("/D");
|
||||
return CompilerOptionsBuilder::defineOption();
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isMsvcToolchain;
|
||||
};
|
||||
|
||||
static AnalyzeUnits unitsToAnalyzeFromCompilerCallData(
|
||||
const ProjectInfo::CompilerCallData &compilerCallData,
|
||||
unsigned char wordWidth)
|
||||
{
|
||||
qCDebug(LOG) << "Taking arguments for analyzing from CompilerCallData.";
|
||||
|
||||
AnalyzeUnits unitsToAnalyze;
|
||||
|
||||
QHashIterator<QString, QList<QStringList> > it(compilerCallData);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
const QString file = it.key();
|
||||
const QList<QStringList> compilerCalls = it.value();
|
||||
foreach (const QStringList &options, compilerCalls) {
|
||||
const QStringList arguments = tweakedArguments(file, options, wordWidth);
|
||||
unitsToAnalyze << AnalyzeUnit(file, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
return unitsToAnalyze;
|
||||
}
|
||||
|
||||
static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QList<ProjectPart::Ptr> projectParts,
|
||||
unsigned char wordWidth)
|
||||
{
|
||||
qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts.";
|
||||
|
||||
AnalyzeUnits unitsToAnalyze;
|
||||
|
||||
foreach (const ProjectPart::Ptr &projectPart, projectParts) {
|
||||
if (!projectPart->selectedForBuilding)
|
||||
continue;
|
||||
|
||||
foreach (const ProjectFile &file, projectPart->files) {
|
||||
if (file.path == CppModelManager::configurationFileName())
|
||||
continue;
|
||||
QTC_CHECK(file.kind != ProjectFile::Unclassified);
|
||||
if (ProjectFile::isSource(file.kind)) {
|
||||
const QStringList arguments
|
||||
= ClangStaticAnalyzerOptionsBuilder::build(*projectPart.data(),
|
||||
file.kind,
|
||||
wordWidth);
|
||||
unitsToAnalyze << AnalyzeUnit(file.path, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unitsToAnalyze;
|
||||
}
|
||||
|
||||
AnalyzeUnits ClangStaticAnalyzerRunControl::sortedUnitsToAnalyze()
|
||||
{
|
||||
QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits());
|
||||
|
||||
AnalyzeUnits units;
|
||||
const ProjectInfo::CompilerCallData compilerCallData = m_projectInfo.compilerCallData();
|
||||
if (compilerCallData.isEmpty()) {
|
||||
units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts(),
|
||||
m_wordWidth);
|
||||
} else {
|
||||
units = unitsToAnalyzeFromCompilerCallData(compilerCallData, m_wordWidth);
|
||||
}
|
||||
|
||||
Utils::sort(units, [](const AnalyzeUnit &a1, const AnalyzeUnit &a2) -> bool {
|
||||
return a1.file < a2.file;
|
||||
});
|
||||
return units;
|
||||
}
|
||||
|
||||
static QDebug operator<<(QDebug debug, const Utils::Environment &environment)
|
||||
{
|
||||
foreach (const QString &entry, environment.toStringList())
|
||||
debug << "\n " << entry;
|
||||
return debug;
|
||||
}
|
||||
|
||||
static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
|
||||
{
|
||||
foreach (const AnalyzeUnit &unit, analyzeUnits)
|
||||
debug << "\n " << unit.file;
|
||||
return debug;
|
||||
}
|
||||
|
||||
static Core::Id toolchainType(ProjectExplorer::RunConfiguration *runConfiguration)
|
||||
{
|
||||
QTC_ASSERT(runConfiguration, return Core::Id());
|
||||
return ToolChainKitInformation::toolChain(runConfiguration->target()->kit())->typeId();
|
||||
}
|
||||
|
||||
bool ClangStaticAnalyzerRunControl::startEngine()
|
||||
{
|
||||
m_success = false;
|
||||
emit starting(this);
|
||||
|
||||
QTC_ASSERT(m_projectInfo.isValid(), emit finished(); return false);
|
||||
const Utils::FileName projectFile = m_projectInfo.project()->projectFilePath();
|
||||
appendMessage(tr("Running Clang Static Analyzer on %1").arg(projectFile.toUserOutput())
|
||||
+ QLatin1Char('\n'), Utils::NormalMessageFormat);
|
||||
|
||||
// Check clang executable
|
||||
bool isValidClangExecutable;
|
||||
const QString executable = clangExecutableFromSettings(toolchainType(runConfiguration()),
|
||||
&isValidClangExecutable);
|
||||
if (!isValidClangExecutable) {
|
||||
const QString errorMessage = tr("Clang Static Analyzer: Invalid executable \"%1\", stop.")
|
||||
.arg(executable);
|
||||
appendMessage(errorMessage + QLatin1Char('\n'), Utils::ErrorMessageFormat);
|
||||
AnalyzerUtils::logToIssuesPane(Task::Error, errorMessage);
|
||||
emit finished();
|
||||
return false;
|
||||
}
|
||||
m_clangExecutable = executable;
|
||||
|
||||
// Create log dir
|
||||
QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
|
||||
temporaryDir.setAutoRemove(false);
|
||||
if (!temporaryDir.isValid()) {
|
||||
const QString errorMessage
|
||||
= tr("Clang Static Analyzer: Failed to create temporary dir, stop.");
|
||||
appendMessage(errorMessage + QLatin1Char('\n'), Utils::ErrorMessageFormat);
|
||||
AnalyzerUtils::logToIssuesPane(Task::Error, errorMessage);
|
||||
emit finished();
|
||||
return false;
|
||||
}
|
||||
m_clangLogFileDir = temporaryDir.path();
|
||||
|
||||
// Collect files
|
||||
const AnalyzeUnits unitsToProcess = sortedUnitsToAnalyze();
|
||||
qCDebug(LOG) << "Files to process:" << unitsToProcess;
|
||||
m_unitsToProcess = unitsToProcess;
|
||||
m_initialFilesToProcessSize = m_unitsToProcess.count();
|
||||
m_filesAnalyzed = 0;
|
||||
m_filesNotAnalyzed = 0;
|
||||
|
||||
// Set up progress information
|
||||
using namespace Core;
|
||||
m_progress = QFutureInterface<void>();
|
||||
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)
|
||||
qCDebug(LOG) << "Environment:" << m_environment;
|
||||
m_runners.clear();
|
||||
const int parallelRuns = ClangStaticAnalyzerSettings::instance()->simultaneousProcesses();
|
||||
QTC_ASSERT(parallelRuns >= 1, emit finished(); return false);
|
||||
m_success = true;
|
||||
|
||||
if (m_unitsToProcess.isEmpty()) {
|
||||
finalize();
|
||||
return false;
|
||||
}
|
||||
while (m_runners.size() < parallelRuns && !m_unitsToProcess.isEmpty())
|
||||
analyzeNextFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClangStaticAnalyzerRunControl::stopEngine()
|
||||
{
|
||||
QSetIterator<ClangStaticAnalyzerRunner *> i(m_runners);
|
||||
while (i.hasNext()) {
|
||||
ClangStaticAnalyzerRunner *runner = i.next();
|
||||
QObject::disconnect(runner, 0, this, 0);
|
||||
delete runner;
|
||||
}
|
||||
m_runners.clear();
|
||||
m_unitsToProcess.clear();
|
||||
appendMessage(tr("Clang Static Analyzer stopped by user.") + QLatin1Char('\n'),
|
||||
Utils::NormalMessageFormat);
|
||||
m_progress.reportFinished();
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void ClangStaticAnalyzerRunControl::analyzeNextFile()
|
||||
{
|
||||
if (m_progress.isFinished())
|
||||
return; // The previous call already reported that we are finished.
|
||||
|
||||
if (m_unitsToProcess.isEmpty()) {
|
||||
if (m_runners.isEmpty())
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
|
||||
const AnalyzeUnit unit = m_unitsToProcess.takeFirst();
|
||||
qCDebug(LOG) << "analyzeNextFile:" << unit.file;
|
||||
|
||||
ClangStaticAnalyzerRunner *runner = createRunner();
|
||||
m_runners.insert(runner);
|
||||
QTC_ASSERT(runner->run(unit.file, unit.arguments), return);
|
||||
|
||||
appendMessage(tr("Analyzing \"%1\".").arg(
|
||||
Utils::FileName::fromString(unit.file).toUserOutput()) + QLatin1Char('\n'),
|
||||
Utils::StdOutFormat);
|
||||
}
|
||||
|
||||
ClangStaticAnalyzerRunner *ClangStaticAnalyzerRunControl::createRunner()
|
||||
{
|
||||
QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0);
|
||||
QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0);
|
||||
|
||||
auto runner = new ClangStaticAnalyzerRunner(m_clangExecutable,
|
||||
m_clangLogFileDir,
|
||||
m_environment,
|
||||
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;
|
||||
|
||||
QString errorMessage;
|
||||
const QList<Diagnostic> diagnostics = LogFileReader::read(logFilePath, &errorMessage);
|
||||
if (!errorMessage.isEmpty()) {
|
||||
qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
|
||||
const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath();
|
||||
appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage)
|
||||
+ QLatin1Char('\n')
|
||||
, Utils::StdErrFormat);
|
||||
} else {
|
||||
++m_filesAnalyzed;
|
||||
if (!diagnostics.isEmpty())
|
||||
emit newDiagnosticsAvailable(diagnostics);
|
||||
}
|
||||
|
||||
handleFinished();
|
||||
}
|
||||
|
||||
void ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure(const QString &errorMessage,
|
||||
const QString &errorDetails)
|
||||
{
|
||||
qCDebug(LOG) << "onRunnerFinishedWithFailure:" << errorMessage << errorDetails;
|
||||
|
||||
++m_filesNotAnalyzed;
|
||||
m_success = false;
|
||||
const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath();
|
||||
appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage)
|
||||
+ QLatin1Char('\n')
|
||||
, Utils::StdErrFormat);
|
||||
appendMessage(errorDetails, Utils::StdErrFormat);
|
||||
AnalyzerUtils::logToIssuesPane(Task::Warning, errorMessage);
|
||||
AnalyzerUtils::logToIssuesPane(Task::Warning, errorDetails);
|
||||
handleFinished();
|
||||
}
|
||||
|
||||
void ClangStaticAnalyzerRunControl::handleFinished()
|
||||
{
|
||||
m_runners.remove(qobject_cast<ClangStaticAnalyzerRunner *>(sender()));
|
||||
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_unitsToProcess.size());
|
||||
}
|
||||
|
||||
void ClangStaticAnalyzerRunControl::finalize()
|
||||
{
|
||||
appendMessage(tr("Clang Static Analyzer finished: "
|
||||
"Processed %1 files successfully, %2 failed.")
|
||||
.arg(m_filesAnalyzed)
|
||||
.arg(m_filesNotAnalyzed)
|
||||
+ QLatin1Char('\n'),
|
||||
Utils::NormalMessageFormat);
|
||||
|
||||
if (m_filesAnalyzed == 0 && m_filesNotAnalyzed != 0) {
|
||||
AnalyzerUtils::logToIssuesPane(Task::Error,
|
||||
tr("Clang Static Analyzer: Failed to analyze any files."));
|
||||
}
|
||||
|
||||
m_progress.reportFinished();
|
||||
emit finished();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangStaticAnalyzer
|
||||
Reference in New Issue
Block a user