Warning, file /kdevelop/kdevelop/plugins/custom-definesandincludes/definesandincludesmanager.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2014 Sergey Kalinichev <kalinichev.so.0@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "definesandincludesmanager.h"
0008 
0009 #include "kcm_widget/definesandincludesconfigpage.h"
0010 #include "compilerprovider/compilerprovider.h"
0011 #include "compilerprovider/widget/compilerswidget.h"
0012 #include "noprojectincludesanddefines/noprojectincludepathsmanager.h"
0013 
0014 #include <interfaces/icore.h>
0015 #include <interfaces/iprojectcontroller.h>
0016 #include <interfaces/iproject.h>
0017 #include <project/interfaces/ibuildsystemmanager.h>
0018 #include <project/projectmodel.h>
0019 
0020 #include <KPluginFactory>
0021 
0022 #include <QThread>
0023 #include <QCoreApplication>
0024 
0025 #include <algorithm>
0026 
0027 using namespace KDevelop;
0028 
0029 namespace
0030 {
0031 ///@return: The ConfigEntry, with includes/defines from @p paths for all parent folders of @p item.
0032 static ConfigEntry findConfigForItem(QVector<ConfigEntry> paths, const KDevelop::ProjectBaseItem* item)
0033 {
0034     ConfigEntry ret;
0035 
0036     const Path itemPath = item->path();
0037     const Path rootDirectory = item->project()->path();
0038     Path closestPath;
0039 
0040     std::sort(paths.begin(), paths.end(), [] (const ConfigEntry& lhs, const ConfigEntry& rhs) {
0041         // sort in reverse order to do a bottom-up search
0042         return lhs.path > rhs.path;
0043     });
0044 
0045     for (const ConfigEntry & entry : paths) {
0046         Path targetDirectory = rootDirectory;
0047         // note: a dot represents the project root
0048         if (entry.path != QLatin1String(".")) {
0049             targetDirectory.addPath(entry.path);
0050         }
0051 
0052         if (targetDirectory == itemPath || targetDirectory.isParentOf(itemPath)) {
0053             ret.includes += entry.includes;
0054 
0055             for (auto it = entry.defines.constBegin(); it != entry.defines.constEnd(); it++) {
0056                 if (!ret.defines.contains(it.key())) {
0057                     ret.defines[it.key()] = it.value();
0058                 }
0059             }
0060 
0061             if (targetDirectory.segments().size() > closestPath.segments().size()) {
0062                 ret.parserArguments = entry.parserArguments;
0063                 closestPath = targetDirectory;
0064             }
0065         }
0066     }
0067     ret.includes.removeDuplicates();
0068 
0069     Q_ASSERT(!ret.parserArguments.isAnyEmpty());
0070 
0071     return ret;
0072 }
0073 
0074 void merge(Defines* target, const Defines& source)
0075 {
0076     if (target->isEmpty()) {
0077         *target = source;
0078         return;
0079     }
0080 
0081     for (auto it = source.constBegin(); it != source.constEnd(); ++it) {
0082         target->insert(it.key(), it.value());
0083     }
0084 }
0085 
0086 QString argumentsForPath(const QString& path, const ParserArguments& arguments)
0087 {
0088     auto languageType = Utils::languageType(path, arguments.parseAmbiguousAsCPP);
0089     if (languageType != Utils::Other)
0090         return arguments[languageType];
0091     else
0092         return {};
0093 }
0094 
0095 }
0096 
0097 K_PLUGIN_FACTORY_WITH_JSON(DefinesAndIncludesManagerFactory, "kdevdefinesandincludesmanager.json", registerPlugin<DefinesAndIncludesManager>(); )
0098 
0099 DefinesAndIncludesManager::DefinesAndIncludesManager( QObject* parent, const QVariantList& )
0100     : IPlugin(QStringLiteral("kdevdefinesandincludesmanager"), parent )
0101     , m_settings(SettingsManager::globalInstance())
0102     , m_noProjectIPM(new NoProjectIncludePathsManager())
0103 {
0104     registerProvider(m_settings->provider());
0105 #ifdef Q_OS_OSX
0106     m_defaultFrameworkDirectories += Path(QStringLiteral("/Library/Frameworks"));
0107     m_defaultFrameworkDirectories += Path(QStringLiteral("/System/Library/Frameworks"));
0108 #endif
0109 }
0110 
0111 DefinesAndIncludesManager::~DefinesAndIncludesManager() = default;
0112 
0113 Defines DefinesAndIncludesManager::defines( ProjectBaseItem* item, Type type  ) const
0114 {
0115     Q_ASSERT(QThread::currentThread() == qApp->thread());
0116 
0117     if (!item) {
0118         return m_settings->provider()->defines(nullptr);
0119     }
0120 
0121     Defines defines;
0122 
0123     for (auto provider : m_providers) {
0124         if (provider->type() & type) {
0125             merge(&defines, provider->defines(item));
0126         }
0127     }
0128 
0129     if ( type & ProjectSpecific ) {
0130         auto buildManager = item->project()->buildSystemManager();
0131         if ( buildManager ) {
0132             merge(&defines, buildManager->defines(item));
0133         }
0134     }
0135 
0136     // Manually set defines have the highest priority and overwrite values of all other types of defines.
0137     if (type & UserDefined) {
0138         auto cfg = item->project()->projectConfiguration().data();
0139 
0140         merge(&defines, findConfigForItem(m_settings->readPaths(cfg), item).defines);
0141     }
0142 
0143     merge(&defines, m_noProjectIPM->includesAndDefines(item->path().path()).second);
0144 
0145     return defines;
0146 }
0147 
0148 Path::List DefinesAndIncludesManager::includes( ProjectBaseItem* item, Type type ) const
0149 {
0150     Q_ASSERT(QThread::currentThread() == qApp->thread());
0151 
0152     if (!item) {
0153         return m_settings->provider()->includes(nullptr);
0154     }
0155 
0156     Path::List includes;
0157 
0158     if (type & UserDefined) {
0159         auto cfg = item->project()->projectConfiguration().data();
0160 
0161         includes += KDevelop::toPathList(findConfigForItem(m_settings->readPaths(cfg), item).includes);
0162     }
0163 
0164     if ( type & ProjectSpecific ) {
0165         auto buildManager = item->project()->buildSystemManager();
0166         if ( buildManager ) {
0167             includes += buildManager->includeDirectories(item);
0168         }
0169     }
0170 
0171     for (auto provider : m_providers) {
0172         if ( !(provider->type() & type) ) {
0173             continue;
0174         }
0175         const auto newItems = provider->includes(item);
0176         if ( provider->type() & DefinesAndIncludesManager::CompilerSpecific ) {
0177             // If an item occurs in the "compiler specific" list, but was previously supplied
0178             // in the user include path list already, remove it from there.
0179             // Re-ordering the system include paths causes confusion in some cases.
0180             for (const auto& x : newItems) {
0181                 includes.removeAll(x);
0182             }
0183         }
0184         includes += newItems;
0185     }
0186 
0187     includes += m_noProjectIPM->includesAndDefines(item->path().path()).first;
0188 
0189     return includes;
0190 }
0191 
0192 Path::List DefinesAndIncludesManager::frameworkDirectories( ProjectBaseItem* item, Type type ) const
0193 {
0194     Q_ASSERT(QThread::currentThread() == qApp->thread());
0195 
0196     if (!item) {
0197         return m_settings->provider()->frameworkDirectories(nullptr);
0198     }
0199 
0200     Path::List frameworkDirectories = m_defaultFrameworkDirectories;
0201 
0202     if ( type & ProjectSpecific ) {
0203         auto buildManager = item->project()->buildSystemManager();
0204         if ( buildManager ) {
0205             frameworkDirectories += buildManager->frameworkDirectories(item);
0206         }
0207     }
0208 
0209     for (auto provider : m_providers) {
0210         if (provider->type() & type) {
0211             frameworkDirectories += provider->frameworkDirectories(item);
0212         }
0213     }
0214 
0215     return frameworkDirectories;
0216 }
0217 
0218 bool DefinesAndIncludesManager::unregisterProvider(IDefinesAndIncludesManager::Provider* provider)
0219 {
0220     int idx = m_providers.indexOf(provider);
0221     if (idx != -1) {
0222         m_providers.remove(idx);
0223         return true;
0224     }
0225 
0226     return false;
0227 }
0228 
0229 void DefinesAndIncludesManager::registerProvider(IDefinesAndIncludesManager::Provider* provider)
0230 {
0231     Q_ASSERT(provider);
0232     if (m_providers.contains(provider)) {
0233         return;
0234     }
0235 
0236     m_providers.push_back(provider);
0237 }
0238 
0239 Defines DefinesAndIncludesManager::defines(const QString& path, Type type) const
0240 {
0241     Defines ret;
0242     if ( type & CompilerSpecific ) {
0243         merge(&ret, m_settings->provider()->defines(path));
0244     }
0245     if ( type & ProjectSpecific ) {
0246         merge(&ret, m_noProjectIPM->includesAndDefines(path).second);
0247     }
0248     return ret;
0249 }
0250 
0251 Path::List DefinesAndIncludesManager::includes(const QString& path, Type type) const
0252 {
0253     Path::List ret;
0254     if ( type & CompilerSpecific ) {
0255         ret += m_settings->provider()->includes(path);
0256     }
0257     if ( type & ProjectSpecific ) {
0258         ret += m_noProjectIPM->includesAndDefines(path).first;
0259     }
0260     return ret;
0261 }
0262 
0263 Path::List DefinesAndIncludesManager::frameworkDirectories(const QString& path, Type type) const
0264 {
0265     return (type & CompilerSpecific) ? m_settings->provider()->frameworkDirectories(path) : Path::List();
0266 }
0267 
0268 void DefinesAndIncludesManager::openConfigurationDialog(const QString& pathToFile)
0269 {
0270     if (auto project = KDevelop::ICore::self()->projectController()->findProjectForUrl(QUrl::fromLocalFile(pathToFile))) {
0271         KDevelop::ICore::self()->projectController()->configureProject(project);
0272     } else {
0273         m_noProjectIPM->openConfigurationDialog(pathToFile);
0274     }
0275 }
0276 
0277 Path::List DefinesAndIncludesManager::includesInBackground(const QString& path) const
0278 {
0279     Path::List includes;
0280 
0281     for (auto provider: m_backgroundProviders) {
0282         includes += provider->includesInBackground(path);
0283     }
0284 
0285     return includes;
0286 }
0287 
0288 Path::List DefinesAndIncludesManager::frameworkDirectoriesInBackground(const QString& path) const
0289 {
0290     Path::List fwDirs;
0291 
0292     for (auto provider: m_backgroundProviders) {
0293         fwDirs += provider->frameworkDirectoriesInBackground(path);
0294     }
0295 
0296     return fwDirs;
0297 }
0298 
0299 Defines DefinesAndIncludesManager::definesInBackground(const QString& path) const
0300 {
0301     QHash<QString, QString> defines;
0302 
0303     for (auto provider: m_backgroundProviders) {
0304         auto result = provider->definesInBackground(path);
0305         for (auto it = result.constBegin(); it != result.constEnd(); it++) {
0306             defines[it.key()] = it.value();
0307         }
0308     }
0309 
0310     merge(&defines, m_noProjectIPM->includesAndDefines(path).second);
0311 
0312     return defines;
0313 }
0314 
0315 bool DefinesAndIncludesManager::unregisterBackgroundProvider(IDefinesAndIncludesManager::BackgroundProvider* provider)
0316 {
0317     int idx = m_backgroundProviders.indexOf(provider);
0318     if (idx != -1) {
0319         m_backgroundProviders.remove(idx);
0320         return true;
0321     }
0322 
0323     return false;
0324 }
0325 
0326 void DefinesAndIncludesManager::registerBackgroundProvider(IDefinesAndIncludesManager::BackgroundProvider* provider)
0327 {
0328     Q_ASSERT(provider);
0329     if (m_backgroundProviders.contains(provider)) {
0330         return;
0331     }
0332 
0333     m_backgroundProviders.push_back(provider);
0334 }
0335 
0336 QString DefinesAndIncludesManager::parserArguments(KDevelop::ProjectBaseItem* item) const
0337 {
0338     Q_ASSERT(item);
0339 
0340     Q_ASSERT(QThread::currentThread() == qApp->thread());
0341 
0342     auto cfg = item->project()->projectConfiguration().data();
0343     const auto parserArguments = findConfigForItem(m_settings->readPaths(cfg), item).parserArguments;
0344     auto arguments = argumentsForPath(item->path().path(), parserArguments);
0345 
0346     auto buildManager = item->project()->buildSystemManager();
0347     if ( buildManager ) {
0348         const auto extraArguments = buildManager->extraArguments(item);
0349         if (!extraArguments.isEmpty())
0350             arguments += QLatin1Char(' ') + extraArguments;
0351     }
0352 
0353     return arguments;
0354 }
0355 
0356 QString DefinesAndIncludesManager::parserArguments(const QString& path) const
0357 {
0358     const auto args = m_settings->defaultParserArguments();
0359     return argumentsForPath(path, args);
0360 }
0361 
0362 QString DefinesAndIncludesManager::parserArgumentsInBackground(const QString& path) const
0363 {
0364     QString ret;
0365     for (auto provider: m_backgroundProviders) {
0366         ret += provider->parserArgumentsInBackground(path) + QLatin1Char(' ');
0367     }
0368 
0369     return ret;
0370 }
0371 
0372 int DefinesAndIncludesManager::perProjectConfigPages() const
0373 {
0374     return 1;
0375 }
0376 
0377 ConfigPage* DefinesAndIncludesManager::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent)
0378 {
0379     if (number == 0) {
0380         return new DefinesAndIncludesConfigPage(this, options, parent);
0381     }
0382     return nullptr;
0383 }
0384 
0385 KDevelop::ConfigPage* DefinesAndIncludesManager::configPage(int number, QWidget* parent)
0386 {
0387     return number == 0 ? new CompilersWidget(parent) : nullptr;
0388 }
0389 
0390 int DefinesAndIncludesManager::configPages() const
0391 {
0392     return 1;
0393 }
0394 
0395 #include "definesandincludesmanager.moc"
0396 #include "moc_definesandincludesmanager.cpp"