File indexing completed on 2024-04-28 04:38:29
0001 /* 0002 SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com> 0003 SPDX-FileCopyrightText: 2011 Milian Wolff <mail@milianw.de> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "custommakemanager.h" 0009 #include "custommakemodelitems.h" 0010 #include <debug.h> 0011 #include <interfaces/icore.h> 0012 #include <interfaces/iproject.h> 0013 #include <interfaces/iprojectcontroller.h> 0014 #include <interfaces/iplugincontroller.h> 0015 #include <makebuilder/imakebuilder.h> 0016 #include <project/projectmodel.h> 0017 #include <project/helper.h> 0018 #include <custom-definesandincludes/idefinesandincludesmanager.h> 0019 #include <makefileresolver/makefileresolver.h> 0020 0021 #include <KPluginFactory> 0022 #include <KLocalizedString> 0023 0024 #include <QFile> 0025 #include <QReadWriteLock> 0026 #include <QReadLocker> 0027 #include <QWriteLocker> 0028 0029 0030 #include <algorithm> 0031 0032 using namespace KDevelop; 0033 0034 class CustomMakeProvider : public IDefinesAndIncludesManager::BackgroundProvider 0035 { 0036 public: 0037 explicit CustomMakeProvider(CustomMakeManager* manager) 0038 : m_customMakeManager(manager) 0039 , m_resolver(new MakeFileResolver()) 0040 {} 0041 0042 // NOTE: Fixes build failures for GCC versions <4.8. 0043 // cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53613 0044 ~CustomMakeProvider() Q_DECL_NOEXCEPT override; 0045 0046 QHash< QString, QString > definesInBackground(const QString&) const override 0047 { 0048 return {}; 0049 } 0050 0051 Path::List resolvePathInBackground(const QString& path, const bool isFrameworks) const 0052 { 0053 { 0054 QReadLocker lock(&m_lock); 0055 0056 bool inProject = std::any_of(m_customMakeManager->m_projectPaths.constBegin(), m_customMakeManager->m_projectPaths.constEnd(), [&path](const QString& projectPath) 0057 { 0058 return path.startsWith(projectPath); 0059 } ); 0060 0061 if (!inProject) { 0062 return {}; 0063 } 0064 } 0065 0066 if (isFrameworks) { 0067 return m_resolver->resolveIncludePath(path).frameworkDirectories; 0068 } else { 0069 return m_resolver->resolveIncludePath(path).paths; 0070 } 0071 } 0072 0073 Path::List includesInBackground(const QString& path) const override 0074 { 0075 return resolvePathInBackground(path, false); 0076 } 0077 0078 Path::List frameworkDirectoriesInBackground(const QString& path) const override 0079 { 0080 return resolvePathInBackground(path, true); 0081 } 0082 0083 QString parserArgumentsInBackground(const QString& /*path*/) const override 0084 { 0085 return {}; 0086 } 0087 0088 IDefinesAndIncludesManager::Type type() const override 0089 { 0090 return IDefinesAndIncludesManager::ProjectSpecific; 0091 } 0092 0093 CustomMakeManager* m_customMakeManager; 0094 QScopedPointer<MakeFileResolver> m_resolver; 0095 mutable QReadWriteLock m_lock; 0096 }; 0097 0098 // NOTE: Fixes build failures for GCC versions <4.8. 0099 // See above. 0100 CustomMakeProvider::~CustomMakeProvider() Q_DECL_NOEXCEPT 0101 {} 0102 0103 K_PLUGIN_FACTORY_WITH_JSON(CustomMakeSupportFactory, "kdevcustommakemanager.json", registerPlugin<CustomMakeManager>(); ) 0104 0105 CustomMakeManager::CustomMakeManager( QObject *parent, const QVariantList& args ) 0106 : KDevelop::AbstractFileManagerPlugin( QStringLiteral("kdevcustommakemanager"), parent ) 0107 , m_provider(new CustomMakeProvider(this)) 0108 { 0109 Q_UNUSED(args) 0110 0111 setXMLFile( QStringLiteral("kdevcustommakemanager.rc") ); 0112 0113 // TODO use CustomMakeBuilder 0114 IPlugin* i = core()->pluginController()->pluginForExtension( QStringLiteral("org.kdevelop.IMakeBuilder") ); 0115 Q_ASSERT(i); 0116 m_builder = i->extension<IMakeBuilder>(); 0117 Q_ASSERT(m_builder); 0118 0119 connect(this, &CustomMakeManager::reloadedFileItem, 0120 this, &CustomMakeManager::reloadMakefile); 0121 0122 connect(ICore::self()->projectController(), &IProjectController::projectClosing, 0123 this, &CustomMakeManager::projectClosing); 0124 0125 0126 IDefinesAndIncludesManager::manager()->registerBackgroundProvider(m_provider.data()); 0127 } 0128 0129 CustomMakeManager::~CustomMakeManager() 0130 { 0131 } 0132 0133 IProjectBuilder* CustomMakeManager::builder() const 0134 { 0135 Q_ASSERT(m_builder); 0136 return m_builder; 0137 } 0138 0139 Path::List CustomMakeManager::includeDirectories(KDevelop::ProjectBaseItem*) const 0140 { 0141 return Path::List(); 0142 } 0143 0144 Path::List CustomMakeManager::frameworkDirectories(KDevelop::ProjectBaseItem*) const 0145 { 0146 return Path::List(); 0147 } 0148 0149 QHash<QString,QString> CustomMakeManager::defines(KDevelop::ProjectBaseItem*) const 0150 { 0151 return QHash<QString,QString>(); 0152 } 0153 0154 QString CustomMakeManager::extraArguments(KDevelop::ProjectBaseItem*) const 0155 { 0156 return {}; 0157 } 0158 0159 ProjectTargetItem* CustomMakeManager::createTarget(const QString& target, KDevelop::ProjectFolderItem *parent) 0160 { 0161 Q_UNUSED(target) 0162 Q_UNUSED(parent) 0163 return nullptr; 0164 } 0165 0166 bool CustomMakeManager::addFilesToTarget(const QList< ProjectFileItem* > &files, ProjectTargetItem* parent) 0167 { 0168 Q_UNUSED( files ) 0169 Q_UNUSED( parent ) 0170 return false; 0171 } 0172 0173 bool CustomMakeManager::removeTarget(KDevelop::ProjectTargetItem *target) 0174 { 0175 Q_UNUSED( target ) 0176 return false; 0177 } 0178 0179 bool CustomMakeManager::removeFilesFromTargets(const QList< ProjectFileItem* > &targetFiles) 0180 { 0181 Q_UNUSED( targetFiles ) 0182 return false; 0183 } 0184 0185 bool CustomMakeManager::hasBuildInfo(KDevelop::ProjectBaseItem* item) const 0186 { 0187 Q_UNUSED(item); 0188 return false; 0189 } 0190 0191 Path CustomMakeManager::buildDirectory(KDevelop::ProjectBaseItem* item) const 0192 { 0193 auto *fi=dynamic_cast<ProjectFolderItem*>(item); 0194 for(; !fi && item; ) 0195 { 0196 item=item->parent(); 0197 fi=dynamic_cast<ProjectFolderItem*>(item); 0198 } 0199 if(!fi) { 0200 return item->project()->path(); 0201 } 0202 return fi->path(); 0203 } 0204 0205 QList<ProjectTargetItem*> CustomMakeManager::targets(KDevelop::ProjectFolderItem*) const 0206 { 0207 QList<ProjectTargetItem*> ret; 0208 return ret; 0209 } 0210 0211 static bool isMakefile(const QString& fileName) 0212 { 0213 return ( fileName == QLatin1String("Makefile") 0214 || fileName == QLatin1String("makefile") 0215 || fileName == QLatin1String("GNUmakefile") 0216 || fileName == QLatin1String("BSDmakefile") ); 0217 } 0218 0219 void CustomMakeManager::createTargetItems(IProject* project, const Path& path, ProjectBaseItem* parent) 0220 { 0221 Q_ASSERT(isMakefile(path.lastPathSegment())); 0222 const auto targets = parseCustomMakeFile(path); 0223 for (const QString& target : targets) { 0224 if (!isValid(Path(parent->path(), target), false, project)) { 0225 continue; 0226 } 0227 new CustomMakeTargetItem( project, target, parent ); 0228 } 0229 } 0230 0231 ProjectFileItem* CustomMakeManager::createFileItem(IProject* project, const Path& path, ProjectBaseItem* parent) 0232 { 0233 auto* item = new ProjectFileItem(project, path, parent); 0234 if (isMakefile(path.lastPathSegment())){ 0235 createTargetItems(project, path, parent); 0236 } 0237 return item; 0238 } 0239 0240 void CustomMakeManager::reloadMakefile(ProjectFileItem* file) 0241 { 0242 if( !isMakefile(file->path().lastPathSegment())){ 0243 return; 0244 } 0245 ProjectBaseItem* parent = file->parent(); 0246 // remove the items that are Makefile targets 0247 const auto items = parent->children(); 0248 for (ProjectBaseItem* item : items) { 0249 if (item->target()){ 0250 delete item; 0251 } 0252 } 0253 // Recreate the targets. 0254 createTargetItems(parent->project(), file->path(), parent); 0255 } 0256 0257 ProjectFolderItem* CustomMakeManager::createFolderItem(IProject* project, const Path& path, ProjectBaseItem* parent) 0258 { 0259 // TODO more faster algorithm. should determine whether this directory 0260 // contains makefile or not. 0261 return new KDevelop::ProjectBuildFolderItem( project, path, parent ); 0262 } 0263 0264 KDevelop::ProjectFolderItem* CustomMakeManager::import(KDevelop::IProject *project) 0265 { 0266 if( project->path().isRemote() ) 0267 { 0268 //FIXME turn this into a real warning 0269 qCWarning(CUSTOMMAKE) << project->path() << "not a local file. Custom make support doesn't handle remote projects"; 0270 return nullptr; 0271 } 0272 0273 { 0274 QWriteLocker lock(&m_provider->m_lock); 0275 m_projectPaths.insert(project->path().path()); 0276 } 0277 0278 return AbstractFileManagerPlugin::import( project ); 0279 } 0280 0281 ///////////////////////////////////////////////////////////////////////////// 0282 // private slots 0283 0284 ///TODO: move to background thread, probably best would be to use a proper ParseJob 0285 QStringList CustomMakeManager::parseCustomMakeFile( const Path &makefile ) 0286 { 0287 if( !makefile.isValid() ) 0288 return QStringList(); 0289 0290 QStringList ret; // the list of targets 0291 QFile f( makefile.toLocalFile() ); 0292 if ( !f.open( QIODevice::ReadOnly | QIODevice::Text ) ) 0293 { 0294 qCDebug(CUSTOMMAKE) << "could not open" << makefile; 0295 return ret; 0296 } 0297 0298 QRegExp targetRe(QStringLiteral("^ *([^\\t$.#]\\S+) *:?:(?!=).*$")); 0299 targetRe.setMinimal( true ); 0300 0301 QString str; 0302 QTextStream stream( &f ); 0303 while ( !stream.atEnd() ) 0304 { 0305 str = stream.readLine(); 0306 0307 if ( targetRe.indexIn( str ) != -1 ) 0308 { 0309 QString tmpTarget = targetRe.cap( 1 ).simplified(); 0310 if ( ! ret.contains( tmpTarget ) ) 0311 ret.append( tmpTarget ); 0312 } 0313 } 0314 f.close(); 0315 return ret; 0316 } 0317 0318 void CustomMakeManager::projectClosing(IProject* project) 0319 { 0320 QWriteLocker lock(&m_provider->m_lock); 0321 m_projectPaths.remove(project->path().path()); 0322 } 0323 0324 void CustomMakeManager::unload() 0325 { 0326 IDefinesAndIncludesManager::manager()->unregisterBackgroundProvider(m_provider.data()); 0327 } 0328 0329 KDevelop::Path CustomMakeManager::compiler(KDevelop::ProjectTargetItem* item) const 0330 { 0331 Q_UNUSED(item); 0332 return {}; 0333 } 0334 0335 #include "custommakemanager.moc" 0336 #include "moc_custommakemanager.cpp"