diff options
author | Marco Bubke <marco.bubke@qt.io> | 2017-02-07 15:00:38 +0100 |
---|---|---|
committer | Marco Bubke <marco.bubke@qt.io> | 2017-09-14 15:23:56 +0000 |
commit | b6e12f4a1c2a8dbc7a672f0cf42ea76ece71b10d (patch) | |
tree | 81550e7fce5053ecb1ead60ddf59534d44517a87 | |
parent | 3adb71d45ebebd8c8fc2ec6beeb7a5ee67d64e4e (diff) | |
download | qt-creator-b6e12f4a1c2a8dbc7a672f0cf42ea76ece71b10d.tar.gz |
Convert macros from plain QByteArray to a vector of structs
The old code model expected the macros as C++ formatted text
("#define Foo 42) but newer targets like the Clang codemodel expect key
value arguments like "-DFoo=42". So instead of parsing the text again and
again we use an abstract data description.
Task-number: QTCREATORBUG-17915
Change-Id: I0179fd13c48a581e91ee79bba9d42d501c26f19f
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
60 files changed, 1145 insertions, 421 deletions
diff --git a/src/plugins/autotest/qtest/qttestparser.cpp b/src/plugins/autotest/qtest/qttestparser.cpp index 93376cba7c..476422295d 100644 --- a/src/plugins/autotest/qtest/qttestparser.cpp +++ b/src/plugins/autotest/qtest/qttestparser.cpp @@ -86,7 +86,7 @@ static bool qtTestLibDefined(const QString &fileName) const QList<CppTools::ProjectPart::Ptr> parts = CppTools::CppModelManager::instance()->projectPart(fileName); if (parts.size() > 0) - return parts.at(0)->projectDefines.contains("#define QT_TESTLIB_LIB"); + return parts.at(0)->projectMacros.contains({"QT_TESTLIB_LIB"}); return false; } diff --git a/src/plugins/autotest/quick/quicktestparser.cpp b/src/plugins/autotest/quick/quicktestparser.cpp index 0d897fe04e..46566952ff 100644 --- a/src/plugins/autotest/quick/quicktestparser.cpp +++ b/src/plugins/autotest/quick/quicktestparser.cpp @@ -89,20 +89,21 @@ static bool includesQtQuickTest(const CPlusPlus::Document::Ptr &doc, static QString quickTestSrcDir(const CppTools::CppModelManager *cppMM, const QString &fileName) { - static const QByteArray qtsd(" QUICK_TEST_SOURCE_DIR "); const QList<CppTools::ProjectPart::Ptr> parts = cppMM->projectPart(fileName); if (parts.size() > 0) { - QByteArray projDefines(parts.at(0)->projectDefines); - for (const QByteArray &line : projDefines.split('\n')) { - if (line.contains(qtsd)) { - QByteArray result = line.mid(line.indexOf(qtsd) + qtsd.length()); - if (result.startsWith('"')) - result.remove(result.length() - 1, 1).remove(0, 1); - if (result.startsWith("\\\"")) - result.remove(result.length() - 2, 2).remove(0, 2); - return QLatin1String(result); - } - } + const ProjectExplorer::Macros ¯os = parts.at(0)->projectMacros; + auto found = std::find_if( + macros.begin(), + macros.end(), + [] (const ProjectExplorer::Macro ¯o) { return macro.key == "QUICK_TEST_SOURCE_DIR"; }); + if (found != macros.end()) { + QByteArray result = found->value; + if (result.startsWith('"')) + result.remove(result.length() - 1, 1).remove(0, 1); + if (result.startsWith("\\\"")) + result.remove(result.length() - 2, 2).remove(0, 2); + return QLatin1String(result); + } } return QString(); } diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index a24993187e..01105d8497 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -298,7 +298,7 @@ void AutotoolsProject::updateCppCodeModel() ? target->activeBuildConfiguration()->buildDirectory().toString() : QString(); rpp.setIncludePaths(filterIncludes(absSrc, absBuild, m_makefileParserThread->includePaths())); - rpp.setDefines(m_makefileParserThread->defines()); + rpp.setMacros(m_makefileParserThread->macros()); rpp.setFiles(m_files); m_cppCodeModelUpdater->update({this, cToolChain, cxxToolChain, k, {rpp}}); diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp index 74e29054ec..9c3e32216b 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp +++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp @@ -108,9 +108,9 @@ QStringList MakefileParser::includePaths() const return m_includePaths; } -QByteArray MakefileParser::defines() const +ProjectExplorer::Macros MakefileParser::macros() const { - return m_defines; + return m_macros; } QStringList MakefileParser::cflags() const @@ -449,11 +449,7 @@ bool MakefileParser::maybeParseDefine(const QString &term) { if (term.startsWith(QLatin1String("-D"))) { QString def = term.mid(2); // remove the "-D" - QByteArray data = def.toUtf8(); - int pos = data.indexOf('='); - if (pos >= 0) - data[pos] = ' '; - m_defines += (QByteArray("#define ") + data + '\n'); + m_macros += ProjectExplorer::Macro::fromKeyValue(def); return true; } return false; diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.h b/src/plugins/autotoolsprojectmanager/makefileparser.h index beaa1a57db..d6387bc29c 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.h +++ b/src/plugins/autotoolsprojectmanager/makefileparser.h @@ -27,10 +27,13 @@ #pragma once +#include <projectexplorer/projectmacro.h> + #include <QMutex> #include <QStringList> #include <QTextStream> #include <QObject> +#include <QVector> QT_FORWARD_DECLARE_CLASS(QDir) @@ -49,6 +52,8 @@ class MakefileParser : public QObject { Q_OBJECT + using Macros = ProjectExplorer::Macros; + public: /** * @param makefile Filename including path of the autotools @@ -98,7 +103,7 @@ public: * #define X12_HAS_DEPRECATED * @endcode */ - QByteArray defines() const; + Macros macros() const; /** * @return List of compiler flags for C. @@ -267,7 +272,7 @@ private: QStringList m_sources; ///< Return value for MakefileParser::sources() QStringList m_makefiles; ///< Return value for MakefileParser::makefiles() QStringList m_includePaths; ///< Return value for MakefileParser::includePaths() - QByteArray m_defines; ///< Return value for MakefileParser::defines() + Macros m_macros; ///< Return value for MakefileParser::macros() QStringList m_cflags; ///< Return value for MakefileParser::cflags() QStringList m_cxxflags; ///< Return value for MakefileParser::cxxflags() QStringList m_cppflags; ///< The cpp flags, which will be part of both cflags and cxxflags diff --git a/src/plugins/autotoolsprojectmanager/makefileparserthread.cpp b/src/plugins/autotoolsprojectmanager/makefileparserthread.cpp index cfe7e9757f..a33d39599b 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparserthread.cpp +++ b/src/plugins/autotoolsprojectmanager/makefileparserthread.cpp @@ -61,10 +61,10 @@ QStringList MakefileParserThread::includePaths() const return m_includePaths; } -QByteArray MakefileParserThread::defines() const +ProjectExplorer::Macros MakefileParserThread::macros() const { QMutexLocker locker(&m_mutex); - return m_defines; + return m_macros; } QStringList MakefileParserThread::cflags() const @@ -109,7 +109,7 @@ void MakefileParserThread::run() m_sources = m_parser.sources(); m_makefiles = m_parser.makefiles(); m_includePaths = m_parser.includePaths(); - m_defines = m_parser.defines(); + m_macros = m_parser.macros(); m_cflags = m_parser.cflags(); m_cxxflags = m_parser.cxxflags(); } diff --git a/src/plugins/autotoolsprojectmanager/makefileparserthread.h b/src/plugins/autotoolsprojectmanager/makefileparserthread.h index 94b965d133..3e16bdccc4 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparserthread.h +++ b/src/plugins/autotoolsprojectmanager/makefileparserthread.h @@ -29,9 +29,12 @@ #include "makefileparser.h" +#include <projectexplorer/projectmacro.h> + #include <QMutex> #include <QStringList> #include <QThread> +#include <QVector> namespace AutotoolsProjectManager { namespace Internal { @@ -47,6 +50,8 @@ class MakefileParserThread : public QThread { Q_OBJECT + using Macros = ProjectExplorer::Macros; + public: MakefileParserThread(const QString &makefile); @@ -82,10 +87,10 @@ public: QStringList includePaths() const; /** - * @return Concatenated defines. Should be invoked, after the signal + * @return Concatenated macros. Should be invoked, after the signal * finished() has been emitted. */ - QByteArray defines() const; + Macros macros() const; /** * @return List of compiler flags for C. Should be invoked, after the signal @@ -134,7 +139,7 @@ private: QStringList m_sources; ///< Return value for MakefileParserThread::sources() QStringList m_makefiles; ///< Return value for MakefileParserThread::makefiles() QStringList m_includePaths; ///< Return value for MakefileParserThread::includePaths() - QByteArray m_defines; ///< Return value for MakefileParserThread::defines() + Macros m_macros; ///< Return value for MakefileParserThread::macros() QStringList m_cflags; ///< Return value for MakefileParserThread::cflags() QStringList m_cxxflags; ///< Return value for MakefileParserThread::cxxflags() }; diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index db09aea672..8a9736a15a 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -512,7 +512,7 @@ bool OpenEditorAtCursorPosition::waitUntil(const std::function<bool ()> &conditi } CppTools::ProjectPart::Ptr createProjectPart(const QStringList &files, - const QString &defines) + const ProjectExplorer::Macros ¯os) { using namespace CppTools; @@ -521,19 +521,19 @@ CppTools::ProjectPart::Ptr createProjectPart(const QStringList &files, foreach (const QString &file, files) projectPart->files.append(ProjectFile(file, ProjectFile::classify(file))); projectPart->qtVersion = ProjectPart::NoQt; - projectPart->projectDefines = defines.toUtf8(); + projectPart->projectMacros = macros; return projectPart; } CppTools::ProjectInfo createProjectInfo(ProjectExplorer::Project *project, const QStringList &files, - const QString &defines) + const ProjectExplorer::Macros ¯os) { using namespace CppTools; QTC_ASSERT(project, return ProjectInfo()); - const CppTools::ProjectPart::Ptr projectPart = createProjectPart(files, defines); + const CppTools::ProjectPart::Ptr projectPart = createProjectPart(files, macros); ProjectInfo projectInfo = ProjectInfo(project); projectInfo.appendProjectPart(projectPart); return projectInfo; @@ -543,11 +543,11 @@ class ProjectLoader { public: ProjectLoader(const QStringList &projectFiles, - const QString &projectDefines, + const ProjectExplorer::Macros &projectMacros, bool testOnlyForCleanedProjects = false) : m_project(0) , m_projectFiles(projectFiles) - , m_projectDefines(projectDefines) + , m_projectMacros(projectMacros) , m_helper(0, testOnlyForCleanedProjects) { } @@ -557,17 +557,17 @@ public: m_project = m_helper.createProject(QLatin1String("testProject")); const CppTools::ProjectInfo projectInfo = createProjectInfo(m_project, m_projectFiles, - m_projectDefines); + m_projectMacros); const QSet<QString> filesIndexedAfterLoading = m_helper.updateProjectInfo(projectInfo); return m_projectFiles.size() == filesIndexedAfterLoading.size(); } - bool updateProject(const QString &updatedProjectDefines) + bool updateProject(const ProjectExplorer::Macros &updatedProjectMacros) { QTC_ASSERT(m_project, return false); const CppTools::ProjectInfo updatedProjectInfo = createProjectInfo(m_project, m_projectFiles, - updatedProjectDefines); + updatedProjectMacros); return updateProjectInfo(updatedProjectInfo); } @@ -581,7 +581,7 @@ private: ProjectExplorer::Project *m_project; QStringList m_projectFiles; - QString m_projectDefines; + ProjectExplorer::Macros m_projectMacros; CppTools::Tests::ModelManagerTestHelper m_helper; }; @@ -865,8 +865,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCode() const TestDocument testDocument("completionWithProject.cpp"); QVERIFY(testDocument.isCreatedAndHasValidCursorPosition()); - ProjectLoader projectLoader(QStringList(testDocument.filePath), - _("#define PROJECT_CONFIGURATION_1\n")); + ProjectLoader projectLoader(QStringList(testDocument.filePath), {{"PROJECT_CONFIGURATION_1"}}); QVERIFY(projectLoader.load()); OpenEditorAtCursorPosition openEditor(testDocument); @@ -891,7 +890,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProje { // Check completion with project configuration 1 ProjectLoader projectLoader(QStringList(testDocument.filePath), - _("#define PROJECT_CONFIGURATION_1\n"), + {{"PROJECT_CONFIGURATION_1"}}, /* testOnlyForCleanedProjects= */ true); QVERIFY(projectLoader.load()); openEditor.waitUntilProjectPartChanged(QLatin1String("myproject.project")); @@ -902,7 +901,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProje QVERIFY(!hasItem(proposal, "projectConfiguration2")); // Check completion with project configuration 2 - QVERIFY(projectLoader.updateProject(_("#define PROJECT_CONFIGURATION_2\n"))); + QVERIFY(projectLoader.updateProject({{"PROJECT_CONFIGURATION_2"}})); proposal = completionResults(openEditor.editor()); QVERIFY(!hasItem(proposal, "projectConfiguration1")); diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp index ad6363bc0e..0cfafa08e1 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp @@ -224,8 +224,8 @@ public: optionsBuilder.addDefineToAvoidIncludingGccOrMinGwIntrinsics(); const Core::Id type = projectPart.toolchainType; if (type != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) - optionsBuilder.addDefines(projectPart.toolchainDefines); - optionsBuilder.addDefines(projectPart.projectDefines); + optionsBuilder.addMacros(projectPart.toolChainMacros); + optionsBuilder.addMacros(projectPart.projectMacros); optionsBuilder.undefineClangVersionMacrosForMsvc(); optionsBuilder.undefineCppLanguageFeatureMacrosForMsvc2015(); optionsBuilder.addHeaderPathOptions(); diff --git a/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp b/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp index 591768f515..96cf7d138d 100644 --- a/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp +++ b/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp @@ -30,6 +30,7 @@ #include <utils/fileutils.h> #include <utils/stringutils.h> #include <utils/algorithm.h> +#include <projectexplorer/projectmacro.h> #include <projectexplorer/projectnodes.h> #include <QLoggingCategory> @@ -71,8 +72,9 @@ void CMakeCbpParser::sortFiles() qCDebug(log) << "# Pre Dump #"; qCDebug(log) << "###############"; foreach (const CMakeBuildTarget &target, m_buildTargets) - qCDebug(log) << target.title << target.sourceDirectory << - target.includeFiles << target.defines << target.files << "\n"; + qCDebug(log) << target.title << target.sourceDirectory << target.includeFiles + << ProjectExplorer::Macro::toByteArray(target.macros) + << target.files << "\n"; // find a good build target to fall back int fallbackIndex = 0; @@ -153,7 +155,9 @@ void CMakeCbpParser::sortFiles() qCDebug(log) << "# After Dump #"; qCDebug(log) << "###############"; foreach (const CMakeBuildTarget &target, m_buildTargets) - qCDebug(log) << target.title << target.sourceDirectory << target.includeFiles << target.defines << target.files << "\n"; + qCDebug(log) << target.title << target.sourceDirectory << target.includeFiles + << ProjectExplorer::Macro::toByteArray(target.macros) + << target.files << "\n"; } bool CMakeCbpParser::parseCbpFile(CMakeTool::PathMapper mapper, const FileName &fileName, @@ -397,12 +401,8 @@ void CMakeCbpParser::parseAdd() m_buildTarget.compilerOptions.append(compilerOption); int macroNameIndex = compilerOption.indexOf("-D") + 2; if (macroNameIndex != 1) { - int assignIndex = compilerOption.indexOf('=', macroNameIndex); - if (assignIndex != -1) - compilerOption[assignIndex] = ' '; - m_buildTarget.defines.append("#define "); - m_buildTarget.defines.append(compilerOption.mid(macroNameIndex).toUtf8()); - m_buildTarget.defines.append('\n'); + const QString keyValue = compilerOption.mid(macroNameIndex); + m_buildTarget.macros.append(ProjectExplorer::Macro::fromKeyValue(keyValue)); } } diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 42226ec6bd..211f564cc8 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -559,7 +559,7 @@ void CMakeBuildTarget::clear() targetType = UtilityType; includeFiles.clear(); compilerOptions.clear(); - defines.clear(); + macros.clear(); files.clear(); } diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index cb035dddf8..8083cf9dec 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -30,6 +30,7 @@ #include "treescanner.h" #include <projectexplorer/extracompiler.h> +#include <projectexplorer/projectmacro.h> #include <projectexplorer/project.h> #include <utils/fileutils.h> @@ -72,7 +73,7 @@ public: // code model QList<Utils::FileName> includeFiles; QStringList compilerOptions; - QByteArray defines; + ProjectExplorer::Macros macros; QList<Utils::FileName> files; void clear(); diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp index fce7288e07..1b97b6b838 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.cpp +++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp @@ -45,6 +45,8 @@ #include <utils/qtcassert.h> #include <utils/qtcprocess.h> +#include <QVector> + using namespace ProjectExplorer; using namespace Utils; @@ -325,14 +327,6 @@ void ServerModeReader::updateCodeModel(CppTools::RawProjectParts &rpps) int counter = 0; for (const FileGroup *fg : Utils::asConst(m_fileGroups)) { ++counter; - const QString defineArg - = transform(fg->defines, [](const QString &s) -> QString { - QString result = QString::fromLatin1("#define ") + s; - int assignIndex = result.indexOf('='); - if (assignIndex != -1) - result[assignIndex] = ' '; - return result; - }).join('\n'); const QStringList flags = QtcProcess::splitArgs(fg->compileFlags); const QStringList includes = transform(fg->includePaths, [](const IncludePath *ip) { return ip->path.toString(); }); @@ -340,7 +334,7 @@ void ServerModeReader::updateCodeModel(CppTools::RawProjectParts &rpps) rpp.setProjectFileLocation(fg->target->sourceDirectory.toString() + "/CMakeLists.txt"); rpp.setBuildSystemTarget(fg->target->name); rpp.setDisplayName(fg->target->name + QString::number(counter)); - rpp.setDefines(defineArg.toUtf8()); + rpp.setMacros(fg->macros); rpp.setIncludePaths(includes); CppTools::RawProjectPartFlags cProjectFlags; @@ -523,7 +517,9 @@ ServerModeReader::FileGroup *ServerModeReader::extractFileGroupData(const QVaria auto fileGroup = new FileGroup; fileGroup->target = t; fileGroup->compileFlags = data.value("compileFlags").toString(); - fileGroup->defines = data.value("defines").toStringList(); + fileGroup->macros = Utils::transform<QVector>(data.value("defines").toStringList(), [](const QString &s) { + return ProjectExplorer::Macro::fromKeyValue(s); + }); fileGroup->includePaths = transform(data.value("includePath").toList(), [](const QVariant &i) -> IncludePath* { const QVariantMap iData = i.toMap(); @@ -662,7 +658,7 @@ void ServerModeReader::fixTarget(ServerModeReader::Target *target) const for (const FileGroup *group : Utils::asConst(target->fileGroups)) { if (group->includePaths.isEmpty() && group->compileFlags.isEmpty() - && group->defines.isEmpty()) + && group->macros.isEmpty()) continue; const FileGroup *fallback = languageFallbacks.value(group->language); @@ -688,13 +684,13 @@ void ServerModeReader::fixTarget(ServerModeReader::Target *target) const (*it)->language = fallback->language.isEmpty() ? "CXX" : fallback->language; if (*it == fallback - || !(*it)->includePaths.isEmpty() || !(*it)->defines.isEmpty() + || !(*it)->includePaths.isEmpty() || !(*it)->macros.isEmpty() || !(*it)->compileFlags.isEmpty()) continue; for (const IncludePath *ip : fallback->includePaths) (*it)->includePaths.append(new IncludePath(*ip)); - (*it)->defines = fallback->defines; + (*it)->macros = fallback->macros; (*it)->compileFlags = fallback->compileFlags; } } diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h index 8dc4f5e353..f861483227 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.h +++ b/src/plugins/cmakeprojectmanager/servermodereader.h @@ -86,7 +86,7 @@ private: Target *target = nullptr; QString compileFlags; - QStringList defines; + ProjectExplorer::Macros macros; QList<IncludePath *> includePaths; QString language; QList<Utils::FileName> sources; diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.cpp b/src/plugins/cmakeprojectmanager/tealeafreader.cpp index ca3794c89d..69b170d38d 100644 --- a/src/plugins/cmakeprojectmanager/tealeafreader.cpp +++ b/src/plugins/cmakeprojectmanager/tealeafreader.cpp @@ -384,7 +384,7 @@ void TeaLeafReader::updateCodeModel(CppTools::RawProjectParts &rpps) cxxProjectFlags.commandLineFlags = cxxflags; rpp.setFlagsForCxx(cxxProjectFlags); - rpp.setDefines(cbt.defines); + rpp.setMacros(cbt.macros); rpp.setDisplayName(cbt.title); rpp.setFiles(transform(cbt.files, [](const FileName &fn) { return fn.toString(); })); diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp index 6283d43d49..51ff075845 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp @@ -35,6 +35,7 @@ #include <cpptools/cppmodelmanager.h> #include <cpptools/cpptoolsbridge.h> #include <cpptools/cppworkingcopy.h> +#include <projectexplorer/projectmacro.h> #include <projectexplorer/project.h> #include <cplusplus/CppDocument.h> @@ -49,6 +50,7 @@ #include <QSortFilterProxyModel> #include <algorithm> +#include <numeric> using namespace CPlusPlus; using namespace CppTools; @@ -756,7 +758,7 @@ class MacrosModel : public QAbstractListModel Q_OBJECT public: MacrosModel(QObject *parent); - void configure(const QList<Macro> ¯os); + void configure(const QList<CPlusPlus::Macro> ¯os); void clear(); enum Columns { LineNumberColumn, MacroColumn, ColumnCount }; @@ -767,14 +769,14 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const; private: - QList<Macro> m_macros; + QList<CPlusPlus::Macro> m_macros; }; MacrosModel::MacrosModel(QObject *parent) : QAbstractListModel(parent) { } -void MacrosModel::configure(const QList<Macro> ¯os) +void MacrosModel::configure(const QList<CPlusPlus::Macro> ¯os) { emit layoutAboutToBeChanged(); m_macros = macros; @@ -802,7 +804,7 @@ QVariant MacrosModel::data(const QModelIndex &index, int role) const { const int column = index.column(); if (role == Qt::DisplayRole || (role == Qt::ToolTipRole && column == MacroColumn)) { - const Macro macro = m_macros.at(index.row()); + const CPlusPlus::Macro macro = m_macros.at(index.row()); if (column == LineNumberColumn) return macro.line(); else if (column == MacroColumn) @@ -1614,7 +1616,8 @@ void CppCodeModelInspectorDialog::refresh() } // Merged entities - dumper.dumpMergedEntities(cmmi->headerPaths(), cmmi->definedMacros()); + dumper.dumpMergedEntities(cmmi->headerPaths(), + ProjectExplorer::Macro::toByteArray(cmmi->definedMacros())); } enum DocumentTabs { @@ -1758,6 +1761,15 @@ void CppCodeModelInspectorDialog::clearProjectPartData() partTabName(ProjectPartPrecompiledHeadersTab)); } +static int defineCount(const ProjectExplorer::Macros ¯os) +{ + using ProjectExplorer::Macro; + return int(std::count_if( + macros.begin(), + macros.end(), + [](const Macro ¯o) { return macro.type == ProjectExplorer::MacroType::Define; })); +} + void CppCodeModelInspectorDialog::updateProjectPartData(const ProjectPart::Ptr &part) { QTC_ASSERT(part, return); @@ -1802,16 +1814,10 @@ void CppCodeModelInspectorDialog::updateProjectPartData(const ProjectPart::Ptr & m_ui->projectPartTab->setTabText(ProjectPartFilesTab, partTabName(ProjectPartFilesTab, part->files.size())); - // Defines - const QList<QByteArray> defineLines = part->toolchainDefines.split('\n') - + part->projectDefines.split('\n'); - int numberOfDefines = 0; - foreach (const QByteArray &line, defineLines) { - if (line.startsWith("#define ")) - ++numberOfDefines; - } - m_ui->partToolchainDefinesEdit->setPlainText(QString::fromUtf8(part->toolchainDefines)); - m_ui->partProjectDefinesEdit->setPlainText(QString::fromUtf8(part->projectDefines)); + int numberOfDefines = defineCount(part->toolChainMacros) + defineCount(part->projectMacros); + + m_ui->partToolchainDefinesEdit->setPlainText(QString::fromUtf8(ProjectExplorer::Macro::toByteArray(part->toolChainMacros))); + m_ui->partProjectDefinesEdit->setPlainText(QString::fromUtf8(ProjectExplorer::Macro::toByteArray(part->projectMacros))); m_ui->projectPartTab->setTabText(ProjectPartDefinesTab, partTabName(ProjectPartDefinesTab, numberOfDefines)); diff --git a/src/plugins/cpptools/builtineditordocumentparser.cpp b/src/plugins/cpptools/builtineditordocumentparser.cpp index ac3cf3ee2a..3fb407cb2d 100644 --- a/src/plugins/cpptools/builtineditordocumentparser.cpp +++ b/src/plugins/cpptools/builtineditordocumentparser.cpp @@ -26,6 +26,7 @@ #include "builtineditordocumentparser.h" #include "cppsourceprocessor.h" +#include <projectexplorer/projectmacro.h> #include <projectexplorer/projectexplorerconstants.h> #include <utils/qtcassert.h> @@ -91,9 +92,9 @@ void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &futur } if (const ProjectPart::Ptr part = baseState.projectPartInfo.projectPart) { - configFile += part->toolchainDefines; + configFile += ProjectExplorer::Macro::toByteArray(part->toolChainMacros); configFile += overwrittenToolchainDefines(*part.data()); - configFile += part->projectDefines; + configFile += ProjectExplorer::Macro::toByteArray(part->projectMacros); if (!part->projectConfigFile.isEmpty()) configFile += ProjectPart::readProjectConfigFile(part); headerPaths = part->headerPaths; diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 170279c9b8..079f76fc6a 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -48,44 +48,9 @@ void CompilerOptionsBuilder::add(const QString &option) m_options.append(option); } -struct Macro { - static Macro fromDefineDirective(const QByteArray &defineDirective); - QByteArray toDefineOption(const QByteArray &option) const; - - QByteArray name; - QByteArray value; -}; - -Macro Macro::fromDefineDirective(const QByteArray &defineDirective) +void CompilerOptionsBuilder::addDefine(const ProjectExplorer::Macro ¯o) { - const QByteArray str = defineDirective.mid(8); - const int spaceIdx = str.indexOf(' '); - const bool hasValue = spaceIdx != -1; - - Macro macro; - macro.name = str.left(hasValue ? spaceIdx : str.size()); - if (hasValue) - macro.value = str.mid(spaceIdx + 1); - - return macro; -} - -QByteArray Macro::toDefineOption(const QByteArray &option) const -{ - QByteArray result; - - result.append(option); - result.append(name); - result.append('='); - if (!value.isEmpty()) - result.append(value); - - return result; -} - -void CompilerOptionsBuilder::addDefine(const QByteArray &defineDirective) -{ - m_options.append(defineDirectiveToDefineOption(defineDirective)); + m_options.append(defineDirectiveToDefineOption(macro)); } void CompilerOptionsBuilder::addWordWidth() @@ -162,19 +127,19 @@ void CompilerOptionsBuilder::addPrecompiledHeaderOptions(PchUsage pchUsage) void CompilerOptionsBuilder::addToolchainAndProjectDefines() { - addDefines(m_projectPart.toolchainDefines); - addDefines(m_projectPart.projectDefines); + addMacros(m_projectPart.toolChainMacros); + addMacros(m_projectPart.projectMacros); } -void CompilerOptionsBuilder::addDefines(const QByteArray &defineDirectives) +void CompilerOptionsBuilder::addMacros(const ProjectExplorer::Macros ¯os) { QStringList result; - foreach (QByteArray def, defineDirectives.split('\n')) { - if (def.isEmpty() || excludeDefineDirective(def)) + for (const ProjectExplorer::Macro ¯o : macros) { + if (excludeDefineDirective(macro)) continue; - const QString defineOption = defineDirectiveToDefineOption(def); + const QString defineOption = defineDirectiveToDefineOption(macro); if (!result.contains(defineOption)) result.append(defineOption); } @@ -303,8 +268,8 @@ void CompilerOptionsBuilder::addDefineToAvoidIncludingGccOrMinGwIntrinsics() const Core::Id type = m_projectPart.toolchainType; if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID || type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID) { - addDefine("#define _X86INTRIN_H_INCLUDED"); - addDefine("#define BOOST_UUID_NO_SIMD"); + addDefine({"_X86INTRIN_H_INCLUDED"}); + addDefine({"BOOST_UUID_NO_SIMD"}); } } @@ -315,14 +280,10 @@ static QByteArray toMsCompatibilityVersionFormat(const QByteArray &mscFullVer) + mscFullVer.mid(2, 2); } -static QByteArray msCompatibilityVersionFromDefines(const QByteArray &defineDirectives) +static QByteArray msCompatibilityVersionFromDefines(const ProjectExplorer::Macros ¯os) { - foreach (QByteArray defineDirective, defineDirectives.split('\n')) { - if (defineDirective.isEmpty()) - continue; - - const Macro macro = Macro::fromDefineDirective(defineDirective); - if (macro.name == "_MSC_FULL_VER") + for (const ProjectExplorer::Macro ¯o : macros) { + if (macro.key == "_MSC_FULL_VER") return toMsCompatibilityVersionFormat(macro.value); } @@ -332,8 +293,8 @@ static QByteArray msCompatibilityVersionFromDefines(const QByteArray &defineDire void CompilerOptionsBuilder::addMsvcCompatibilityVersion() { if (m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) { - const QByteArray defines = m_projectPart.toolchainDefines + m_projectPart.projectDefines; - const QByteArray msvcVersion = msCompatibilityVersionFromDefines(defines); + const ProjectExplorer::Macros macros = m_projectPart.toolChainMacros + m_projectPart.projectMacros; + const QByteArray msvcVersion = msCompatibilityVersionFromDefines(macros); if (!msvcVersion.isEmpty()) { const QString option = QLatin1String("-fms-compatibility-version=") @@ -398,7 +359,7 @@ void CompilerOptionsBuilder::addDefineFloat128ForMingw() // CLANG-UPGRADE-CHECK: Workaround still needed? // https://llvm.org/bugs/show_bug.cgi?id=30685 if (m_projectPart.toolchainType == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) - addDefine("#define __float128 short"); + addDefine({"__float128", "short", ProjectExplorer::MacroType::Define}); } QString CompilerOptionsBuilder::includeDirOption() const @@ -406,12 +367,25 @@ QString CompilerOptionsBuilder::includeDirOption() const return QLatin1String("-I"); } -QString CompilerOptionsBuilder::defineDirectiveToDefineOption(const QByteArray &defineDirective) +QByteArray CompilerOptionsBuilder::macroOption(const ProjectExplorer::Macro ¯o) const +{ + switch (macro.type) { + case ProjectExplorer::MacroType::Define: return defineOption().toUtf8(); + case ProjectExplorer::MacroType::Undefine: return undefineOption().toUtf8(); + default: return QByteArray(); + } +} + +QByteArray CompilerOptionsBuilder::toDefineOption(const ProjectExplorer::Macro ¯o) const +{ + return macro.toKeyValue(macroOption(macro)); +} + +QString CompilerOptionsBuilder::defineDirectiveToDefineOption(const ProjectExplorer::Macro ¯o) const { - const Macro macro = Macro::fromDefineDirective(defineDirective); - const QByteArray option = macro.toDefineOption(defineOption().toLatin1()); + const QByteArray option = toDefineOption(macro); - return QString::fromLatin1(option); + return QString::fromUtf8(option); } QString CompilerOptionsBuilder::defineOption() const @@ -435,11 +409,11 @@ static bool isGccOrMinGwToolchain(const Core::Id &toolchainType) || toolchainType == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID; } -bool CompilerOptionsBuilder::excludeDefineDirective(const QByteArray &defineDirective) const +bool CompilerOptionsBuilder::excludeDefineDirective(const ProjectExplorer::Macro ¯o) const { // This is a quick fix for QTCREATORBUG-11501. // TODO: do a proper fix, see QTCREATORBUG-11709. - if (defineDirective.startsWith("#define __cplusplus")) + if (macro.key == "__cplusplus") return true; // gcc 4.9 has: @@ -449,7 +423,7 @@ bool CompilerOptionsBuilder::excludeDefineDirective(const QByteArray &defineDire // override clang's own (non-macro, it seems) definitions of the symbols on the left-hand // side. if (isGccOrMinGwToolchain(m_projectPart.toolchainType) - && defineDirective.contains("has_include")) { + && macro.key.contains("has_include")) { return true; } @@ -459,14 +433,14 @@ bool CompilerOptionsBuilder::excludeDefineDirective(const QByteArray &defineDire // __builtin_va_arg_pack, which clang does not support (yet), so avoid // including those. if (m_projectPart.toolchainType == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID - && defineDirective.startsWith("#define _FORTIFY_SOURCE")) { + && macro.key == "_FORTIFY_SOURCE") { return true; } // MinGW 6 supports some fancy asm output flags and uses them in an // intrinsics header pulled in by windows.h. Clang does not know them. if (m_projectPart.toolchainType == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID - && defineDirective.startsWith("#define __GCC_ASM_FLAG_OUTPUTS__")) { + && macro.key == "__GCC_ASM_FLAG_OUTPUTS__") { return true; } diff --git a/src/plugins/cpptools/compileroptionsbuilder.h b/src/plugins/cpptools/compileroptionsbuilder.h index 75b0c4f530..48c23b3e91 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.h +++ b/src/plugins/cpptools/compileroptionsbuilder.h @@ -46,7 +46,7 @@ public: // Add custom options void add(const QString &option); - void addDefine(const QByteArray &defineDirective); + void addDefine(const ProjectExplorer::Macro &marco); // Add options based on project part void addWordWidth(); @@ -55,7 +55,7 @@ public: void addHeaderPathOptions(); void addPrecompiledHeaderOptions(PchUsage pchUsage); void addToolchainAndProjectDefines(); - void addDefines(const QByteArray &defineDirectives); + void addMacros(const ProjectExplorer::Macros ¯os); virtual void addLanguageOption(ProjectFile::Kind fileKind); virtual void addOptionsForLanguage(bool checkForBorlandExtensions = true); @@ -67,7 +67,7 @@ public: void addDefineFloat128ForMingw(); protected: - virtual bool excludeDefineDirective(const QByteArray &defineDirective) const; + virtual bool excludeDefineDirective(const ProjectExplorer::Macro ¯o) const; virtual bool excludeHeaderPath(const QString &headerPath) const; virtual QString defineOption() const; @@ -78,7 +78,9 @@ protected: const ProjectPart m_projectPart; private: - QString defineDirectiveToDefineOption(const QByteArray &defineDirective); + QByteArray macroOption(const ProjectExplorer::Macro ¯o) const; + QByteArray toDefineOption(const ProjectExplorer::Macro ¯o) const; + QString defineDirectiveToDefineOption(const ProjectExplorer::Macro &marco) const; QStringList m_options; }; diff --git a/src/plugins/cpptools/cppcodemodelinspectordumper.cpp b/src/plugins/cpptools/cppcodemodelinspectordumper.cpp index 199f185d63..8797a2ba63 100644 --- a/src/plugins/cpptools/cppcodemodelinspectordumper.cpp +++ b/src/plugins/cpptools/cppcodemodelinspectordumper.cpp @@ -31,6 +31,7 @@ #include <app/app_version.h> #include <coreplugin/icore.h> #include <cpptools/cppprojectfile.h> +#include <projectexplorer/projectmacro.h> #include <projectexplorer/project.h> #include <utils/algorithm.h> #include <utils/temporarydirectory.h> @@ -495,15 +496,17 @@ void Dumper::dumpProjectInfos( const QList<ProjectInfo> &projectInfos) } } - if (!part->toolchainDefines.isEmpty()) { + if (!part->toolChainMacros.isEmpty()) { m_out << i3 << "Toolchain Defines:{{{4\n"; - const QList<QByteArray> defineLines = part->toolchainDefines.split('\n'); + const QList<QByteArray> defineLines = + ProjectExplorer::Macro::toByteArray(part->toolChainMacros).split('\n'); foreach (const QByteArray &defineLine, defineLines) m_out << i4 << defineLine << "\n"; } - if (!part->projectDefines.isEmpty()) { + if (!part->projectMacros.isEmpty()) { m_out << i3 << "Project Defines:{{{4\n"; - const QList<QByteArray> defineLines = part->projectDefines.split('\n'); + const QList<QByteArray> defineLines = + ProjectExplorer::Macro::toByteArray(part->projectMacros).split('\n'); foreach (const QByteArray &defineLine, defineLines) m_out << i4 << defineLine << "\n"; } diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp index 26b139e647..3e1c19399e 100644 --- a/src/plugins/cpptools/cppcompletionassist.cpp +++ b/src/plugins/cpptools/cppcompletionassist.cpp @@ -1902,7 +1902,7 @@ void InternalCppCompletionAssistProcessor::addMacros_helper(const Snapshot &snap foreach (const Document::Include &i, doc->resolvedIncludes()) addMacros_helper(snapshot, i.resolvedFileName(), processed, definedMacros); - foreach (const Macro ¯o, doc->definedMacros()) { + foreach (const CPlusPlus::Macro ¯o, doc->definedMacros()) { const QString macroName = macro.nameToQString(); if (!macro.isHidden()) definedMacros->insert(macroName); diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp index a27233f1c1..9e521ac46f 100644 --- a/src/plugins/cpptools/cppfindreferences.cpp +++ b/src/plugins/cpptools/cppfindreferences.cpp @@ -607,13 +607,13 @@ class FindMacroUsesInFile: public std::unary_function<QString, QList<Usage> > { const WorkingCopy workingCopy; const Snapshot snapshot; - const Macro ¯o; + const CPlusPlus::Macro ¯o; QFutureInterface<Usage> *future; public: FindMacroUsesInFile(const WorkingCopy &workingCopy, const Snapshot snapshot, - const Macro ¯o, + const CPlusPlus::Macro ¯o, QFutureInterface<Usage> *future) : workingCopy(workingCopy), snapshot(snapshot), macro(macro), future(future) { } @@ -632,7 +632,7 @@ restart_search: usages.clear(); foreach (const Document::MacroUse &use, doc->macroUses()) { - const Macro &useMacro = use.macro(); + const CPlusPlus::Macro &useMacro = use.macro(); if (useMacro.fileName() == macro.fileName()) { // Check if this is a match, but possibly against an outdated document. if (source.isEmpty()) @@ -687,7 +687,7 @@ restart_search: static void findMacroUses_helper(QFutureInterface<Usage> &future, const WorkingCopy workingCopy, const Snapshot snapshot, - const Macro macro) + const CPlusPlus::Macro macro) { const Utils::FileName sourceFile = Utils::FileName::fromString(macro.fileName()); Utils::FileNameList files{sourceFile}; @@ -704,12 +704,13 @@ static void findMacroUses_helper(QFutureInterface<Usage> &future, future.setProgressValue(files.size()); } -void CppFindReferences::findMacroUses(const Macro ¯o) +void CppFindReferences::findMacroUses(const CPlusPlus::Macro ¯o) { findMacroUses(macro, QString(), false); } -void CppFindReferences::findMacroUses(const Macro ¯o, const QString &replacement, bool replace) +void CppFindReferences::findMacroUses(const CPlusPlus::Macro ¯o, const QString &replacement, + bool replace) { SearchResult *search = SearchResultWindow::instance()->startNewSearch( tr("C++ Macro Usages:"), @@ -753,7 +754,7 @@ void CppFindReferences::findMacroUses(const Macro ¯o, const QString &replace connect(progress, &FutureProgress::clicked, search, &SearchResult::popup); } -void CppFindReferences::renameMacroUses(const Macro ¯o, const QString &replacement) +void CppFindReferences::renameMacroUses(const CPlusPlus::Macro ¯o, const QString &replacement) { const QString textToReplace = replacement.isEmpty() ? macro.nameToQString() : replacement; findMacroUses(macro, textToReplace, true); diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index d2493e2e45..08799709b1 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -47,6 +47,7 @@ #include <texteditor/textdocument.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectmacro.h> #include <projectexplorer/session.h> #include <extensionsystem/pluginmanager.h> #include <utils/fileutils.h> @@ -138,7 +139,7 @@ public: bool m_dirty; QStringList m_projectFiles; ProjectPartHeaderPaths m_headerPaths; - QByteArray m_definedMacros; + ProjectExplorer::Macros m_definedMacros; // Editor integration mutable QMutex m_cppEditorDocumentsMutex; @@ -446,35 +447,31 @@ ProjectPartHeaderPaths CppModelManager::internalHeaderPaths() const return headerPaths; } -static void addUnique(const QList<QByteArray> &defs, QByteArray *macros, QSet<QByteArray> *alreadyIn) +static void addUnique(const ProjectExplorer::Macros &newMacros, + ProjectExplorer::Macros ¯os, + QSet<ProjectExplorer::Macro> &alreadyIn) { - Q_ASSERT(macros); - Q_ASSERT(alreadyIn); - - foreach (const QByteArray &def, defs) { - if (def.trimmed().isEmpty()) - continue; - if (!alreadyIn->contains(def)) { - macros->append(def); - macros->append('\n'); - alreadyIn->insert(def); + for (const ProjectExplorer::Macro ¯o : newMacros) { + if (!alreadyIn.contains(macro)) { + macros += macro; + alreadyIn.insert(macro); } } } -QByteArray CppModelManager::internalDefinedMacros() const +ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const { - QByteArray macros; - QSet<QByteArray> alreadyIn; + ProjectExplorer::Macros macros; + QSet<ProjectExplorer::Macro> alreadyIn; QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo); while (it.hasNext()) { it.next(); const ProjectInfo pinfo = it.value(); - foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) { - addUnique(part->toolchainDefines.split('\n'), ¯os, &alreadyIn); - addUnique(part->projectDefines.split('\n'), ¯os, &alreadyIn); + for (const ProjectPart::Ptr &part : pinfo.projectParts()) { + addUnique(part->toolChainMacros, macros, alreadyIn); + addUnique(part->projectMacros, macros, alreadyIn); if (!part->projectConfigFile.isEmpty()) - macros += ProjectPart::readProjectConfigFile(part); + macros += ProjectExplorer::Macro::toMacros(ProjectPart::readProjectConfigFile(part)); } } return macros; @@ -491,7 +488,8 @@ void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId) dumper.dumpProjectInfos(projectInfos()); dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true); dumper.dumpWorkingCopy(workingCopy()); - dumper.dumpMergedEntities(headerPaths(), definedMacros()); + dumper.dumpMergedEntities(headerPaths(), + ProjectExplorer:: Macro::toByteArray(definedMacros())); } QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const @@ -569,12 +567,12 @@ void CppModelManager::renameUsages(Symbol *symbol, d->m_findReferences->renameUsages(symbol, context, replacement); } -void CppModelManager::findMacroUsages(const Macro ¯o) +void CppModelManager::findMacroUsages(const CPlusPlus::Macro ¯o) { d->m_findReferences->findMacroUses(macro); } -void CppModelManager::renameMacroUsages(const Macro ¯o, const QString &replacement) +void CppModelManager::renameMacroUsages(const CPlusPlus::Macro ¯o, const QString &replacement) { d->m_findReferences->renameMacroUses(macro, replacement); } @@ -603,7 +601,7 @@ WorkingCopy CppModelManager::buildWorkingCopyList() // Add the project configuration file QByteArray conf = codeModelConfiguration(); - conf += definedMacros(); + conf += ProjectExplorer::Macro::toByteArray(definedMacros()); workingCopy.insert(configurationFileName(), conf); return workingCopy; @@ -991,7 +989,7 @@ ProjectPart::Ptr CppModelManager::fallbackProjectPart() { ProjectPart::Ptr part(new ProjectPart); - part->projectDefines = definedMacros(); + part->projectMacros = definedMacros(); part->headerPaths = headerPaths(); // Do not activate ObjectiveCExtensions since this will lead to the @@ -1270,7 +1268,7 @@ void CppModelManager::setHeaderPaths(const ProjectPartHeaderPaths &headerPaths) d->m_headerPaths = headerPaths; } -QByteArray CppModelManager::definedMacros() +ProjectExplorer::Macros CppModelManager::definedMacros() { QMutexLocker locker(&d->m_projectMutex); ensureUpdated(); diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 97a93fce60..1cabaf50ef 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -163,7 +163,7 @@ public: // Use this *only* for auto tests void setHeaderPaths(const ProjectPartHeaderPaths &headerPaths); - QByteArray definedMacros(); + ProjectExplorer::Macros definedMacros(); void enableGarbageCollector(bool enable); @@ -229,7 +229,7 @@ private: void ensureUpdated(); QStringList internalProjectFiles() const; ProjectPartHeaderPaths internalHeaderPaths() const; - QByteArray internalDefinedMacros() const; + ProjectExplorer::Macros internalDefinedMacros() const; void dumpModelManagerConfiguration(const QString &logFileId); diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp index 35f9931f21..c50c12402c 100644 --- a/src/plugins/cpptools/cppmodelmanager_test.cpp +++ b/src/plugins/cpptools/cppmodelmanager_test.cpp @@ -187,7 +187,7 @@ void CppToolsPlugin::test_modelmanager_paths_are_clean() ProjectPart::Ptr part(new ProjectPart); part->qtVersion = ProjectPart::Qt5; - part->projectDefines = QByteArray("#define OH_BEHAVE -1\n"); + part->projectMacros = {ProjectExplorer::Macro("OH_BEHAVE", "-1")}; part->headerPaths = {HeaderPath(testDataDir.includeDir(false), HeaderPath::IncludePath), HeaderPath(testDataDir.frameworksDir(false), HeaderPath::FrameworkPath)}; pi.appendProjectPart(part); @@ -219,7 +219,7 @@ void CppToolsPlugin::test_modelmanager_framework_headers() ProjectPart::Ptr part(new ProjectPart); part->qtVersion = ProjectPart::Qt5; - part->projectDefines = QByteArray("#define OH_BEHAVE -1\n"); + part->projectMacros = {{"OH_BEHAVE", "-1"}}; part->headerPaths = {HeaderPath(testDataDir.includeDir(false), HeaderPath::IncludePath), HeaderPath(testDataDir.frameworksDir(false), HeaderPath::FrameworkPath)}; const QString &source = testDataDir.fileFromSourcesDir( @@ -268,7 +268,7 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() ProjectPart::Ptr part(new ProjectPart); part->qtVersion = ProjectPart::Qt5; - part->projectDefines = QByteArray("#define OH_BEHAVE -1\n"); + part->projectMacros = {{"OH_BEHAVE", "-1"}}; part->headerPaths = {HeaderPath(testDataDir.includeDir(false), HeaderPath::IncludePath)}; part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); pi.appendProjectPart(part); @@ -286,7 +286,7 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() QVERIFY(macrosInHeaderBefore.first().name() == "test_modelmanager_refresh_h"); // Introduce a define that will enable another define once the document is reparsed. - part->projectDefines = QByteArray("#define TEST_DEFINE 1\n"); + part->projectMacros = {{"TEST_DEFINE", "1"}}; pi = ProjectInfo(project); pi.appendProjectPart(part); @@ -334,13 +334,13 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() QSet<QString> refreshedFiles; CPlusPlus::Document::Ptr document; - QByteArray defines = "#define FIRST_DEFINE"; + ProjectExplorer::Macros macros = {{"FIRST_DEFINE"}}; for (int i = 0; i < 2; ++i) { pi = ProjectInfo(project); ProjectPart::Ptr part(new ProjectPart); // Simulate project configuration change by having different defines each time. - defines += "\n#define ANOTHER_DEFINE"; - part->projectDefines = defines; + macros += {"ANOTHER_DEFINE"}; + part->projectMacros = macros; part->qtVersion = ProjectPart::Qt5; part->files.append(ProjectFile(testHeader1, ProjectFile::CXXHeader)); part->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader)); @@ -762,7 +762,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_project() part1->files.append(ProjectFile(main1File, ProjectFile::CXXSource)); part1->files.append(ProjectFile(header, ProjectFile::CXXHeader)); part1->qtVersion = ProjectPart::NoQt; - part1->projectDefines = QByteArray("#define SUB1\n"); + part1->projectMacros = {{"SUB1"}}; part1->headerPaths = {HeaderPath(testDataDirectory.includeDir(false), HeaderPath::IncludePath)}; ProjectPart::Ptr part2(new ProjectPart); @@ -770,7 +770,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_project() part2->files.append(ProjectFile(main2File, ProjectFile::CXXSource)); part2->files.append(ProjectFile(header, ProjectFile::CXXHeader)); part2->qtVersion = ProjectPart::NoQt; - part2->projectDefines = QByteArray("#define SUB2\n"); + part2->projectMacros = {{"SUB2"}}; part2->headerPaths = {HeaderPath(testDataDirectory.includeDir(false), HeaderPath::IncludePath)}; ProjectInfo pi = ProjectInfo(project); diff --git a/src/plugins/cpptools/cppprojectinfogenerator.cpp b/src/plugins/cpptools/cppprojectinfogenerator.cpp index bff5d36ec4..3c8ac37654 100644 --- a/src/plugins/cpptools/cppprojectinfogenerator.cpp +++ b/src/plugins/cpptools/cppprojectinfogenerator.cpp @@ -143,7 +143,7 @@ private: if (!m_tcInfo.predefinedMacrosRunner) return; // No compiler set in kit. - m_projectPart.toolchainDefines = m_tcInfo.predefinedMacrosRunner(m_flags.commandLineFlags); + m_projectPart.toolChainMacros = m_tcInfo.predefinedMacrosRunner(m_flags.commandLineFlags); } private: @@ -187,7 +187,7 @@ static ProjectPart::Ptr projectPartFromRawProjectPart(const RawProjectPart &rawP part->callGroupId = rawProjectPart.callGroupId; part->buildSystemTarget = rawProjectPart.buildSystemTarget; part->qtVersion = rawProjectPart.qtVersion; - part->projectDefines = rawProjectPart.projectDefines; + part->projectMacros = rawProjectPart.projectMacros; part->headerPaths = rawProjectPart.headerPaths; part->precompiledHeaders = rawProjectPart.precompiledHeaders; part->selectedForBuilding = rawProjectPart.selectedForBuilding; diff --git a/src/plugins/cpptools/cpprawprojectpart.cpp b/src/plugins/cpptools/cpprawprojectpart.cpp index 3668f8caec..001bf01af3 100644 --- a/src/plugins/cpptools/cpprawprojectpart.cpp +++ b/src/plugins/cpptools/cpprawprojectpart.cpp @@ -81,9 +81,9 @@ void RawProjectPart::setQtVersion(ProjectPart::QtVersion qtVersion) this->qtVersion = qtVersion; } -void RawProjectPart::setDefines(const QByteArray &defines) +void RawProjectPart::setMacros(const ProjectExplorer::Macros ¯os) { - this->projectDefines = defines; + this->projectMacros = macros; } void RawProjectPart::setHeaderPaths(const ProjectPartHeaderPaths &headerPaths) diff --git a/src/plugins/cpptools/cpprawprojectpart.h b/src/plugins/cpptools/cpprawprojectpart.h index 18f0591ec2..aafe74281d 100644 --- a/src/plugins/cpptools/cpprawprojectpart.h +++ b/src/plugins/cpptools/cpprawprojectpart.h @@ -67,7 +67,7 @@ public: void setQtVersion(ProjectPart::QtVersion qtVersion); - void setDefines(const QByteArray &defines); + void setMacros(const ProjectExplorer::Macros ¯os); void setHeaderPaths(const ProjectPartHeaderPaths &headerPaths); void setIncludePaths(const QStringList &includePaths); @@ -88,7 +88,7 @@ public: QString buildSystemTarget; QStringList precompiledHeaders; ProjectPartHeaderPaths headerPaths; - QByteArray projectDefines; + ProjectExplorer::Macros projectMacros; ProjectPart::QtVersion qtVersion = ProjectPart::UnknownQt; bool selectedForBuilding = true; diff --git a/src/plugins/cpptools/cppsourceprocessor.cpp b/src/plugins/cpptools/cppsourceprocessor.cpp index fc28a6135e..507e33c90b 100644 --- a/src/plugins/cpptools/cppsourceprocessor.cpp +++ b/src/plugins/cpptools/cppsourceprocessor.cpp @@ -62,11 +62,12 @@ static Q_LOGGING_CATEGORY(log, "qtc.cpptools.sourceprocessor") namespace { -inline QByteArray generateFingerPrint(const QList<Macro> &definedMacros, const QByteArray &code) +inline QByteArray generateFingerPrint(const QList<CPlusPlus::Macro> &definedMacros, + const QByteArray &code) { QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(code); - foreach (const Macro ¯o, definedMacros) { + foreach (const CPlusPlus::Macro ¯o, definedMacros) { if (macro.isHidden()) { static const QByteArray undef("#undef "); hash.addData(undef); @@ -98,10 +99,10 @@ inline Message messageNoFileContents(Document::Ptr &document, const QString &fil return Message(Message::Warning, document->fileName(), line, /*column =*/ 0, text); } -inline const Macro revision(const WorkingCopy &workingCopy, - const Macro ¯o) +inline const CPlusPlus::Macro revision(const WorkingCopy &workingCopy, + const CPlusPlus::Macro ¯o) { - Macro newMacro(macro); + CPlusPlus::Macro newMacro(macro); newMacro.setFileRevision(workingCopy.get(macro.fileName()).second); return newMacro; } @@ -316,7 +317,7 @@ QString CppSourceProcessor::resolveFile_helper(const QString &fileName, return QString(); } -void CppSourceProcessor::macroAdded(const Macro ¯o) +void CppSourceProcessor::macroAdded(const CPlusPlus::Macro ¯o) { if (!m_currentDoc) return; @@ -325,7 +326,7 @@ void CppSourceProcessor::macroAdded(const Macro ¯o) } void CppSourceProcessor::passedMacroDefinitionCheck(unsigned bytesOffset, unsigned utf16charsOffset, - unsigned line, const Macro ¯o) + unsigned line, const CPlusPlus::Macro ¯o) { if (!m_currentDoc) return; @@ -347,7 +348,7 @@ void CppSourceProcessor::failedMacroDefinitionCheck(unsigned bytesOffset, unsign } void CppSourceProcessor::notifyMacroReference(unsigned bytesOffset, unsigned utf16charOffset, - unsigned line, const Macro ¯o) + unsigned line, const CPlusPlus::Macro ¯o) { if (!m_currentDoc) return; @@ -359,7 +360,7 @@ void CppSourceProcessor::notifyMacroReference(unsigned bytesOffset, unsigned utf } void CppSourceProcessor::startExpandingMacro(unsigned bytesOffset, unsigned utf16charOffset, - unsigned line, const Macro ¯o, + unsigned line, const CPlusPlus::Macro ¯o, const QVector<MacroArgumentReference> &actuals) { if (!m_currentDoc) @@ -371,7 +372,7 @@ void CppSourceProcessor::startExpandingMacro(unsigned bytesOffset, unsigned utf1 line, actuals); } -void CppSourceProcessor::stopExpandingMacro(unsigned, const Macro &) +void CppSourceProcessor::stopExpandingMacro(unsigned, const CPlusPlus::Macro &) { if (!m_currentDoc) return; diff --git a/src/plugins/cpptools/cpptoolsunittestfiles.pri b/src/plugins/cpptools/cpptoolsunittestfiles.pri index 6960ccbde9..48c70fce6e 100644 --- a/src/plugins/cpptools/cpptoolsunittestfiles.pri +++ b/src/plugins/cpptools/cpptoolsunittestfiles.pri @@ -1,11 +1,7 @@ -# Currently there are no tests for the project explorer plugin, but we include -# headers from it that needs to have the export/import adapted for Windows. shared { DEFINES += CPPTOOLS_LIBRARY - DEFINES += PROJECTEXPLORER_LIBRARY } else { DEFINES += CPPTOOLS_STATIC_LIBRARY - DEFINES += PROJECTEXPLORER_STATIC_LIBRARY } HEADERS += \ diff --git a/src/plugins/cpptools/projectinfo.cpp b/src/plugins/cpptools/projectinfo.cpp index 1ca95e3e9c..e0dbee363f 100644 --- a/src/plugins/cpptools/projectinfo.cpp +++ b/src/plugins/cpptools/projectinfo.cpp @@ -160,13 +160,10 @@ void ProjectInfo::finish() m_sourceFiles.insert(file.path); // Update defines - m_defines.append(part->toolchainDefines); - m_defines.append(part->projectDefines); - if (!part->projectConfigFile.isEmpty()) { - m_defines.append('\n'); - m_defines += ProjectPart::readProjectConfigFile(part); - m_defines.append('\n'); - } + m_defines.append(part->toolChainMacros); + m_defines.append(part->projectMacros); + if (!part->projectConfigFile.isEmpty()) + m_defines += ProjectExplorer::Macro::toMacros(ProjectPart::readProjectConfigFile(part)); } } diff --git a/src/plugins/cpptools/projectinfo.h b/src/plugins/cpptools/projectinfo.h index 9096322989..ee016f5b3e 100644 --- a/src/plugins/cpptools/projectinfo.h +++ b/src/plugins/cpptools/projectinfo.h @@ -123,7 +123,7 @@ private: // The members below are (re)calculated from the project parts with finish() ProjectPartHeaderPaths m_headerPaths; QSet<QString> m_sourceFiles; - QByteArray m_defines; + ProjectExplorer::Macros m_defines; }; } // namespace CppTools diff --git a/src/plugins/cpptools/projectpart.cpp b/src/plugins/cpptools/projectpart.cpp index 6a87adf93f..e77e079c80 100644 --- a/src/plugins/cpptools/projectpart.cpp +++ b/src/plugins/cpptools/projectpart.cpp @@ -25,6 +25,8 @@ #include "projectpart.h" +#include <utils/algorithm.h> + #include <QFile> #include <QDir> #include <QTextStream> @@ -43,16 +45,9 @@ void ProjectPart::updateLanguageFeatures() if (!hasQt) { languageFeatures.qtKeywordsEnabled = false; } else { - const QByteArray noKeywordsMacro = "#define QT_NO_KEYWORDS"; - const int noKeywordsIndex = projectDefines.indexOf(noKeywordsMacro); - if (noKeywordsIndex == -1) { - languageFeatures.qtKeywordsEnabled = true; - } else { - const char nextChar = projectDefines.at(noKeywordsIndex + noKeywordsMacro.length()); - // Detect "#define QT_NO_KEYWORDS" and "#define QT_NO_KEYWORDS 1", but exclude - // "#define QT_NO_KEYWORDS_FOO" - languageFeatures.qtKeywordsEnabled = nextChar != '\n' && nextChar != ' '; - } + languageFeatures.qtKeywordsEnabled = !Utils::contains( + projectMacros, + [] (const ProjectExplorer::Macro ¯o) { return macro.key == "QT_NO_KEYWORDS"; }); } } diff --git a/src/plugins/cpptools/projectpart.h b/src/plugins/cpptools/projectpart.h index c04a18c61f..a6851e001d 100644 --- a/src/plugins/cpptools/projectpart.h +++ b/src/plugins/cpptools/projectpart.h @@ -31,6 +31,7 @@ #include "projectpartheaderpath.h" #include <projectexplorer/projectexplorer_global.h> +#include <projectexplorer/projectmacro.h> #include <coreplugin/id.h> @@ -118,7 +119,7 @@ public: QStringList precompiledHeaders; ProjectPartHeaderPaths headerPaths; - QByteArray projectDefines; + ProjectExplorer::Macros projectMacros; LanguageVersion languageVersion = LatestCxxVersion; LanguageExtensions languageExtensions = NoExtensions; @@ -130,7 +131,7 @@ public: Core::Id toolchainType; bool isMsvc2015Toolchain = false; - QByteArray toolchainDefines; + ProjectExplorer::Macros toolChainMacros; ToolChainWordWidth toolChainWordWidth = WordWidth32Bit; QString toolChainTargetTriple; }; diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp index 6c8fd0f32b..3605b5bcb6 100644 --- a/src/plugins/nim/project/nimtoolchain.cpp +++ b/src/plugins/nim/project/nimtoolchain.cpp @@ -82,9 +82,9 @@ ToolChain::PredefinedMacrosRunner NimToolChain::createPredefinedMacrosRunner() c return ToolChain::PredefinedMacrosRunner(); } -QByteArray NimToolChain::predefinedMacros(const QStringList &) const +Macros NimToolChain::predefinedMacros(const QStringList &) const { - return QByteArray(); + return Macros(); } ToolChain::CompilerFlags NimToolChain::compilerFlags(const QStringList &) const diff --git a/src/plugins/nim/project/nimtoolchain.h b/src/plugins/nim/project/nimtoolchain.h index 7061a9751b..2da07dd2e2 100644 --- a/src/plugins/nim/project/nimtoolchain.h +++ b/src/plugins/nim/project/nimtoolchain.h @@ -41,7 +41,7 @@ public: bool isValid() const override; PredefinedMacrosRunner createPredefinedMacrosRunner() const override; - QByteArray predefinedMacros(const QStringList &flags) const final; + ProjectExplorer::Macros predefinedMacros(const QStringList &flags) const final; CompilerFlags compilerFlags(const QStringList &flags) const final; ProjectExplorer::WarningFlags warningFlags(const QStringList &flags) const final; diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp b/src/plugins/projectexplorer/abstractmsvctoolchain.cpp index 121e2b43b4..44d961ba08 100644 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp +++ b/src/plugins/projectexplorer/abstractmsvctoolchain.cpp @@ -118,7 +118,7 @@ ToolChain::PredefinedMacrosRunner AbstractMsvcToolChain::createPredefinedMacrosR }; } -QByteArray AbstractMsvcToolChain::predefinedMacros(const QStringList &cxxflags) const +ProjectExplorer::Macros AbstractMsvcToolChain::predefinedMacros(const QStringList &cxxflags) const { return createPredefinedMacrosRunner()(cxxflags); } @@ -277,13 +277,6 @@ bool AbstractMsvcToolChain::canClone() const return true; } -// Function must be thread-safe! -QByteArray AbstractMsvcToolChain::msvcPredefinedMacros(const QStringList, - const Utils::Environment&) const -{ - return QByteArray(); -} - bool AbstractMsvcToolChain::generateEnvironmentSettings(const Utils::Environment &env, const QString &batchFile, const QString &batchArgs, diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.h b/src/plugins/projectexplorer/abstractmsvctoolchain.h index 9a96faea0e..0851e09cc0 100644 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.h +++ b/src/plugins/projectexplorer/abstractmsvctoolchain.h @@ -53,7 +53,7 @@ public: QString originalTargetTriple() const override; PredefinedMacrosRunner createPredefinedMacrosRunner() const override; - QByteArray predefinedMacros(const QStringList &cxxflags) const override; + Macros predefinedMacros(const QStringList &cxxflags) const override; CompilerFlags compilerFlags(const QStringList &cxxflags) const override; WarningFlags warningFlags(const QStringList &cflags) const override; SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override; @@ -92,13 +92,14 @@ protected: static void inferWarningsForLevel(int warningLevel, WarningFlags &flags); virtual Utils::Environment readEnvironmentSetting(const Utils::Environment& env) const = 0; - virtual QByteArray msvcPredefinedMacros(const QStringList cxxflags, - const Utils::Environment& env) const; + // Function must be thread-safe! + virtual Macros msvcPredefinedMacros(const QStringList cxxflags, + const Utils::Environment& env) const = 0; Utils::FileName m_debuggerCommand; mutable QMutex *m_predefinedMacrosMutex = nullptr; - mutable QByteArray m_predefinedMacros; + mutable Macros m_predefinedMacros; mutable Utils::Environment m_lastEnvironment; // Last checked 'incoming' environment. mutable Utils::Environment m_resultEnvironment; // Resulting environment for VC mutable QMutex *m_headerPathsMutex = nullptr; diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index 06a3b041e8..dc0989eed1 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -32,6 +32,7 @@ #include "customparser.h" #include "customparserconfigdialog.h" #include "projectexplorerconstants.h" +#include "projectmacro.h" #include "toolchainmanager.h" #include <utils/algorithm.h> @@ -118,39 +119,24 @@ bool CustomToolChain::isValid() const ToolChain::PredefinedMacrosRunner CustomToolChain::createPredefinedMacrosRunner() const { - const QStringList theMacros = m_predefinedMacros; + const Macros theMacros = m_predefinedMacros; // This runner must be thread-safe! return [theMacros](const QStringList &cxxflags){ QByteArray result; - QStringList macros = theMacros; + Macros macros = theMacros; for (const QString &cxxFlag : cxxflags) { - if (cxxFlag.startsWith(QLatin1String("-D"))) { - macros << cxxFlag.mid(2).trimmed(); - } else if (cxxFlag.startsWith(QLatin1String("-U"))) { - const QString &removedName = cxxFlag.mid(2).trimmed(); - for (int i = macros.size() - 1; i >= 0; --i) { - const QString &m = macros.at(i); - if (m.left(m.indexOf(QLatin1Char('='))) == removedName) - macros.removeAt(i); - } - } - } - for (const QString &str : Utils::asConst(macros)) { - QByteArray ba = str.toUtf8(); - int equals = ba.indexOf('='); - if (equals == -1) { - result += "#define " + ba.trimmed() + '\n'; - } else { - result += "#define " + ba.left(equals).trimmed() + ' ' - + ba.mid(equals + 1).trimmed() + '\n'; - } + if (cxxFlag.startsWith(QLatin1String("-D"))) + macros.append(Macro::fromKeyValue(cxxFlag.mid(2).trimmed())); + else if (cxxFlag.startsWith(QLatin1String("-U")) && !cxxFlag.contains('=')) + macros.append({cxxFlag.mid(2).trimmed().toUtf8(), MacroType::Undefine}); + } - return result; + return macros; }; } -QByteArray CustomToolChain::predefinedMacros(const QStringList &cxxflags) const +Macros CustomToolChain::predefinedMacros(const QStringList &cxxflags) const { return createPredefinedMacrosRunner()(cxxflags); } @@ -169,16 +155,16 @@ WarningFlags CustomToolChain::warningFlags(const QStringList &cxxflags) const return WarningFlags::Default; } -const QStringList &CustomToolChain::rawPredefinedMacros() const +const Macros &CustomToolChain::rawPredefinedMacros() const { return m_predefinedMacros; } -void CustomToolChain::setPredefinedMacros(const QStringList &list) +void CustomToolChain::setPredefinedMacros(const Macros ¯os) { - if (m_predefinedMacros == list) + if (m_predefinedMacros == macros) return; - m_predefinedMacros = list; + m_predefinedMacros = macros; toolChainUpdated(); } @@ -323,7 +309,8 @@ QVariantMap CustomToolChain::toMap() const data.insert(QLatin1String(compilerCommandKeyC), m_compilerCommand.toString()); data.insert(QLatin1String(makeCommandKeyC), m_makeCommand.toString()); data.insert(QLatin1String(targetAbiKeyC), m_targetAbi.toString()); - data.insert(QLatin1String(predefinedMacrosKeyC), m_predefinedMacros); + QStringList macros = Utils::transform<QList>(m_predefinedMacros, [](const Macro &m) { return QString::fromUtf8(m.toByteArray()); }); + data.insert(QLatin1String(predefinedMacrosKeyC), macros); data.insert(QLatin1String(headerPathsKeyC), headerPathsList()); data.insert(QLatin1String(cxx11FlagsKeyC), m_cxx11Flags); data.insert(QLatin1String(mkspecsKeyC), mkspecs()); @@ -352,7 +339,8 @@ bool CustomToolChain::fromMap(const QVariantMap &data) m_compilerCommand = FileName::fromString(data.value(QLatin1String(compilerCommandKeyC)).toString()); m_makeCommand = FileName::fromString(data.value(QLatin1String(makeCommandKeyC)).toString()); m_targetAbi = Abi(data.value(QLatin1String(targetAbiKeyC)).toString()); - m_predefinedMacros = data.value(QLatin1String(predefinedMacrosKeyC)).toStringList(); + const QStringList macros = data.value(QLatin1String(predefinedMacrosKeyC)).toStringList(); + m_predefinedMacros = Macro::toMacros(macros.join('\n').toUtf8()); setHeaderPaths(data.value(QLatin1String(headerPathsKeyC)).toStringList()); m_cxx11Flags = data.value(QLatin1String(cxx11FlagsKeyC)).toStringList(); setMkspecs(data.value(QLatin1String(mkspecsKeyC)).toString()); @@ -526,11 +514,16 @@ public: return static_cast<QPlainTextEdit *>(widget()); } - inline QStringList entries() const + QStringList entries() const { return textEditWidget()->toPlainText().split(QLatin1Char('\n'), QString::SkipEmptyParts); } + QString text() const + { + return textEditWidget()->toPlainText(); + } + // not accurate, counts empty lines (except last) int entryCount() const { @@ -656,7 +649,7 @@ void CustomToolChainConfigWidget::applyImpl() tc->setCompilerCommand(m_compilerCommand->fileName()); tc->setMakeCommand(m_makeCommand->fileName()); tc->setTargetAbi(m_abiWidget->currentAbi()); - tc->setPredefinedMacros(m_predefinedDetails->entries()); + tc->setPredefinedMacros(Macro::toMacros(m_predefinedDetails->text().toUtf8())); tc->setHeaderPaths(m_headerDetails->entries()); tc->setCxx11Flags(m_cxx11Flags->text().split(QLatin1Char(','))); tc->setMkspecs(m_mkspecs->text()); @@ -673,8 +666,8 @@ void CustomToolChainConfigWidget::setFromToolchain() m_compilerCommand->setFileName(tc->compilerCommand()); m_makeCommand->setFileName(FileName::fromString(tc->makeCommand(Environment()))); m_abiWidget->setAbis(QList<Abi>(), tc->targetAbi()); - m_predefinedMacros->setPlainText(tc->rawPredefinedMacros().join(QLatin1Char('\n'))); - m_headerPaths->setPlainText(tc->headerPathsList().join(QLatin1Char('\n'))); + m_predefinedMacros->setPlainText(QString::fromUtf8(Macro::toByteArray(tc->rawPredefinedMacros()))); + m_headerPaths->setPlainText(tc->headerPathsList().join('\n')); m_cxx11Flags->setText(tc->cxx11Flags().join(QLatin1Char(','))); m_mkspecs->setText(tc->mkspecs()); int index = m_errorParserComboBox->findData(tc->outputParserId().toSetting()); @@ -690,7 +683,7 @@ bool CustomToolChainConfigWidget::isDirtyImpl() const return m_compilerCommand->fileName() != tc->compilerCommand() || m_makeCommand->path() != tc->makeCommand(Environment()) || m_abiWidget->currentAbi() != tc->targetAbi() - || m_predefinedDetails->entries() != tc->rawPredefinedMacros() + || Macro::toMacros(m_predefinedDetails->text().toUtf8()) != tc->rawPredefinedMacros() || m_headerDetails->entries() != tc->headerPathsList() || m_cxx11Flags->text().split(QLatin1Char(',')) != tc->cxx11Flags() || m_mkspecs->text() != tc->mkspecs() diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index fa5ec68ed0..75ba190c66 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -72,11 +72,11 @@ public: bool isValid() const override; PredefinedMacrosRunner createPredefinedMacrosRunner() const override; - QByteArray predefinedMacros(const QStringList &cxxflags) const override; + Macros predefinedMacros(const QStringList &cxxflags) const override; CompilerFlags compilerFlags(const QStringList &cxxflags) const override; WarningFlags warningFlags(const QStringList &cxxflags) const override; - const QStringList &rawPredefinedMacros() const; - void setPredefinedMacros(const QStringList &list); + const Macros &rawPredefinedMacros() const; + void setPredefinedMacros(const Macros ¯os); SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override; QList<HeaderPath> systemHeaderPaths(const QStringList &cxxFlags, @@ -124,7 +124,7 @@ private: Utils::FileName m_makeCommand; Abi m_targetAbi; - QStringList m_predefinedMacros; + Macros m_predefinedMacros; QList<HeaderPath> m_systemHeaderPaths; QStringList m_cxx11Flags; Utils::FileNameList m_mkspecs; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 102441bdf1..9800908a53 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -28,6 +28,7 @@ #include "gcctoolchainfactories.h" #include "gccparser.h" #include "linuxiccparser.h" +#include "projectmacro.h" #include "projectexplorerconstants.h" #include "toolchainmanager.h" @@ -48,6 +49,7 @@ #include <QLineEdit> #include <QRegularExpression> +#include <algorithm> #include <memory> using namespace Utils; @@ -119,6 +121,11 @@ HeaderPathsCache::Cache HeaderPathsCache::cache() const return m_cache; } +MacroCache::MacroCache() : m_mutex(QMutex::Recursive) +{ + m_cache.reserve(CACHE_SIZE + 1); +} + MacroCache::MacroCache(const MacroCache &other) : MacroCache() { @@ -126,40 +133,21 @@ MacroCache::MacroCache(const MacroCache &other) m_cache = other.cache(); } -void MacroCache::insert(const QStringList &compilerCommand, const QByteArray ¯os) +void MacroCache::insert(const QStringList &compilerCommand, const Macros ¯os) { - if (macros.isNull()) + QMutexLocker locker(&m_mutex); + if (macros.isEmpty() || unlockedCheck(compilerCommand).isEmpty()) return; - CacheItem runResults; - QByteArray data = macros; - runResults.first = compilerCommand; - if (macros.isNull()) - data = QByteArray(""); - runResults.second = data; - - QMutexLocker locker(&m_mutex); - if (check(compilerCommand).isNull()) { - m_cache.push_back(runResults); - if (m_cache.size() > CACHE_SIZE) - m_cache.pop_front(); - } + m_cache.push_back(qMakePair(compilerCommand, macros)); + if (m_cache.size() > CACHE_SIZE) + m_cache.pop_front(); } -QByteArray MacroCache::check(const QStringList &compilerCommand) const +Macros MacroCache::check(const QStringList &compilerCommand) const { QMutexLocker locker(&m_mutex); - for (Cache::iterator it = m_cache.begin(); it != m_cache.end(); ++it) { - if (it->first == compilerCommand) { - // Increase cached item priority - CacheItem pair = *it; - m_cache.erase(it); - m_cache.push_back(pair); - - return pair.second; - } - } - return QByteArray(); + return unlockedCheck(compilerCommand); } MacroCache::Cache MacroCache::cache() const @@ -168,6 +156,16 @@ MacroCache::Cache MacroCache::cache() const return m_cache; } +Macros MacroCache::unlockedCheck(const QStringList &compilerCommand) const +{ + auto it = std::stable_partition(m_cache.begin(), m_cache.end(), [&](const CacheItem &ci) { + return ci.first == compilerCommand; + }); + if (it != m_cache.end()) + return it->second; + return {}; +} + static QByteArray runGcc(const FileName &gcc, const QStringList &arguments, const QStringList &env) { if (gcc.isEmpty() || !gcc.toFileInfo().isExecutable()) @@ -196,25 +194,28 @@ static const QStringList gccPredefinedMacrosOptions(Core::Id languageId) return QStringList({langOption, "-E", "-dM"}); } -static QByteArray gccPredefinedMacros(const FileName &gcc, const QStringList &args, const QStringList &env) +static ProjectExplorer::Macros gccPredefinedMacros(const FileName &gcc, + const QStringList &args, + const QStringList &env) { QStringList arguments = args; arguments << "-"; - QByteArray predefinedMacros = runGcc(gcc, arguments, env); + ProjectExplorer::Macros predefinedMacros = Macro::toMacros(runGcc(gcc, arguments, env)); // Sanity check in case we get an error message instead of real output: - QTC_CHECK(predefinedMacros.isNull() || predefinedMacros.startsWith("#define ")); + QTC_CHECK(predefinedMacros.isEmpty() + || predefinedMacros.front().type == ProjectExplorer::MacroType::Define); if (HostOsInfo::isMacHost()) { // Turn off flag indicating Apple's blocks support - const QByteArray blocksDefine("#define __BLOCKS__ 1"); - const QByteArray blocksUndefine("#undef __BLOCKS__"); + const ProjectExplorer::Macro blocksDefine("__BLOCKS__", "1"); + const ProjectExplorer::Macro blocksUndefine("__BLOCKS__", ProjectExplorer::MacroType::Undefine); const int idx = predefinedMacros.indexOf(blocksDefine); if (idx != -1) - predefinedMacros.replace(idx, blocksDefine.length(), blocksUndefine); + predefinedMacros[idx] = blocksUndefine; // Define __strong and __weak (used for Apple's GC extension of C) to be empty - predefinedMacros.append("#define __strong\n"); - predefinedMacros.append("#define __weak\n"); + predefinedMacros.append({"__strong"}); + predefinedMacros.append({"__weak"}); } return predefinedMacros; } @@ -261,7 +262,7 @@ QList<HeaderPath> GccToolChain::gccHeaderPaths(const FileName &gcc, const QStrin return systemHeaderPaths; } -static QList<Abi> guessGccAbi(const QString &m, const QByteArray ¯os) +static QList<Abi> guessGccAbi(const QString &m, const ProjectExplorer::Macros ¯os) { QList<Abi> abiList; @@ -274,19 +275,13 @@ static QList<Abi> guessGccAbi(const QString &m, const QByteArray ¯os) Abi::OSFlavor flavor = guessed.osFlavor(); Abi::BinaryFormat format = guessed.binaryFormat(); int width = guessed.wordWidth(); - const QByteArray mscVer = "#define _MSC_VER "; - - if (macros.contains("#define __SIZEOF_SIZE_T__ 8")) - width = 64; - else if (macros.contains("#define __SIZEOF_SIZE_T__ 4")) - width = 32; - else if (macros.contains("#define __SIZEOF_SIZE_T__ 2")) - width = 16; - int mscVerIndex = macros.indexOf(mscVer); - if (mscVerIndex != -1) { - mscVerIndex += mscVer.length(); - const int eol = macros.indexOf('\n', mscVerIndex); - const int msvcVersion = macros.mid(mscVerIndex, eol - mscVerIndex).toInt(); + + const Macro sizeOfMacro = Utils::findOrDefault(macros, [](const Macro &m) { return m.key == "__SIZEOF_SIZE_T__"; }); + if (sizeOfMacro.isValid() && sizeOfMacro.type == MacroType::Define) + width = sizeOfMacro.value.toInt() * 8; + const Macro &mscVerMacro = Utils::findOrDefault(macros, [](const Macro &m) { return m.key == "_MSC_VER"; }); + if (mscVerMacro.type == MacroType::Define) { + const int msvcVersion = mscVerMacro.value.toInt(); flavor = Abi::flavorForMsvcVersion(msvcVersion); } @@ -305,7 +300,7 @@ static QList<Abi> guessGccAbi(const QString &m, const QByteArray ¯os) static GccToolChain::DetectedAbisResult guessGccAbi(const FileName &path, const QStringList &env, - const QByteArray ¯os, + const ProjectExplorer::Macros ¯os, const QStringList &extraArgs = QStringList()) { if (path.isEmpty()) @@ -495,8 +490,8 @@ ToolChain::PredefinedMacrosRunner GccToolChain::createPredefinedMacrosRunner() c } arguments = reinterpretOptions(arguments); - QByteArray macros = macroCache->check(arguments); - if (!macros.isNull()) + Macros macros = macroCache->check(arguments); + if (!macros.isEmpty()) return macros; macros = gccPredefinedMacros(findLocalCompiler(compilerCommand, env), @@ -517,7 +512,7 @@ ToolChain::PredefinedMacrosRunner GccToolChain::createPredefinedMacrosRunner() c * adds _OPENMP macro, for full list of macro search by word "when" on this page: * http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html */ -QByteArray GccToolChain::predefinedMacros(const QStringList &cxxflags) const +ProjectExplorer::Macros GccToolChain::predefinedMacros(const QStringList &cxxflags) const { return createPredefinedMacrosRunner()(cxxflags); } @@ -701,6 +696,8 @@ void GccToolChain::addCommandPathToEnvironment(const FileName &command, Environm env.prependOrSetPath(command.parentDir().toString()); } +GccToolChain::GccToolChain(const GccToolChain &) = default; + void GccToolChain::addToEnvironment(Environment &env) const { addCommandPathToEnvironment(m_compilerCommand, env); @@ -898,7 +895,7 @@ GccToolChain::DetectedAbisResult GccToolChain::detectSupportedAbis() const { Environment env = Environment::systemEnvironment(); addToEnvironment(env); - QByteArray macros = predefinedMacros(QStringList()); + ProjectExplorer::Macros macros = predefinedMacros(QStringList()); return guessGccAbi(findLocalCompiler(m_compilerCommand, env), env.toStringList(), macros, @@ -1060,7 +1057,7 @@ QList<ToolChain *> GccToolChainFactory::autoDetectToolChain(const FileName &comp Environment systemEnvironment = Environment::systemEnvironment(); GccToolChain::addCommandPathToEnvironment(compilerPath, systemEnvironment); const FileName localCompilerPath = findLocalCompiler(compilerPath, systemEnvironment); - QByteArray macros + Macros macros = gccPredefinedMacros(localCompilerPath, gccPredefinedMacrosOptions(language), systemEnvironment.toStringList()); const GccToolChain::DetectedAbisResult detectedAbis = guessGccAbi(localCompilerPath, @@ -1765,7 +1762,7 @@ void ProjectExplorerPlugin::testGccAbiGuessing() QFETCH(QByteArray, macros); QFETCH(QStringList, abiList); - QList<Abi> al = guessGccAbi(input, macros); + QList<Abi> al = guessGccAbi(input, ProjectExplorer::Macro::toMacros(macros)); QCOMPARE(al.count(), abiList.count()); for (int i = 0; i < al.count(); ++i) QCOMPARE(al.at(i).toString(), abiList.at(i)); diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index 0399b1ec14..a3c4b4eaca 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -51,7 +51,7 @@ class LinuxIccToolChainFactory; // GccToolChain // -------------------------------------------------------------------------- -class PROJECTEXPLORER_EXPORT HeaderPathsCache +class HeaderPathsCache { public: HeaderPathsCache() : m_mutex(QMutex::Recursive) {} @@ -69,20 +69,22 @@ private: mutable Cache m_cache; }; -class PROJECTEXPLORER_EXPORT MacroCache +class MacroCache { public: - MacroCache() : m_mutex(QMutex::Recursive) {} + MacroCache(); MacroCache(const MacroCache &other); - void insert(const QStringList &compilerCommand, const QByteArray ¯os); - QByteArray check(const QStringList &compilerCommand) const; + void insert(const QStringList &compilerCommand, const Macros ¯os); + Macros check(const QStringList &compilerCommand) const; protected: - using CacheItem = QPair<QStringList, QByteArray>; - using Cache = QList<CacheItem>; + using CacheItem = QPair<QStringList, Macros>; + using Cache = QVector<CacheItem>; Cache cache() const; private: + // Does not lock! + Macros unlockedCheck(const QStringList &compilerCommand) const; mutable QMutex m_mutex; mutable Cache m_cache; }; @@ -104,7 +106,7 @@ public: WarningFlags warningFlags(const QStringList &cflags) const override; PredefinedMacrosRunner createPredefinedMacrosRunner() const override; - QByteArray predefinedMacros(const QStringList &cxxflags) const override; + Macros predefinedMacros(const QStringList &cxxflags) const override; SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override; QList<HeaderPath> systemHeaderPaths(const QStringList &cxxflags, @@ -147,14 +149,16 @@ public: }; protected: - GccToolChain(const GccToolChain &) = default; + using CacheItem = QPair<QStringList, Macros>; + using GccCache = QVector<CacheItem>; + + GccToolChain(const GccToolChain &); void setCompilerCommand(const Utils::FileName &path); void setSupportedAbis(const QList<Abi> &m_abis); void setOriginalTargetTriple(const QString &targetTriple); - - void setMacroCache(const QStringList &allCxxflags, const QByteArray ¯os) const; - QByteArray macroCache(const QStringList &allCxxflags) const; + void setMacroCache(const QStringList &allCxxflags, const Macros ¯oCache) const; + Macros macroCache(const QStringList &allCxxflags) const; virtual QString defaultDisplayName() const; virtual CompilerFlags defaultCompilerFlags() const; diff --git a/src/plugins/projectexplorer/gcctoolchainfactories.h b/src/plugins/projectexplorer/gcctoolchainfactories.h index 4417fc712f..e4c0a583e9 100644 --- a/src/plugins/projectexplorer/gcctoolchainfactories.h +++ b/src/plugins/projectexplorer/gcctoolchainfactories.h @@ -99,7 +99,7 @@ private: AbiWidget *m_abiWidget; bool m_isReadOnly = false; - QByteArray m_macros; + ProjectExplorer::Macros m_macros; }; // -------------------------------------------------------------------------- diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 2562b7fcc4..2ff34dbcb8 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -410,31 +410,18 @@ static QByteArray msvcCompilationFile() // // [1] https://msdn.microsoft.com/en-us/library/b0084kay.aspx // [2] http://stackoverflow.com/questions/3665537/how-to-find-out-cl-exes-built-in-macros -QByteArray MsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags, - const Utils::Environment &env) const +Macros MsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags, + const Utils::Environment &env) const { - QByteArray predefinedMacros; + Macros predefinedMacros; QStringList toProcess; - foreach (const QString &arg, cxxflags) { + for (const QString &arg : cxxflags) { if (arg.startsWith(QLatin1String("/D"))) { - QString define = arg.mid(2); - int pos = define.indexOf(QLatin1Char('=')); - if (pos < 0) { - predefinedMacros += "#define "; - predefinedMacros += define.toLocal8Bit(); - predefinedMacros += '\n'; - } else { - predefinedMacros += "#define "; - predefinedMacros += define.left(pos).toLocal8Bit(); - predefinedMacros += ' '; - predefinedMacros += define.mid(pos + 1).toLocal8Bit(); - predefinedMacros += '\n'; - } + const QString define = arg.mid(2); + predefinedMacros.append(Macro::fromKeyValue(define)); } else if (arg.startsWith(QLatin1String("/U"))) { - predefinedMacros += "#undef "; - predefinedMacros += arg.mid(2).toLocal8Bit(); - predefinedMacros += '\n'; + predefinedMacros.append({arg.mid(2).toLocal8Bit(), ProjectExplorer::MacroType::Undefine}); } else if (arg.startsWith(QLatin1String("-I"))) { // Include paths should not have any effect on defines } else { @@ -468,18 +455,8 @@ QByteArray MsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags, const QStringList output = Utils::filtered(response.stdOut().split('\n'), [](const QString &s) { return s.startsWith('V'); }); - foreach (const QString& line, output) { - QStringList split = line.split('='); - const QString key = split.at(0).mid(1); - QString value = split.at(1); - predefinedMacros += "#define "; - predefinedMacros += key.toUtf8(); - predefinedMacros += ' '; - predefinedMacros += value.toUtf8(); - predefinedMacros += '\n'; - } - if (debug) - qDebug() << "msvcPredefinedMacros" << predefinedMacros; + for (const QString &line : output) + predefinedMacros.append(Macro::fromKeyValue(line.mid(1))); return predefinedMacros; } diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index fd341177eb..3dcb715286 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -82,8 +82,8 @@ protected: Utils::Environment readEnvironmentSetting(const Utils::Environment& env) const final; // Function must be thread-safe! - QByteArray msvcPredefinedMacros(const QStringList cxxflags, - const Utils::Environment &env) const override; + Macros msvcPredefinedMacros(const QStringList cxxflags, + const Utils::Environment &env) const override; private: QList<Utils::EnvironmentItem> environmentModifications() const; diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index d702d3a120..979a3e865d 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -149,7 +149,8 @@ HEADERS += projectexplorer.h \ projectexplorer_global.h \ extracompiler.h \ customexecutableconfigurationwidget.h \ - customexecutablerunconfiguration.h + customexecutablerunconfiguration.h \ + projectmacro.h SOURCES += projectexplorer.cpp \ abi.cpp \ @@ -284,7 +285,8 @@ SOURCES += projectexplorer.cpp \ projectexplorericons.cpp \ extracompiler.cpp \ customexecutableconfigurationwidget.cpp \ - customexecutablerunconfiguration.cpp + customexecutablerunconfiguration.cpp \ + projectmacro.cpp FORMS += processstep.ui \ editorsettingspropertiespage.ui \ diff --git a/src/plugins/projectexplorer/projectexplorerunittestfiles.pri b/src/plugins/projectexplorer/projectexplorerunittestfiles.pri new file mode 100644 index 0000000000..6b005059e6 --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorerunittestfiles.pri @@ -0,0 +1,12 @@ +shared { + DEFINES += PROJECTEXPLORER_LIBRARY +} else { + DEFINES += PROJECTEXPLORER_STATIC_LIBRARY +} + +HEADERS += \ + $$PWD/projectmacro.h + +SOURCES += \ + $$PWD/projectmacro.cpp + diff --git a/src/plugins/projectexplorer/projectmacro.cpp b/src/plugins/projectexplorer/projectmacro.cpp new file mode 100644 index 0000000000..e69e60421d --- /dev/null +++ b/src/plugins/projectexplorer/projectmacro.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "projectmacro.h" + +#include <utils/algorithm.h> + +namespace ProjectExplorer { + +bool Macro::isValid() const +{ + return !key.isEmpty() && type != MacroType::Invalid; +} + +QByteArray Macro::toByteArray() const +{ + switch (type) { + case MacroType::Define: return QByteArray("#define ") + key + ' ' + value; + case MacroType::Undefine: return QByteArray("#undef ") + key; + case MacroType::Invalid: break; + } + + return QByteArray(); +} + +QByteArray Macro::toByteArray(const Macros ¯os) +{ + QByteArray text; + + for (const Macro ¯o : macros) { + const QByteArray macroText = macro.toByteArray(); + if (!macroText.isEmpty()) + text += macroText + '\n'; + } + + return text; +} + +QByteArray Macro::toByteArray(const QVector<Macros> ¯osVector) +{ + QByteArray text; + + for (const Macros ¯os : macrosVector) + text += toByteArray(macros); + + return text; +} + +Macros Macro::toMacros(const QByteArray &text) +{ + return tokensLinesToMacros(tokenizeLines(splitLines(text))); +} + +Macro Macro::fromKeyValue(const QString &utf16text) +{ + return fromKeyValue(utf16text.toUtf8()); +} + +Macro Macro::fromKeyValue(const QByteArray &text) +{ + QByteArray key; + QByteArray value; + MacroType type = MacroType::Invalid; + + if (!text.isEmpty()) { + type = MacroType::Define; + + int index = text.indexOf('='); + + if (index != -1) { + key = text.left(index).trimmed(); + value = text.mid(index + 1).trimmed(); + } else { + key = text.trimmed(); + value = "1"; + } + } + + return Macro(key, value, type); +} + +QByteArray Macro::toKeyValue(const QByteArray &prefix) const +{ + QByteArray keyValue; + if (type != MacroType::Invalid) + keyValue = prefix; + + if (value.isEmpty()) + keyValue += key; + else + keyValue += key + '=' + value; + + return keyValue; +} + +static void removeCarriageReturn(QByteArray &line) +{ + if (line.endsWith('\r')) + line.truncate(line.size() - 1); +} + +static void removeCarriageReturns(QList<QByteArray> &lines) +{ + for (QByteArray &line : lines) + removeCarriageReturn(line); +} + +QList<QByteArray> Macro::splitLines(const QByteArray &text) +{ + QList<QByteArray> splitLines = text.split('\n'); + + splitLines.removeAll(""); + removeCarriageReturns(splitLines); + + return splitLines; +} + +QByteArray Macro::removeNonsemanticSpaces(QByteArray line) +{ + auto begin = line.begin(); + auto end = line.end(); + bool notInString = true; + + auto newEnd = std::unique(begin, end, [&] (char first, char second) { + notInString = notInString && first != '\"'; + return notInString && (first == '#' || std::isspace(first)) && std::isspace(second); + }); + + line.truncate(line.size() - int(std::distance(newEnd, end))); + + return line.trimmed(); +} + +QList<QByteArray> Macro::tokenizeLine(const QByteArray &line) +{ + const QByteArray normalizedLine = removeNonsemanticSpaces(line); + + const auto begin = normalizedLine.begin(); + auto first = std::find(normalizedLine.begin(), normalizedLine.end(), ' '); + auto second = std::find(std::next(first), normalizedLine.end(), ' '); + const auto end = normalizedLine.end(); + + QList<QByteArray> tokens; + + if (first != end) { + tokens.append(QByteArray(begin, int(std::distance(begin, first)))); + + std::advance(first, 1); + tokens.append(QByteArray(first, int(std::distance(first, second)))); + + if (second != end) { + std::advance(second, 1); + tokens.append(QByteArray(second, int(std::distance(second, end)))); + } + } + + return tokens; +} + +QList<QList<QByteArray>> Macro::tokenizeLines(const QList<QByteArray> &lines) +{ + QList<QList<QByteArray>> tokensLines = Utils::transform(lines, &Macro::tokenizeLine); + + return tokensLines; +} + +Macro Macro::tokensToMacro(const QList<QByteArray> &tokens) +{ + Macro macro; + + if (tokens.size() >= 2 && tokens[0] == "#define") { + macro.type = MacroType::Define; + macro.key = tokens[1]; + + if (tokens.size() >= 3) + macro.value = tokens[2]; + } + + return macro; +} + +Macros Macro::tokensLinesToMacros(const QList<QList<QByteArray>> &tokensLines) +{ + Macros macros; + macros.reserve(tokensLines.size()); + + for (const QList<QByteArray> &tokens : tokensLines) { + Macro macro = tokensToMacro(tokens); + + if (macro.type != MacroType::Invalid) + macros.push_back(std::move(macro)); + } + + return macros; +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectmacro.h b/src/plugins/projectexplorer/projectmacro.h new file mode 100644 index 0000000000..9489b54d80 --- /dev/null +++ b/src/plugins/projectexplorer/projectmacro.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "projectexplorer_export.h" + +#include <QByteArray> +#include <QHash> +#include <QVector> + +namespace ProjectExplorer { + +enum class MacroType +{ + Invalid, + Define, + Undefine +}; + +class Macro; + +using Macros = QVector<Macro>; + +class PROJECTEXPLORER_EXPORT Macro +{ +public: + Macro() = default; + + Macro(QByteArray key, QByteArray value, MacroType type = MacroType::Define) + : key(key), value(value), type(type) + {} + + Macro(QByteArray key, MacroType type = MacroType::Define) + : key(key), type(type) + {} + + bool isValid() const; + + QByteArray toByteArray() const; + static QByteArray toByteArray(const Macros ¯os); + static QByteArray toByteArray(const QVector<Macros> ¯oss); + + static Macros toMacros(const QByteArray &text); + + // define Foo will be converted to Foo=1 + static Macro fromKeyValue(const QString &utf16text); + static Macro fromKeyValue(const QByteArray &text); + QByteArray toKeyValue(const QByteArray &prefix) const; + +public: + QByteArray key; + QByteArray value; + MacroType type = MacroType::Invalid; + +private: + static QList<QByteArray> splitLines(const QByteArray &text); + static QByteArray removeNonsemanticSpaces(QByteArray line); + static QList<QByteArray> tokenizeLine(const QByteArray &line); + static QList<QList<QByteArray>> tokenizeLines(const QList<QByteArray> &lines); + static Macro tokensToMacro(const QList<QByteArray> &tokens); + static Macros tokensLinesToMacros(const QList<QList<QByteArray>> &tokensLines); +}; + +inline +uint qHash(const Macro ¯o) +{ + using ::qHash; + return qHash(macro.key) ^ qHash(macro.value) ^ qHash(int(macro.type)); +} + +inline +bool operator==(const Macro &first, const Macro &second) +{ + return first.type == second.type + && first.key == second.key + && first.value == second.value; +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 8c461bd398..4ee56f05a8 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -27,6 +27,7 @@ #include "projectexplorer_export.h" #include "projectexplorer_global.h" +#include "projectmacro.h" #include <coreplugin/id.h> @@ -122,9 +123,9 @@ public: virtual WarningFlags warningFlags(const QStringList &cflags) const = 0; // A PredefinedMacrosRunner is created in the ui thread and runs in another thread. - using PredefinedMacrosRunner = std::function<QByteArray(const QStringList &cxxflags)>; + using PredefinedMacrosRunner = std::function<Macros(const QStringList &cxxflags)>; virtual PredefinedMacrosRunner createPredefinedMacrosRunner() const = 0; - virtual QByteArray predefinedMacros(const QStringList &cxxflags) const = 0; + virtual Macros predefinedMacros(const QStringList &cxxflags) const = 0; // A SystemHeaderPathsRunner is created in the ui thread and runs in another thread. using SystemHeaderPathsRunner = std::function<QList<HeaderPath>(const QStringList &cxxflags, const QString &sysRoot)>; diff --git a/src/plugins/projectexplorer/toolchainmanager.cpp b/src/plugins/projectexplorer/toolchainmanager.cpp index df7d6888ec..b6c79b8773 100644 --- a/src/plugins/projectexplorer/toolchainmanager.cpp +++ b/src/plugins/projectexplorer/toolchainmanager.cpp @@ -492,7 +492,7 @@ public: Abi targetAbi() const override { return Abi::hostAbi(); } bool isValid() const override { return m_valid; } PredefinedMacrosRunner createPredefinedMacrosRunner() const override { return PredefinedMacrosRunner(); } - QByteArray predefinedMacros(const QStringList &cxxflags) const override { Q_UNUSED(cxxflags); return QByteArray(); } + Macros predefinedMacros(const QStringList &cxxflags) const override { Q_UNUSED(cxxflags); return Macros(); } CompilerFlags compilerFlags(const QStringList &cxxflags) const override { Q_UNUSED(cxxflags); return NoFlags; } WarningFlags warningFlags(const QStringList &cflags) const override { Q_UNUSED(cflags); return WarningFlags::NoWarnings; } SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override { return SystemHeaderPathsRunner(); } diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 11e035a0e7..902bed93f0 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -975,17 +975,7 @@ void QbsProject::updateCppCodeModel() QStringList list = props.getModulePropertiesAsStringList( QLatin1String(CONFIG_CPP_MODULE), QLatin1String(CONFIG_DEFINES)); - QByteArray grpDefines; - foreach (const QString &def, list) { - QByteArray data = def.toUtf8(); - int pos = data.indexOf('='); - if (pos >= 0) - data[pos] = ' '; - else - data.append(" 1"); // cpp.defines: [ "FOO" ] is considered to be "FOO=1" - grpDefines += (QByteArray("#define ") + data + '\n'); - } - rpp.setDefines(grpDefines); + rpp.setMacros(Utils::transform<QVector>(list, [](const QString &s) { return ProjectExplorer::Macro::fromKeyValue(s); })); list = props.getModulePropertiesAsStringList(QLatin1String(CONFIG_CPP_MODULE), QLatin1String(CONFIG_INCLUDEPATHS)); diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 0d4703b15d..f53192e016 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -292,7 +292,7 @@ void QmakeProject::updateCppCodeModel() rpp.setBuildSystemTarget(pro->targetInformation().target); // TODO: Handle QMAKE_CFLAGS rpp.setFlagsForCxx({cxxToolChain, pro->variableValue(Variable::CppFlags)}); - rpp.setDefines(pro->cxxDefines()); + rpp.setMacros(ProjectExplorer::Macro::toMacros(pro->cxxDefines())); rpp.setPreCompiledHeaders(pro->variableValue(Variable::PrecompiledHeader)); rpp.setSelectedForBuilding(pro->includedInExactParse()); diff --git a/tests/unit/mockup/projectexplorer/toolchain.h b/tests/unit/mockup/projectexplorer/toolchain.h index 33bfa836df..9bcf43a59b 100644 --- a/tests/unit/mockup/projectexplorer/toolchain.h +++ b/tests/unit/mockup/projectexplorer/toolchain.h @@ -27,6 +27,7 @@ #include <projectexplorer/headerpath.h> #include <projectexplorer/abi.h> +#include <projectexplorer/projectmacro.h> #include <coreplugin/id.h> #include <functional> @@ -59,7 +60,7 @@ public: using SystemHeaderPathsRunner = std::function<QList<HeaderPath>(const QStringList &cxxflags, const QString &sysRoot)>; virtual SystemHeaderPathsRunner createSystemHeaderPathsRunner() const { return SystemHeaderPathsRunner(); } - using PredefinedMacrosRunner = std::function<QByteArray(const QStringList &cxxflags)>; + using PredefinedMacrosRunner = std::function<Macros(const QStringList &cxxflags)>; virtual PredefinedMacrosRunner createPredefinedMacrosRunner() const { return PredefinedMacrosRunner(); } virtual QString originalTargetTriple() const { return QString(); } diff --git a/tests/unit/unittest/creator_dependency.pri b/tests/unit/unittest/creator_dependency.pri index edf7417029..b9a5db097c 100644 --- a/tests/unit/unittest/creator_dependency.pri +++ b/tests/unit/unittest/creator_dependency.pri @@ -7,13 +7,13 @@ include($$PWD/../../../src/libs/utils/utils-lib.pri) include($$PWD/../../../src/libs/sqlite/sqlite-lib.pri) include($$PWD/../../../src/libs/clangsupport/clangsupport-lib.pri) include($$PWD/../../../src/plugins/coreplugin/corepluginunittestfiles.pri) +include($$PWD/../../../src/plugins/projectexplorer/projectexplorerunittestfiles.pri) include($$PWD/../../../src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri) include($$PWD/../../../src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri) include($$PWD/../../../src/plugins/clangrefactoring/clangrefactoring-source.pri) include($$PWD/../../../src/plugins/clangpchmanager/clangpchmanager-source.pri) include($$PWD/../../../src/plugins/cpptools/cpptoolsunittestfiles.pri) include(cplusplus.pri) - !isEmpty(LLVM_INSTALL_DIR) { include($$PWD/../../../src/shared/clang/clang_defines.pri) include($$PWD/../../../src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri) diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index 4a04cec394..7a1fe3b30f 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -43,4 +43,5 @@ using testing::Property; using testing::Return; using testing::ReturnRef; using testing::Sequence; +using testing::StrEq; using testing::UnorderedElementsAre; diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 5980ec6c63..d7cb22b7ce 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -29,6 +29,8 @@ #include <coreplugin/find/searchresultitem.h> +#include <projectexplorer/projectmacro.h> + namespace Core { namespace Search { @@ -54,3 +56,35 @@ void PrintTo(const TextRange &range, ::std::ostream *os) } } + +namespace ProjectExplorer { + +static const char *typeToString(const MacroType &type) +{ + switch (type) { + case MacroType::Invalid: return "MacroType::Invalid"; + case MacroType::Define: return "MacroType::Define"; + case MacroType::Undefine: return "MacroType::Undefine"; + } + + return ""; +} + +std::ostream &operator<<(std::ostream &out, const MacroType &type) +{ + out << typeToString(type); + + return out; +} + +std::ostream &operator<<(std::ostream &out, const Macro ¯o) +{ + out << "(" + << macro.key.data() << ", " + << macro.value.data() << ", " + << macro.type << ")"; + + return out; +} + +} diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index 814130f227..92db70f6ed 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -42,3 +42,13 @@ void PrintTo(const TextRange &range, ::std::ostream *os); } } + +namespace ProjectExplorer { + +enum class MacroType; +class Macro; + +std::ostream &operator<<(std::ostream &out, const MacroType &type); +std::ostream &operator<<(std::ostream &out, const Macro ¯o); + +} diff --git a/tests/unit/unittest/projectmacro-test.cpp b/tests/unit/unittest/projectmacro-test.cpp new file mode 100644 index 0000000000..2c9258e635 --- /dev/null +++ b/tests/unit/unittest/projectmacro-test.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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" + +#define private public +#include <projectexplorer/projectmacro.h> +#undef private + +namespace { + +using ProjectExplorer::MacroType; +using ProjectExplorer::Macro; +using ProjectExplorer::Macros; + +MATCHER_P3(IsMacro, key, value, type, + std::string(negation ? "isn't" : "is") + + " key "+ PrintToString(key) + + ", value " + PrintToString(value) + + " and type " + PrintToString(type)) +{ + return arg.key == key && arg.value == value && arg.type == type; +} + +TEST(Macro, SplitLines) +{ + QByteArray text = "#define Foo 42\n\n#define Bar\n#define HoHoHo Bar\n// foo"; + + auto textLines = Macro::splitLines(text); + + ASSERT_THAT(textLines, ElementsAre("#define Foo 42", "#define Bar", "#define HoHoHo Bar", "// foo")); +} + +TEST(Macro, RemoveCarriageReturn) +{ + QByteArray text = "#define Foo 42\r\n#define Bar\n"; + + auto textLines = Macro::splitLines(text); + + ASSERT_THAT(textLines, ElementsAre("#define Foo 42", "#define Bar")); +} + +TEST(Macro, TokenizeNullLine) +{ + QByteArray line; + + auto tokens = Macro::tokenizeLine(line); + + ASSERT_THAT(tokens, IsEmpty()); +} + +TEST(Macro, TokenizeEmptyLine) +{ + QByteArray line = ""; + + auto tokens = Macro::tokenizeLine(line); + + ASSERT_THAT(tokens, IsEmpty()); +} + +TEST(Macro, TokenizeSpaces) +{ + QByteArray line = " "; + + auto tokens = Macro::tokenizeLine(line); + + ASSERT_THAT(tokens, IsEmpty()); +} + +TEST(Macro, TokenizeOneEntry) +{ + QByteArray line = "//blah"; + + auto tokens = Macro::tokenizeLine(line); + + ASSERT_THAT(tokens, IsEmpty()); +} + +TEST(Macro, TokenizeThreeEntries) +{ + QByteArray line = "#define Foo 42"; + + auto tokens = Macro::tokenizeLine(line); + + ASSERT_THAT(tokens, ElementsAre("#define", "Foo", "42")); +} + +TEST(Macro, TokenizeManyEntries) +{ + QByteArray line = "#define Foo unsigned long long int"; + + auto tokens = Macro::tokenizeLine(line); + + ASSERT_THAT(tokens, ElementsAre("#define", "Foo", "unsigned long long int")); +} + +TEST(Macro, TokenizeWithMutipleSpaces) +{ + QByteArray line = "#define Foo 42"; + + auto tokens = Macro::tokenizeLine(line); + + ASSERT_THAT(tokens, ElementsAre("#define", "Foo", "42")); +} + +TEST(Macro, TokenizeLines) +{ + QList<QByteArray> lines = {"#define Foo 42", + "#define Bar Ho", + "// this is a comment", + " "}; + + auto tokensLines = Macro::tokenizeLines(lines); + + ASSERT_THAT(tokensLines, + ElementsAre(ElementsAre("#define", "Foo", "42"), + ElementsAre("#define", "Bar", "Ho"), + ElementsAre("//", "this", "is a comment"), + IsEmpty())); +} + +TEST(Macro, SpacesBeforEntries) +{ + QByteArray line = " #define Foo"; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo"); +} + +TEST(Macro, SpacesAfterEntries) +{ + QByteArray line = "#define Foo "; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo"); +} + +TEST(Macro, ManySpacesInbetweenEntries) +{ + QByteArray line = "#define \t Foo 42"; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo 42"); +} + +TEST(Macro, EmptyString) +{ + QByteArray line = "#define \t Foo \"\""; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo \"\""); +} + +TEST(Macro, StringWithQuotes) +{ + QByteArray line = "#define \t Foo \"\\\"string\\\"\""; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo \"\\\"string\\\"\""); +} + +TEST(Macro, DISABLED_StringConcatenation) +{ + QByteArray line = "#define \t Foo \"a\" \"b\""; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo \"a\" \"b\""); +} + +TEST(Macro, DISABLED_TokenConcatenation) +{ + QByteArray line = "#define \t Foo \"a\" ## c"; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo \"a\" ## c"); +} + +TEST(Macro, StringWithQuotesAndSpaces) +{ + QByteArray line = "#define \t Foo \"\\\"string \\\"\""; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine.toStdString(), "#define Foo \"\\\"string \\\"\""); +} + +TEST(Macro, SpacesAferHashEntries) +{ + QByteArray line = "# define Foo 42"; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo 42"); +} + +TEST(Macro, SpacesInStringEntries) +{ + QByteArray line = "#define Foo \"some text with spaces\""; + + auto strippedLine = Macro::removeNonsemanticSpaces(line); + + ASSERT_THAT(strippedLine, "#define Foo \"some text with spaces\""); +} + +TEST(Macro, EmptyTokensToMacro) +{ + QList<QByteArray> tokens; + + auto macro = Macro::tokensToMacro(tokens); + + ASSERT_THAT(macro, IsMacro("", "", MacroType::Invalid)); +} + +TEST(Macro, DefineTwoTokensToMacro) +{ + QList<QByteArray> tokens = {"#define", "Foo"}; + + auto macro = Macro::tokensToMacro(tokens); + + ASSERT_THAT(macro, IsMacro("Foo", "", MacroType::Define)); +} + +TEST(Macro, DefineThreeTokensToMacro) +{ + QList<QByteArray> tokens = {"#define", "Foo", "42"}; + + auto macro = Macro::tokensToMacro(tokens); + + ASSERT_THAT(macro, IsMacro("Foo", "42", MacroType::Define)); +} + +TEST(Macro, DoNotParseNonDefines) +{ + QList<QByteArray> tokens = {"//", "this", "is a comment"}; + + auto macro = Macro::tokensToMacro(tokens); + + ASSERT_THAT(macro, IsMacro("", "", MacroType::Invalid)); +} + +TEST(Macro, TokensLinesToMacros) +{ + QList<QList<QByteArray>> tokensLines = {{"#define", "Foo", "42"}, + {"#define", "Bar", "Ho"}, + {"//", "this", "is", "a", "comment"}, + {}}; + + auto macros = Macro::tokensLinesToMacros(tokensLines); + + ASSERT_THAT(macros, ElementsAre(IsMacro("Foo", "42", MacroType::Define), + IsMacro("Bar", "Ho", MacroType::Define))); +} + + +TEST(Macro, TextToMacros) +{ + QByteArray text = {"#define Foo 42\n" + "#define Bar Ho\n" + "// this is a comment\n" + " "}; + + auto macros = Macro::toMacros(text); + + ASSERT_THAT(macros, ElementsAre(IsMacro("Foo", "42", MacroType::Define), + IsMacro("Bar", "Ho", MacroType::Define))); +} + +TEST(Macro, InvalidToText) +{ + Macro macro; + + auto text = macro.toByteArray(); + + ASSERT_THAT(text, QByteArray()); +} + +TEST(Macro, DefineToText) +{ + Macro macro{"Foo", "Bar", MacroType::Define}; + + auto text = macro.toByteArray(); + + ASSERT_THAT(text, "#define Foo Bar"); +} + +TEST(Macro, DefineWithSpacesToText) +{ + Macro macro{"LongInt", "long long int", MacroType::Define}; + + auto text = macro.toByteArray(); + + ASSERT_THAT(text, "#define LongInt long long int"); +} + +TEST(Macro, UndefineToText) +{ + Macro macro{"Foo", "Bar", MacroType::Undefine}; + + auto text = macro.toByteArray(); + + ASSERT_THAT(text, "#undef Foo"); +} + +TEST(Macro, MacrosToText) +{ + Macros macros{{"LongInt", "long long int"}, {"Foo", "Bar"}, {}}; + + auto text = Macro::toByteArray(macros); + + ASSERT_THAT(text, "#define LongInt long long int\n#define Foo Bar\n"); +} + +TEST(Macro, MacrosVectorToText) +{ + Macros macros{{"LongInt", "long long int"}, {"Foo", "Bar"}, {}, {"Foo", MacroType::Undefine}}; + + auto text = Macro::toByteArray(macros); + + ASSERT_THAT(text, "#define LongInt long long int\n#define Foo Bar\n#undef Foo\n"); +} + +TEST(Macro, EmptyKeyValueToMacro) +{ + QString text; + + auto macro = Macro::fromKeyValue(text); + + ASSERT_THAT(macro, IsMacro("", "", MacroType::Invalid)); +} + +TEST(Macro, KeyToMacro) +{ + QString text = "Foo"; + + auto macro = Macro::fromKeyValue(text); + + ASSERT_THAT(macro, IsMacro("Foo", "1", MacroType::Define)); +} + +TEST(Macro, KeyValueToMacro) +{ + QString text = "Foo=\"Some=Text\""; + + auto macro = Macro::fromKeyValue(text); + + ASSERT_THAT(macro, IsMacro("Foo", "\"Some=Text\"", MacroType::Define)); +} + +TEST(Macro, InvalidMacroToKeyValue) +{ + Macro macro; + QByteArray prefix = "-D"; + + auto keyValue = macro.toKeyValue(prefix); + + ASSERT_THAT(keyValue, ""); +} + +TEST(Macro, DefineKeyOnlyMacroToKeyValue) +{ + Macro macro{"Foo"}; + QByteArray prefix = "-D"; + + auto keyValue = macro.toKeyValue(prefix); + + ASSERT_THAT(keyValue, "-DFoo"); +} + +TEST(Macro, DefineMacroToKeyValue) +{ + Macro macro{"Foo", "Bar"}; + QByteArray prefix = "-D"; + + auto keyValue = macro.toKeyValue(prefix); + + ASSERT_THAT(keyValue, "-DFoo=Bar"); +} +} + diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 1f1b490536..e4f2c363c0 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -37,7 +37,6 @@ DEFINES += CPPTOOLS_JSON=\"R\\\"xxx($${cpptoolsjson.output})xxx\\\"\" SOURCES += \ changedfilepathcompressor-test.cpp \ - clangfollowsymbol-test.cpp \ clangpathwatcher-test.cpp \ clangqueryexamplehighlightmarker-test.cpp \ clangqueryhighlightmarker-test.cpp \ @@ -59,6 +58,7 @@ SOURCES += \ pchmanagerclient-test.cpp \ pchmanagerserver-test.cpp \ processevents-utilities.cpp \ + projectmacro-test.cpp \ projectparts-test.cpp \ projectupdater-test.cpp \ readandwritemessageblock-test.cpp \ @@ -96,6 +96,7 @@ SOURCES += \ clangdocumentsuspenderresumer-test.cpp \ clangdocument-test.cpp \ clangfixitoperation-test.cpp \ + clangfollowsymbol-test.cpp \ clangisdiagnosticrelatedtolocation-test.cpp \ clangjobqueue-test.cpp \ clangjobs-test.cpp \ |