Commit b6e12f4a authored by Marco Bubke's avatar Marco Bubke

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's avatarTobias Hunger <tobias.hunger@qt.io>
parent 3adb71d4
......@@ -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;
}
......
......@@ -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 &macros = parts.at(0)->projectMacros;
auto found = std::find_if(
macros.begin(),
macros.end(),
[] (const ProjectExplorer::Macro &macro) { 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();
}
......
......@@ -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}});
......
......@@ -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;
......
......@@ -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
......
......@@ -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();
}
......@@ -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()
};
......
......@@ -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 &macros)
{
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 &macros)
{
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"));
......
......@@ -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();
......
......@@ -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));
}
}
......
......@@ -559,7 +559,7 @@ void CMakeBuildTarget::clear()
targetType = UtilityType;
includeFiles.clear();
compilerOptions.clear();
defines.clear();
macros.clear();
files.clear();
}
......
......@@ -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();
......
......@@ -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;
}
}
......
......@@ -86,7 +86,7 @@ private:
Target *target = nullptr;
QString compileFlags;
QStringList defines;
ProjectExplorer::Macros macros;
QList<IncludePath *> includePaths;
QString language;
QList<Utils::FileName> sources;
......
......@@ -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(); }));
......
......@@ -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> &macros);
void configure(const QList<CPlusPlus::Macro> &macros);
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> &macros)
void MacrosModel::configure(const QList<CPlusPlus::Macro> &macros)
{
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 &macros)
{
using ProjectExplorer::Macro;
return int(std::count_if(
macros.begin(),
macros.end(),
[](const Macro &macro) { 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));
......
......@@ -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;
......
......@@ -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 &macro)
{
const QByteArray str = defineDirective.mid(8);
const int spaceIdx = str.indexOf(' ');
const bool hasValue = spaceIdx != -1;
Macro macro;