aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/compilationdatabaseprojectmanager
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/compilationdatabaseprojectmanager')
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/CMakeLists.txt16
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseconstants.h1
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp405
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h51
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro6
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs2
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp50
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h6
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp1
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp35
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h25
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp228
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h78
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 &macros,
- 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> &macros,
- 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