File indexing completed on 2024-05-12 04:39:39
0001 /* 0002 SPDX-FileCopyrightText: 2014 Sergey Kalinichev <kalinichev.so.0@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "compilerprovider.h" 0008 0009 #include "debug.h" 0010 #include "compilerfactories.h" 0011 #include "settingsmanager.h" 0012 0013 #include <interfaces/iruntime.h> 0014 #include <interfaces/iruntimecontroller.h> 0015 #include <interfaces/iproject.h> 0016 #include <interfaces/iprojectcontroller.h> 0017 #include <interfaces/ibuildsystemmanager.h> 0018 #include <project/projectmodel.h> 0019 0020 #include <KLocalizedString> 0021 #include <QStandardPaths> 0022 #include <QDir> 0023 0024 using namespace KDevelop; 0025 0026 namespace 0027 { 0028 class NoCompiler : public ICompiler 0029 { 0030 public: 0031 NoCompiler(): 0032 ICompiler(i18nc("@item no compiler", "None"), QString(), QString(), false) 0033 {} 0034 0035 QHash< QString, QString > defines(Utils::LanguageType, const QString&) const override 0036 { 0037 return {}; 0038 } 0039 0040 Path::List includes(Utils::LanguageType, const QString&) const override 0041 { 0042 return {}; 0043 } 0044 }; 0045 0046 static CompilerPointer createDummyCompiler() 0047 { 0048 static CompilerPointer compiler(new NoCompiler()); 0049 return compiler; 0050 } 0051 0052 ConfigEntry configForItem(KDevelop::ProjectBaseItem* item) 0053 { 0054 if(!item){ 0055 return ConfigEntry(); 0056 } 0057 0058 const Path itemPath = item->path(); 0059 const Path rootDirectory = item->project()->path(); 0060 0061 const auto paths = SettingsManager::globalInstance()->readPaths(item->project()->projectConfiguration().data()); 0062 ConfigEntry config; 0063 Path closestPath; 0064 0065 // find config entry closest to the requested item 0066 for (const auto& entry : paths) { 0067 auto configEntry = entry; 0068 Path targetDirectory = rootDirectory; 0069 0070 targetDirectory.addPath(entry.path); 0071 0072 if (targetDirectory == itemPath) { 0073 return configEntry; 0074 } 0075 0076 if (targetDirectory.isParentOf(itemPath)) { 0077 if (config.path.isEmpty() || targetDirectory.segments().size() > closestPath.segments().size()) { 0078 config = configEntry; 0079 closestPath = targetDirectory; 0080 } 0081 } 0082 } 0083 0084 return config; 0085 } 0086 0087 QString parserArguments( const ConfigEntry& config, Utils::LanguageType languageType, ProjectBaseItem* item ) 0088 { 0089 auto args = config.parserArguments[languageType]; 0090 if (item && item->project()->buildSystemManager()) { 0091 args += QLatin1Char(' '); 0092 args += item->project()->buildSystemManager()->extraArguments(item); 0093 } 0094 return args; 0095 } 0096 } 0097 0098 ProjectTargetItem* findCompiledTarget(ProjectBaseItem* item) 0099 { 0100 const auto targets = item->targetList(); 0101 for (auto* item : targets) { 0102 if (item->type() == ProjectBaseItem::ExecutableTarget || item->type() == ProjectBaseItem::LibraryTarget) { 0103 return item; 0104 } 0105 } 0106 0107 const auto folders = item->folderList(); 0108 for (auto* folder: folders) { 0109 auto target = findCompiledTarget(folder); 0110 if (target) 0111 return target; 0112 } 0113 return nullptr; 0114 } 0115 0116 CompilerProvider::CompilerProvider( SettingsManager* settings, QObject* parent ) 0117 : QObject( parent ) 0118 , m_settings(settings) 0119 { 0120 m_factories = { 0121 CompilerFactoryPointer(new GccFactory()), 0122 CompilerFactoryPointer(new ClangFactory()), 0123 #ifdef _WIN32 0124 CompilerFactoryPointer(new MsvcFactory()), 0125 #endif 0126 }; 0127 0128 if (!QStandardPaths::findExecutable( QStringLiteral("clang") ).isEmpty()) { 0129 m_factories[1]->registerDefaultCompilers(this); 0130 } 0131 if (!QStandardPaths::findExecutable( QStringLiteral("gcc") ).isEmpty()) { 0132 m_factories[0]->registerDefaultCompilers(this); 0133 } 0134 #ifdef _WIN32 0135 if (!QStandardPaths::findExecutable(QStringLiteral("cl.exe")).isEmpty()) { 0136 m_factories[2]->registerDefaultCompilers(this); 0137 } 0138 #endif 0139 0140 registerCompiler(createDummyCompiler()); 0141 retrieveUserDefinedCompilers(); 0142 0143 connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, [this]() { m_defaultProvider.clear(); }); 0144 connect(ICore::self()->projectController(), &IProjectController::projectConfigurationChanged, this, &CompilerProvider::projectChanged); 0145 connect(ICore::self()->projectController(), &IProjectController::projectOpened, this, &CompilerProvider::projectChanged); 0146 } 0147 0148 CompilerProvider::~CompilerProvider() = default; 0149 0150 void CompilerProvider::projectChanged(KDevelop::IProject* p) 0151 { 0152 const auto target = findCompiledTarget(p->projectItem()); 0153 if (!target) 0154 return; 0155 0156 auto path = p->buildSystemManager()->compiler(target); 0157 qCDebug(DEFINESANDINCLUDES) << "found compiler" << path; 0158 if (path.isEmpty()) 0159 return; 0160 0161 Q_ASSERT(QDir::isAbsolutePath(path.toLocalFile())); 0162 const auto pathString = path.toLocalFile(); 0163 auto it = std::find_if(m_compilers.begin(), m_compilers.end(), 0164 [&pathString](const CompilerPointer& compiler) { return compiler->path() == pathString; }); 0165 if (it != m_compilers.end()) { 0166 m_defaultProvider = *it; 0167 return; 0168 } 0169 0170 //we need to search, sdk compiler names are weird: arm-linux-androideabi-g++ 0171 for (auto& factory : qAsConst(m_factories)) { 0172 if (factory->isSupported(path)) { 0173 auto compiler = factory->createCompiler(path.lastPathSegment(), pathString); 0174 registerCompiler(compiler); 0175 m_defaultProvider = compiler; 0176 } 0177 } 0178 0179 qCDebug(DEFINESANDINCLUDES) << "using compiler" << m_defaultProvider << path; 0180 } 0181 0182 QHash<QString, QString> CompilerProvider::defines( const QString& path ) const 0183 { 0184 auto config = configForItem(nullptr); 0185 auto languageType = Utils::languageType(path, config.parserArguments.parseAmbiguousAsCPP); 0186 // If called on files that we can't compile, return an empty set of defines. 0187 if (languageType == Utils::Other) { 0188 return {}; 0189 } 0190 0191 return config.compiler->defines(languageType, parserArguments(config, languageType, nullptr)); 0192 } 0193 0194 QHash<QString, QString> CompilerProvider::defines( ProjectBaseItem* item ) const 0195 { 0196 auto config = configForItem(item); 0197 auto languageType = Utils::Cpp; 0198 if (item) { 0199 languageType = Utils::languageType(item->path().path(), config.parserArguments.parseAmbiguousAsCPP); 0200 } 0201 // If called on files that we can't compile, return an empty set of defines. 0202 if (languageType == Utils::Other) { 0203 return {}; 0204 } 0205 0206 return config.compiler->defines(languageType, parserArguments(config, languageType, item)); 0207 } 0208 0209 Path::List CompilerProvider::includes( const QString& path ) const 0210 { 0211 auto config = configForItem(nullptr); 0212 auto languageType = Utils::languageType(path, config.parserArguments.parseAmbiguousAsCPP); 0213 // If called on files that we can't compile, return an empty set of includes. 0214 if (languageType == Utils::Other) { 0215 return {}; 0216 } 0217 0218 return config.compiler->includes(languageType, parserArguments(config, languageType, nullptr)); 0219 } 0220 0221 Path::List CompilerProvider::includes( ProjectBaseItem* item ) const 0222 { 0223 auto config = configForItem(item); 0224 auto languageType = Utils::Cpp; 0225 if (item) { 0226 languageType = Utils::languageType(item->path().path(), config.parserArguments.parseAmbiguousAsCPP); 0227 } 0228 // If called on files that we can't compile, return an empty set of includes. 0229 if (languageType == Utils::Other) { 0230 return {}; 0231 } 0232 0233 return config.compiler->includes(languageType, parserArguments(config, languageType, item)); 0234 } 0235 0236 Path::List CompilerProvider::frameworkDirectories( const QString& /* path */ ) const 0237 { 0238 return {}; 0239 } 0240 0241 Path::List CompilerProvider::frameworkDirectories( ProjectBaseItem* /* item */ ) const 0242 { 0243 return {}; 0244 } 0245 0246 IDefinesAndIncludesManager::Type CompilerProvider::type() const 0247 { 0248 return IDefinesAndIncludesManager::CompilerSpecific; 0249 } 0250 0251 CompilerPointer CompilerProvider::defaultCompiler() const 0252 { 0253 if (m_defaultProvider) 0254 return m_defaultProvider; 0255 0256 auto rt = ICore::self()->runtimeController()->currentRuntime(); 0257 0258 for ( const CompilerPointer& compiler : m_compilers ) { 0259 if (rt->findExecutable(compiler->path()).isEmpty()) 0260 continue; 0261 m_defaultProvider = compiler; 0262 break; 0263 } 0264 if (!m_defaultProvider) 0265 m_defaultProvider = createDummyCompiler(); 0266 0267 qCDebug(DEFINESANDINCLUDES) << "new default compiler" << rt->name() << m_defaultProvider->name() << m_defaultProvider->path(); 0268 return m_defaultProvider; 0269 } 0270 0271 QVector< CompilerPointer > CompilerProvider::compilers() const 0272 { 0273 return m_compilers; 0274 } 0275 0276 CompilerPointer CompilerProvider::compilerForItem( KDevelop::ProjectBaseItem* item ) const 0277 { 0278 auto compiler = configForItem(item).compiler; 0279 Q_ASSERT(compiler); 0280 return compiler; 0281 } 0282 0283 bool CompilerProvider::registerCompiler(const CompilerPointer& compiler) 0284 { 0285 if (!compiler) { 0286 return false; 0287 } 0288 0289 for (auto& c : qAsConst(m_compilers)) { 0290 if (c->name() == compiler->name()) { 0291 return false; 0292 } 0293 } 0294 m_compilers.append(compiler); 0295 return true; 0296 } 0297 0298 void CompilerProvider::unregisterCompiler(const CompilerPointer& compiler) 0299 { 0300 if (!compiler->editable()) { 0301 return; 0302 } 0303 0304 for (int i = 0; i < m_compilers.count(); i++) { 0305 if (m_compilers[i]->name() == compiler->name()) { 0306 m_compilers.remove(i); 0307 break; 0308 } 0309 } 0310 } 0311 0312 QVector< CompilerFactoryPointer > CompilerProvider::compilerFactories() const 0313 { 0314 return m_factories; 0315 } 0316 0317 void CompilerProvider::retrieveUserDefinedCompilers() 0318 { 0319 const auto compilers = m_settings->userDefinedCompilers(); 0320 for (auto& c : compilers) { 0321 registerCompiler(c); 0322 } 0323 } 0324 0325 #include "moc_compilerprovider.cpp"