diff options
author | Ivan Donchevskii <ivan.donchevskii@qt.io> | 2018-10-30 10:31:34 +0100 |
---|---|---|
committer | Ivan Donchevskii <ivan.donchevskii@qt.io> | 2018-11-01 14:52:05 +0000 |
commit | f604c8a77cf86663fc751bcf23c94697088cfda1 (patch) | |
tree | c98bc2ff2e694f3d93650260f8144e1e8c522f2f /src/plugins/compilationdatabaseprojectmanager | |
parent | cb75aa3e58987ed3cd8ac5e1dde089b855f731b2 (diff) | |
download | qt-creator-f604c8a77cf86663fc751bcf23c94697088cfda1.tar.gz |
CompilationDatabase: Support both code models
Extract headers, defines and fileKind from flags in
order to have complete project parts.
Side-effect: better support for MSVC-specific flags.
Change-Id: Iaa1413c91c96c3cf89ddbe76a7a1f0f46c5289c0
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
Diffstat (limited to 'src/plugins/compilationdatabaseprojectmanager')
14 files changed, 790 insertions, 178 deletions
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index 106d0926b9..522a8356ab 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -26,158 +26,286 @@ #include "compilationdatabaseproject.h" #include "compilationdatabaseconstants.h" +#include "compilationdatabaseutils.h" #include <coreplugin/icontext.h> #include <cpptools/projectinfo.h> #include <cpptools/cppprojectupdater.h> #include <projectexplorer/gcctoolchain.h> +#include <projectexplorer/headerpath.h> #include <projectexplorer/kitinformation.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectnodes.h> -#include <projectexplorer/target.h> #include <projectexplorer/toolchainconfigwidget.h> +#include <projectexplorer/toolchainmanager.h> #include <texteditor/textdocument.h> + +#include <utils/algorithm.h> +#include <utils/qtcassert.h> #include <utils/runextensions.h> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> -#include <QRegularExpression> +#ifdef Q_OS_WIN +#include <Windows.h> +#endif + +using namespace ProjectExplorer; namespace CompilationDatabaseProjectManager { namespace Internal { -class DBProjectNode : public ProjectExplorer::ProjectNode +namespace { +class DBProjectNode : public ProjectNode { public: explicit DBProjectNode(const Utils::FileName &projectFilePath) - : ProjectExplorer::ProjectNode(projectFilePath) + : ProjectNode(projectFilePath) {} }; -static QStringList splitCommandLine(QString commandLine) +QStringList jsonObjectFlags(const QJsonObject &object) { - QStringList result; - bool insideQuotes = false; - - // Remove escaped quotes. - commandLine.replace("\\\"", "'"); - 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); - } else { // If 's' is outside quotes ... - result.append(part.split(QRegularExpression("\\s+"), QString::SkipEmptyParts)); - } - insideQuotes = !insideQuotes; + QStringList flags; + const QJsonArray arguments = object["arguments"].toArray(); + if (arguments.isEmpty()) { + flags = splitCommandLine(object["command"].toString()); + } else { + for (const QJsonValue &arg : arguments) + flags.append(arg.toString()); } - return result; + + return flags; } -static QString updatedPathFlag(const QString &pathStr, const QString &workingDir, - const QString &originalFlag) +bool isGccCompiler(const QString &compilerName) { - QString result = pathStr; - if (!QDir(pathStr).exists() - && QDir(workingDir + "/" + pathStr).exists()) { - result = workingDir + "/" + pathStr; - } - - if (originalFlag.startsWith("-I")) - return "-I" + result; - - if (originalFlag.startsWith("-isystem")) - return "-isystem" + result; - - return result; + return compilerName.contains("gcc") || compilerName.contains("g++"); } -static QStringList filteredFlags(const QStringList &flags, const QString &fileName, - const QString &workingDir) +Core::Id getCompilerId(QString compilerName) { - QStringList filtered; - // Skip compiler call if present. - bool skipNext = !flags.first().startsWith('-'); - bool includePath = false; - - for (const QString &flag : flags) { - if (skipNext) { - skipNext = false; - continue; - } + 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; - QString pathStr; - if (includePath) { - includePath = false; - pathStr = flag; - } else if ((flag.startsWith("-I") || flag.startsWith("-isystem")) - && flag != "-I" && flag != "-isystem") { - pathStr = flag.mid(flag.startsWith("-I") ? 2 : 8); - } + // Default is clang + return ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID; +} - if (!pathStr.isEmpty()) { - filtered.push_back(updatedPathFlag(pathStr, workingDir, flag)); - continue; - } +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; + }); +} - if (flag == "-c" || flag == "-pedantic" || flag.startsWith("/") || flag.startsWith("-m") - || flag.startsWith("-O") || flag.startsWith("-W") || flag.startsWith("-w") - || flag.startsWith("--sysroot=")) { - continue; - } +QString compilerPath(QString pathFlag) +{ + if (pathFlag.isEmpty()) + 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; +#endif + return QDir::fromNativeSeparators(pathFlag); +} - if (flag == "-target" || flag == "-triple" || flag == "-isysroot" || flag == "-isystem" - || flag == "--sysroot") { - skipNext = true; - continue; - } +ToolChain *toolchainFromFlags(const Kit *kit, const QStringList &flags, const Core::Id &language) +{ + if (flags.empty()) + return ToolChainKitInformation::toolChain(kit, language); - if (flag.endsWith(fileName)) - continue; + // 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; + } - if (flag == "-I" || flag == "-isystem") - includePath = true; + toolchain = ToolChainKitInformation::toolChain(kit, language); + qWarning() << QCoreApplication::translate("CompilationDatabaseProject", + "No matching toolchain found, use the default."); + return toolchain; +} - filtered.push_back(flag); +Utils::FileName jsonObjectFilename(const QJsonObject &object) +{ + const QString workingDir = 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 filtered; + return fileName; } -static CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projectFile, - const QJsonObject &object, - const QString &workingDir, - const Utils::FileName &fileName) +void addDriverModeFlagIfNeeded(const ToolChain *toolchain, QStringList &flags) { - QStringList flags; - const QJsonArray arguments = object["arguments"].toArray(); - if (arguments.isEmpty()) { - flags = splitCommandLine(object["command"].toString()); - } else { - for (const QJsonValue &arg : arguments) - flags.append(arg.toString()); + if (toolchain->typeId() == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID + && !flags.empty() && !flags.front().endsWith("cl") + && !flags.front().endsWith("cl.exe")) { + flags.insert(1, "--driver-mode=g++"); } +} - flags = filteredFlags(flags, fileName.fileName(), workingDir); +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); CppTools::RawProjectPart rpp; rpp.setProjectFileLocation(projectFile.toString()); rpp.setBuildSystemTarget(workingDir); rpp.setDisplayName(fileName.fileName()); rpp.setFiles({fileName.toString()}); - - CppTools::RawProjectPartFlags cxxProjectFlags; - cxxProjectFlags.commandLineFlags = flags; - rpp.setFlagsForCxx(cxxProjectFlags); + 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); + ToolChainKitInformation::setToolChain(kit, cToolchain); + } + addDriverModeFlagIfNeeded(cToolchain, flags); + rpp.setFlagsForC({cToolchain, flags}); + } else { + if (!cxxToolchain) { + cxxToolchain = toolchainFromFlags(kit, originalFlags, + ProjectExplorer::Constants::CXX_LANGUAGE_ID); + ToolChainKitInformation::setToolChain(kit, cxxToolchain); + } + addDriverModeFlagIfNeeded(cxxToolchain, flags); + rpp.setFlagsForCxx({cxxToolchain, flags}); + } return rpp; } +} // anonymous namespace + +void CompilationDatabaseProject::buildTreeAndProjectParts(const Utils::FileName &projectFile) +{ + QFile file(projectFilePath().toString()); + if (!file.open(QIODevice::ReadOnly)) { + emitParsingFinished(false); + return; + } + + const QJsonArray array = QJsonDocument::fromJson(file.readAll()).array(); + + auto root = std::make_unique<DBProjectNode>(projectDirectory()); + root->addNode(std::make_unique<FileNode>( + projectFile, + FileType::Project, + false)); + auto headers = std::make_unique<VirtualFolderNode>( + Utils::FileName::fromString("Headers"), 0); + auto sources = std::make_unique<VirtualFolderNode>( + Utils::FileName::fromString("Sources"), 0); + + CppTools::RawProjectParts rpps; + ToolChain *cToolchain = nullptr; + ToolChain *cxxToolchain = nullptr; + for (const QJsonValue &element : array) { + const QJsonObject object = element.toObject(); + + Utils::FileName fileName = jsonObjectFilename(object); + const QStringList flags = jsonObjectFlags(object); + const QString filePath = fileName.toString(); + + const CppTools::ProjectFile::Kind kind = CppTools::ProjectFile::classify(filePath); + FolderNode *parent = nullptr; + FileType type = FileType::Unknown; + if (CppTools::ProjectFile::isHeader(kind)) { + parent = headers.get(); + type = FileType::Header; + } else if (CppTools::ProjectFile::isSource(kind)) { + parent = sources.get(); + type = FileType::Source; + } else { + parent = root.get(); + } + parent->addNode(std::make_unique<FileNode>(fileName, type, false)); + + CppTools::RawProjectPart rpp = makeRawProjectPart(projectFile, + m_kit.get(), + cToolchain, + cxxToolchain, + object["directory"].toString(), + fileName, + flags); + int rppIndex = Utils::indexOf(rpps, [&rpp](const CppTools::RawProjectPart ¤tRpp) { + return rpp.buildSystemTarget == currentRpp.buildSystemTarget + && rpp.headerPaths == currentRpp.headerPaths + && rpp.projectMacros == currentRpp.projectMacros + && rpp.flagsForCxx.commandLineFlags == currentRpp.flagsForCxx.commandLineFlags; + }); + if (rppIndex == -1) + rpps.append(rpp); + else + rpps[rppIndex].files.append(rpp.files); + } + + root->addNode(std::move(headers)); + root->addNode(std::move(sources)); + + setRootProjectNode(std::move(root)); + + m_cppCodeModelUpdater->update({this, cToolchain, cxxToolchain, m_kit.get(), rpps}); + + emitParsingFinished(true); +} + CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &projectFile) : Project(Constants::COMPILATIONDATABASEMIMETYPE, projectFile) , m_cppCodeModelUpdater(std::make_unique<CppTools::CppProjectUpdater>(this)) @@ -185,90 +313,17 @@ CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &pr setId(Constants::COMPILATIONDATABASEPROJECT_ID); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); setDisplayName(projectDirectory().fileName()); + setRequiredKitPredicate([](const Kit *) { return false; }); + setPreferredKitPredicate([](const Kit *) { return false; }); - connect(this, &Project::activeTargetChanged, [this, projectFile](ProjectExplorer::Target *target) { - if (!target) - return; - - ProjectExplorer::Kit *kit = target->kit(); - if (!kit) - return; - - auto toolchains = ProjectExplorer::ToolChainKitInformation::toolChains(kit); - if (toolchains.isEmpty()) - return; - - emitParsingStarted(); - - const QFuture<void> future = ::Utils::runAsync([this, projectFile, kit, - tc = toolchains.first()](){ - QFile file(projectFilePath().toString()); - if (!file.open(QIODevice::ReadOnly)) { - emitParsingFinished(false); - return; - } - - const QJsonArray array = QJsonDocument::fromJson(file.readAll()).array(); - - auto root = std::make_unique<DBProjectNode>(projectDirectory()); - root->addNode(std::make_unique<ProjectExplorer::FileNode>( - projectFile, - ProjectExplorer::FileType::Project, - false)); - auto headers = std::make_unique<ProjectExplorer::VirtualFolderNode>( - Utils::FileName::fromString("Headers"), 0); - auto sources = std::make_unique<ProjectExplorer::VirtualFolderNode>( - Utils::FileName::fromString("Sources"), 0); - CppTools::RawProjectParts rpps; - for (const QJsonValue &element : array) { - const QJsonObject object = element.toObject(); - const QString workingDir = object["directory"].toString(); - Utils::FileName fileName = Utils::FileName::fromString( - QDir::fromNativeSeparators(object["file"].toString())); - if (!fileName.exists()) { - fileName = Utils::FileUtils::canonicalPath( - Utils::FileName::fromString(workingDir + "/" + fileName.toString())); - } - const QString filePath = fileName.toString(); - const CppTools::ProjectFile::Kind kind = CppTools::ProjectFile::classify(filePath); - ProjectExplorer::FolderNode *parent = nullptr; - ProjectExplorer::FileType type = ProjectExplorer::FileType::Unknown; - if (CppTools::ProjectFile::isHeader(kind)) { - parent = headers.get(); - type = ProjectExplorer::FileType::Header; - } else if (CppTools::ProjectFile::isSource(kind)) { - parent = sources.get(); - type = ProjectExplorer::FileType::Source; - } else { - parent = root.get(); - } - parent->addNode(std::make_unique<ProjectExplorer::FileNode>( - fileName, type, false)); - - rpps.append(makeRawProjectPart(projectFile, object, workingDir, fileName)); - } - - root->addNode(std::move(headers)); - root->addNode(std::move(sources)); - - setRootProjectNode(std::move(root)); - - CppTools::ToolChainInfo tcInfo; - tcInfo.type = ProjectExplorer::Constants::COMPILATION_DATABASE_TOOLCHAIN_TYPEID; - tcInfo.isMsvc2015ToolChain - = tc->targetAbi().osFlavor() == ProjectExplorer::Abi::WindowsMsvc2015Flavor; - tcInfo.wordWidth = tc->targetAbi().wordWidth(); - tcInfo.targetTriple = tc->originalTargetTriple(); - tcInfo.sysRootPath = ProjectExplorer::SysRootKitInformation::sysRoot(kit).toString(); - tcInfo.headerPathsRunner = tc->createBuiltInHeaderPathsRunner(); - tcInfo.macroInspectionRunner = tc->createMacroInspectionRunner(); - - m_cppCodeModelUpdater->update({this, tcInfo, tcInfo, rpps}); - - emitParsingFinished(true); - }); - m_parserWatcher.setFuture(future); + m_kit.reset(KitManager::defaultKit()->clone()); + + emitParsingStarted(); + + const QFuture<void> future = ::Utils::runAsync([this, projectFile](){ + buildTreeAndProjectParts(projectFile); }); + m_parserWatcher.setFuture(future); } CompilationDatabaseProject::~CompilationDatabaseProject() diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h index 2ba01adbaa..26fabebe47 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h @@ -35,6 +35,8 @@ namespace CppTools { class CppProjectUpdater; } +namespace ProjectExplorer { class Kit; } + namespace CompilationDatabaseProjectManager { namespace Internal { @@ -45,10 +47,15 @@ class CompilationDatabaseProject : public ProjectExplorer::Project public: explicit CompilationDatabaseProject(const Utils::FileName &filename); ~CompilationDatabaseProject() override; + bool needsConfiguration() const override { return false; } + bool needsBuildConfigurations() const override { return false; } private: + void buildTreeAndProjectParts(const Utils::FileName &projectFile); + QFutureWatcher<void> m_parserWatcher; std::unique_ptr<CppTools::CppProjectUpdater> m_cppCodeModelUpdater; + std::unique_ptr<ProjectExplorer::Kit> m_kit; }; class CompilationDatabaseEditorFactory : public TextEditor::TextEditorFactory diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro index a815c0db8e..e7efefb47e 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro @@ -2,9 +2,21 @@ include(../../qtcreatorplugin.pri) SOURCES = \ compilationdatabaseproject.cpp \ - compilationdatabaseprojectmanagerplugin.cpp + compilationdatabaseprojectmanagerplugin.cpp \ + compilationdatabaseutils.cpp HEADERS = \ compilationdatabaseproject.h \ compilationdatabaseprojectmanagerplugin.h \ - compilationdatabaseconstants.h + compilationdatabaseconstants.h \ + compilationdatabaseutils.h + +equals(TEST, 1) { + HEADERS += \ + compilationdatabasetests.h + + SOURCES += \ + compilationdatabasetests.cpp + + RESOURCES += compilationdatabasetests.qrc +} diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs index e21dc9e123..4cbe2af284 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs @@ -13,7 +13,26 @@ QtcPlugin { "compilationdatabaseconstants.h", "compilationdatabaseproject.cpp", "compilationdatabaseproject.h", + "compilationdatabaseutils.cpp", + "compilationdatabaseutils.h", "compilationdatabaseprojectmanagerplugin.cpp", "compilationdatabaseprojectmanagerplugin.h", ] + + Group { + name: "Tests" + condition: qtc.testsEnabled + files: [ + "compilationdatabasetests.cpp", + "compilationdatabasetests.h", + "compilationdatabasetests.qrc", + ] + } + + Group { + name: "Test resources" + prefix: "database_samples/" + fileTags: [] + files: ["**/*"] + } } diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp index 1fe0d6d032..77359adc70 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp @@ -27,6 +27,7 @@ #include "compilationdatabaseconstants.h" #include "compilationdatabaseproject.h" +#include "compilationdatabasetests.h" #include <coreplugin/fileiconprovider.h> #include <projectexplorer/projectmanager.h> @@ -52,5 +53,14 @@ void CompilationDatabaseProjectManagerPlugin::extensionsInitialized() { } +QList<QObject *> CompilationDatabaseProjectManagerPlugin::createTestObjects() const +{ + QList<QObject *> tests; +#ifdef WITH_TESTS + tests << new CompilationDatabaseTests; +#endif + return tests; +} + } // namespace Internal } // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h index a89db25e37..37c9c1db1d 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.h @@ -43,6 +43,8 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage) final; void extensionsInitialized() final; private: + QList<QObject *> createTestObjects() const final; + CompilationDatabaseEditorFactory factory; }; diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp new file mode 100644 index 0000000000..72a1b6ac74 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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 "compilationdatabasetests.h" + +#include <coreplugin/icore.h> +#include <cpptools/cpptoolstestcase.h> +#include <cpptools/projectinfo.h> +#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitmanager.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/toolchain.h> +#include <projectexplorer/toolchainmanager.h> + +#include <QtTest> + +using namespace ProjectExplorer; + +namespace CompilationDatabaseProjectManager { + +CompilationDatabaseTests::CompilationDatabaseTests(QObject *parent) + : QObject(parent) +{} + +CompilationDatabaseTests::~CompilationDatabaseTests() = default; + +void CompilationDatabaseTests::initTestCase() +{ + const QList<Kit *> allKits = KitManager::kits(); + if (allKits.empty()) + QSKIP("This test requires at least one kit to be present."); + + ToolChain *toolchain = ToolChainManager::toolChain([](const ToolChain *tc) { + return tc->isValid() && tc->language() == Constants::CXX_LANGUAGE_ID; + }); + if (!toolchain) + QSKIP("This test requires that there is at least one C++ toolchain present."); + + m_tmpDir = std::make_unique<CppTools::Tests::TemporaryCopiedDir>(":/database_samples"); + QVERIFY(m_tmpDir->isValid()); +} + +void CompilationDatabaseTests::cleanupTestCase() +{ + m_tmpDir.reset(); +} + +void CompilationDatabaseTests::testProject() +{ + QFETCH(QString, projectFilePath); + + CppTools::Tests::ProjectOpenerAndCloser projectManager; + const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true); + QVERIFY(projectInfo.isValid()); + + projectInfo.projectParts(); + QVector<CppTools::ProjectPart::Ptr> projectParts = projectInfo.projectParts(); + QVERIFY(!projectParts.isEmpty()); + + CppTools::ProjectPart &projectPart = *projectParts.first(); + QVERIFY(!projectPart.headerPaths.isEmpty()); + QVERIFY(!projectPart.projectMacros.isEmpty()); + QVERIFY(!projectPart.toolChainMacros.isEmpty()); + QVERIFY(!projectPart.files.isEmpty()); +} + +void CompilationDatabaseTests::testProject_data() +{ + QTest::addColumn<QString>("projectFilePath"); + + addTestRow("qtc/compile_commands.json"); + addTestRow("llvm/compile_commands.json"); +} + +void CompilationDatabaseTests::addTestRow(const QByteArray &relativeFilePath) +{ + const QString absoluteFilePath = m_tmpDir->absolutePath(relativeFilePath); + const QString fileName = QFileInfo(absoluteFilePath).fileName(); + + QTest::newRow(fileName.toUtf8().constData()) << absoluteFilePath; +} + +} // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.h new file mode 100644 index 0000000000..f508a44013 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include <QObject> +#include <memory> + +namespace CppTools { namespace Tests { class TemporaryCopiedDir; } } + +namespace CompilationDatabaseProjectManager { + +class CompilationDatabaseTests : public QObject +{ + Q_OBJECT +public: + explicit CompilationDatabaseTests(QObject *parent = nullptr); + ~CompilationDatabaseTests(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void testProject(); + void testProject_data(); + +private: + void addTestRow(const QByteArray &relativeFilePath); + + std::unique_ptr<CppTools::Tests::TemporaryCopiedDir> m_tmpDir; +}; + +} // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.qrc b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.qrc new file mode 100644 index 0000000000..571437b4c9 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>database_samples/llvm/compile_commands.json</file> + <file>database_samples/qtc/compile_commands.json</file> + </qresource> +</RCC> diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri new file mode 100644 index 0000000000..c1b3188b4c --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/compilationdatabaseutils.cpp + +HEADERS += \ + $$PWD/compilationdatabaseutils.h diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp new file mode 100644 index 0000000000..30ad52160e --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** 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 "compilationdatabaseutils.h" + +#include <projectexplorer/headerpath.h> +#include <projectexplorer/projectmacro.h> + +#include <utils/hostosinfo.h> +#include <utils/optional.h> + +#include <QDir> +#include <QRegularExpression> + +using namespace ProjectExplorer; + +namespace CompilationDatabaseProjectManager { + +static QString updatedPathFlag(const QString &pathStr, const QString &workingDir) +{ + QString result = pathStr; + if (!QDir(pathStr).exists() + && QDir(workingDir + "/" + pathStr).exists()) { + result = workingDir + "/" + pathStr; + } + + return result; +} + +static CppTools::ProjectFile::Kind fileKindFromString(const QString &flag) +{ + using namespace CppTools; + if (flag == "c++-header") + return ProjectFile::CXXHeader; + if (flag == "c-header") + return ProjectFile::CHeader; + if (flag == "c++" || flag == "/TP" || flag.startsWith("/Tp")) + return ProjectFile::CXXSource; + if (flag == "c" || flag == "/TC" || flag.startsWith("/Tc")) + return ProjectFile::CSource; + + if (flag == "objective-c++") + return ProjectFile::ObjCXXSource; + if (flag == "objective-c++-header") + return ProjectFile::ObjCXXHeader; + if (flag == "objective-c") + return ProjectFile::ObjCSource; + if (flag == "objective-c-header") + return ProjectFile::ObjCHeader; + + if (flag == "cl") + return ProjectFile::OpenCLSource; + if (flag == "cuda") + return ProjectFile::CudaSource; + + return ProjectFile::Unclassified; +} + +void filteredFlags(const QString &fileName, + const QString &workingDir, + QStringList &flags, + HeaderPaths &headerPaths, + Macros ¯os, + CppTools::ProjectFile::Kind &fileKind) +{ + if (flags.isEmpty()) + return; + + // Skip compiler call if present. + bool skipNext = Utils::HostOsInfo::isWindowsHost() + ? (!flags.first().startsWith('/') && !flags.first().startsWith('-')) + : (!flags.first().startsWith('-')); + Utils::optional<HeaderPathType> includePathType; + Utils::optional<MacroType> macroType; + bool fileKindIsNext = false; + + QStringList filtered; + for (const QString &flag : flags) { + if (skipNext) { + skipNext = false; + continue; + } + + if (includePathType) { + const QString pathStr = updatedPathFlag(flag, workingDir); + headerPaths.append({pathStr, includePathType.value()}); + includePathType.reset(); + continue; + } + + if (macroType) { + Macro macro = Macro::fromKeyValue(flag); + macro.type = macroType.value(); + macros.append(macro); + macroType.reset(); + continue; + } + + if (fileKindIsNext || flag == "/TC" || flag == "/TP" + || flag.startsWith("/Tc") || flag.startsWith("/Tp")) { + fileKindIsNext = false; + fileKind = fileKindFromString(flag); + continue; + } + + if (flag == "-x") { + fileKindIsNext = true; + continue; + } + + if (flag == "-c" || flag == "-pedantic" + || flag.startsWith("-O") || flag.startsWith("-W") || flag.startsWith("-w") + || QString::compare(flag, "-fpic", Qt::CaseInsensitive) == 0 + || QString::compare(flag, "-fpie", Qt::CaseInsensitive) == 0) { + continue; + } + + if (flag.endsWith(fileName)) + continue; + + if ((flag.startsWith("-I") || flag.startsWith("-isystem") || flag.startsWith("/I")) + && flag != "-I" && flag != "-isystem" && flag != "/I") { + bool userInclude = flag.startsWith("-I"); + const QString pathStr = updatedPathFlag(flag.mid(userInclude ? 2 : 8), + workingDir); + headerPaths.append({pathStr, userInclude + ? HeaderPathType::User + : HeaderPathType::System}); + continue; + } + + if ((flag.startsWith("-D") || flag.startsWith("-U") || flag.startsWith("/D") || flag.startsWith("/U")) + && flag != "-D" && flag != "-U" && flag != "/D" && flag != "/U") { + Macro macro = Macro::fromKeyValue(flag.mid(2)); + macro.type = (flag.startsWith("-D") || flag.startsWith("/D")) ? MacroType::Define : MacroType::Undefine; + macros.append(macro); + continue; + } + + if (flag == "-I" || flag == "-isystem" || flag == "/I") { + includePathType = (flag != "-isystem") ? HeaderPathType::User : HeaderPathType::System; + continue; + } + + if (flag == "-D" || flag == "-U" || flag == "/D" || flag == "/U") { + macroType = (flag == "-D" || flag == "/D") ? MacroType::Define : MacroType::Undefine; + continue; + } + + if ((flag.startsWith("-std=") || flag.startsWith("/std:")) + && fileKind == CppTools::ProjectFile::Unclassified) { + const bool cpp = (flag.contains("c++") || flag.contains("gnu++")); + if (CppTools::ProjectFile::isHeader(CppTools::ProjectFile::classify(fileName))) + fileKind = cpp ? CppTools::ProjectFile::CXXHeader : CppTools::ProjectFile::CHeader; + else + fileKind = cpp ? CppTools::ProjectFile::CXXSource : CppTools::ProjectFile::CXXHeader; + } + + // Skip all remaining Windows flags except feature flags. + if (flag.startsWith("/") && !flag.startsWith("/Z")) + continue; + + filtered.push_back(flag); + } + + if (fileKind == CppTools::ProjectFile::Unclassified) + fileKind = CppTools::ProjectFile::classify(fileName); + + flags = filtered; +} + +QStringList splitCommandLine(QString commandLine) +{ + QStringList result; + bool insideQuotes = false; + + // Remove escaped quotes. + commandLine.replace("\\\"", "'"); + 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); + } else { // If 's' is outside quotes ... + result.append(part.split(QRegularExpression("\\s+"), QString::SkipEmptyParts)); + } + insideQuotes = !insideQuotes; + } + return result; +} + +} // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h new file mode 100644 index 0000000000..5e123a8c27 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include "compilationdatabaseconstants.h" + +#include <cpptools/cppprojectfile.h> + +#include <QStringList> + +namespace ProjectExplorer { +class HeaderPath; +class Macro; +} + +namespace CompilationDatabaseProjectManager { + +void filteredFlags(const QString &fileName, + const QString &workingDir, + QStringList &flags, + QVector<ProjectExplorer::HeaderPath> &headerPaths, + QVector<ProjectExplorer::Macro> ¯os, + CppTools::ProjectFile::Kind &fileKind); + +QStringList splitCommandLine(QString commandLine); + +} // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/database_samples/llvm/compile_commands.json b/src/plugins/compilationdatabaseprojectmanager/database_samples/llvm/compile_commands.json new file mode 100644 index 0000000000..051439c960 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/database_samples/llvm/compile_commands.json @@ -0,0 +1,7 @@ +[ +{ + "directory": "C:/build-qt_llvm-msvc2017_64bit-Debug", + "command": "C:\\PROGRA~2\\MICROS~2\\2017\\COMMUN~1\\VC\\Tools\\MSVC\\1415~1.267\\bin\\HostX64\\x64\\cl.exe /nologo /TP -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_GNU_SOURCE -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Dclang=clang_qtcreator -Dllvm=llvm_qtcreator -Itools\\clang\\lib\\Sema -IC:\\qt_llvm\\tools\\clang\\lib\\Sema -IC:\\qt_llvm\\tools\\clang\\include -Itools\\clang\\include -Iinclude -IC:\\qt_llvm\\include /DWIN32 /D_WINDOWS /Zc:inline /Zc:strictStrings /Oi /Zc:rvalueCast /W4 -wd4141 -wd4146 -wd4180 -wd4244 -wd4258 -wd4267 -wd4291 -wd4345 -wd4351 -wd4355 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4800 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4324 -w14062 -we4238 /MDd /Zi /Ob0 /Od /RTC1 /EHs-c- /GR /Fotools\\clang\\lib\\Sema\\CMakeFiles\\clangSema.dir\\SemaCodeComplete.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\qt_llvm\\tools\\clang\\lib\\Sema\\SemaCodeComplete.cpp", + "file": "C:\\qt_llvm\\tools\\clang\\lib\\Sema\\SemaCodeComplete.cpp" +} +]
\ No newline at end of file diff --git a/src/plugins/compilationdatabaseprojectmanager/database_samples/qtc/compile_commands.json b/src/plugins/compilationdatabaseprojectmanager/database_samples/qtc/compile_commands.json new file mode 100644 index 0000000000..d75e8d9654 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/database_samples/qtc/compile_commands.json @@ -0,0 +1,62 @@ +[ +{ + "arguments": [ + "clang++", + "-c", + "-m32", + "-target", + "i686-w64-mingw32", + "-std=gnu++14", + "-fcxx-exceptions", + "-fexceptions", + "-DUNICODE", + "-D_UNICODE", + "-DCPPTOOLS_LIBRARY", + "-DWITH_TESTS", + "-DRELATIVE_PLUGIN_PATH=\"../lib/qtcreator/plugins\"", + "-DRELATIVE_LIBEXEC_PATH=\".\"", + "-DRELATIVE_DATA_PATH=\"../share/qtcreator\"", + "-DRELATIVE_DOC_PATH=\"../share/doc/qtcreator\"", + "-DIDE_LIBRARY_BASENAME=\"lib\"", + "-DQT_CREATOR", + "-DQT_NO_CAST_TO_ASCII", + "-DQT_RESTRICTED_CAST_FROM_ASCII", + "-DQT_DISABLE_DEPRECATED_BEFORE=0x050600", + "-DQT_USE_FAST_OPERATOR_PLUS", + "-DQT_USE_FAST_CONCATENATION", + "-DSRCDIR=\"C:/qt-creator/src/plugins/cpptools\"", + "-DQT_QML_DEBUG", + "-DQT_PLUGIN", + "-DQT_WIDGETS_LIB", + "-DQT_GUI_LIB", + "-DQT_TESTLIB_LIB", + "-DQT_CONCURRENT_LIB", + "-DQT_NETWORK_LIB", + "-DQT_CORE_LIB", + "-fPIC", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtWidgets", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtGui", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtANGLE", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtTest", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtConcurrent", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtNetwork", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtCore", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\mkspecs\\win32-g++", + "-x", + "c++", + "C:\\qt-creator\\src\\plugins\\cpptools\\compileroptionsbuilder.cpp" + ], + "directory": "C:/build-qtcreator-MinGW_32bit-Debug", + "file": "C:/qt-creator/src/plugins/cpptools/compileroptionsbuilder.cpp" +} +]
\ No newline at end of file |