2018-08-13 11:15:27 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2018 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 "compilationdatabaseproject.h"
|
|
|
|
|
|
|
|
|
|
#include "compilationdatabaseconstants.h"
|
2018-10-30 10:31:34 +01:00
|
|
|
#include "compilationdatabaseutils.h"
|
2018-08-13 11:15:27 +02:00
|
|
|
|
|
|
|
|
#include <coreplugin/icontext.h>
|
2019-03-14 15:34:14 +01:00
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
2019-01-08 12:33:18 +01:00
|
|
|
#include <cpptools/cppkitinfo.h>
|
2018-08-13 11:15:27 +02:00
|
|
|
#include <cpptools/cppprojectupdater.h>
|
2019-03-14 15:34:14 +01:00
|
|
|
#include <cpptools/projectinfo.h>
|
2018-08-13 11:15:27 +02:00
|
|
|
#include <projectexplorer/gcctoolchain.h>
|
2018-10-30 10:31:34 +01:00
|
|
|
#include <projectexplorer/headerpath.h>
|
2018-08-13 11:15:27 +02:00
|
|
|
#include <projectexplorer/kitinformation.h>
|
|
|
|
|
#include <projectexplorer/kitmanager.h>
|
|
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
#include <projectexplorer/projectnodes.h>
|
2018-11-01 15:10:52 +01:00
|
|
|
#include <projectexplorer/target.h>
|
2018-08-13 11:15:27 +02:00
|
|
|
#include <projectexplorer/toolchainconfigwidget.h>
|
2018-10-30 10:31:34 +01:00
|
|
|
#include <projectexplorer/toolchainmanager.h>
|
2018-08-13 11:15:27 +02:00
|
|
|
#include <texteditor/textdocument.h>
|
2018-10-30 10:31:34 +01:00
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
2018-08-13 11:15:27 +02:00
|
|
|
#include <utils/runextensions.h>
|
|
|
|
|
|
2019-03-14 15:34:14 +01:00
|
|
|
#include <QFileDialog>
|
2018-08-13 11:15:27 +02:00
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
#include <Windows.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
2018-08-13 11:15:27 +02:00
|
|
|
|
|
|
|
|
namespace CompilationDatabaseProjectManager {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
namespace {
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2019-04-15 13:44:01 +02:00
|
|
|
QStringList jsonObjectFlags(const QJsonObject &object, QSet<QString> &flagsCache)
|
2018-08-13 11:15:27 +02:00
|
|
|
{
|
2018-10-30 10:31:34 +01:00
|
|
|
QStringList flags;
|
|
|
|
|
const QJsonArray arguments = object["arguments"].toArray();
|
|
|
|
|
if (arguments.isEmpty()) {
|
2019-04-15 13:44:01 +02:00
|
|
|
flags = splitCommandLine(object["command"].toString(), flagsCache);
|
2018-10-30 10:31:34 +01:00
|
|
|
} else {
|
2018-11-02 16:07:42 +01:00
|
|
|
flags.reserve(arguments.size());
|
2019-04-15 13:44:01 +02:00
|
|
|
for (const QJsonValue &arg : arguments) {
|
|
|
|
|
auto flagIt = flagsCache.insert(arg.toString());
|
|
|
|
|
flags.append(*flagIt);
|
|
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
}
|
2018-10-30 10:31:34 +01:00
|
|
|
|
|
|
|
|
return flags;
|
2018-08-13 11:15:27 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
bool isGccCompiler(const QString &compilerName)
|
2018-08-13 11:15:27 +02:00
|
|
|
{
|
2019-04-11 12:42:13 +02:00
|
|
|
return compilerName.contains("gcc")
|
|
|
|
|
|| (compilerName.contains("g++") && !compilerName.contains("clang"));
|
2018-08-13 11:15:27 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
Core::Id getCompilerId(QString compilerName)
|
2018-08-13 11:15:27 +02:00
|
|
|
{
|
2018-10-30 10:31:34 +01:00
|
|
|
if (Utils::HostOsInfo::isWindowsHost()) {
|
|
|
|
|
if (compilerName.endsWith(".exe"))
|
|
|
|
|
compilerName.chop(4);
|
|
|
|
|
if (isGccCompiler(compilerName))
|
|
|
|
|
return ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID;
|
|
|
|
|
|
|
|
|
|
// Default is clang-cl
|
|
|
|
|
return ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID;
|
|
|
|
|
}
|
|
|
|
|
if (isGccCompiler(compilerName))
|
|
|
|
|
return ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID;
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
// Default is clang
|
|
|
|
|
return ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID;
|
|
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
ToolChain *toolchainFromCompilerId(const Core::Id &compilerId, const Core::Id &language)
|
|
|
|
|
{
|
|
|
|
|
return ToolChainManager::toolChain([&compilerId, &language](const ToolChain *tc) {
|
|
|
|
|
if (!tc->isValid() || tc->language() != language)
|
|
|
|
|
return false;
|
|
|
|
|
return tc->typeId() == compilerId;
|
|
|
|
|
});
|
|
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
QString compilerPath(QString pathFlag)
|
|
|
|
|
{
|
|
|
|
|
if (pathFlag.isEmpty())
|
|
|
|
|
return pathFlag;
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
// Handle short DOS style file names (cmake can generate them).
|
2019-04-11 12:42:13 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2018-10-30 10:31:34 +01:00
|
|
|
#endif
|
|
|
|
|
return QDir::fromNativeSeparators(pathFlag);
|
|
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
ToolChain *toolchainFromFlags(const Kit *kit, const QStringList &flags, const Core::Id &language)
|
|
|
|
|
{
|
|
|
|
|
if (flags.empty())
|
2019-02-06 12:50:51 +01:00
|
|
|
return ToolChainKitAspect::toolChain(kit, language);
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
// Try exact compiler match.
|
|
|
|
|
const Utils::FileName compiler = Utils::FileName::fromString(compilerPath(flags.front()));
|
|
|
|
|
ToolChain *toolchain = ToolChainManager::toolChain([&compiler, &language](const ToolChain *tc) {
|
|
|
|
|
return tc->isValid() && tc->language() == language && tc->compilerCommand() == compiler;
|
|
|
|
|
});
|
|
|
|
|
if (toolchain)
|
|
|
|
|
return toolchain;
|
|
|
|
|
|
|
|
|
|
Core::Id compilerId = getCompilerId(compiler.fileName());
|
|
|
|
|
if ((toolchain = toolchainFromCompilerId(compilerId, language)))
|
|
|
|
|
return toolchain;
|
|
|
|
|
|
|
|
|
|
if (compilerId != ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID &&
|
|
|
|
|
compilerId != ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) {
|
|
|
|
|
compilerId = Utils::HostOsInfo::isWindowsHost()
|
|
|
|
|
? ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID
|
|
|
|
|
: ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID;
|
|
|
|
|
if ((toolchain = toolchainFromCompilerId(compilerId, language)))
|
|
|
|
|
return toolchain;
|
|
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2019-02-06 12:50:51 +01:00
|
|
|
toolchain = ToolChainKitAspect::toolChain(kit, language);
|
2018-11-14 12:11:33 +01:00
|
|
|
qWarning() << "No matching toolchain found, use the default.";
|
2018-10-30 10:31:34 +01:00
|
|
|
return toolchain;
|
|
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
Utils::FileName jsonObjectFilename(const QJsonObject &object)
|
|
|
|
|
{
|
2018-10-30 14:21:34 +01:00
|
|
|
const QString workingDir = QDir::fromNativeSeparators(object["directory"].toString());
|
2018-10-30 10:31:34 +01:00
|
|
|
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()));
|
2018-08-13 11:15:27 +02:00
|
|
|
}
|
2018-10-30 10:31:34 +01:00
|
|
|
return fileName;
|
2018-08-13 11:15:27 +02:00
|
|
|
}
|
|
|
|
|
|
2019-04-11 12:42:13 +02:00
|
|
|
void addDriverModeFlagIfNeeded(const ToolChain *toolchain,
|
|
|
|
|
QStringList &flags,
|
|
|
|
|
const QStringList &originalFlags)
|
2018-08-13 11:15:27 +02:00
|
|
|
{
|
2018-10-30 10:31:34 +01:00
|
|
|
if (toolchain->typeId() == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID
|
2019-04-11 12:42:13 +02:00
|
|
|
&& !originalFlags.empty() && !originalFlags.front().endsWith("cl")
|
|
|
|
|
&& !originalFlags.front().endsWith("cl.exe")) {
|
2018-11-01 09:22:52 +01:00
|
|
|
flags.prepend("--driver-mode=g++");
|
2018-08-13 11:15:27 +02:00
|
|
|
}
|
2018-10-30 10:31:34 +01:00
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projectFile,
|
|
|
|
|
Kit *kit,
|
|
|
|
|
ToolChain *&cToolchain,
|
|
|
|
|
ToolChain *&cxxToolchain,
|
|
|
|
|
const QString &workingDir,
|
|
|
|
|
const Utils::FileName &fileName,
|
|
|
|
|
QStringList flags)
|
|
|
|
|
{
|
|
|
|
|
HeaderPaths headerPaths;
|
|
|
|
|
Macros macros;
|
|
|
|
|
CppTools::ProjectFile::Kind fileKind = CppTools::ProjectFile::Unclassified;
|
|
|
|
|
|
|
|
|
|
const QStringList originalFlags = flags;
|
|
|
|
|
filteredFlags(fileName.fileName(),
|
|
|
|
|
workingDir,
|
|
|
|
|
flags,
|
|
|
|
|
headerPaths,
|
|
|
|
|
macros,
|
|
|
|
|
fileKind);
|
2018-08-13 11:15:27 +02:00
|
|
|
|
|
|
|
|
CppTools::RawProjectPart rpp;
|
|
|
|
|
rpp.setProjectFileLocation(projectFile.toString());
|
|
|
|
|
rpp.setBuildSystemTarget(workingDir);
|
|
|
|
|
rpp.setDisplayName(fileName.fileName());
|
|
|
|
|
rpp.setFiles({fileName.toString()});
|
2018-10-30 10:31:34 +01:00
|
|
|
rpp.setHeaderPaths(headerPaths);
|
|
|
|
|
rpp.setMacros(macros);
|
|
|
|
|
|
|
|
|
|
if (fileKind == CppTools::ProjectFile::Kind::CHeader
|
|
|
|
|
|| fileKind == CppTools::ProjectFile::Kind::CSource) {
|
|
|
|
|
if (!cToolchain) {
|
|
|
|
|
cToolchain = toolchainFromFlags(kit, originalFlags,
|
|
|
|
|
ProjectExplorer::Constants::C_LANGUAGE_ID);
|
2019-02-06 12:50:51 +01:00
|
|
|
ToolChainKitAspect::setToolChain(kit, cToolchain);
|
2018-10-30 10:31:34 +01:00
|
|
|
}
|
2019-04-11 12:42:13 +02:00
|
|
|
addDriverModeFlagIfNeeded(cToolchain, flags, originalFlags);
|
2018-10-30 10:31:34 +01:00
|
|
|
rpp.setFlagsForC({cToolchain, flags});
|
|
|
|
|
} else {
|
|
|
|
|
if (!cxxToolchain) {
|
|
|
|
|
cxxToolchain = toolchainFromFlags(kit, originalFlags,
|
|
|
|
|
ProjectExplorer::Constants::CXX_LANGUAGE_ID);
|
2019-02-06 12:50:51 +01:00
|
|
|
ToolChainKitAspect::setToolChain(kit, cxxToolchain);
|
2018-10-30 10:31:34 +01:00
|
|
|
}
|
2019-04-11 12:42:13 +02:00
|
|
|
addDriverModeFlagIfNeeded(cxxToolchain, flags, originalFlags);
|
2018-10-30 10:31:34 +01:00
|
|
|
rpp.setFlagsForCxx({cxxToolchain, flags});
|
|
|
|
|
}
|
2018-08-13 11:15:27 +02:00
|
|
|
|
|
|
|
|
return rpp;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 14:21:34 +01:00
|
|
|
QStringList relativeDirsList(Utils::FileName currentPath, const Utils::FileName &rootPath)
|
|
|
|
|
{
|
|
|
|
|
QStringList dirsList;
|
|
|
|
|
while (!currentPath.isEmpty() && currentPath != rootPath) {
|
|
|
|
|
QString dirName = currentPath.fileName();
|
|
|
|
|
if (dirName.isEmpty())
|
|
|
|
|
dirName = currentPath.toString();
|
|
|
|
|
dirsList.prepend(dirName);
|
|
|
|
|
currentPath = currentPath.parentDir();
|
|
|
|
|
}
|
|
|
|
|
return dirsList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FolderNode *addChildFolderNode(FolderNode *parent, const QString &childName)
|
|
|
|
|
{
|
|
|
|
|
Utils::FileName parentPath = parent->filePath();
|
2019-02-25 18:32:12 +01:00
|
|
|
auto node = std::make_unique<FolderNode>(parentPath.appendPath(childName));
|
2018-10-30 14:21:34 +01:00
|
|
|
FolderNode *childNode = node.get();
|
2019-02-25 18:08:36 +01:00
|
|
|
childNode->setDisplayName(childName);
|
2018-10-30 14:21:34 +01:00
|
|
|
parent->addNode(std::move(node));
|
|
|
|
|
|
|
|
|
|
return childNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FolderNode *addOrGetChildFolderNode(FolderNode *parent, const QString &childName)
|
|
|
|
|
{
|
2018-12-14 12:56:57 +01:00
|
|
|
for (FolderNode *folder : parent->folderNodes()) {
|
|
|
|
|
if (folder->filePath().fileName() == childName) {
|
|
|
|
|
return folder;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-30 14:21:34 +01:00
|
|
|
|
|
|
|
|
return addChildFolderNode(parent, childName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the node for folderPath.
|
|
|
|
|
FolderNode *createFoldersIfNeeded(FolderNode *root, const Utils::FileName &folderPath)
|
|
|
|
|
{
|
|
|
|
|
const QStringList dirsList = relativeDirsList(folderPath, root->filePath());
|
|
|
|
|
|
|
|
|
|
FolderNode *parent = root;
|
|
|
|
|
for (const QString &dir : dirsList)
|
|
|
|
|
parent = addOrGetChildFolderNode(parent, dir);
|
|
|
|
|
|
|
|
|
|
return parent;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
FileType fileTypeForName(const QString &fileName)
|
2018-10-30 14:21:34 +01:00
|
|
|
{
|
2018-11-02 16:07:42 +01:00
|
|
|
CppTools::ProjectFile::Kind fileKind = CppTools::ProjectFile::classify(fileName);
|
|
|
|
|
if (CppTools::ProjectFile::isHeader(fileKind))
|
|
|
|
|
return FileType::Header;
|
|
|
|
|
return FileType::Source;
|
|
|
|
|
}
|
2018-10-30 14:21:34 +01:00
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
void createTree(FolderNode *root,
|
|
|
|
|
const Utils::FileName &rootPath,
|
2019-03-14 15:34:14 +01:00
|
|
|
const CppTools::RawProjectParts &rpps,
|
|
|
|
|
const QList<FileNode *> &scannedFiles = QList<FileNode *>())
|
2018-11-02 16:07:42 +01:00
|
|
|
{
|
|
|
|
|
root->setAbsoluteFilePathAndLine(rootPath, -1);
|
2018-10-30 14:21:34 +01:00
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
for (const CppTools::RawProjectPart &rpp : rpps) {
|
|
|
|
|
for (const QString &filePath : rpp.files) {
|
|
|
|
|
Utils::FileName fileName = Utils::FileName::fromString(filePath);
|
2019-03-14 15:34:14 +01:00
|
|
|
if (!fileName.isChildOf(rootPath))
|
|
|
|
|
continue;
|
|
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
FolderNode *parentNode = createFoldersIfNeeded(root, fileName.parentDir());
|
|
|
|
|
if (!parentNode->fileNode(fileName)) {
|
|
|
|
|
parentNode->addNode(std::make_unique<FileNode>(fileName,
|
2019-02-25 12:08:58 +01:00
|
|
|
fileTypeForName(fileName.fileName())));
|
2018-11-02 16:07:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-10-30 14:21:34 +01:00
|
|
|
}
|
2019-03-14 15:34:14 +01:00
|
|
|
|
|
|
|
|
for (FileNode *node : scannedFiles) {
|
|
|
|
|
if (node->fileType() != FileType::Header)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const Utils::FileName fileName = node->filePath();
|
|
|
|
|
if (!fileName.isChildOf(rootPath))
|
|
|
|
|
continue;
|
|
|
|
|
FolderNode *parentNode = createFoldersIfNeeded(root, fileName.parentDir());
|
|
|
|
|
if (!parentNode->fileNode(fileName)) {
|
|
|
|
|
std::unique_ptr<FileNode> headerNode(node->clone());
|
|
|
|
|
headerNode->setEnabled(false);
|
|
|
|
|
parentNode->addNode(std::move(headerNode));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
qDeleteAll(scannedFiles);
|
2018-10-30 14:21:34 +01:00
|
|
|
}
|
|
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
struct Entry
|
|
|
|
|
{
|
|
|
|
|
QStringList flags;
|
|
|
|
|
Utils::FileName fileName;
|
|
|
|
|
QString workingDir;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<Entry> readJsonObjects(const QString &filePath)
|
2018-11-01 13:05:34 +01:00
|
|
|
{
|
2018-11-02 16:07:42 +01:00
|
|
|
std::vector<Entry> result;
|
2018-11-01 13:05:34 +01:00
|
|
|
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);
|
|
|
|
|
|
2019-04-15 13:44:01 +02:00
|
|
|
QSet<QString> flagsCache;
|
2018-11-01 13:05:34 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
const QJsonObject object = document.object();
|
|
|
|
|
const Utils::FileName fileName = jsonObjectFilename(object);
|
2019-04-15 13:44:01 +02:00
|
|
|
const QStringList flags = filterFromFileName(jsonObjectFlags(object, flagsCache),
|
|
|
|
|
fileName.toFileInfo().baseName());
|
2018-11-02 16:07:42 +01:00
|
|
|
result.push_back({flags, fileName, object["directory"].toString()});
|
|
|
|
|
|
2018-11-01 13:05:34 +01:00
|
|
|
objectStart = contents.indexOf('{', objectEnd + 1);
|
|
|
|
|
objectEnd = contents.indexOf('}', objectStart + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName &projectFile)
|
|
|
|
|
{
|
2018-11-02 16:07:42 +01:00
|
|
|
std::vector<Entry> array = readJsonObjects(projectFilePath().toString());
|
2018-11-01 13:05:34 +01:00
|
|
|
if (array.empty()) {
|
2018-10-30 10:31:34 +01:00
|
|
|
emitParsingFinished(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-25 18:35:34 +01:00
|
|
|
auto root = std::make_unique<ProjectNode>(projectDirectory());
|
2018-10-30 10:31:34 +01:00
|
|
|
|
2019-01-08 12:33:18 +01:00
|
|
|
CppTools::KitInfo kitInfo(this);
|
|
|
|
|
QTC_ASSERT(kitInfo.isValid(), return);
|
2019-04-11 12:42:13 +02:00
|
|
|
// Reset toolchains to pick them based on the database entries.
|
|
|
|
|
kitInfo.cToolChain = nullptr;
|
|
|
|
|
kitInfo.cxxToolChain = nullptr;
|
2018-10-30 10:31:34 +01:00
|
|
|
CppTools::RawProjectParts rpps;
|
2018-10-30 14:21:34 +01:00
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
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());
|
|
|
|
|
});
|
2018-10-30 14:21:34 +01:00
|
|
|
|
2018-11-02 16:07:42 +01:00
|
|
|
const Entry *prevEntry = nullptr;
|
|
|
|
|
for (const Entry &entry : array) {
|
|
|
|
|
if (prevEntry && prevEntry->flags == entry.flags) {
|
|
|
|
|
rpps.back().files.append(entry.fileName.toString());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prevEntry = &entry;
|
|
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
CppTools::RawProjectPart rpp = makeRawProjectPart(projectFile,
|
|
|
|
|
m_kit.get(),
|
2019-01-08 12:33:18 +01:00
|
|
|
kitInfo.cToolChain,
|
|
|
|
|
kitInfo.cxxToolChain,
|
2018-11-02 16:07:42 +01:00
|
|
|
entry.workingDir,
|
|
|
|
|
entry.fileName,
|
|
|
|
|
entry.flags);
|
|
|
|
|
|
|
|
|
|
rpps.append(rpp);
|
2018-10-30 10:31:34 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-14 15:34:14 +01:00
|
|
|
m_treeScanner.future().waitForFinished();
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
|
|
if (m_treeScanner.future().isCanceled())
|
|
|
|
|
createTree(root.get(), rootProjectDirectory(), rpps);
|
|
|
|
|
else
|
|
|
|
|
createTree(root.get(), rootProjectDirectory(), rpps, m_treeScanner.release());
|
2018-10-30 14:21:34 +01:00
|
|
|
|
2019-02-25 12:08:58 +01:00
|
|
|
root->addNode(std::make_unique<FileNode>(projectFile, FileType::Project));
|
2018-10-30 10:31:34 +01:00
|
|
|
|
|
|
|
|
setRootProjectNode(std::move(root));
|
|
|
|
|
|
2019-01-08 12:33:18 +01:00
|
|
|
m_cppCodeModelUpdater->update({this, kitInfo, rpps});
|
2018-10-30 10:31:34 +01:00
|
|
|
|
|
|
|
|
emitParsingFinished(true);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-13 11:15:27 +02:00
|
|
|
CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &projectFile)
|
|
|
|
|
: Project(Constants::COMPILATIONDATABASEMIMETYPE, projectFile)
|
2018-12-11 15:37:59 +01:00
|
|
|
, m_cppCodeModelUpdater(std::make_unique<CppTools::CppProjectUpdater>())
|
2018-08-13 11:15:27 +02:00
|
|
|
{
|
|
|
|
|
setId(Constants::COMPILATIONDATABASEPROJECT_ID);
|
|
|
|
|
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
|
|
|
|
|
setDisplayName(projectDirectory().fileName());
|
2018-10-30 10:31:34 +01:00
|
|
|
setRequiredKitPredicate([](const Kit *) { return false; });
|
|
|
|
|
setPreferredKitPredicate([](const Kit *) { return false; });
|
2018-08-13 11:15:27 +02:00
|
|
|
|
2018-10-30 10:31:34 +01:00
|
|
|
m_kit.reset(KitManager::defaultKit()->clone());
|
2019-03-14 11:09:36 +01:00
|
|
|
connect(this, &CompilationDatabaseProject::parsingFinished, this, [this]() {
|
|
|
|
|
if (!m_hasTarget) {
|
|
|
|
|
addTarget(createTarget(m_kit.get()));
|
|
|
|
|
m_hasTarget = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-11-02 10:00:20 +01:00
|
|
|
|
2019-03-14 15:34:14 +01:00
|
|
|
m_treeScanner.setFilter([this](const Utils::MimeType &mimeType, const Utils::FileName &fn) {
|
|
|
|
|
// Mime checks requires more resources, so keep it last in check list
|
|
|
|
|
auto isIgnored = fn.toString().startsWith(projectFilePath().toString() + ".user")
|
|
|
|
|
|| TreeScanner::isWellKnownBinary(mimeType, fn);
|
|
|
|
|
|
|
|
|
|
// Cache mime check result for speed up
|
|
|
|
|
if (!isIgnored) {
|
|
|
|
|
auto it = m_mimeBinaryCache.find(mimeType.name());
|
|
|
|
|
if (it != m_mimeBinaryCache.end()) {
|
|
|
|
|
isIgnored = *it;
|
|
|
|
|
} else {
|
|
|
|
|
isIgnored = TreeScanner::isMimeBinary(mimeType, fn);
|
|
|
|
|
m_mimeBinaryCache[mimeType.name()] = isIgnored;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return isIgnored;
|
|
|
|
|
});
|
|
|
|
|
m_treeScanner.setTypeFactory([](const Utils::MimeType &mimeType, const Utils::FileName &fn) {
|
|
|
|
|
return TreeScanner::genericFileType(mimeType, fn);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(this,
|
|
|
|
|
&CompilationDatabaseProject::rootProjectDirectoryChanged,
|
|
|
|
|
this,
|
|
|
|
|
&CompilationDatabaseProject::reparseProject);
|
2018-10-30 10:31:34 +01:00
|
|
|
|
2019-03-14 11:09:36 +01:00
|
|
|
m_fileSystemWatcher.addFile(projectFile.toString(), Utils::FileSystemWatcher::WatchModifiedDate);
|
|
|
|
|
connect(&m_fileSystemWatcher,
|
|
|
|
|
&Utils::FileSystemWatcher::fileChanged,
|
|
|
|
|
this,
|
2019-03-14 15:34:14 +01:00
|
|
|
&CompilationDatabaseProject::reparseProject);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-11 12:42:13 +02:00
|
|
|
Utils::FileName CompilationDatabaseProject::rootPathFromSettings() const
|
|
|
|
|
{
|
|
|
|
|
#ifdef WITH_TESTS
|
|
|
|
|
return Utils::FileName::fromString(projectDirectory().fileName());
|
|
|
|
|
#else
|
|
|
|
|
return Utils::FileName::fromString(
|
|
|
|
|
namedSettings(ProjectExplorer::Constants::PROJECT_ROOT_PATH_KEY).toString());
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-14 15:34:14 +01:00
|
|
|
Project::RestoreResult CompilationDatabaseProject::fromMap(const QVariantMap &map,
|
|
|
|
|
QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
Project::RestoreResult result = Project::fromMap(map, errorMessage);
|
|
|
|
|
if (result == Project::RestoreResult::Ok) {
|
2019-04-11 12:42:13 +02:00
|
|
|
const Utils::FileName rootPath = rootPathFromSettings();
|
2019-03-14 15:34:14 +01:00
|
|
|
if (rootPath.isEmpty())
|
|
|
|
|
changeRootProjectDirectory(); // This triggers reparse itself.
|
|
|
|
|
else
|
|
|
|
|
reparseProject();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
2019-03-14 11:09:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-14 15:34:14 +01:00
|
|
|
void CompilationDatabaseProject::reparseProject()
|
2019-03-14 11:09:36 +01:00
|
|
|
{
|
|
|
|
|
emitParsingStarted();
|
2019-03-14 15:34:14 +01:00
|
|
|
|
2019-04-11 12:42:13 +02:00
|
|
|
const Utils::FileName rootPath = rootPathFromSettings();
|
2019-03-14 15:34:14 +01:00
|
|
|
if (!rootPath.isEmpty()) {
|
|
|
|
|
m_treeScanner.asyncScanForFiles(rootPath);
|
|
|
|
|
|
|
|
|
|
Core::ProgressManager::addTask(m_treeScanner.future(),
|
|
|
|
|
tr("Scan \"%1\" project tree").arg(displayName()),
|
|
|
|
|
"CompilationDatabase.Scan.Tree");
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-14 11:09:36 +01:00
|
|
|
const QFuture<void> future = ::Utils::runAsync(
|
2019-03-14 15:34:14 +01:00
|
|
|
[this]() { buildTreeAndProjectParts(projectFilePath()); });
|
|
|
|
|
Core::ProgressManager::addTask(future,
|
|
|
|
|
tr("Parse \"%1\" project").arg(displayName()),
|
|
|
|
|
"CompilationDatabase.Parse");
|
2018-10-30 10:31:34 +01:00
|
|
|
m_parserWatcher.setFuture(future);
|
2018-08-13 11:15:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompilationDatabaseProject::~CompilationDatabaseProject()
|
|
|
|
|
{
|
|
|
|
|
m_parserWatcher.cancel();
|
|
|
|
|
m_parserWatcher.waitForFinished();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static TextEditor::TextDocument *createCompilationDatabaseDocument()
|
|
|
|
|
{
|
|
|
|
|
auto doc = new TextEditor::TextDocument;
|
|
|
|
|
doc->setId(Constants::COMPILATIONDATABASEPROJECT_ID);
|
|
|
|
|
doc->setMimeType(Constants::COMPILATIONDATABASEMIMETYPE);
|
|
|
|
|
return doc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompilationDatabaseEditorFactory::CompilationDatabaseEditorFactory()
|
|
|
|
|
{
|
|
|
|
|
setId(Constants::COMPILATIONDATABASEPROJECT_ID);
|
|
|
|
|
setDisplayName("Compilation Database");
|
|
|
|
|
addMimeType(Constants::COMPILATIONDATABASEMIMETYPE);
|
|
|
|
|
|
|
|
|
|
setEditorCreator([]() { return new TextEditor::BaseTextEditor; });
|
|
|
|
|
setEditorWidgetCreator([]() { return new TextEditor::TextEditorWidget; });
|
|
|
|
|
setDocumentCreator(createCompilationDatabaseDocument);
|
|
|
|
|
setUseGenericHighlighter(true);
|
|
|
|
|
setCommentDefinition(Utils::CommentDefinition::HashStyle);
|
|
|
|
|
setCodeFoldingSupported(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace CompilationDatabaseProjectManager
|