From 0ffbe6a9e4cf2e6a03eb36d571913dd7e1ca5f62 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 16 May 2019 15:36:55 +0200 Subject: [PATCH] CompilationDbProjectManager: Sanitize project parsing The parsing code used to access all kinds of stuff (e.g. the project tree and the toolchain manager) from the non-UI thread, which is not allowed. Fixes: QTCREATORBUG-22420 Change-Id: I4be47919d7e543376d31826dd380f66f4e060458 Reviewed-by: Nikolai Kosjar --- .../compilationdatabaseproject.cpp | 212 +++------------- .../compilationdatabaseproject.h | 15 +- .../compilationdatabaseprojectmanager.pro | 6 +- .../compilationdatabaseprojectmanager.qbs | 2 + .../compilationdatabaseutils.cpp | 2 + .../compilationdatabaseutils.h | 20 ++ .../compilationdbparser.cpp | 228 ++++++++++++++++++ .../compilationdbparser.h | 78 ++++++ .../compilationdatabaseutils-test.cpp | 3 +- 9 files changed, 385 insertions(+), 181 deletions(-) create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index 7f3b77a942d..82c5c616c2d 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -26,10 +26,9 @@ #include "compilationdatabaseproject.h" #include "compilationdatabaseconstants.h" -#include "compilationdatabaseutils.h" +#include "compilationdbparser.h" #include -#include #include #include #include @@ -54,9 +53,7 @@ #include #include -#include -#include -#include +#include #ifdef Q_OS_WIN #include @@ -69,22 +66,6 @@ namespace Internal { namespace { -QStringList jsonObjectFlags(const QJsonObject &object, QSet &flagsCache) -{ - QStringList flags; - const QJsonArray arguments = object["arguments"].toArray(); - if (arguments.isEmpty()) { - flags = splitCommandLine(object["command"].toString(), flagsCache); - } else { - flags.reserve(arguments.size()); - for (const QJsonValue &arg : arguments) { - auto flagIt = flagsCache.insert(arg.toString()); - flags.append(*flagIt); - } - } - - return flags; -} bool isGccCompiler(const QString &compilerName) { @@ -176,16 +157,6 @@ ToolChain *toolchainFromFlags(const Kit *kit, const QStringList &flags, const Co return toolchain; } -Utils::FileName jsonObjectFilename(const QJsonObject &object) -{ - const QString workingDir = QDir::fromNativeSeparators(object["directory"].toString()); - Utils::FileName fileName = Utils::FileName::fromString( - QDir::fromNativeSeparators(object["file"].toString())); - if (fileName.toFileInfo().isRelative()) - fileName = Utils::FileName::fromString(workingDir + "/" + fileName.toString()).canonicalPath(); - return fileName; -} - void addDriverModeFlagIfNeeded(const ToolChain *toolchain, QStringList &flags, const QStringList &originalFlags) @@ -362,85 +333,11 @@ void createTree(std::unique_ptr &root, } } -struct Entry -{ - QStringList flags; - Utils::FileName fileName; - QString workingDir; -}; - -std::vector readJsonObjects(const QString &filePath) -{ - std::vector result; - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly)) - return result; - - const QByteArray contents = file.readAll(); - int objectStart = contents.indexOf('{'); - int objectEnd = contents.indexOf('}', objectStart + 1); - - QSet flagsCache; - while (objectStart >= 0 && objectEnd >= 0) { - const QJsonDocument document = QJsonDocument::fromJson( - contents.mid(objectStart, objectEnd - objectStart + 1)); - if (document.isNull()) { - // The end was found incorrectly, search for the next one. - objectEnd = contents.indexOf('}', objectEnd + 1); - continue; - } - - const QJsonObject object = document.object(); - const Utils::FileName fileName = jsonObjectFilename(object); - const QStringList flags = filterFromFileName(jsonObjectFlags(object, flagsCache), - fileName.toFileInfo().baseName()); - result.push_back({flags, fileName, object["directory"].toString()}); - - objectStart = contents.indexOf('{', objectEnd + 1); - objectEnd = contents.indexOf('}', objectStart + 1); - } - - return result; -} - -QStringList readExtraFiles(const QString &filePath) -{ - QStringList result; - - QFile file(filePath); - if (file.open(QFile::ReadOnly)) { - QTextStream stream(&file); - - while (!stream.atEnd()) { - QString line = stream.readLine(); - line = line.trimmed(); - - if (line.isEmpty() || line.startsWith('#')) - continue; - - result.push_back(line); - } - } - - return result; -} } // anonymous namespace -void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName &projectFile) +void CompilationDatabaseProject::buildTreeAndProjectParts() { - std::vector array = readJsonObjects(projectFilePath().toString()); - const QString jsonExtraFilename = projectFilePath().toString() + - Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX; - const QStringList &extras = readExtraFiles(jsonExtraFilename); - - if (array.empty() && extras.empty()) { - emitParsingFinished(false); - return; - } - - auto root = std::make_unique(projectDirectory()); - CppTools::KitInfo kitInfo(this); QTC_ASSERT(kitInfo.isValid(), return); // Reset toolchains to pick them based on the database entries. @@ -448,13 +345,10 @@ void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName kitInfo.cxxToolChain = nullptr; CppTools::RawProjectParts rpps; - std::sort(array.begin(), array.end(), [](const Entry &lhs, const Entry &rhs) { - return std::lexicographical_compare(lhs.flags.begin(), lhs.flags.end(), - rhs.flags.begin(), rhs.flags.end()); - }); - - const Entry *prevEntry = nullptr; - for (const Entry &entry : array) { + QTC_ASSERT(m_parser, return); + const DbContents dbContents = m_parser->dbContents(); + const DbEntry *prevEntry = nullptr; + for (const DbEntry &entry : dbContents.entries) { if (prevEntry && prevEntry->flags == entry.flags) { rpps.back().files.append(entry.fileName.toString()); continue; @@ -462,7 +356,7 @@ void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName prevEntry = &entry; - CppTools::RawProjectPart rpp = makeRawProjectPart(projectFile, + CppTools::RawProjectPart rpp = makeRawProjectPart(projectFilePath(), m_kit.get(), kitInfo, entry.workingDir, @@ -472,11 +366,11 @@ void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName rpps.append(rpp); } - if (!extras.empty()) { - const Utils::FileName baseDir = projectFile.parentDir(); + if (!dbContents.extras.empty()) { + const Utils::FileName baseDir = projectFilePath().parentDir(); QStringList extraFiles; - for (const QString &extra : extras) + for (const QString &extra : dbContents.extras) extraFiles.append(baseDir.pathAppended(extra).toString()); CppTools::RawProjectPart rppExtra; @@ -484,30 +378,25 @@ void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName rpps.append(rppExtra); } - m_treeScanner.future().waitForFinished(); - QCoreApplication::processEvents(); - if (m_treeScanner.future().isCanceled()) - createTree(root, rootProjectDirectory(), rpps); - else - createTree(root, rootProjectDirectory(), rpps, m_treeScanner.release()); + auto root = std::make_unique(projectDirectory()); + createTree(root, rootProjectDirectory(), rpps, m_parser->scannedFiles()); - root->addNode(std::make_unique(projectFile, FileType::Project)); + root->addNode(std::make_unique(projectFilePath(), FileType::Project)); - if (QFile::exists(jsonExtraFilename)) - root->addNode(std::make_unique(Utils::FileName::fromString(jsonExtraFilename), + if (QFile::exists(dbContents.extraFileName)) + root->addNode(std::make_unique(Utils::FileName::fromString(dbContents.extraFileName), FileType::Project)); setRootProjectNode(std::move(root)); m_cppCodeModelUpdater->update({this, kitInfo, rpps}); - - emitParsingFinished(true); } CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &projectFile) : Project(Constants::COMPILATIONDATABASEMIMETYPE, projectFile) , m_cppCodeModelUpdater(std::make_unique()) + , m_parseDelay(new QTimer(this)) { setId(Constants::COMPILATIONDATABASEPROJECT_ID); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); @@ -523,40 +412,17 @@ CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &pr } }); - m_treeScanner.setFilter([this](const Utils::MimeType &mimeType, const Utils::FileName &fn) { - // Mime checks requires more resources, so keep it last in check list - auto isIgnored = fn.toString().startsWith(projectFilePath().toString() + ".user") - || TreeScanner::isWellKnownBinary(mimeType, fn); - - // Cache mime check result for speed up - if (!isIgnored) { - auto it = m_mimeBinaryCache.find(mimeType.name()); - if (it != m_mimeBinaryCache.end()) { - isIgnored = *it; - } else { - isIgnored = TreeScanner::isMimeBinary(mimeType, fn); - m_mimeBinaryCache[mimeType.name()] = isIgnored; - } - } - - return isIgnored; - }); - m_treeScanner.setTypeFactory([](const Utils::MimeType &mimeType, const Utils::FileName &fn) { - return TreeScanner::genericFileType(mimeType, fn); - }); - - connect(this, - &CompilationDatabaseProject::rootProjectDirectoryChanged, - this, - &CompilationDatabaseProject::reparseProject); + connect(this, &CompilationDatabaseProject::rootProjectDirectoryChanged, + m_parseDelay, qOverload<>(&QTimer::start)); m_fileSystemWatcher.addFile(projectFile.toString(), Utils::FileSystemWatcher::WatchModifiedDate); m_fileSystemWatcher.addFile(projectFile.toString() + Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX, Utils::FileSystemWatcher::WatchModifiedDate); - connect(&m_fileSystemWatcher, - &Utils::FileSystemWatcher::fileChanged, - this, - &CompilationDatabaseProject::reparseProject); + connect(&m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged, + m_parseDelay, qOverload<>(&QTimer::start)); + connect(m_parseDelay, &QTimer::timeout, this, &CompilationDatabaseProject::reparseProject); + m_parseDelay->setSingleShot(true); + m_parseDelay->setInterval(1000); } Utils::FileName CompilationDatabaseProject::rootPathFromSettings() const @@ -586,23 +452,21 @@ Project::RestoreResult CompilationDatabaseProject::fromMap(const QVariantMap &ma void CompilationDatabaseProject::reparseProject() { - emitParsingStarted(); - - const Utils::FileName rootPath = rootPathFromSettings(); - if (!rootPath.isEmpty()) { - m_treeScanner.asyncScanForFiles(rootPath); - - Core::ProgressManager::addTask(m_treeScanner.future(), - tr("Scan \"%1\" project tree").arg(displayName()), - "CompilationDatabase.Scan.Tree"); + if (m_parser) { + QTC_CHECK(isParsing()); + m_parser->stop(); + emitParsingFinished(false); } - - const QFuture future = ::Utils::runAsync( - [this]() { buildTreeAndProjectParts(projectFilePath()); }); - Core::ProgressManager::addTask(future, - tr("Parse \"%1\" project").arg(displayName()), - "CompilationDatabase.Parse"); - m_parserWatcher.setFuture(future); + m_parser = new CompilationDbParser(displayName(), projectFilePath(), rootPathFromSettings(), + m_mimeBinaryCache, this); + connect(m_parser, &CompilationDbParser::finished, this, [this](bool success) { + if (success) + buildTreeAndProjectParts(); + m_parser = nullptr; + emitParsingFinished(success); + }); + emitParsingStarted(); + m_parser->start(); } CompilationDatabaseProject::~CompilationDatabaseProject() diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h index 0351721774c..54ca07a2e3f 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h @@ -25,14 +25,19 @@ #pragma once +#include "compilationdatabaseutils.h" + #include #include -#include #include #include #include +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + namespace CppTools { class CppProjectUpdater; } @@ -43,6 +48,7 @@ class Kit; namespace CompilationDatabaseProjectManager { namespace Internal { +class CompilationDbParser; class CompilationDatabaseProject : public ProjectExplorer::Project { @@ -58,15 +64,16 @@ private: RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override; void reparseProject(); - void buildTreeAndProjectParts(const Utils::FileName &projectFile); + void buildTreeAndProjectParts(); Utils::FileName rootPathFromSettings() const; QFutureWatcher m_parserWatcher; std::unique_ptr m_cppCodeModelUpdater; std::unique_ptr m_kit; Utils::FileSystemWatcher m_fileSystemWatcher; - ProjectExplorer::TreeScanner m_treeScanner; - QHash m_mimeBinaryCache; + MimeBinaryCache m_mimeBinaryCache; + QTimer * const m_parseDelay; + CompilationDbParser *m_parser = nullptr; bool m_hasTarget = false; }; diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro index e7efefb47ea..e11a8cc06ba 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro @@ -3,13 +3,15 @@ include(../../qtcreatorplugin.pri) SOURCES = \ compilationdatabaseproject.cpp \ compilationdatabaseprojectmanagerplugin.cpp \ - compilationdatabaseutils.cpp + compilationdatabaseutils.cpp \ + compilationdbparser.cpp HEADERS = \ compilationdatabaseproject.h \ compilationdatabaseprojectmanagerplugin.h \ compilationdatabaseconstants.h \ - compilationdatabaseutils.h + compilationdatabaseutils.h \ + compilationdbparser.h equals(TEST, 1) { HEADERS += \ diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs index 4cbe2af2846..8f592cbc514 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs @@ -17,6 +17,8 @@ QtcPlugin { "compilationdatabaseutils.h", "compilationdatabaseprojectmanagerplugin.cpp", "compilationdatabaseprojectmanagerplugin.h", + "compilationdbparser.cpp", + "compilationdbparser.h", ] Group { diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp index ff931cc3879..96434540d2d 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp @@ -38,6 +38,7 @@ using namespace ProjectExplorer; namespace CompilationDatabaseProjectManager { +namespace Internal { static QString updatedPathFlag(const QString &pathStr, const QString &workingDir) { @@ -240,4 +241,5 @@ QStringList splitCommandLine(QString commandLine, QSet &flagsCache) return result; } +} // namespace Internal } // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h index be3c7939201..c1686fce0ff 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h @@ -28,7 +28,9 @@ #include "compilationdatabaseconstants.h" #include +#include +#include #include namespace ProjectExplorer { @@ -37,6 +39,23 @@ class Macro; } namespace CompilationDatabaseProjectManager { +namespace Internal { + +class DbEntry { +public: + QStringList flags; + Utils::FileName fileName; + QString workingDir; +}; + +class DbContents { +public: + std::vector entries; + QString extraFileName; + QStringList extras; +}; + +using MimeBinaryCache = QHash; QStringList filterFromFileName(const QStringList &flags, QString baseName); @@ -50,4 +69,5 @@ void filteredFlags(const QString &fileName, QStringList splitCommandLine(QString commandLine, QSet &flagsCache); +} // namespace Internal } // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp new file mode 100644 index 00000000000..f36eec46e5e --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "compilationdbparser.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; + +namespace CompilationDatabaseProjectManager { +namespace Internal { + +CompilationDbParser::CompilationDbParser(const QString &projectName, const FileName &projectPath, + const FileName &rootPath, MimeBinaryCache &mimeBinaryCache, + QObject *parent) + : QObject(parent), + m_projectName(projectName), + m_projectFilePath(projectPath), + m_rootPath(rootPath), + m_mimeBinaryCache(mimeBinaryCache) +{ + connect(&m_parserWatcher, &QFutureWatcher::finished, this, [this] { + m_dbContents = m_parserWatcher.result(); + if (!m_treeScanner || m_treeScanner->isFinished()) + finish(); + }); +} + +void CompilationDbParser::start() +{ + // Thread 1: Scan disk. + if (!m_rootPath.isEmpty()) { + m_treeScanner = new TreeScanner(this); + m_treeScanner->setFilter([this](const MimeType &mimeType, const FileName &fn) { + // Mime checks requires more resources, so keep it last in check list + bool isIgnored = fn.toString().startsWith(m_projectFilePath.toString() + ".user") + || TreeScanner::isWellKnownBinary(mimeType, fn); + + // Cache mime check result for speed up + if (!isIgnored) { + auto it = m_mimeBinaryCache.find(mimeType.name()); + if (it != m_mimeBinaryCache.end()) { + isIgnored = *it; + } else { + isIgnored = TreeScanner::isMimeBinary(mimeType, fn); + m_mimeBinaryCache[mimeType.name()] = isIgnored; + } + } + + return isIgnored; + }); + m_treeScanner->setTypeFactory([](const Utils::MimeType &mimeType, const Utils::FileName &fn) { + return TreeScanner::genericFileType(mimeType, fn); + }); + m_treeScanner->asyncScanForFiles(m_rootPath); + Core::ProgressManager::addTask(m_treeScanner->future(), + tr("Scan \"%1\" project tree").arg(m_projectName), + "CompilationDatabase.Scan.Tree"); + connect(m_treeScanner, &TreeScanner::finished, this, [this] { + if (m_parserWatcher.isFinished()) + finish(); + }); + } + + // Thread 2: Parse the project file. + const QFuture future = runAsync(&CompilationDbParser::parseProject, this); + Core::ProgressManager::addTask(future, + tr("Parse \"%1\" project").arg(m_projectName), + "CompilationDatabase.Parse"); + m_parserWatcher.setFuture(future); +} + +void CompilationDbParser::stop() +{ + disconnect(); + m_parserWatcher.disconnect(); + m_parserWatcher.cancel(); + if (m_treeScanner) { + m_treeScanner->disconnect(); + m_treeScanner->future().cancel(); + } + deleteLater(); +} + +QList CompilationDbParser::scannedFiles() const +{ + return m_treeScanner && !m_treeScanner->future().isCanceled() + ? m_treeScanner->release() : QList(); +} + +void CompilationDbParser::finish() +{ + emit finished(true); + deleteLater(); +} + +static QStringList jsonObjectFlags(const QJsonObject &object, QSet &flagsCache) +{ + QStringList flags; + const QJsonArray arguments = object["arguments"].toArray(); + if (arguments.isEmpty()) { + flags = splitCommandLine(object["command"].toString(), flagsCache); + } else { + flags.reserve(arguments.size()); + for (const QJsonValue &arg : arguments) { + auto flagIt = flagsCache.insert(arg.toString()); + flags.append(*flagIt); + } + } + + return flags; +} + +static FileName jsonObjectFilename(const QJsonObject &object) +{ + const QString workingDir = QDir::fromNativeSeparators(object["directory"].toString()); + FileName fileName = FileName::fromString(QDir::fromNativeSeparators(object["file"].toString())); + if (fileName.toFileInfo().isRelative()) + fileName = FileName::fromString(workingDir + "/" + fileName.toString()).canonicalPath(); + return fileName; +} + +static std::vector readJsonObjects(const QString &filePath) +{ + std::vector result; + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) + return result; + + const QByteArray contents = file.readAll(); + int objectStart = contents.indexOf('{'); + int objectEnd = contents.indexOf('}', objectStart + 1); + + QSet flagsCache; + while (objectStart >= 0 && objectEnd >= 0) { + const QJsonDocument document = QJsonDocument::fromJson( + contents.mid(objectStart, objectEnd - objectStart + 1)); + if (document.isNull()) { + // The end was found incorrectly, search for the next one. + objectEnd = contents.indexOf('}', objectEnd + 1); + continue; + } + + const QJsonObject object = document.object(); + const Utils::FileName fileName = jsonObjectFilename(object); + const QStringList flags = filterFromFileName(jsonObjectFlags(object, flagsCache), + fileName.toFileInfo().baseName()); + result.push_back({flags, fileName, object["directory"].toString()}); + + objectStart = contents.indexOf('{', objectEnd + 1); + objectEnd = contents.indexOf('}', objectStart + 1); + } + + return result; +} + +QStringList readExtraFiles(const QString &filePath) +{ + QStringList result; + + QFile file(filePath); + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + + while (!stream.atEnd()) { + QString line = stream.readLine(); + line = line.trimmed(); + + if (line.isEmpty() || line.startsWith('#')) + continue; + + result.push_back(line); + } + } + + return result; +} + +DbContents CompilationDbParser::parseProject() +{ + DbContents dbContents; + dbContents.entries = readJsonObjects(m_projectFilePath.toString()); + dbContents.extraFileName = m_projectFilePath.toString() + + Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX; + dbContents.extras = readExtraFiles(dbContents.extraFileName); + std::sort(dbContents.entries.begin(), dbContents.entries.end(), + [](const DbEntry &lhs, const DbEntry &rhs) { + return std::lexicographical_compare(lhs.flags.begin(), lhs.flags.end(), + rhs.flags.begin(), rhs.flags.end()); + }); + return dbContents; +} + +} // namespace Internal +} // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h new file mode 100644 index 00000000000..571f38d3692 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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. +** +****************************************************************************/ + +#pragma once + +#include "compilationdatabaseutils.h" + +#include + +#include +#include +#include +#include + +#include + +namespace ProjectExplorer { +class FileNode; +class TreeScanner; +} + +namespace CompilationDatabaseProjectManager { +namespace Internal { + +class CompilationDbParser : public QObject +{ + Q_OBJECT +public: + explicit CompilationDbParser(const QString &projectName, const Utils::FileName &projectPath, + const Utils::FileName &rootPath, MimeBinaryCache &mimeBinaryCache, + QObject *parent = nullptr); + + void start(); + void stop(); + + QList scannedFiles() const; + DbContents dbContents() const { return m_dbContents; } + +signals: + void finished(bool success); + +private: + void finish(); + DbContents parseProject(); + + const QString m_projectName; + const Utils::FileName m_projectFilePath; + const Utils::FileName m_rootPath; + MimeBinaryCache &m_mimeBinaryCache; + ProjectExplorer::TreeScanner *m_treeScanner = nullptr; + QFutureWatcher m_parserWatcher; + DbContents m_dbContents; +}; + +} // namespace Internal +} // namespace CompilationDatabaseProjectManager diff --git a/tests/unit/unittest/compilationdatabaseutils-test.cpp b/tests/unit/unittest/compilationdatabaseutils-test.cpp index d8edbb721df..11ad1f4cf7d 100644 --- a/tests/unit/unittest/compilationdatabaseutils-test.cpp +++ b/tests/unit/unittest/compilationdatabaseutils-test.cpp @@ -33,6 +33,7 @@ using namespace ProjectExplorer; using namespace CompilationDatabaseProjectManager; +using namespace CompilationDatabaseProjectManager::Internal; namespace { @@ -42,7 +43,7 @@ protected: QStringList splitCommandLine(const QString &commandLine) { QSet flagsCache; - return CompilationDatabaseProjectManager::splitCommandLine(commandLine, flagsCache); + return CompilationDatabaseProjectManager::Internal::splitCommandLine(commandLine, flagsCache); } HeaderPaths headerPaths;