diff options
Diffstat (limited to 'src/plugins/compilationdatabaseprojectmanager')
13 files changed, 718 insertions, 186 deletions
diff --git a/src/plugins/compilationdatabaseprojectmanager/CMakeLists.txt b/src/plugins/compilationdatabaseprojectmanager/CMakeLists.txt new file mode 100644 index 0000000000..163342854f --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/CMakeLists.txt @@ -0,0 +1,16 @@ +add_qtc_plugin(CompilationDatabaseProjectManager + DEPENDS Utils + PLUGIN_DEPENDS Core CppTools ProjectExplorer TextEditor + SOURCES + compilationdatabaseconstants.h + compilationdatabaseproject.cpp compilationdatabaseproject.h + compilationdatabaseprojectmanagerplugin.cpp compilationdatabaseprojectmanagerplugin.h + compilationdatabasetests.qrc + compilationdatabaseutils.cpp compilationdatabaseutils.h + compilationdbparser.cpp compilationdbparser.h +) + +extend_qtc_plugin(CompilationDatabaseProjectManager + CONDITION WITH_TESTS + SOURCES compilationdatabasetests.cpp compilationdatabasetests.h +) diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseconstants.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseconstants.h index 1cb47d5dfd..4fab0e4980 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseconstants.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseconstants.h @@ -30,6 +30,7 @@ namespace Constants { const char COMPILATIONDATABASEMIMETYPE[] = "text/x-compilation-database-project"; const char COMPILATIONDATABASEPROJECT_ID[] = "CompilationDatabase.CompilationDatabaseEditor"; +const char COMPILATIONDATABASEPROJECT_FILES_SUFFIX[] = ".files"; } // Constants } // CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index 9909b22d94..94bc8b40a7 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -26,16 +26,21 @@ #include "compilationdatabaseproject.h" #include "compilationdatabaseconstants.h" -#include "compilationdatabaseutils.h" +#include "compilationdbparser.h" #include <coreplugin/icontext.h> -#include <cpptools/projectinfo.h> #include <cpptools/cppkitinfo.h> #include <cpptools/cppprojectupdater.h> +#include <cpptools/projectinfo.h> +#include <projectexplorer/buildinfo.h> +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/buildtargetinfo.h> #include <projectexplorer/gcctoolchain.h> #include <projectexplorer/headerpath.h> #include <projectexplorer/kitinformation.h> #include <projectexplorer/kitmanager.h> +#include <projectexplorer/namedwidget.h> +#include <projectexplorer/processstep.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/target.h> @@ -47,9 +52,8 @@ #include <utils/qtcassert.h> #include <utils/runextensions.h> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> +#include <QFileDialog> +#include <QTimer> #ifdef Q_OS_WIN #include <Windows.h> @@ -61,32 +65,17 @@ namespace CompilationDatabaseProjectManager { namespace Internal { namespace { -class DBProjectNode : public ProjectNode -{ -public: - explicit DBProjectNode(const Utils::FileName &projectFilePath) - : ProjectNode(projectFilePath) - {} -}; -QStringList jsonObjectFlags(const QJsonObject &object) -{ - QStringList flags; - const QJsonArray arguments = object["arguments"].toArray(); - if (arguments.isEmpty()) { - flags = splitCommandLine(object["command"].toString()); - } else { - flags.reserve(arguments.size()); - for (const QJsonValue &arg : arguments) - flags.append(arg.toString()); - } - return flags; +bool isGccCompiler(const QString &compilerName) +{ + return compilerName.contains("gcc") + || (compilerName.contains("g++") && !compilerName.contains("clang")); } -bool isGccCompiler(const QString &compilerName) +bool isClCompatibleCompiler(const QString &compilerName) { - return compilerName.contains("gcc") || compilerName.contains("g++"); + return compilerName.endsWith("cl"); } Core::Id getCompilerId(QString compilerName) @@ -96,9 +85,9 @@ Core::Id getCompilerId(QString compilerName) compilerName.chop(4); if (isGccCompiler(compilerName)) return ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID; - - // Default is clang-cl - return ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID; + if (isClCompatibleCompiler(compilerName)) + return ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID; + return ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID; } if (isGccCompiler(compilerName)) return ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID; @@ -122,11 +111,17 @@ QString compilerPath(QString pathFlag) return pathFlag; #ifdef Q_OS_WIN // Handle short DOS style file names (cmake can generate them). - const DWORD pathLength = GetLongPathNameW((LPCWSTR)pathFlag.utf16(), 0, 0); - wchar_t* buffer = new wchar_t[pathLength]; - GetLongPathNameW((LPCWSTR)pathFlag.utf16(), buffer, pathLength); - pathFlag = QString::fromUtf16((ushort *)buffer, pathLength - 1); - delete[] buffer; + const DWORD pathLength = GetLongPathNameW(reinterpret_cast<LPCWSTR>(pathFlag.utf16()), + nullptr, + 0); + if (pathLength > 0) { + // Works only with existing paths. + wchar_t *buffer = new wchar_t[pathLength]; + GetLongPathNameW(reinterpret_cast<LPCWSTR>(pathFlag.utf16()), buffer, pathLength); + pathFlag = QString::fromUtf16(reinterpret_cast<ushort *>(buffer), + static_cast<int>(pathLength - 1)); + delete[] buffer; + } #endif return QDir::fromNativeSeparators(pathFlag); } @@ -134,10 +129,10 @@ QString compilerPath(QString pathFlag) ToolChain *toolchainFromFlags(const Kit *kit, const QStringList &flags, const Core::Id &language) { if (flags.empty()) - return ToolChainKitInformation::toolChain(kit, language); + return ToolChainKitAspect::toolChain(kit, language); // Try exact compiler match. - const Utils::FileName compiler = Utils::FileName::fromString(compilerPath(flags.front())); + const Utils::FilePath compiler = Utils::FilePath::fromString(compilerPath(flags.front())); ToolChain *toolchain = ToolChainManager::toolChain([&compiler, &language](const ToolChain *tc) { return tc->isValid() && tc->language() == language && tc->compilerCommand() == compiler; }); @@ -157,38 +152,27 @@ ToolChain *toolchainFromFlags(const Kit *kit, const QStringList &flags, const Co return toolchain; } - toolchain = ToolChainKitInformation::toolChain(kit, language); + toolchain = ToolChainKitAspect::toolChain(kit, language); qWarning() << "No matching toolchain found, use the default."; 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::FileUtils::canonicalPath( - Utils::FileName::fromString(workingDir + "/" + fileName.toString())); - } - return fileName; -} - -void addDriverModeFlagIfNeeded(const ToolChain *toolchain, QStringList &flags) +void addDriverModeFlagIfNeeded(const ToolChain *toolchain, + QStringList &flags, + const QStringList &originalFlags) { if (toolchain->typeId() == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID - && !flags.empty() && !flags.front().endsWith("cl") - && !flags.front().endsWith("cl.exe")) { + && !originalFlags.empty() && !originalFlags.front().endsWith("cl") + && !originalFlags.front().endsWith("cl.exe")) { flags.prepend("--driver-mode=g++"); } } -CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projectFile, +CppTools::RawProjectPart makeRawProjectPart(const Utils::FilePath &projectFile, Kit *kit, - ToolChain *&cToolchain, - ToolChain *&cxxToolchain, + CppTools::KitInfo &kitInfo, const QString &workingDir, - const Utils::FileName &fileName, + const Utils::FilePath &fileName, QStringList flags) { HeaderPaths headerPaths; @@ -201,7 +185,8 @@ CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projectFile, flags, headerPaths, macros, - fileKind); + fileKind, + kitInfo.sysRootPath); CppTools::RawProjectPart rpp; rpp.setProjectFileLocation(projectFile.toString()); @@ -213,27 +198,29 @@ CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projectFile, if (fileKind == CppTools::ProjectFile::Kind::CHeader || fileKind == CppTools::ProjectFile::Kind::CSource) { - if (!cToolchain) { - cToolchain = toolchainFromFlags(kit, originalFlags, - ProjectExplorer::Constants::C_LANGUAGE_ID); - ToolChainKitInformation::setToolChain(kit, cToolchain); + if (!kitInfo.cToolChain) { + kitInfo.cToolChain = toolchainFromFlags(kit, + originalFlags, + ProjectExplorer::Constants::C_LANGUAGE_ID); + ToolChainKitAspect::setToolChain(kit, kitInfo.cToolChain); } - addDriverModeFlagIfNeeded(cToolchain, flags); - rpp.setFlagsForC({cToolchain, flags}); + addDriverModeFlagIfNeeded(kitInfo.cToolChain, flags, originalFlags); + rpp.setFlagsForC({kitInfo.cToolChain, flags}); } else { - if (!cxxToolchain) { - cxxToolchain = toolchainFromFlags(kit, originalFlags, - ProjectExplorer::Constants::CXX_LANGUAGE_ID); - ToolChainKitInformation::setToolChain(kit, cxxToolchain); + if (!kitInfo.cxxToolChain) { + kitInfo.cxxToolChain = toolchainFromFlags(kit, + originalFlags, + ProjectExplorer::Constants::CXX_LANGUAGE_ID); + ToolChainKitAspect::setToolChain(kit, kitInfo.cxxToolChain); } - addDriverModeFlagIfNeeded(cxxToolchain, flags); - rpp.setFlagsForCxx({cxxToolchain, flags}); + addDriverModeFlagIfNeeded(kitInfo.cxxToolChain, flags, originalFlags); + rpp.setFlagsForCxx({kitInfo.cxxToolChain, flags}); } return rpp; } -QStringList relativeDirsList(Utils::FileName currentPath, const Utils::FileName &rootPath) +QStringList relativeDirsList(Utils::FilePath currentPath, const Utils::FilePath &rootPath) { QStringList dirsList; while (!currentPath.isEmpty() && currentPath != rootPath) { @@ -248,10 +235,10 @@ QStringList relativeDirsList(Utils::FileName currentPath, const Utils::FileName FolderNode *addChildFolderNode(FolderNode *parent, const QString &childName) { - Utils::FileName parentPath = parent->filePath(); - auto node = std::make_unique<FolderNode>( - parentPath.appendPath(childName), NodeType::Folder, childName); + const Utils::FilePath path = parent->filePath().pathAppended(childName); + auto node = std::make_unique<FolderNode>(path); FolderNode *childNode = node.get(); + childNode->setDisplayName(childName); parent->addNode(std::move(node)); return childNode; @@ -269,7 +256,7 @@ FolderNode *addOrGetChildFolderNode(FolderNode *parent, const QString &childName } // Return the node for folderPath. -FolderNode *createFoldersIfNeeded(FolderNode *root, const Utils::FileName &folderPath) +FolderNode *createFoldersIfNeeded(FolderNode *root, const Utils::FilePath &folderPath) { const QStringList dirsList = relativeDirsList(folderPath, root->filePath()); @@ -288,89 +275,80 @@ FileType fileTypeForName(const QString &fileName) return FileType::Source; } -void createTree(FolderNode *root, - const Utils::FileName &rootPath, - const CppTools::RawProjectParts &rpps) +void addChild(FolderNode *root, const Utils::FilePath &fileName) +{ + FolderNode *parentNode = createFoldersIfNeeded(root, fileName.parentDir()); + if (!parentNode->fileNode(fileName)) { + parentNode->addNode( + std::make_unique<FileNode>(fileName, fileTypeForName(fileName.fileName()))); + } +} + +void createTree(std::unique_ptr<ProjectNode> &root, + const Utils::FilePath &rootPath, + const CppTools::RawProjectParts &rpps, + const QList<FileNode *> &scannedFiles = QList<FileNode *>()) { root->setAbsoluteFilePathAndLine(rootPath, -1); + std::unique_ptr<FolderNode> secondRoot; for (const CppTools::RawProjectPart &rpp : rpps) { for (const QString &filePath : rpp.files) { - Utils::FileName fileName = Utils::FileName::fromString(filePath); - FolderNode *parentNode = createFoldersIfNeeded(root, fileName.parentDir()); - if (!parentNode->fileNode(fileName)) { - parentNode->addNode(std::make_unique<FileNode>(fileName, - fileTypeForName(fileName.fileName()), - false)); + Utils::FilePath fileName = Utils::FilePath::fromString(filePath); + if (!fileName.isChildOf(rootPath)) { + if (fileName.isChildOf(Utils::FilePath::fromString(rpp.buildSystemTarget))) { + if (!secondRoot) + secondRoot = std::make_unique<ProjectNode>( + Utils::FilePath::fromString(rpp.buildSystemTarget)); + addChild(secondRoot.get(), fileName); + } + } else { + addChild(root.get(), fileName); } } } -} -struct Entry -{ - QStringList flags; - Utils::FileName fileName; - QString workingDir; -}; + for (FileNode *node : scannedFiles) { + if (node->fileType() != FileType::Header) + continue; -std::vector<Entry> readJsonObjects(const QString &filePath) -{ - std::vector<Entry> 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); - - 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); + const Utils::FilePath fileName = node->filePath(); + if (!fileName.isChildOf(rootPath)) continue; + FolderNode *parentNode = createFoldersIfNeeded(root.get(), fileName.parentDir()); + if (!parentNode->fileNode(fileName)) { + std::unique_ptr<FileNode> headerNode(node->clone()); + headerNode->setEnabled(false); + parentNode->addNode(std::move(headerNode)); } - - const QJsonObject object = document.object(); - const Utils::FileName fileName = jsonObjectFilename(object); - const QStringList flags - = filterFromFileName(jsonObjectFlags(object), fileName.toFileInfo().baseName()); - result.push_back({flags, fileName, object["directory"].toString()}); - - objectStart = contents.indexOf('{', objectEnd + 1); - objectEnd = contents.indexOf('}', objectStart + 1); } - - return result; + qDeleteAll(scannedFiles); + + if (secondRoot) { + std::unique_ptr<ProjectNode> firstRoot = std::move(root); + root = std::make_unique<ProjectNode>(firstRoot->filePath()); + firstRoot->setDisplayName(rootPath.fileName()); + root->addNode(std::move(firstRoot)); + root->addNode(std::move(secondRoot)); + } } + } // anonymous namespace -void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName &projectFile) +void CompilationDatabaseProject::buildTreeAndProjectParts() { - std::vector<Entry> array = readJsonObjects(projectFilePath().toString()); - if (array.empty()) { - emitParsingFinished(false); - return; - } - - auto root = std::make_unique<DBProjectNode>(projectDirectory()); - CppTools::KitInfo kitInfo(this); QTC_ASSERT(kitInfo.isValid(), return); + // Reset toolchains to pick them based on the database entries. + kitInfo.cToolChain = nullptr; + kitInfo.cxxToolChain = nullptr; CppTools::RawProjectParts rpps; - Utils::FileName commonPath; - 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; @@ -378,14 +356,9 @@ void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName prevEntry = &entry; - commonPath = rpps.empty() - ? entry.fileName.parentDir() - : Utils::FileUtils::commonPath(commonPath, entry.fileName); - - CppTools::RawProjectPart rpp = makeRawProjectPart(projectFile, + CppTools::RawProjectPart rpp = makeRawProjectPart(projectFilePath(), m_kit.get(), - kitInfo.cToolChain, - kitInfo.cxxToolChain, + kitInfo, entry.workingDir, entry.fileName, entry.flags); @@ -393,23 +366,37 @@ void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName rpps.append(rpp); } - createTree(root.get(), commonPath, rpps); + if (!dbContents.extras.empty()) { + const Utils::FilePath baseDir = projectFilePath().parentDir(); + + QStringList extraFiles; + for (const QString &extra : dbContents.extras) + extraFiles.append(baseDir.pathAppended(extra).toString()); + + CppTools::RawProjectPart rppExtra; + rppExtra.setFiles(extraFiles); + rpps.append(rppExtra); + } + + + auto root = std::make_unique<ProjectNode>(projectDirectory()); + createTree(root, rootProjectDirectory(), rpps, m_parser->scannedFiles()); - root->addNode(std::make_unique<FileNode>( - projectFile, - FileType::Project, - false)); + root->addNode(std::make_unique<FileNode>(projectFilePath(), FileType::Project)); + + if (QFile::exists(dbContents.extraFileName)) + root->addNode(std::make_unique<FileNode>(Utils::FilePath::fromString(dbContents.extraFileName), + FileType::Project)); setRootProjectNode(std::move(root)); m_cppCodeModelUpdater->update({this, kitInfo, rpps}); - - emitParsingFinished(true); } -CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &projectFile) +CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FilePath &projectFile) : Project(Constants::COMPILATIONDATABASEMIMETYPE, projectFile) , m_cppCodeModelUpdater(std::make_unique<CppTools::CppProjectUpdater>()) + , m_parseDelay(new QTimer(this)) { setId(Constants::COMPILATIONDATABASEPROJECT_ID); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); @@ -418,7 +405,6 @@ CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &pr setPreferredKitPredicate([](const Kit *) { return false; }); m_kit.reset(KitManager::defaultKit()->clone()); - connect(this, &CompilationDatabaseProject::parsingFinished, this, [this]() { if (!m_hasTarget) { addTarget(createTarget(m_kit.get())); @@ -426,23 +412,61 @@ CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &pr } }); - reparseProject(projectFile); + connect(this, &CompilationDatabaseProject::rootProjectDirectoryChanged, + m_parseDelay, QOverload<>::of(&QTimer::start)); m_fileSystemWatcher.addFile(projectFile.toString(), Utils::FileSystemWatcher::WatchModifiedDate); - connect(&m_fileSystemWatcher, - &Utils::FileSystemWatcher::fileChanged, - this, - [this](const QString &projectFile) { - reparseProject(Utils::FileName::fromString(projectFile)); - }); + m_fileSystemWatcher.addFile(projectFile.toString() + Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX, + Utils::FileSystemWatcher::WatchModifiedDate); + connect(&m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged, + m_parseDelay, QOverload<>::of(&QTimer::start)); + connect(m_parseDelay, &QTimer::timeout, this, &CompilationDatabaseProject::reparseProject); + m_parseDelay->setSingleShot(true); + m_parseDelay->setInterval(1000); } -void CompilationDatabaseProject::reparseProject(const Utils::FileName &projectFile) +Utils::FilePath CompilationDatabaseProject::rootPathFromSettings() const { +#ifdef WITH_TESTS + return Utils::FilePath::fromString(projectDirectory().fileName()); +#else + return Utils::FileName::fromString( + namedSettings(ProjectExplorer::Constants::PROJECT_ROOT_PATH_KEY).toString()); +#endif +} + +Project::RestoreResult CompilationDatabaseProject::fromMap(const QVariantMap &map, + QString *errorMessage) +{ + Project::RestoreResult result = Project::fromMap(map, errorMessage); + if (result == Project::RestoreResult::Ok) { + const Utils::FilePath rootPath = rootPathFromSettings(); + if (rootPath.isEmpty()) + changeRootProjectDirectory(); // This triggers reparse itself. + else + reparseProject(); + } + + return result; +} + +void CompilationDatabaseProject::reparseProject() +{ + if (m_parser) { + QTC_CHECK(isParsing()); + m_parser->stop(); + emitParsingFinished(false); + } + 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(); - const QFuture<void> future = ::Utils::runAsync( - [this, projectFile]() { buildTreeAndProjectParts(projectFile); }); - m_parserWatcher.setFuture(future); + m_parser->start(); } CompilationDatabaseProject::~CompilationDatabaseProject() @@ -473,5 +497,62 @@ CompilationDatabaseEditorFactory::CompilationDatabaseEditorFactory() setCodeFoldingSupported(true); } +CompilationDatabaseBuildConfiguration::CompilationDatabaseBuildConfiguration( + ProjectExplorer::Target *target, Core::Id id) + : ProjectExplorer::BuildConfiguration(target, id) +{ + target->setApplicationTargets({BuildTargetInfo()}); +} + +void CompilationDatabaseBuildConfiguration::initialize(const ProjectExplorer::BuildInfo &info) +{ + ProjectExplorer::BuildConfiguration::initialize(info); + BuildStepList *buildSteps = stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD); + buildSteps->appendStep(new ProjectExplorer::ProcessStep(buildSteps)); +} + +ProjectExplorer::NamedWidget *CompilationDatabaseBuildConfiguration::createConfigWidget() +{ + return new ProjectExplorer::NamedWidget(); +} + +ProjectExplorer::BuildConfiguration::BuildType CompilationDatabaseBuildConfiguration::buildType() const +{ + return ProjectExplorer::BuildConfiguration::Release; +} + +CompilationDatabaseBuildConfigurationFactory::CompilationDatabaseBuildConfigurationFactory() +{ + registerBuildConfiguration<CompilationDatabaseBuildConfiguration>( + "CompilationDatabase.CompilationDatabaseBuildConfiguration"); + + setSupportedProjectType(Constants::COMPILATIONDATABASEPROJECT_ID); + setSupportedProjectMimeTypeName(Constants::COMPILATIONDATABASEMIMETYPE); +} + +static QList<ProjectExplorer::BuildInfo> defaultBuildInfos( + const ProjectExplorer::BuildConfigurationFactory *factory, const QString &name) +{ + ProjectExplorer::BuildInfo info(factory); + info.typeName = name; + info.displayName = name; + info.buildType = BuildConfiguration::Release; + QList<ProjectExplorer::BuildInfo> buildInfos; + buildInfos << info; + return buildInfos; +} + +QList<ProjectExplorer::BuildInfo> CompilationDatabaseBuildConfigurationFactory::availableBuilds( + const ProjectExplorer::Target * /*parent*/) const +{ + return defaultBuildInfos(this, tr("Release")); +} + +QList<ProjectExplorer::BuildInfo> CompilationDatabaseBuildConfigurationFactory::availableSetups( + const ProjectExplorer::Kit * /*k*/, const QString & /*projectPath*/) const +{ + return defaultBuildInfos(this, tr("Release")); +} + } // namespace Internal } // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h index 5b6fc0a116..376ebcc63b 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h @@ -25,39 +25,55 @@ #pragma once +#include "compilationdatabaseutils.h" + +#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/project.h> #include <texteditor/texteditor.h> #include <utils/filesystemwatcher.h> #include <QFutureWatcher> +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + namespace CppTools { class CppProjectUpdater; } -namespace ProjectExplorer { class Kit; } +namespace ProjectExplorer { +class Kit; +} namespace CompilationDatabaseProjectManager { namespace Internal { +class CompilationDbParser; class CompilationDatabaseProject : public ProjectExplorer::Project { Q_OBJECT public: - explicit CompilationDatabaseProject(const Utils::FileName &filename); + explicit CompilationDatabaseProject(const Utils::FilePath &filename); ~CompilationDatabaseProject() override; bool needsConfiguration() const override { return false; } - bool needsBuildConfigurations() const override { return false; } + bool needsBuildConfigurations() const override { return true; } private: - void reparseProject(const Utils::FileName &projectFile); - void buildTreeAndProjectParts(const Utils::FileName &projectFile); + RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override; + + void reparseProject(); + void buildTreeAndProjectParts(); + Utils::FilePath rootPathFromSettings() const; QFutureWatcher<void> m_parserWatcher; std::unique_ptr<CppTools::CppProjectUpdater> m_cppCodeModelUpdater; std::unique_ptr<ProjectExplorer::Kit> m_kit; Utils::FileSystemWatcher m_fileSystemWatcher; + MimeBinaryCache m_mimeBinaryCache; + QTimer * const m_parseDelay; + CompilationDbParser *m_parser = nullptr; bool m_hasTarget = false; }; @@ -69,5 +85,30 @@ public: CompilationDatabaseEditorFactory(); }; +class CompilationDatabaseBuildConfiguration : public ProjectExplorer::BuildConfiguration +{ + Q_OBJECT +public: + CompilationDatabaseBuildConfiguration(ProjectExplorer::Target *target, Core::Id id); + ProjectExplorer::NamedWidget *createConfigWidget() override; + BuildType buildType() const override; + +protected: + void initialize(const ProjectExplorer::BuildInfo &info) override; +}; + +class CompilationDatabaseBuildConfigurationFactory + : public ProjectExplorer::BuildConfigurationFactory +{ + Q_OBJECT +public: + CompilationDatabaseBuildConfigurationFactory(); + + QList<ProjectExplorer::BuildInfo> availableBuilds( + const ProjectExplorer::Target *parent) const override; + QList<ProjectExplorer::BuildInfo> availableSetups(const ProjectExplorer::Kit *k, + const QString &projectPath) const override; +}; + } // namespace Internal } // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro index e7efefb47e..e11a8cc06b 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 4cbe2af284..8f592cbc51 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/compilationdatabaseprojectmanagerplugin.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp index 77359adc70..5da42c8bfd 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp @@ -29,33 +29,75 @@ #include "compilationdatabaseproject.h" #include "compilationdatabasetests.h" +#include <coreplugin/actionmanager/actioncontainer.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/command.h> #include <coreplugin/fileiconprovider.h> +#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/projecttree.h> +#include <projectexplorer/session.h> #include <utils/utilsicons.h> namespace CompilationDatabaseProjectManager { namespace Internal { +const char CHANGEROOTDIR[] = "CompilationDatabaseProjectManager.ChangeRootDirectory"; +const char COMPILE_COMMANDS_JSON[] = "compile_commands.json"; + bool CompilationDatabaseProjectManagerPlugin::initialize(const QStringList &arguments, QString *errorMessage) { Q_UNUSED(arguments); Q_UNUSED(errorMessage); - Core::FileIconProvider::registerIconOverlayForFilename(Utils::Icons::PROJECT.imageFileName(), - "compile_commands.json"); + Core::FileIconProvider::registerIconOverlayForFilename( + Utils::Icons::PROJECT.imageFileName(), + COMPILE_COMMANDS_JSON); + Core::FileIconProvider::registerIconOverlayForFilename( + Utils::Icons::PROJECT.imageFileName(), + QString(COMPILE_COMMANDS_JSON) + Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX); ProjectExplorer::ProjectManager::registerProjectType<CompilationDatabaseProject>( Constants::COMPILATIONDATABASEMIMETYPE); + m_changeProjectRootDirectoryAction = new QAction(tr("Change Root Directory"), this); + Core::Command *cmd = Core::ActionManager::registerAction(m_changeProjectRootDirectoryAction, + CHANGEROOTDIR); + Core::ActionContainer *mprojectContextMenu = Core::ActionManager::actionContainer( + ProjectExplorer::Constants::M_PROJECTCONTEXT); + mprojectContextMenu->addSeparator(ProjectExplorer::Constants::G_PROJECT_TREE); + mprojectContextMenu->addAction(cmd, ProjectExplorer::Constants::G_PROJECT_TREE); + + connect(m_changeProjectRootDirectoryAction, + &QAction::triggered, + ProjectExplorer::ProjectTree::instance(), + &ProjectExplorer::ProjectTree::changeProjectRootDirectory); + + connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, + &CompilationDatabaseProjectManagerPlugin::projectChanged); + connect(ProjectExplorer::ProjectTree::instance(), + &ProjectExplorer::ProjectTree::currentProjectChanged, + this, + &CompilationDatabaseProjectManagerPlugin::projectChanged); return true; } +void CompilationDatabaseProjectManagerPlugin::projectChanged() +{ + const auto *currentProject = qobject_cast<CompilationDatabaseProject *>( + ProjectExplorer::ProjectTree::currentProject()); + + m_changeProjectRootDirectoryAction->setEnabled(currentProject); +} + void CompilationDatabaseProjectManagerPlugin::extensionsInitialized() { } -QList<QObject *> CompilationDatabaseProjectManagerPlugin::createTestObjects() const +QVector<QObject *> CompilationDatabaseProjectManagerPlugin::createTestObjects() const { - QList<QObject *> tests; + QVector<QObject *> tests; #ifdef WITH_TESTS tests << new CompilationDatabaseTests; #endif diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h index 37c9c1db1d..42a15ad3cf 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h @@ -43,9 +43,13 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage) final; void extensionsInitialized() final; private: - QList<QObject *> createTestObjects() const final; + void projectChanged(); + + QVector<QObject *> createTestObjects() const final; CompilationDatabaseEditorFactory factory; + CompilationDatabaseBuildConfigurationFactory buildConfigFactory; + QAction *m_changeProjectRootDirectoryAction; }; } // namespace Internal diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp index 72a1b6ac74..42ba0e0887 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp @@ -33,6 +33,7 @@ #include <projectexplorer/projectexplorer.h> #include <projectexplorer/toolchain.h> #include <projectexplorer/toolchainmanager.h> +#include <utils/algorithm.h> #include <QtTest> diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp index 1884aa1c6d..1affca78fa 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp @@ -33,18 +33,18 @@ #include <QDir> #include <QRegularExpression> +#include <QSet> using namespace ProjectExplorer; namespace CompilationDatabaseProjectManager { +namespace Internal { static QString updatedPathFlag(const QString &pathStr, const QString &workingDir) { QString result = pathStr; - if (!QDir(pathStr).exists() - && QDir(workingDir + "/" + pathStr).exists()) { + if (QDir(pathStr).isRelative()) result = workingDir + "/" + pathStr; - } return result; } @@ -99,7 +99,8 @@ void filteredFlags(const QString &fileName, QStringList &flags, HeaderPaths &headerPaths, Macros ¯os, - CppTools::ProjectFile::Kind &fileKind) + CppTools::ProjectFile::Kind &fileKind, + QString &sysRoot) { if (flags.empty()) return; @@ -183,6 +184,12 @@ void filteredFlags(const QString &fileName, continue; } + if (flag.startsWith("--sysroot=")) { + if (sysRoot.isEmpty()) + sysRoot = updatedPathFlag(flag.mid(10), workingDir); + continue; + } + if ((flag.startsWith("-std=") || flag.startsWith("/std:")) && fileKind == CppTools::ProjectFile::Unclassified) { const bool cpp = (flag.contains("c++") || flag.contains("gnu++")); @@ -205,7 +212,7 @@ void filteredFlags(const QString &fileName, flags = filtered; } -QStringList splitCommandLine(QString commandLine) +QStringList splitCommandLine(QString commandLine, QSet<QString> &flagsCache) { QStringList result; bool insideQuotes = false; @@ -215,16 +222,24 @@ QStringList splitCommandLine(QString commandLine) for (const QString &part : commandLine.split(QRegularExpression("\""))) { if (insideQuotes) { const QString quotedPart = "\"" + part + "\""; - if (result.last().endsWith("=")) - result.last().append(quotedPart); - else - result.append(quotedPart); + if (result.last().endsWith("=")) { + auto flagIt = flagsCache.insert(result.last() + quotedPart); + result.last() = *flagIt; + } else { + auto flagIt = flagsCache.insert(quotedPart); + result.append(*flagIt); + } } else { // If 's' is outside quotes ... - result.append(part.split(QRegularExpression("\\s+"), QString::SkipEmptyParts)); + for (const QString &flag : + part.split(QRegularExpression("\\s+"), QString::SkipEmptyParts)) { + auto flagIt = flagsCache.insert(flag); + result.append(*flagIt); + } } insideQuotes = !insideQuotes; } return result; } +} // namespace Internal } // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h index e465b6ba23..d2c062e7c3 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h @@ -28,7 +28,9 @@ #include "compilationdatabaseconstants.h" #include <cpptools/cppprojectfile.h> +#include <utils/fileutils.h> +#include <QHash> #include <QStringList> namespace ProjectExplorer { @@ -37,6 +39,23 @@ class Macro; } namespace CompilationDatabaseProjectManager { +namespace Internal { + +class DbEntry { +public: + QStringList flags; + Utils::FilePath fileName; + QString workingDir; +}; + +class DbContents { +public: + std::vector<DbEntry> entries; + QString extraFileName; + QStringList extras; +}; + +using MimeBinaryCache = QHash<QString, bool>; QStringList filterFromFileName(const QStringList &flags, QString baseName); @@ -45,8 +64,10 @@ void filteredFlags(const QString &fileName, QStringList &flags, QVector<ProjectExplorer::HeaderPath> &headerPaths, QVector<ProjectExplorer::Macro> ¯os, - CppTools::ProjectFile::Kind &fileKind); + CppTools::ProjectFile::Kind &fileKind, + QString &sysRoot); -QStringList splitCommandLine(QString commandLine); +QStringList splitCommandLine(QString commandLine, QSet<QString> &flagsCache); +} // namespace Internal } // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp new file mode 100644 index 0000000000..d75bf7be40 --- /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 <coreplugin/progressmanager/progressmanager.h> +#include <projectexplorer/task.h> +#include <projectexplorer/treescanner.h> +#include <utils/mimetypes/mimetype.h> +#include <utils/runextensions.h> + +#include <QDir> +#include <QFileInfo> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> + +using namespace ProjectExplorer; +using namespace Utils; + +namespace CompilationDatabaseProjectManager { +namespace Internal { + +CompilationDbParser::CompilationDbParser(const QString &projectName, const FilePath &projectPath, + const FilePath &rootPath, MimeBinaryCache &mimeBinaryCache, + QObject *parent) + : QObject(parent), + m_projectName(projectName), + m_projectFilePath(projectPath), + m_rootPath(rootPath), + m_mimeBinaryCache(mimeBinaryCache) +{ + connect(&m_parserWatcher, &QFutureWatcher<void>::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 FilePath &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::FilePath &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<DbContents> 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<FileNode *> CompilationDbParser::scannedFiles() const +{ + return m_treeScanner && !m_treeScanner->future().isCanceled() + ? m_treeScanner->release() : QList<FileNode *>(); +} + +void CompilationDbParser::finish() +{ + emit finished(true); + deleteLater(); +} + +static QStringList jsonObjectFlags(const QJsonObject &object, QSet<QString> &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 FilePath jsonObjectFilename(const QJsonObject &object) +{ + const QString workingDir = QDir::fromNativeSeparators(object["directory"].toString()); + FilePath fileName = FilePath::fromString(QDir::fromNativeSeparators(object["file"].toString())); + if (fileName.toFileInfo().isRelative()) + fileName = FilePath::fromString(workingDir + "/" + fileName.toString()).canonicalPath(); + return fileName; +} + +static std::vector<DbEntry> readJsonObjects(const QString &filePath) +{ + std::vector<DbEntry> 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<QString> 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::FilePath 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 0000000000..ded713d590 --- /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 <utils/fileutils.h> + +#include <QFutureWatcher> +#include <QList> +#include <QObject> +#include <QStringList> + +#include <vector> + +namespace ProjectExplorer { +class FileNode; +class TreeScanner; +} + +namespace CompilationDatabaseProjectManager { +namespace Internal { + +class CompilationDbParser : public QObject +{ + Q_OBJECT +public: + explicit CompilationDbParser(const QString &projectName, const Utils::FilePath &projectPath, + const Utils::FilePath &rootPath, MimeBinaryCache &mimeBinaryCache, + QObject *parent = nullptr); + + void start(); + void stop(); + + QList<ProjectExplorer::FileNode *> scannedFiles() const; + DbContents dbContents() const { return m_dbContents; } + +signals: + void finished(bool success); + +private: + void finish(); + DbContents parseProject(); + + const QString m_projectName; + const Utils::FilePath m_projectFilePath; + const Utils::FilePath m_rootPath; + MimeBinaryCache &m_mimeBinaryCache; + ProjectExplorer::TreeScanner *m_treeScanner = nullptr; + QFutureWatcher<DbContents> m_parserWatcher; + DbContents m_dbContents; +}; + +} // namespace Internal +} // namespace CompilationDatabaseProjectManager |