diff --git a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp index 2756f0a7e7..ae90d623c7 100644 --- a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp +++ b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp @@ -1,253 +1,251 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "compilerprovider.h" #include "debug.h" #include "qtcompat_p.h" #include "compilerfactories.h" #include "settingsmanager.h" #include #include #include #include #include #include #include using namespace KDevelop; namespace { class NoCompiler : public ICompiler { public: NoCompiler(): ICompiler(i18n("None"), QString(), QString(), false) {} QHash< QString, QString > defines(Utils::LanguageType, const QString&) const override { return {}; } Path::List includes(Utils::LanguageType, const QString&) const override { return {}; } }; static CompilerPointer createDummyCompiler() { static CompilerPointer compiler(new NoCompiler()); return compiler; } ConfigEntry configForItem(KDevelop::ProjectBaseItem* item) { if(!item){ return ConfigEntry(); } const Path itemPath = item->path(); const Path rootDirectory = item->project()->path(); auto paths = SettingsManager::globalInstance()->readPaths(item->project()->projectConfiguration().data()); ConfigEntry config; Path closestPath; // find config entry closest to the requested item for (const auto& entry : paths) { auto configEntry = entry; Path targetDirectory = rootDirectory; targetDirectory.addPath(entry.path); if (targetDirectory == itemPath) { return configEntry; } if (targetDirectory.isParentOf(itemPath)) { if (config.path.isEmpty() || targetDirectory.segments().size() > closestPath.segments().size()) { config = configEntry; closestPath = targetDirectory; } } } return config; } } CompilerProvider::CompilerProvider( SettingsManager* settings, QObject* parent ) : QObject( parent ) , m_settings(settings) { m_factories.append(CompilerFactoryPointer(new GccFactory())); m_factories.append(CompilerFactoryPointer(new ClangFactory())); #ifdef _WIN32 m_factories.append(CompilerFactoryPointer(new MsvcFactory())); #endif if (!QStandardPaths::findExecutable( QStringLiteral("clang") ).isEmpty()) { m_factories[1]->registerDefaultCompilers(this); } if (!QStandardPaths::findExecutable( QStringLiteral("gcc") ).isEmpty()) { m_factories[0]->registerDefaultCompilers(this); } #ifdef _WIN32 if (!QStandardPaths::findExecutable("cl.exe").isEmpty()) { m_factories[2]->registerDefaultCompilers(this); } #endif registerCompiler(createDummyCompiler()); retrieveUserDefinedCompilers(); connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, [this]() { m_defaultProvider.clear(); }); } CompilerProvider::~CompilerProvider() = default; QHash CompilerProvider::defines( ProjectBaseItem* item ) const { auto config = configForItem(item); auto languageType = Utils::Cpp; if (item) { languageType = Utils::languageType(item->path(), config.parserArguments.parseAmbiguousAsCPP); } // If called on files that we can't compile, return an empty set of defines. if (languageType == Utils::Other) { return {}; } - return config.compiler->defines(languageType, - languageType == Utils::C ? config.parserArguments.cArguments : config.parserArguments.cppArguments); + return config.compiler->defines(languageType, config.parserArguments[languageType]); } Path::List CompilerProvider::includes( ProjectBaseItem* item ) const { auto config = configForItem(item); auto languageType = Utils::Cpp; if (item) { languageType = Utils::languageType(item->path(), config.parserArguments.parseAmbiguousAsCPP); } // If called on files that we can't compile, return an empty set of includes. if (languageType == Utils::Other) { return {}; } - return config.compiler->includes(languageType, - languageType == Utils::C ? config.parserArguments.cArguments : config.parserArguments.cppArguments); + return config.compiler->includes(languageType, config.parserArguments[languageType]); } Path::List CompilerProvider::frameworkDirectories( ProjectBaseItem* /* item */ ) const { return {}; } IDefinesAndIncludesManager::Type CompilerProvider::type() const { return IDefinesAndIncludesManager::CompilerSpecific; } CompilerPointer CompilerProvider::defaultCompiler() const { if (m_defaultProvider) return m_defaultProvider; auto rt = ICore::self()->runtimeController()->currentRuntime(); const auto path = QFile::decodeName(rt->getenv("PATH")).split(QtCompat::listSeparator()); for ( const CompilerPointer& compiler : m_compilers ) { const bool absolutePath = QDir::isAbsolutePath(compiler->path()); if ((absolutePath && QFileInfo::exists(rt->pathInHost(Path(compiler->path())).toLocalFile())) || QStandardPaths::findExecutable( compiler->path(), path).isEmpty() ) { continue; } m_defaultProvider = compiler; break; } if (!m_defaultProvider) m_defaultProvider = createDummyCompiler(); qCDebug(DEFINESANDINCLUDES) << "new default compiler" << rt->name() << m_defaultProvider->name() << m_defaultProvider->path(); return m_defaultProvider; } QVector< CompilerPointer > CompilerProvider::compilers() const { return m_compilers; } CompilerPointer CompilerProvider::compilerForItem( KDevelop::ProjectBaseItem* item ) const { auto compiler = configForItem(item).compiler; Q_ASSERT(compiler); return compiler; } bool CompilerProvider::registerCompiler(const CompilerPointer& compiler) { if (!compiler) { return false; } for(auto c: m_compilers){ if (c->name() == compiler->name()) { return false; } } m_compilers.append(compiler); return true; } void CompilerProvider::unregisterCompiler(const CompilerPointer& compiler) { if (!compiler->editable()) { return; } for (int i = 0; i < m_compilers.count(); i++) { if (m_compilers[i]->name() == compiler->name()) { m_compilers.remove(i); break; } } } QVector< CompilerFactoryPointer > CompilerProvider::compilerFactories() const { return m_factories; } void CompilerProvider::retrieveUserDefinedCompilers() { auto compilers = m_settings->userDefinedCompilers(); for (auto c : compilers) { registerCompiler(c); } } diff --git a/plugins/custom-definesandincludes/compilerprovider/icompiler.h b/plugins/custom-definesandincludes/compilerprovider/icompiler.h index 6d4325971e..450f6e69ad 100644 --- a/plugins/custom-definesandincludes/compilerprovider/icompiler.h +++ b/plugins/custom-definesandincludes/compilerprovider/icompiler.h @@ -1,99 +1,99 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef ICOMPILER_H #define ICOMPILER_H #include #include #include "../idefinesandincludesmanager.h" namespace Utils { enum LanguageType { C, Cpp, OpenCl, Cuda, ObjC, - Other = 100 + Other }; } /// An interface that represents a compiler. Compiler provides standard include directories and standard defined macros. class ICompiler { public: /** * @param name The user visible name * @param path path to the compiler * @param factoryName name of the factory that created this compiler * @param editable whether user can change the name and the path to the compiler (should be set to false for automatically detected compilers) **/ ICompiler( const QString& name, const QString& path, const QString& factoryName, bool editable ); /** * @param arguments compiler command-line arguments * @return list of defined macros for the compiler */ virtual KDevelop::Defines defines(Utils::LanguageType type, const QString& arguments) const = 0; /** * @param arguments compiler command-line arguments * @return list of include directories for the compiler */ virtual KDevelop::Path::List includes(Utils::LanguageType type, const QString& arguments) const = 0; void setPath( const QString &path ); /// @return path to the compiler QString path() const; void setName( const QString &name ); /// @return user visible name QString name() const; /// Indicates if the compiler name/path can be set manually bool editable() const; /// @return name of the factory that created this compiler QString factoryName() const; virtual ~ICompiler() = default; private: bool m_editable; QString m_name; QString m_path; QString m_factoryName; }; typedef QSharedPointer CompilerPointer; Q_DECLARE_METATYPE(CompilerPointer); #endif // ICOMPILER_H diff --git a/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp b/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp index 5b4fad717c..fcdf041cdc 100644 --- a/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp +++ b/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp @@ -1,431 +1,456 @@ /* * This file is part of KDevelop * * Copyright 2010 Andreas Pakulat * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "settingsmanager.h" #include #include #include #include #include #include #include #include #include +#include + #include "compilerprovider.h" using namespace KDevelop; namespace { +constexpr Utils::LanguageType configurableLanguageTypes[] = + { Utils::C, Utils::Cpp, Utils::OpenCl, Utils::Cuda }; + namespace ConfigConstants { const QString configKey = QStringLiteral( "CustomDefinesAndIncludes" ); const QString definesKey = QStringLiteral( "Defines" ); const QString includesKey = QStringLiteral( "Includes" ); const QString projectPathPrefix = QStringLiteral( "ProjectPath" ); const QString projectPathKey = QStringLiteral( "Path" ); const QString customBuildSystemGroup = QStringLiteral( "CustomBuildSystem" ); const QString definesAndIncludesGroup = QStringLiteral( "Defines And Includes" ); const QString compilersGroup = QStringLiteral( "Compilers" ); const QString compilerNameKey = QStringLiteral( "Name" ); const QString compilerPathKey = QStringLiteral( "Path" ); const QString compilerTypeKey = QStringLiteral( "Type" ); -QString parserArgumentsCPP() -{ - return QStringLiteral("parserArguments"); -} - -QString parserArgumentsC() +QString parserArgumentsKey(Utils::LanguageType languageType) { - return QStringLiteral("parserArgumentsC"); + switch (languageType) { + case Utils::C: + return QStringLiteral("parserArgumentsC"); + case Utils::Cpp: + return QStringLiteral("parserArguments"); + case Utils::OpenCl: + return QStringLiteral("parserArgumentsOpenCL"); + case Utils::Cuda: + return QStringLiteral("parserArgumentsCuda"); + case Utils::ObjC: + case Utils::Other: + break; + } + Q_UNREACHABLE(); } QString parseAmbiguousAsCPP() { return QStringLiteral("parseAmbiguousAsCPP"); } } // the grouplist is randomly sorted b/c it uses QSet internally // we sort the keys here, as the structure is properly defined for us QStringList sorted(QStringList list) { std::sort(list.begin(), list.end()); return list; } -ParserArguments defaultArguments() +ParserArguments createDefaultArguments() { - const static ParserArguments arguments{ - QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c99"), - QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c++11"), - QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=CL1.1"), - QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c++11"), - true - }; + ParserArguments arguments; + arguments[Utils::C] = QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c99"); + arguments[Utils::Cpp] = QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c++11"); + arguments[Utils::OpenCl] = QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -cl-std=CL1.1"); + arguments[Utils::Cuda] = QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c++11"); + arguments[Utils::ObjC] = QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c99"); + arguments.parseAmbiguousAsCPP = true; + + return arguments; +} +const ParserArguments& defaultArguments() +{ + static ParserArguments arguments = createDefaultArguments(); return arguments; } CompilerPointer createCompilerFromConfig(KConfigGroup& cfg) { auto grp = cfg.group("Compiler"); auto name = grp.readEntry( ConfigConstants::compilerNameKey, QString() ); if (name.isEmpty()) { return SettingsManager::globalInstance()->provider()->defaultCompiler(); } for (auto c : SettingsManager::globalInstance()->provider()->compilers()) { if (c->name() == name) { return c; } } // Otherwise we have no such compiler registered (broken config file), return default one return SettingsManager::globalInstance()->provider()->defaultCompiler(); } void writeCompilerToConfig(KConfigGroup& cfg, const CompilerPointer& compiler) { Q_ASSERT(compiler); auto grp = cfg.group("Compiler"); // Store only compiler name, path and type retrieved from registered compilers grp.writeEntry(ConfigConstants::compilerNameKey, compiler->name()); } void doWriteSettings( KConfigGroup grp, const QVector& paths ) { int pathIndex = 0; for ( const auto& path : paths ) { KConfigGroup pathgrp = grp.group( ConfigConstants::projectPathPrefix + QString::number( pathIndex++ ) ); pathgrp.writeEntry(ConfigConstants::projectPathKey, path.path); - pathgrp.writeEntry(ConfigConstants::parserArgumentsCPP(), path.parserArguments.cppArguments); - pathgrp.writeEntry(ConfigConstants::parserArgumentsC(), path.parserArguments.cArguments); + for (auto type : configurableLanguageTypes) { + pathgrp.writeEntry(ConfigConstants::parserArgumentsKey(type), path.parserArguments[type]); + } pathgrp.writeEntry(ConfigConstants::parseAmbiguousAsCPP(), path.parserArguments.parseAmbiguousAsCPP); { int index = 0; KConfigGroup includes(pathgrp.group(ConfigConstants::includesKey)); for( auto it = path.includes.begin() ; it != path.includes.end(); ++it){ includes.writeEntry(QString::number(++index), *it); } } { KConfigGroup defines(pathgrp.group(ConfigConstants::definesKey)); for (auto it = path.defines.begin(); it != path.defines.end(); ++it) { defines.writeEntry(it.key(), it.value()); } } writeCompilerToConfig(pathgrp, path.compiler); } } /// @param remove if true all read entries will be removed from the config file QVector doReadSettings( KConfigGroup grp, bool remove = false ) { QVector paths; for( const QString &grpName : sorted(grp.groupList()) ) { if ( !grpName.startsWith( ConfigConstants::projectPathPrefix ) ) { continue; } KConfigGroup pathgrp = grp.group( grpName ); ConfigEntry path; path.path = pathgrp.readEntry( ConfigConstants::projectPathKey, "" ); - path.parserArguments.cppArguments = pathgrp.readEntry(ConfigConstants::parserArgumentsCPP(), defaultArguments().cppArguments); - path.parserArguments.cArguments = pathgrp.readEntry(ConfigConstants::parserArgumentsC(), defaultArguments().cArguments); - path.parserArguments.parseAmbiguousAsCPP = pathgrp.readEntry(ConfigConstants::parseAmbiguousAsCPP(), defaultArguments().parseAmbiguousAsCPP); - - if (path.parserArguments.cppArguments.isEmpty()) { - path.parserArguments.cppArguments = defaultArguments().cppArguments; + for (auto type : configurableLanguageTypes) { + path.parserArguments[type] = pathgrp.readEntry(ConfigConstants::parserArgumentsKey(type), defaultArguments()[type]); } + path.parserArguments.parseAmbiguousAsCPP = pathgrp.readEntry(ConfigConstants::parseAmbiguousAsCPP(), defaultArguments().parseAmbiguousAsCPP); - if (path.parserArguments.cArguments.isEmpty()) { - path.parserArguments.cArguments = defaultArguments().cArguments; + for (auto type : configurableLanguageTypes) { + if (path.parserArguments[type].isEmpty()) { + path.parserArguments[type] = defaultArguments()[type]; + } } { // defines // Backwards compatibility with old config style if(pathgrp.hasKey(ConfigConstants::definesKey)) { QByteArray tmp = pathgrp.readEntry( ConfigConstants::definesKey, QByteArray() ); QDataStream s( tmp ); s.setVersion( QDataStream::Qt_4_5 ); // backwards compatible reading QHash defines; s >> defines; path.setDefines(defines); } else { KConfigGroup defines(pathgrp.group(ConfigConstants::definesKey)); QMap defMap = defines.entryMap(); path.defines.reserve(defMap.size()); for(auto it = defMap.constBegin(); it != defMap.constEnd(); ++it) { QString key = it.key(); if(key.isEmpty()) { // Toss out the invalid key and value since a valid // value needs a valid key continue; } else { path.defines.insert(key, it.value()); } } } } { // includes // Backwards compatibility with old config style if(pathgrp.hasKey(ConfigConstants::includesKey)){ QByteArray tmp = pathgrp.readEntry( ConfigConstants::includesKey, QByteArray() ); QDataStream s( tmp ); s.setVersion( QDataStream::Qt_4_5 ); s >> path.includes; } else { KConfigGroup includes(pathgrp.group(ConfigConstants::includesKey)); QMap incMap = includes.entryMap(); for(auto it = incMap.begin(); it != incMap.end(); ++it){ QString value = it.value(); if(value.isEmpty()){ continue; } path.includes += value; } } } path.compiler = createCompilerFromConfig(pathgrp); if ( remove ) { pathgrp.deleteGroup(); } - Q_ASSERT(!path.parserArguments.cppArguments.isEmpty()); - Q_ASSERT(!path.parserArguments.cArguments.isEmpty()); + Q_ASSERT(!path.parserArguments.isAnyEmpty()); paths << path; } return paths; } /** * Reads and converts paths from old (Custom Build System's) format to the current one. * @return all converted paths (if any) */ QVector convertedPaths( KConfig* cfg ) { KConfigGroup group = cfg->group( ConfigConstants::customBuildSystemGroup ); if ( !group.isValid() ) return {}; QVector paths; foreach( const QString &grpName, sorted(group.groupList()) ) { KConfigGroup subgroup = group.group( grpName ); if ( !subgroup.isValid() ) continue; paths += doReadSettings( subgroup, true ); } return paths; } } void ConfigEntry::setDefines(const QHash& newDefines) { defines.clear(); defines.reserve(newDefines.size()); for (auto it = newDefines.begin(); it != newDefines.end(); ++it) { defines[it.key()] = it.value().toString(); } } SettingsManager::SettingsManager() : m_provider(this) {} SettingsManager::~SettingsManager() {} SettingsManager* SettingsManager::globalInstance() { Q_ASSERT(QThread::currentThread() == qApp->thread()); static SettingsManager s_globalInstance; return &s_globalInstance; } CompilerProvider* SettingsManager::provider() { return &m_provider; } const CompilerProvider* SettingsManager::provider() const { return &m_provider; } void SettingsManager::writePaths( KConfig* cfg, const QVector< ConfigEntry >& paths ) { Q_ASSERT(QThread::currentThread() == qApp->thread()); KConfigGroup grp = cfg->group( ConfigConstants::configKey ); if ( !grp.isValid() ) return; grp.deleteGroup(); doWriteSettings( grp, paths ); } QVector SettingsManager::readPaths( KConfig* cfg ) const { Q_ASSERT(QThread::currentThread() == qApp->thread()); auto converted = convertedPaths( cfg ); if ( !converted.isEmpty() ) { const_cast(this)->writePaths( cfg, converted ); return converted; } KConfigGroup grp = cfg->group( ConfigConstants::configKey ); if ( !grp.isValid() ) { return {}; } return doReadSettings( grp ); } bool SettingsManager::needToReparseCurrentProject( KConfig* cfg ) const { auto grp = cfg->group( ConfigConstants::definesAndIncludesGroup ); return grp.readEntry( "reparse", true ); } void SettingsManager::writeUserDefinedCompilers(const QVector< CompilerPointer >& compilers) { QVector< CompilerPointer > editableCompilers; for (const auto& compiler : compilers) { if (!compiler->editable()) { continue; } editableCompilers.append(compiler); } KConfigGroup config = KSharedConfig::openConfig()->group(ConfigConstants::compilersGroup); config.deleteGroup(); config.writeEntry("number", editableCompilers.count()); int i = 0; for (const auto& compiler : editableCompilers) { KConfigGroup grp = config.group(QString::number(i)); ++i; grp.writeEntry(ConfigConstants::compilerNameKey, compiler->name()); grp.writeEntry(ConfigConstants::compilerPathKey, compiler->path()); grp.writeEntry(ConfigConstants::compilerTypeKey, compiler->factoryName()); } config.sync(); } QVector< CompilerPointer > SettingsManager::userDefinedCompilers() const { QVector< CompilerPointer > compilers; KConfigGroup config = KSharedConfig::openConfig()->group(ConfigConstants::compilersGroup); int count = config.readEntry("number", 0); for (int i = 0; i < count; i++) { KConfigGroup grp = config.group(QString::number(i)); auto name = grp.readEntry(ConfigConstants::compilerNameKey, QString()); auto path = grp.readEntry(ConfigConstants::compilerPathKey, QString()); auto type = grp.readEntry(ConfigConstants::compilerTypeKey, QString()); auto cf = m_provider.compilerFactories(); for (auto f : cf) { if (f->name() == type) { auto compiler = f->createCompiler(name, path); compilers.append(compiler); } } } return compilers; } ParserArguments SettingsManager::defaultParserArguments() const { return defaultArguments(); } ConfigEntry::ConfigEntry(const QString& path) : path(path) , compiler(SettingsManager::globalInstance()->provider()->defaultCompiler()) , parserArguments(defaultArguments()) {} namespace Utils { LanguageType languageType(const KDevelop::Path& path, bool treatAmbiguousAsCPP) { QMimeDatabase db; const auto mimeType = db.mimeTypeForFile(path.path()).name(); if (mimeType == QStringLiteral("text/x-csrc") || mimeType == QStringLiteral("text/x-chdr") ) { if (treatAmbiguousAsCPP) { if (path.lastPathSegment().endsWith(QLatin1String(".h"), Qt::CaseInsensitive)) { return Cpp; } } // TODO: No proper mime type detection possible yet // cf. https://bugs.freedesktop.org/show_bug.cgi?id=26913 if (path.lastPathSegment().endsWith(QLatin1String(".cl"), Qt::CaseInsensitive)) { return OpenCl; } // TODO: No proper mime type detection possible yet // cf. https://bugs.freedesktop.org/show_bug.cgi?id=23700 if (path.lastPathSegment().endsWith(QLatin1String(".cu"), Qt::CaseInsensitive)) { return Cuda; } return C; } if (mimeType == QStringLiteral("text/x-c++src") || mimeType == QStringLiteral("text/x-c++hdr") ) { return Cpp; } if (mimeType == QStringLiteral("text/x-objcsrc")) { return ObjC; } if (mimeType == QStringLiteral("text/x-opencl-src")) { return OpenCl; } return Other; } } + +bool ParserArguments::isAnyEmpty() const +{ + return std::any_of(std::begin(arguments), std::end(arguments), + [](const QString& args) { return args.isEmpty(); } + ); +} diff --git a/plugins/custom-definesandincludes/compilerprovider/settingsmanager.h b/plugins/custom-definesandincludes/compilerprovider/settingsmanager.h index 1229d06bfe..2c6268e0b4 100644 --- a/plugins/custom-definesandincludes/compilerprovider/settingsmanager.h +++ b/plugins/custom-definesandincludes/compilerprovider/settingsmanager.h @@ -1,94 +1,110 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef SETTINGSMANAGER_H #define SETTINGSMANAGER_H #include "../idefinesandincludesmanager.h" #include "compilerprovider.h" #include "icompiler.h" #include class KConfig; namespace KDevelop { class IProject; class ProjectBaseItem; } struct ParserArguments { - QString cArguments; - QString cppArguments; - QString openClArguments; - QString cudaArguments; +public: + const QString& operator[](Utils::LanguageType languageType) const + { + Q_ASSERT(languageType >= Utils::C && languageType < Utils::Other); + return arguments[languageType]; + } + + QString& operator[](Utils::LanguageType languageType) + { + Q_ASSERT(languageType >= Utils::C && languageType < Utils::Other); + return arguments[languageType]; + } + + /// Is any of the arguments empty? + bool isAnyEmpty() const; + +private: + QString arguments[Utils::Other]; + +public: bool parseAmbiguousAsCPP; }; Q_DECLARE_METATYPE(ParserArguments); struct ConfigEntry { QString path; QStringList includes; KDevelop::Defines defines; CompilerPointer compiler; ParserArguments parserArguments; explicit ConfigEntry( const QString& path = QString() ); // FIXME: get rid of this but stay backwards compatible void setDefines(const QHash& defines); }; namespace Utils { LanguageType languageType(const KDevelop::Path& path, bool treatAmbiguousAsCPP = true); } class SettingsManager { public: ~SettingsManager(); QVector readPaths(KConfig* cfg) const; void writePaths(KConfig* cfg, const QVector& paths); QVector userDefinedCompilers() const; void writeUserDefinedCompilers(const QVector& compilers); bool needToReparseCurrentProject( KConfig* cfg ) const; ParserArguments defaultParserArguments() const; CompilerProvider* provider(); const CompilerProvider* provider() const; static SettingsManager* globalInstance(); private: SettingsManager(); CompilerProvider m_provider; }; #endif // SETTINGSMANAGER_H diff --git a/plugins/custom-definesandincludes/definesandincludesmanager.cpp b/plugins/custom-definesandincludes/definesandincludesmanager.cpp index 4092a76535..93f0713cac 100644 --- a/plugins/custom-definesandincludes/definesandincludesmanager.cpp +++ b/plugins/custom-definesandincludes/definesandincludesmanager.cpp @@ -1,413 +1,400 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "definesandincludesmanager.h" #include "kcm_widget/definesandincludesconfigpage.h" #include "compilerprovider/compilerprovider.h" #include "compilerprovider/widget/compilerswidget.h" #include "noprojectincludesanddefines/noprojectincludepathsmanager.h" #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { ///@return: The ConfigEntry, with includes/defines from @p paths for all parent folders of @p item. static ConfigEntry findConfigForItem(QVector paths, const KDevelop::ProjectBaseItem* item) { ConfigEntry ret; const Path itemPath = item->path(); const Path rootDirectory = item->project()->path(); Path closestPath; std::sort(paths.begin(), paths.end(), [] (const ConfigEntry& lhs, const ConfigEntry& rhs) { // sort in reverse order to do a bottom-up search return lhs.path > rhs.path; }); for (const ConfigEntry & entry : paths) { Path targetDirectory = rootDirectory; // note: a dot represents the project root if (entry.path != QLatin1String(".")) { targetDirectory.addPath(entry.path); } if (targetDirectory == itemPath || targetDirectory.isParentOf(itemPath)) { ret.includes += entry.includes; for (auto it = entry.defines.constBegin(); it != entry.defines.constEnd(); it++) { if (!ret.defines.contains(it.key())) { ret.defines[it.key()] = it.value(); } } if (targetDirectory.segments().size() > closestPath.segments().size()) { ret.parserArguments = entry.parserArguments; closestPath = targetDirectory; } } } ret.includes.removeDuplicates(); - Q_ASSERT(!ret.parserArguments.cppArguments.isEmpty()); - Q_ASSERT(!ret.parserArguments.cArguments.isEmpty()); + Q_ASSERT(!ret.parserArguments.isAnyEmpty()); return ret; } void merge(Defines* target, const Defines& source) { if (target->isEmpty()) { *target = source; return; } for (auto it = source.constBegin(); it != source.constEnd(); ++it) { target->insert(it.key(), it.value()); } } QString argumentsForPath(const Path& path, const ParserArguments& arguments) { auto languageType = Utils::languageType(path, arguments.parseAmbiguousAsCPP); - switch (languageType) { - case Utils::C: - return arguments.cArguments; - case Utils::Cpp: - return arguments.cppArguments; - case Utils::OpenCl: - return arguments.openClArguments; - case Utils::Cuda: - return arguments.cudaArguments; - case Utils::ObjC: - return QString(); - case Utils::Other: - return QString(); - } - Q_UNREACHABLE(); - return QString(); + if (languageType != Utils::Other) + return arguments[languageType]; + else + return {}; } } K_PLUGIN_FACTORY_WITH_JSON(DefinesAndIncludesManagerFactory, "kdevdefinesandincludesmanager.json", registerPlugin(); ) DefinesAndIncludesManager::DefinesAndIncludesManager( QObject* parent, const QVariantList& ) : IPlugin(QStringLiteral("kdevdefinesandincludesmanager"), parent ) , m_settings(SettingsManager::globalInstance()) , m_noProjectIPM(new NoProjectIncludePathsManager()) { registerProvider(m_settings->provider()); #ifdef Q_OS_OSX m_defaultFrameworkDirectories += Path(QStringLiteral("/Library/Frameworks")); m_defaultFrameworkDirectories += Path(QStringLiteral("/System/Library/Frameworks")); #endif } DefinesAndIncludesManager::~DefinesAndIncludesManager() = default; Defines DefinesAndIncludesManager::defines( ProjectBaseItem* item, Type type ) const { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (!item) { return m_settings->provider()->defines(nullptr); } Defines defines; for (auto provider : m_providers) { if (provider->type() & type) { merge(&defines, provider->defines(item)); } } if ( type & ProjectSpecific ) { auto buildManager = item->project()->buildSystemManager(); if ( buildManager ) { merge(&defines, buildManager->defines(item)); } } // Manually set defines have the highest priority and overwrite values of all other types of defines. if (type & UserDefined) { auto cfg = item->project()->projectConfiguration().data(); merge(&defines, findConfigForItem(m_settings->readPaths(cfg), item).defines); } merge(&defines, m_noProjectIPM->includesAndDefines(item->path().path()).second); return defines; } Path::List DefinesAndIncludesManager::includes( ProjectBaseItem* item, Type type ) const { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (!item) { return m_settings->provider()->includes(nullptr); } Path::List includes; if (type & UserDefined) { auto cfg = item->project()->projectConfiguration().data(); includes += KDevelop::toPathList(findConfigForItem(m_settings->readPaths(cfg), item).includes); } if ( type & ProjectSpecific ) { auto buildManager = item->project()->buildSystemManager(); if ( buildManager ) { includes += buildManager->includeDirectories(item); } } for (auto provider : m_providers) { if ( !(provider->type() & type) ) { continue; } auto newItems = provider->includes(item); if ( provider->type() & DefinesAndIncludesManager::CompilerSpecific ) { // If an item occurs in the "compiler specific" list, but was previously supplied // in the user include path list already, remove it from there. // Re-ordering the system include paths causes confusion in some cases. Q_FOREACH (const auto& x, newItems ) { includes.removeAll(x); } } includes += newItems; } includes += m_noProjectIPM->includesAndDefines(item->path().path()).first; return includes; } Path::List DefinesAndIncludesManager::frameworkDirectories( ProjectBaseItem* item, Type type ) const { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (!item) { return m_settings->provider()->frameworkDirectories(nullptr); } Path::List frameworkDirectories = m_defaultFrameworkDirectories; if ( type & ProjectSpecific ) { auto buildManager = item->project()->buildSystemManager(); if ( buildManager ) { frameworkDirectories += buildManager->frameworkDirectories(item); } } for (auto provider : m_providers) { if (provider->type() & type) { frameworkDirectories += provider->frameworkDirectories(item); } } return frameworkDirectories; } bool DefinesAndIncludesManager::unregisterProvider(IDefinesAndIncludesManager::Provider* provider) { int idx = m_providers.indexOf(provider); if (idx != -1) { m_providers.remove(idx); return true; } return false; } void DefinesAndIncludesManager::registerProvider(IDefinesAndIncludesManager::Provider* provider) { Q_ASSERT(provider); if (m_providers.contains(provider)) { return; } m_providers.push_back(provider); } Defines DefinesAndIncludesManager::defines(const QString& path, Type type) const { Defines ret; if ( type & CompilerSpecific ) { merge(&ret, m_settings->provider()->defines(nullptr)); } if ( type & ProjectSpecific ) { merge(&ret, m_noProjectIPM->includesAndDefines(path).second); } return ret; } Path::List DefinesAndIncludesManager::includes(const QString& path, Type type) const { Path::List ret; if ( type & CompilerSpecific ) { ret += m_settings->provider()->includes(nullptr); } if ( type & ProjectSpecific ) { ret += m_noProjectIPM->includesAndDefines(path).first; } return ret; } Path::List DefinesAndIncludesManager::frameworkDirectories(const QString& /* path */, Type type) const { return (type & CompilerSpecific) ? m_settings->provider()->frameworkDirectories(nullptr) : Path::List(); } void DefinesAndIncludesManager::openConfigurationDialog(const QString& pathToFile) { if (auto project = KDevelop::ICore::self()->projectController()->findProjectForUrl(QUrl::fromLocalFile(pathToFile))) { KDevelop::ICore::self()->projectController()->configureProject(project); } else { m_noProjectIPM->openConfigurationDialog(pathToFile); } } Path::List DefinesAndIncludesManager::includesInBackground(const QString& path) const { Path::List includes; for (auto provider: m_backgroundProviders) { includes += provider->includesInBackground(path); } return includes; } Path::List DefinesAndIncludesManager::frameworkDirectoriesInBackground(const QString& path) const { Path::List fwDirs; for (auto provider: m_backgroundProviders) { fwDirs += provider->frameworkDirectoriesInBackground(path); } return fwDirs; } Defines DefinesAndIncludesManager::definesInBackground(const QString& path) const { QHash defines; for (auto provider: m_backgroundProviders) { auto result = provider->definesInBackground(path); for (auto it = result.constBegin(); it != result.constEnd(); it++) { defines[it.key()] = it.value(); } } merge(&defines, m_noProjectIPM->includesAndDefines(path).second); return defines; } bool DefinesAndIncludesManager::unregisterBackgroundProvider(IDefinesAndIncludesManager::BackgroundProvider* provider) { int idx = m_backgroundProviders.indexOf(provider); if (idx != -1) { m_backgroundProviders.remove(idx); return true; } return false; } void DefinesAndIncludesManager::registerBackgroundProvider(IDefinesAndIncludesManager::BackgroundProvider* provider) { Q_ASSERT(provider); if (m_backgroundProviders.contains(provider)) { return; } m_backgroundProviders.push_back(provider); } QString DefinesAndIncludesManager::parserArguments(KDevelop::ProjectBaseItem* item) const { Q_ASSERT(item); Q_ASSERT(QThread::currentThread() == qApp->thread()); auto cfg = item->project()->projectConfiguration().data(); const auto parserArguments = findConfigForItem(m_settings->readPaths(cfg), item).parserArguments; auto arguments = argumentsForPath(item->path(), parserArguments); auto buildManager = item->project()->buildSystemManager(); if ( buildManager ) { const auto extraArguments = buildManager->extraArguments(item); if (!extraArguments.isEmpty()) arguments += ' ' + extraArguments; } return arguments; } QString DefinesAndIncludesManager::parserArguments(const QString& path) const { const auto args = m_settings->defaultParserArguments(); return argumentsForPath(Path(path), args); } int DefinesAndIncludesManager::perProjectConfigPages() const { return 1; } ConfigPage* DefinesAndIncludesManager::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent) { if (number == 0) { return new DefinesAndIncludesConfigPage(this, options, parent); } return nullptr; } KDevelop::ConfigPage* DefinesAndIncludesManager::configPage(int number, QWidget* parent) { return number == 0 ? new CompilersWidget(parent) : nullptr; } int DefinesAndIncludesManager::configPages() const { return 1; } #include "definesandincludesmanager.moc" diff --git a/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp b/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp index 71361257b5..0992564a80 100644 --- a/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp +++ b/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp @@ -1,198 +1,218 @@ /* * This file is part of KDevelop * * Copyright 2015 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "parserwidget.h" #include "ui_parserwidget.h" #include "compilerprovider/settingsmanager.h" #include namespace { QString languageStandard(const QString& arguments) { int idx = arguments.indexOf(QLatin1String("-std=")); if(idx == -1){ return QStringLiteral("c++11"); } idx += 5; int end = arguments.indexOf(' ', idx) != -1 ? arguments.indexOf(' ', idx) : arguments.size(); return arguments.mid(idx, end - idx); } -bool isCustomParserArguments(const QString& arguments, const QStringList& standards) +QString languageDefaultStandard(Utils::LanguageType languageType) +{ + switch (languageType) { + case Utils::C: + return QLatin1String("c99"); + case Utils::Cpp: + return QLatin1String("c++11"); + case Utils::OpenCl: + return QLatin1String("CL1.1"); + case Utils::Cuda: + return QLatin1String("c++11"); + case Utils::ObjC: + return QLatin1String("c99"); + case Utils::Other: + break; + } + Q_UNREACHABLE(); +} + +bool isCustomParserArguments(Utils::LanguageType languageType, const QString& arguments, const QStringList& standards) { const auto defaultArguments = SettingsManager::globalInstance()->defaultParserArguments(); auto standard = languageStandard(arguments); auto tmpArgs(arguments); - tmpArgs.replace(standard, QLatin1String("c++11")); + tmpArgs.replace(standard, languageDefaultStandard(languageType)); - if (tmpArgs == defaultArguments.cppArguments && standards.contains(standard)) { + if (tmpArgs == defaultArguments[languageType] && standards.contains(standard)) { return false; } return true; } const int customProfileIdx = 0; } ParserWidget::ParserWidget(QWidget* parent) : QWidget(parent) , m_ui(new Ui::ParserWidget()) { m_ui->setupUi(this); connect(m_ui->parserOptionsC, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsCpp, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsOpenCl, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsCuda, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parseHeadersInPlainC, &QCheckBox::stateChanged, this, &ParserWidget::textEdited); connect(m_ui->languageStandardsC, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedC); connect(m_ui->languageStandardsCpp, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedCpp); connect(m_ui->languageStandardsOpenCl, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedOpenCl); connect(m_ui->languageStandardsCuda, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedCuda); updateEnablements(); } ParserWidget::~ParserWidget() = default; void ParserWidget::textEdited() { emit changed(); } void ParserWidget::languageStandardChangedC(const QString& standard) { if (m_ui->languageStandardsC->currentIndex() == customProfileIdx) { - m_ui->parserOptionsC->setText(SettingsManager::globalInstance()->defaultParserArguments().cArguments); + m_ui->parserOptionsC->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::C]); } else { - auto text = SettingsManager::globalInstance()->defaultParserArguments().cArguments; + auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::C]; auto currentStandard = languageStandard(text); m_ui->parserOptionsC->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedCpp(const QString& standard) { if (m_ui->languageStandardsCpp->currentIndex() == customProfileIdx) { - m_ui->parserOptionsCpp->setText(SettingsManager::globalInstance()->defaultParserArguments().cppArguments); + m_ui->parserOptionsCpp->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cpp]); } else { - auto text = SettingsManager::globalInstance()->defaultParserArguments().cppArguments; + auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cpp]; auto currentStandard = languageStandard(text); m_ui->parserOptionsCpp->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedOpenCl(const QString& standard) { if (m_ui->languageStandardsOpenCl->currentIndex() == customProfileIdx) { - m_ui->parserOptionsOpenCl->setText(SettingsManager::globalInstance()->defaultParserArguments().openClArguments); + m_ui->parserOptionsOpenCl->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::OpenCl]); } else { - auto text = SettingsManager::globalInstance()->defaultParserArguments().openClArguments; + auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::OpenCl]; auto currentStandard = languageStandard(text); m_ui->parserOptionsOpenCl->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedCuda(const QString& standard) { if (m_ui->languageStandardsCuda->currentIndex() == customProfileIdx) { - m_ui->parserOptionsCuda->setText(SettingsManager::globalInstance()->defaultParserArguments().cudaArguments); + m_ui->parserOptionsCuda->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cuda]); } else { - auto text = SettingsManager::globalInstance()->defaultParserArguments().cudaArguments; + auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cuda]; auto currentStandard = languageStandard(text); m_ui->parserOptionsCuda->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::setParserArguments(const ParserArguments& arguments) { - auto setArguments = [](QComboBox* languageStandards, QLineEdit* parserOptions, const QString& arguments) { + auto setArguments = [arguments](QComboBox* languageStandards, QLineEdit* parserOptions, Utils::LanguageType languageType) { QStringList standards; const int languageStandardsCount = languageStandards->count(); standards.reserve(languageStandardsCount-1); for (int i = 1; i < languageStandardsCount; ++i) { standards << languageStandards->itemText(i); } - if (isCustomParserArguments(arguments, standards)) { + const QString& arg = arguments[languageType]; + if (isCustomParserArguments(languageType, arg, standards)) { languageStandards->setCurrentIndex(customProfileIdx); } else { - languageStandards->setCurrentText(languageStandard(arguments)); + languageStandards->setCurrentText(languageStandard(arg)); } - parserOptions->setText(arguments); + parserOptions->setText(arg); }; - setArguments(m_ui->languageStandardsCpp, m_ui->parserOptionsCpp, arguments.cppArguments); - setArguments(m_ui->languageStandardsC, m_ui->parserOptionsC, arguments.cArguments); - setArguments(m_ui->languageStandardsOpenCl, m_ui->parserOptionsOpenCl, arguments.openClArguments); - setArguments(m_ui->languageStandardsCuda, m_ui->parserOptionsCuda, arguments.cudaArguments); + setArguments(m_ui->languageStandardsCpp, m_ui->parserOptionsCpp, Utils::Cpp); + setArguments(m_ui->languageStandardsC, m_ui->parserOptionsC, Utils::C); + setArguments(m_ui->languageStandardsOpenCl, m_ui->parserOptionsOpenCl, Utils::OpenCl); + setArguments(m_ui->languageStandardsCuda, m_ui->parserOptionsCuda, Utils::Cuda); m_ui->parseHeadersInPlainC->setChecked(!arguments.parseAmbiguousAsCPP); updateEnablements(); } ParserArguments ParserWidget::parserArguments() const { - return { - m_ui->parserOptionsC->text(), - m_ui->parserOptionsCpp->text(), - m_ui->parserOptionsOpenCl->text(), - m_ui->parserOptionsCuda->text(), - !m_ui->parseHeadersInPlainC->isChecked() - }; + ParserArguments arguments; + arguments[Utils::C] = m_ui->parserOptionsC->text(); + arguments[Utils::Cpp] = m_ui->parserOptionsCpp->text(); + arguments[Utils::OpenCl] = m_ui->parserOptionsOpenCl->text(); + arguments[Utils::Cuda] = m_ui->parserOptionsCuda->text(); + arguments.parseAmbiguousAsCPP = !m_ui->parseHeadersInPlainC->isChecked(); + return arguments; } void ParserWidget::updateEnablements() { m_ui->parserOptionsCpp->setEnabled(m_ui->languageStandardsCpp->currentIndex() == customProfileIdx); m_ui->parserOptionsC->setEnabled(m_ui->languageStandardsC->currentIndex() == customProfileIdx); m_ui->parserOptionsOpenCl->setEnabled(m_ui->languageStandardsOpenCl->currentIndex() == customProfileIdx); m_ui->parserOptionsCuda->setEnabled(m_ui->languageStandardsCuda->currentIndex() == customProfileIdx); } diff --git a/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp b/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp index 6babad1b3f..b1e9d716e0 100644 --- a/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp +++ b/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp @@ -1,239 +1,238 @@ /************************************************************************ * * * Copyright 2010 Andreas Pakulat * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 or version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ************************************************************************/ #include "projectpathsmodel.h" #include #include #include #include using namespace KDevelop; ProjectPathsModel::ProjectPathsModel( QObject* parent ) : QAbstractListModel( parent ), project( nullptr ) { } void ProjectPathsModel::setProject(IProject* w_project) { project = w_project; } QVariant ProjectPathsModel::data( const QModelIndex& index, int role ) const { if( !index.isValid() || index.row() < 0 || index.row() >= rowCount() || index.column() != 0 ) { return QVariant(); } const ConfigEntry& pathConfig = projectPaths.at( index.row() ); switch( role ) { case IncludesDataRole: return pathConfig.includes; break; case DefinesDataRole: return QVariant::fromValue(pathConfig.defines); break; case Qt::EditRole: return sanitizePath( pathConfig.path, true, false ); break; case Qt::DisplayRole: { const QString& path = pathConfig.path; return (path == QLatin1String(".")) ? QStringLiteral("(project root)") : path; break; } case FullUrlDataRole: return QVariant::fromValue(QUrl::fromUserInput( sanitizePath( pathConfig.path, true, false ) )); break; case CompilerDataRole: return QVariant::fromValue(pathConfig.compiler); break; case ParserArgumentsRole: return QVariant::fromValue(pathConfig.parserArguments); break; default: break; } return QVariant(); } int ProjectPathsModel::rowCount( const QModelIndex& parent ) const { if( parent.isValid() ) { return 0; } return projectPaths.count(); } bool ProjectPathsModel::setData( const QModelIndex& index, const QVariant& value, int role ) { if( !index.isValid() || index.row() < 0 || index.row() >= rowCount() || index.column() != 0 ) { return false; } // Do not allow to change path of the first entry; instead, add a new one in that case if( index.row() == 0 && ( role == Qt::EditRole || role == Qt::DisplayRole || role == FullUrlDataRole ) ) { QString addedPath = sanitizePath( value.toString(), false ); // Do not allow duplicates foreach( const ConfigEntry& existingConfig, projectPaths ) { if( addedPath == existingConfig.path ) { return false; } } projectPaths.insert( 1, ConfigEntry(sanitizePath( value.toString(), false ) )); emit dataChanged( this->index( 1, 0 ), this->index( projectPaths.count() - 1, 0 ) ); return true; } ConfigEntry& pathConfig = projectPaths[ index.row() ]; switch( role ) { case IncludesDataRole: pathConfig.includes = value.toStringList(); break; case DefinesDataRole: pathConfig.defines = value.value(); break; case Qt::EditRole: pathConfig.path = sanitizePath( value.toString(), false ); break; case Qt::DisplayRole: pathConfig.path = sanitizePath( value.toString(), true ); break; case FullUrlDataRole: pathConfig.path = sanitizeUrl(value.toUrl()); break; case CompilerDataRole: pathConfig.compiler = value.value(); break; case ParserArgumentsRole: pathConfig.parserArguments = value.value(); break; default: return false; break; } emit dataChanged( index, index ); return true; } Qt::ItemFlags ProjectPathsModel::flags( const QModelIndex& index ) const { if( !index.isValid() ) { return Qt::NoItemFlags; } if( index.row() == 0 ) { return Qt::ItemFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); } return Qt::ItemFlags( Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled ); } QVector< ConfigEntry > ProjectPathsModel::paths() const { return projectPaths; } void ProjectPathsModel::setPaths(const QVector< ConfigEntry >& paths ) { beginResetModel(); projectPaths.clear(); foreach( const ConfigEntry& existingPathConfig, paths ) { // Sanitize the path of loaded config ConfigEntry config = existingPathConfig; bool rootPath = config.path == QLatin1String(".") ? true : false; config.path = sanitizePath(rootPath ? QString() : config.path ); addPathInternal(config, rootPath); } addPathInternal( ConfigEntry(sanitizePath( QString() )), true ); // add an empty "root" config entry if one does not exist endResetModel(); } bool ProjectPathsModel::removeRows( int row, int count, const QModelIndex& parent ) { if( row >= 0 && count > 0 && row < rowCount() ) { beginRemoveRows( parent, row, row + count - 1 ); for( int i = 0; i < count; ++i ) { if( projectPaths.at(row).path == QLatin1String(".") ) { continue; // we won't remove the root item } projectPaths.removeAt(row); } endRemoveRows(); return true; } return false; } void ProjectPathsModel::addPath( const QUrl &url ) { if( !project->path().isParentOf(KDevelop::Path(url)) ) { return; } beginInsertRows( QModelIndex(), rowCount(), rowCount() ); addPathInternal( ConfigEntry(sanitizeUrl(url)), false ); endInsertRows(); } void ProjectPathsModel::addPathInternal( const ConfigEntry& config, bool prepend ) { - Q_ASSERT(!config.parserArguments.cppArguments.isEmpty()); - Q_ASSERT(!config.parserArguments.cArguments.isEmpty()); + Q_ASSERT(!config.parserArguments.isAnyEmpty()); // Do not allow duplicates foreach( const ConfigEntry& existingConfig, projectPaths ) { if( config.path == existingConfig.path ) { return; } } if( prepend ) { projectPaths.prepend( config ); } else { projectPaths.append( config ); } } QString ProjectPathsModel::sanitizeUrl( const QUrl& url, bool needRelative ) const { Q_ASSERT( project ); if (needRelative) { const auto relativePath = project->path().relativePath(KDevelop::Path(url)); return relativePath.isEmpty() ? QStringLiteral(".") : relativePath; } return url.adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments).toString(QUrl::PreferLocalFile); } QString ProjectPathsModel::sanitizePath( const QString& path, bool expectRelative, bool needRelative ) const { Q_ASSERT( project ); Q_ASSERT( expectRelative || project->inProject(IndexedString(path)) ); QUrl url; if( expectRelative ) { url = KDevelop::Path(project->path(), path).toUrl(); } else { url = QUrl::fromUserInput(path); } return sanitizeUrl( url, needRelative ); }