From f604c8a77cf86663fc751bcf23c94697088cfda1 Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Tue, 30 Oct 2018 10:31:34 +0100 Subject: [PATCH] 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 --- .../compilationdatabaseproject.cpp | 423 ++++++++++-------- .../compilationdatabaseproject.h | 7 + .../compilationdatabaseprojectmanager.pro | 16 +- .../compilationdatabaseprojectmanager.qbs | 19 + ...ompilationdatabaseprojectmanagerplugin.cpp | 10 + .../compilationdatabaseprojectmanagerplugin.h | 2 + .../compilationdatabasetests.cpp | 105 +++++ .../compilationdatabasetests.h | 54 +++ .../compilationdatabasetests.qrc | 6 + .../compilationdatabaseunittestfiles.pri | 7 + .../compilationdatabaseutils.cpp | 216 +++++++++ .../compilationdatabaseutils.h | 50 +++ .../llvm/compile_commands.json | 7 + .../qtc/compile_commands.json | 62 +++ .../cpptools/compileroptionsbuilder.cpp | 7 - .../cpptools/cppprojectinfogenerator.cpp | 3 - .../projectexplorerconstants.h | 1 - .../compilationdatabaseutils-test.cpp | 169 +++++++ tests/unit/unittest/creator_dependency.pri | 1 + tests/unit/unittest/unittest.pro | 3 +- 20 files changed, 970 insertions(+), 198 deletions(-) create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.h create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.qrc create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp create mode 100644 src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h create mode 100644 src/plugins/compilationdatabaseprojectmanager/database_samples/llvm/compile_commands.json create mode 100644 src/plugins/compilationdatabaseprojectmanager/database_samples/qtc/compile_commands.json create mode 100644 tests/unit/unittest/compilationdatabaseutils-test.cpp diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index 106d0926b97..522a8356abd 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -26,133 +26,48 @@ #include "compilationdatabaseproject.h" #include "compilationdatabaseconstants.h" +#include "compilationdatabaseutils.h" #include #include #include #include +#include #include #include #include #include -#include #include +#include #include + +#include +#include #include #include #include #include -#include +#ifdef Q_OS_WIN +#include +#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 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; -} - -static QString updatedPathFlag(const QString &pathStr, const QString &workingDir, - const QString &originalFlag) -{ - 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; -} - -static QStringList filteredFlags(const QStringList &flags, const QString &fileName, - const QString &workingDir) -{ - 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; - } - - 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); - } - - if (!pathStr.isEmpty()) { - filtered.push_back(updatedPathFlag(pathStr, workingDir, flag)); - continue; - } - - if (flag == "-c" || flag == "-pedantic" || flag.startsWith("/") || flag.startsWith("-m") - || flag.startsWith("-O") || flag.startsWith("-W") || flag.startsWith("-w") - || flag.startsWith("--sysroot=")) { - continue; - } - - if (flag == "-target" || flag == "-triple" || flag == "-isysroot" || flag == "-isystem" - || flag == "--sysroot") { - skipNext = true; - continue; - } - - if (flag.endsWith(fileName)) - continue; - - if (flag == "-I" || flag == "-isystem") - includePath = true; - - filtered.push_back(flag); - } - - return filtered; -} - -static CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projectFile, - const QJsonObject &object, - const QString &workingDir, - const Utils::FileName &fileName) +QStringList jsonObjectFlags(const QJsonObject &object) { QStringList flags; const QJsonArray arguments = object["arguments"].toArray(); @@ -163,21 +78,234 @@ static CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projec flags.append(arg.toString()); } - flags = filteredFlags(flags, fileName.fileName(), workingDir); + return flags; +} + +bool isGccCompiler(const QString &compilerName) +{ + return compilerName.contains("gcc") || compilerName.contains("g++"); +} + +Core::Id getCompilerId(QString compilerName) +{ + 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; + + // Default is clang + return ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID; +} + +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; + }); +} + +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); +} + +ToolChain *toolchainFromFlags(const Kit *kit, const QStringList &flags, const Core::Id &language) +{ + if (flags.empty()) + return ToolChainKitInformation::toolChain(kit, language); + + // 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; + } + + toolchain = ToolChainKitInformation::toolChain(kit, language); + qWarning() << QCoreApplication::translate("CompilationDatabaseProject", + "No matching toolchain found, use the default."); + return toolchain; +} + +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 fileName; +} + +void addDriverModeFlagIfNeeded(const ToolChain *toolchain, QStringList &flags) +{ + 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++"); + } +} + +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()}); + rpp.setHeaderPaths(headerPaths); + rpp.setMacros(macros); - CppTools::RawProjectPartFlags cxxProjectFlags; - cxxProjectFlags.commandLineFlags = flags; - rpp.setFlagsForCxx(cxxProjectFlags); + 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(projectDirectory()); + root->addNode(std::make_unique( + projectFile, + FileType::Project, + false)); + auto headers = std::make_unique( + Utils::FileName::fromString("Headers"), 0); + auto sources = std::make_unique( + 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(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(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; + m_kit.reset(KitManager::defaultKit()->clone()); - ProjectExplorer::Kit *kit = target->kit(); - if (!kit) - return; + emitParsingStarted(); - auto toolchains = ProjectExplorer::ToolChainKitInformation::toolChains(kit); - if (toolchains.isEmpty()) - return; - - emitParsingStarted(); - - const QFuture 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(projectDirectory()); - root->addNode(std::make_unique( - projectFile, - ProjectExplorer::FileType::Project, - false)); - auto headers = std::make_unique( - Utils::FileName::fromString("Headers"), 0); - auto sources = std::make_unique( - 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( - 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); + const QFuture 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 2ba01adbaac..26fabebe472 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 m_parserWatcher; std::unique_ptr m_cppCodeModelUpdater; + std::unique_ptr m_kit; }; class CompilationDatabaseEditorFactory : public TextEditor::TextEditorFactory diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.pro index a815c0db8eb..e7efefb47ea 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 e21dc9e1230..4cbe2af2846 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 1fe0d6d032e..77359adc70d 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 #include @@ -52,5 +53,14 @@ void CompilationDatabaseProjectManagerPlugin::extensionsInitialized() { } +QList CompilationDatabaseProjectManagerPlugin::createTestObjects() const +{ + QList 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 a89db25e37b..37c9c1db1d8 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 createTestObjects() const final; + CompilationDatabaseEditorFactory factory; }; diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp new file mode 100644 index 00000000000..72a1b6ac74a --- /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 +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ProjectExplorer; + +namespace CompilationDatabaseProjectManager { + +CompilationDatabaseTests::CompilationDatabaseTests(QObject *parent) + : QObject(parent) +{} + +CompilationDatabaseTests::~CompilationDatabaseTests() = default; + +void CompilationDatabaseTests::initTestCase() +{ + const QList 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(":/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 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("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 00000000000..f508a44013a --- /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 +#include + +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 m_tmpDir; +}; + +} // namespace CompilationDatabaseProjectManager diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.qrc b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.qrc new file mode 100644 index 00000000000..571437b4c96 --- /dev/null +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.qrc @@ -0,0 +1,6 @@ + + + database_samples/llvm/compile_commands.json + database_samples/qtc/compile_commands.json + + diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri new file mode 100644 index 00000000000..c1b3188b4cf --- /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 00000000000..30ad52160ed --- /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 +#include + +#include +#include + +#include +#include + +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 includePathType; + Utils::optional 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 00000000000..5e123a8c271 --- /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 + +#include + +namespace ProjectExplorer { +class HeaderPath; +class Macro; +} + +namespace CompilationDatabaseProjectManager { + +void filteredFlags(const QString &fileName, + const QString &workingDir, + QStringList &flags, + QVector &headerPaths, + QVector ¯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 00000000000..051439c9608 --- /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 00000000000..d75e8d9654e --- /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 diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 3c88cce384f..4857e20c2ed 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -76,13 +76,6 @@ QStringList CompilerOptionsBuilder::build(CppTools::ProjectFile::Kind fileKind, addTargetTriple(); addExtraCodeModelFlags(); - if (m_projectPart.toolchainType - == ProjectExplorer::Constants::COMPILATION_DATABASE_TOOLCHAIN_TYPEID) { - addHeaderPathOptions(); - insertWrappedQtHeaders(); - return options(); - } - updateLanguageOption(fileKind); addOptionsForLanguage(/*checkForBorlandExtensions*/ true); enableExceptions(); diff --git a/src/plugins/cpptools/cppprojectinfogenerator.cpp b/src/plugins/cpptools/cppprojectinfogenerator.cpp index 0eb1af9c6e7..9fae0db0179 100644 --- a/src/plugins/cpptools/cppprojectinfogenerator.cpp +++ b/src/plugins/cpptools/cppprojectinfogenerator.cpp @@ -165,9 +165,6 @@ ProjectPart::Ptr ProjectInfoGenerator::createProjectPart( part->warningFlags = flags.warningFlags; part->languageExtensions = flags.languageExtensions; - if (part->toolchainType == ProjectExplorer::Constants::COMPILATION_DATABASE_TOOLCHAIN_TYPEID) - part->extraCodeModelFlags = flags.commandLineFlags; - // Toolchain macros and language version if (tcInfo.macroInspectionRunner) { auto macroInspectionReport = tcInfo.macroInspectionRunner(flags.commandLineFlags); diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 5fcb7f95f19..61f1429fbe9 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -163,7 +163,6 @@ const char MINGW_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Mingw"; const char MSVC_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Msvc"; const char CLANG_CL_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.ClangCl"; const char CUSTOM_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Custom"; -const char COMPILATION_DATABASE_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Empty"; // Default directory to run custom (build) commands in. const char DEFAULT_WORKING_DIR[] = "%{buildDir}"; diff --git a/tests/unit/unittest/compilationdatabaseutils-test.cpp b/tests/unit/unittest/compilationdatabaseutils-test.cpp new file mode 100644 index 00000000000..20a27b42354 --- /dev/null +++ b/tests/unit/unittest/compilationdatabaseutils-test.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** 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 "googletest.h" + +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace CompilationDatabaseProjectManager; + +namespace { + +class CompilationDatabaseUtils : public ::testing::Test +{ +protected: + HeaderPaths headerPaths; + Macros macros; + CppTools::ProjectFile::Kind fileKind = CppTools::ProjectFile::Unclassified; + QStringList flags; + QString fileName; + QString workingDir; +}; + +TEST_F(CompilationDatabaseUtils, FilterEmptyFlags) +{ + filteredFlags(fileName, workingDir, flags, headerPaths, macros, fileKind); + + ASSERT_THAT(flags.isEmpty(), true); +} + +TEST_F(CompilationDatabaseUtils, FilterArguments) +{ + fileName = "compileroptionsbuilder.cpp"; + workingDir = "C:/build-qtcreator-MinGW_32bit-Debug"; + flags = QStringList { + "clang++", + "-c", + "-m32", + "-target", + "i686-w64-mingw32", + "-std=gnu++14", + "-fcxx-exceptions", + "-fexceptions", + "-DUNICODE", + "-DRELATIVE_PLUGIN_PATH=\"../lib/qtcreator/plugins\"", + "-DQT_CREATOR", + "-fPIC", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include", + "-I", + "C:\\Qt\\5.9.2\\mingw53_32\\include\\QtWidgets", + "-x", + "c++", + "C:\\qt-creator\\src\\plugins\\cpptools\\compileroptionsbuilder.cpp" + }; + + filteredFlags(fileName, workingDir, flags, headerPaths, macros, fileKind); + + ASSERT_THAT(flags, Eq(QStringList{"-m32", + "-target", + "i686-w64-mingw32", + "-std=gnu++14", + "-fcxx-exceptions", + "-fexceptions"})); + ASSERT_THAT(headerPaths, Eq(HeaderPaths{ + {"C:\\Qt\\5.9.2\\mingw53_32\\include", HeaderPathType::User}, + {"C:\\Qt\\5.9.2\\mingw53_32\\include\\QtWidgets", HeaderPathType::User} + })); + ASSERT_THAT(macros, Eq(Macros{ + {"UNICODE", "1"}, + {"RELATIVE_PLUGIN_PATH", "\"../lib/qtcreator/plugins\""}, + {"QT_CREATOR", "1"} + })); + ASSERT_THAT(fileKind, CppTools::ProjectFile::Kind::CXXSource); +} + +static QString kCmakeCommand = "C:\\PROGRA~2\\MICROS~2\\2017\\COMMUN~1\\VC\\Tools\\MSVC\\1415~1.267\\bin\\HostX64\\x64\\cl.exe " + "/nologo " + "/TP " + "-DUNICODE " + "-D_HAS_EXCEPTIONS=0 " + "-Itools\\clang\\lib\\Sema " + "/DWIN32 " + "/D_WINDOWS " + "/Zc:inline " + "/Zc:strictStrings " + "/Oi " + "/Zc:rvalueCast " + "/W4 " + "-wd4141 " + "-wd4146 " + "/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"; + +TEST_F(CompilationDatabaseUtils, SplitFlags) +{ + flags = splitCommandLine(kCmakeCommand); + + ASSERT_THAT(flags.size(), 27); +} + +TEST_F(CompilationDatabaseUtils, SplitFlagsWithEscapedQuotes) +{ + flags = splitCommandLine("-DRC_FILE_VERSION=\\\"7.0.0\\\" " + "-DRELATIVE_PLUGIN_PATH=\"../lib/qtcreator/plugins\""); + + ASSERT_THAT(flags.size(), 2); +} + +TEST_F(CompilationDatabaseUtils, FilterCommand) +{ + fileName = "SemaCodeComplete.cpp"; + workingDir = "C:/build-qt_llvm-msvc2017_64bit-Debug"; + flags = splitCommandLine(kCmakeCommand); + + filteredFlags(fileName, workingDir, flags, headerPaths, macros, fileKind); + + ASSERT_THAT(flags, Eq(QStringList{"/Zc:inline", + "/Zc:strictStrings", + "/Zc:rvalueCast", + "/Zi"})); + ASSERT_THAT(headerPaths, Eq(HeaderPaths{ + {"tools\\clang\\lib\\Sema", HeaderPathType::User} + })); + ASSERT_THAT(macros, Eq(Macros{ + {"UNICODE", "1"}, + {"_HAS_EXCEPTIONS", "0"}, + {"WIN32", "1"}, + {"_WINDOWS", "1"} + })); + ASSERT_THAT(fileKind, CppTools::ProjectFile::Kind::CXXSource); +} + +} diff --git a/tests/unit/unittest/creator_dependency.pri b/tests/unit/unittest/creator_dependency.pri index 27cd5868539..eff01179a2e 100644 --- a/tests/unit/unittest/creator_dependency.pri +++ b/tests/unit/unittest/creator_dependency.pri @@ -13,6 +13,7 @@ include($$PWD/../../../src/tools/clangpchmanagerbackend/source/clangpchmanagerba include($$PWD/../../../src/plugins/clangrefactoring/clangrefactoring-source.pri) include($$PWD/../../../src/plugins/clangpchmanager/clangpchmanager-source.pri) include($$PWD/../../../src/plugins/cpptools/cpptoolsunittestfiles.pri) +include($$PWD/../../../src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri) include(cplusplus.pri) !isEmpty(LLVM_VERSION) { include($$PWD/../../../src/shared/clang/clang_defines.pri) diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index e5d56d8b044..f4cdc4c495a 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -104,7 +104,8 @@ SOURCES += \ taskscheduler-test.cpp \ compileroptionsbuilder-test.cpp \ usedmacroandsourcestorage-test.cpp \ - pchtaskgenerator-test.cpp + pchtaskgenerator-test.cpp \ + compilationdatabaseutils-test.cpp !isEmpty(LIBCLANG_LIBS) { SOURCES += \