diff --git a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp index ae90d623c7..30e7bb2936 100644 --- a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp +++ b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp @@ -1,251 +1,280 @@ /* * 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( const QString& path ) const +{ + auto config = configForItem(nullptr); + auto languageType = Utils::languageType(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, config.parserArguments[languageType]); +} + QHash CompilerProvider::defines( ProjectBaseItem* item ) const { auto config = configForItem(item); auto languageType = Utils::Cpp; if (item) { - languageType = Utils::languageType(item->path(), config.parserArguments.parseAmbiguousAsCPP); + languageType = Utils::languageType(item->path().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, config.parserArguments[languageType]); } +Path::List CompilerProvider::includes( const QString& path ) const +{ + auto config = configForItem(nullptr); + auto languageType = Utils::languageType(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, 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); + languageType = Utils::languageType(item->path().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, config.parserArguments[languageType]); } +Path::List CompilerProvider::frameworkDirectories( const QString& /* path */ ) const +{ + return {}; +} + 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/compilerprovider.h b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.h index eb9b5785bf..61ac6c846c 100644 --- a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.h +++ b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.h @@ -1,77 +1,80 @@ /* * 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 COMPILERSPROVIDER_H #define COMPILERSPROVIDER_H #include "icompilerfactory.h" #include class SettingsManager; class CompilerProvider : public QObject, public KDevelop::IDefinesAndIncludesManager::Provider { Q_OBJECT public: explicit CompilerProvider( SettingsManager* settings, QObject* parent = nullptr ); ~CompilerProvider() override; + KDevelop::Defines defines( const QString& path ) const override; KDevelop::Defines defines( KDevelop::ProjectBaseItem* item ) const override; + KDevelop::Path::List includes( const QString& path ) const override; KDevelop::Path::List includes( KDevelop::ProjectBaseItem* item ) const override; + KDevelop::Path::List frameworkDirectories( const QString& path ) const override; KDevelop::Path::List frameworkDirectories( KDevelop::ProjectBaseItem* item ) const override; KDevelop::IDefinesAndIncludesManager::Type type() const override; /// @return current compiler for the @p item CompilerPointer compilerForItem( KDevelop::ProjectBaseItem* item ) const; /// @return list of all available compilers QVector compilers() const; /** * Adds compiler to the list of available compilers * @return true on success (if there is no compiler with the same name registered). */ bool registerCompiler(const CompilerPointer& compiler); /// Removes compiler from the list of available compilers void unregisterCompiler( const CompilerPointer& compiler ); /// @return All available factories QVector compilerFactories() const; /// @returns a default compiler CompilerPointer defaultCompiler() const; private Q_SLOTS: void retrieveUserDefinedCompilers(); private: mutable CompilerPointer m_defaultProvider; QVector m_compilers; QVector m_factories; SettingsManager* m_settings; }; #endif // COMPILERSPROVIDER_H diff --git a/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp b/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp index fcdf041cdc..504d2b6a99 100644 --- a/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp +++ b/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp @@ -1,456 +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 parserArgumentsKey(Utils::LanguageType languageType) { 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 createDefaultArguments() { 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); 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, "" ); for (auto type : configurableLanguageTypes) { path.parserArguments[type] = pathgrp.readEntry(ConfigConstants::parserArgumentsKey(type), defaultArguments()[type]); } path.parserArguments.parseAmbiguousAsCPP = pathgrp.readEntry(ConfigConstants::parseAmbiguousAsCPP(), defaultArguments().parseAmbiguousAsCPP); 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.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) +LanguageType languageType(const QString& path, bool treatAmbiguousAsCPP) { QMimeDatabase db; - const auto mimeType = db.mimeTypeForFile(path.path()).name(); + const auto mimeType = db.mimeTypeForFile(path).name(); if (mimeType == QStringLiteral("text/x-csrc") || mimeType == QStringLiteral("text/x-chdr") ) { if (treatAmbiguousAsCPP) { - if (path.lastPathSegment().endsWith(QLatin1String(".h"), Qt::CaseInsensitive)) { + if (path.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)) { + if (path.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)) { + if (path.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 ede81e8929..8f9a21479a 100644 --- a/plugins/custom-definesandincludes/compilerprovider/settingsmanager.h +++ b/plugins/custom-definesandincludes/compilerprovider/settingsmanager.h @@ -1,112 +1,112 @@ /* * 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 { 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); Q_DECLARE_TYPEINFO(ParserArguments, Q_MOVABLE_TYPE); 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); }; Q_DECLARE_TYPEINFO(ConfigEntry, Q_MOVABLE_TYPE); namespace Utils { -LanguageType languageType(const KDevelop::Path& path, bool treatAmbiguousAsCPP = true); +LanguageType languageType(const QString& 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 93f0713cac..03a1575f43 100644 --- a/plugins/custom-definesandincludes/definesandincludesmanager.cpp +++ b/plugins/custom-definesandincludes/definesandincludesmanager.cpp @@ -1,400 +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.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) +QString argumentsForPath(const QString& path, const ParserArguments& arguments) { auto languageType = Utils::languageType(path, arguments.parseAmbiguousAsCPP); 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)); + merge(&ret, m_settings->provider()->defines(path)); } 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); + ret += m_settings->provider()->includes(path); } if ( type & ProjectSpecific ) { ret += m_noProjectIPM->includesAndDefines(path).first; } return ret; } -Path::List DefinesAndIncludesManager::frameworkDirectories(const QString& /* path */, Type type) const +Path::List DefinesAndIncludesManager::frameworkDirectories(const QString& path, Type type) const { - return (type & CompilerSpecific) ? m_settings->provider()->frameworkDirectories(nullptr) : Path::List(); + return (type & CompilerSpecific) ? m_settings->provider()->frameworkDirectories(path) : 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 arguments = argumentsForPath(item->path().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); + return argumentsForPath(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/idefinesandincludesmanager.h b/plugins/custom-definesandincludes/idefinesandincludesmanager.h index 2b47302e10..8300307c0e 100644 --- a/plugins/custom-definesandincludes/idefinesandincludesmanager.h +++ b/plugins/custom-definesandincludes/idefinesandincludesmanager.h @@ -1,220 +1,223 @@ /* * 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 IDEFINESANDINCLUDES_H #define IDEFINESANDINCLUDES_H #include #include #include #include #include #include namespace KDevelop { class ProjectBaseItem; class IProject; using Defines = QHash; /** An interface that provides language plugins with include directories/files and defines. * Call IDefinesAndIncludesManager::manager() to get the instance of the plugin. **/ class IDefinesAndIncludesManager { public: /// The type of includes/defines enum Type { CompilerSpecific = 1, ///< Those that compiler provides ProjectSpecific = 2, ///< Those that project manager provides UserDefined = 4, ///< Those that user defines All = CompilerSpecific | ProjectSpecific | UserDefined }; /** * Class that actually does all the work of calculating i/d. * * Implement one in e.g. project manager and register it with @see registerProvider * To unregister it use @see unregisterProvider * * @sa BackgroundProvider **/ class Provider { public: virtual ~Provider() = default; + virtual Defines defines( const QString& path ) const = 0; virtual Defines defines( ProjectBaseItem* item ) const = 0; + virtual Path::List includes( const QString& path ) const = 0; virtual Path::List includes( ProjectBaseItem* item ) const = 0; + virtual Path::List frameworkDirectories( const QString& path ) const = 0; virtual Path::List frameworkDirectories( ProjectBaseItem* item ) const = 0; /// @return the type of i/d this provider provides virtual Type type() const = 0; }; /** * Use this as base class for provider, if computing includes/defines can takes a long time (e.g. parsing Makefile by running make). * * This provider will be queried for includes/defines in a background thread. * * @sa Provider **/ class BackgroundProvider { public: virtual ~BackgroundProvider() = default; virtual Path::List includesInBackground( const QString& path ) const = 0; virtual Path::List frameworkDirectoriesInBackground( const QString& path ) const = 0; virtual Defines definesInBackground( const QString& path ) const = 0; /// @return the type of i/d this provider provides virtual Type type() const = 0; }; ///@param item project item ///@param type Data sources to be used. ///@return list of defines for @p item ///NOTE: call it from the foreground thread only. virtual Defines defines( ProjectBaseItem* item, Type type = All ) const = 0; ///@param item project item ///@param type Data sources to be used. ///@return list of include directories/files for @p item ///NOTE: call it from the foreground thread only. virtual Path::List includes( ProjectBaseItem* item, Type type = All ) const = 0; ///@param item project item ///@param type Data sources to be used. ///@return list of framework directories for @p item ///NOTE: call it from the foreground thread only. virtual Path::List frameworkDirectories( ProjectBaseItem* item, Type type = All ) const = 0; ///@param path path to an out-of-project file. ///@param type Data sources to be used. ///@return list of defines for @p path ///NOTE: call it from the foreground thread only. virtual Defines defines( const QString& path, Type type = All ) const = 0; ///@param path path to an out-of-project file. ///@param type Data sources to be used. ///@return list of include directories/files for @p path ///NOTE: call it from the foreground thread only. virtual Path::List includes( const QString& path, Type type = All ) const = 0; ///@param path path to an out-of-project file. ///@param type Data sources to be used. ///@return list of framework directories for @p path ///NOTE: call it from the foreground thread only. virtual Path::List frameworkDirectories( const QString& path, Type type = All ) const = 0; /** * Computes include directories in background thread. * * This is especially useful for CustomMake projects. * * Call it from background thread if possible. **/ virtual Path::List includesInBackground( const QString& path ) const = 0; /** * Computes framework directories in background thread. * * This is especially useful for CustomMake projects. * * Call it from background thread if possible. **/ virtual Path::List frameworkDirectoriesInBackground( const QString& path ) const = 0; /** * Computes defined macros in background thread. * * Call it from background thread if possible. **/ virtual Defines definesInBackground( const QString& path ) const = 0; /** * @param item project item. Use nullptr to get default arguments * @return The parser command-line arguments used to parse the @p item */ virtual QString parserArguments(ProjectBaseItem* item) const = 0; virtual QString parserArguments(const QString& path) const = 0; ///@return the instance of the plugin. inline static IDefinesAndIncludesManager* manager(); virtual ~IDefinesAndIncludesManager() = default; /** * Register the @p provider */ virtual void registerProvider(Provider* provider) = 0; /** * Unregister the provider * * @return true on success, false otherwise (e.g. if not registered) */ virtual bool unregisterProvider(Provider* provider) = 0; /** * Use this to register the background provider * * This provider will be queried for includes/defines in a background thread. */ virtual void registerBackgroundProvider(BackgroundProvider* provider) = 0; /** * Unregister the background provider. * * @sa registerBackgroundProvider */ virtual bool unregisterBackgroundProvider(BackgroundProvider* provider) = 0; /// Opens a configuration dialog for @p pathToFile to modify include directories/files and defined macros. virtual void openConfigurationDialog(const QString& pathToFile) = 0; }; inline IDefinesAndIncludesManager* IDefinesAndIncludesManager::manager() { static QPointer manager; if (!manager) { manager = ICore::self()->pluginController()->pluginForExtension( QStringLiteral("org.kdevelop.IDefinesAndIncludesManager") ); } Q_ASSERT(manager); auto extension = manager->extension(); return extension; } } Q_DECLARE_INTERFACE( KDevelop::IDefinesAndIncludesManager, "org.kdevelop.IDefinesAndIncludesManager" ) #endif