/* KDevelop CMake Support * * Copyright 2006 Matt Rogers * Copyright 2007-2013 Aleix Pol * * 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 "cmakemanager.h" #include "cmakeedit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmakenavigationwidget.h" #include "cmakecachereader.h" #include "cmakecodecompletionmodel.h" #include #include "icmakedocumentation.h" #ifdef CMAKEDEBUGVISITOR #include "cmakedebugvisitor.h" #endif #include "ui_cmakepossibleroots.h" #include "cmakemodelitems.h" #include "cmakeprojectdata.h" #include "cmakecommitchangesjob.h" #include "cmakeimportjob.h" #include "cmakeutils.h" Q_DECLARE_METATYPE(KDevelop::IProject*); using namespace KDevelop; K_PLUGIN_FACTORY(CMakeSupportFactory, registerPlugin(); ) K_EXPORT_PLUGIN(CMakeSupportFactory(KAboutData("kdevcmakemanager","kdevcmake", ki18n("CMake Manager"), "0.1", ki18n("Support for managing CMake projects"), KAboutData::License_GPL))) const QString DIALOG_CAPTION = i18n("KDevelop - CMake Support"); CMakeManager::CMakeManager( QObject* parent, const QVariantList& ) : KDevelop::IPlugin( CMakeSupportFactory::componentData(), parent ) , m_filter( new ProjectFilterManager( this ) ) { KDEV_USE_EXTENSION_INTERFACE( KDevelop::IBuildSystemManager ) KDEV_USE_EXTENSION_INTERFACE( KDevelop::IProjectFileManager ) KDEV_USE_EXTENSION_INTERFACE( KDevelop::ILanguageSupport ) KDEV_USE_EXTENSION_INTERFACE( ICMakeManager) if (hasError()) { return; } m_highlight = new KDevelop::CodeHighlighting(this); new CodeCompletion(this, new CMakeCodeCompletionModel(this), name()); connect(ICore::self()->projectController(), SIGNAL(projectClosing(KDevelop::IProject*)), SLOT(projectClosing(KDevelop::IProject*))); m_fileSystemChangeTimer = new QTimer(this); m_fileSystemChangeTimer->setSingleShot(true); m_fileSystemChangeTimer->setInterval(100); connect(m_fileSystemChangeTimer,SIGNAL(timeout()),SLOT(filesystemBuffererTimeout())); } bool CMakeManager::hasError() const { return KStandardDirs::findExe("cmake").isEmpty(); } QString CMakeManager::errorDescription() const { return hasError() ? i18n("cmake is not installed") : QString(); } CMakeManager::~CMakeManager() {} Path CMakeManager::buildDirectory(KDevelop::ProjectBaseItem *item) const { CMakeFolderItem *fi=dynamic_cast(item); Path ret; ProjectBaseItem* parent = fi ? fi->formerParent() : item->parent(); if (parent) ret=buildDirectory(parent); else ret=Path(CMake::currentBuildDir(item->project())); if(fi) ret.addPath(fi->buildDir()); return ret; } KDevelop::ProjectFolderItem* CMakeManager::import( KDevelop::IProject *project ) { kDebug(9042) << "== migrating cmake settings"; CMake::attemptMigrate(project); kDebug(9042) << "== completed cmake migration"; kDebug(9042) << "== updating cmake settings from model"; int buildDirCount = CMake::buildDirCount(project); for( int i = 0; i < buildDirCount; ++i ) CMake::updateConfig( project, i ); kDebug(9042) << "== completed updating cmake settings"; const Path cmakeInfoFile(project->projectFile().parent(), "CMakeLists.txt"); Path folderPath = project->path(); kDebug(9042) << "file is" << cmakeInfoFile.toLocalFile(); if ( !cmakeInfoFile.isLocalFile() ) { kWarning() << "error. not a local file. CMake support doesn't handle remote projects"; return 0; } if(CMake::hasProjectRootRelative(project)) { QString relative=CMake::projectRootRelative(project); folderPath = folderPath.cd(relative); } else { KDialog chooseRoot; QWidget *e=new QWidget(&chooseRoot); Ui::CMakePossibleRoots ui; ui.setupUi(e); chooseRoot.setMainWidget(e); for(Path aux = folderPath; QFile::exists(aux.toLocalFile()+"/CMakeLists.txt"); aux = aux.parent()) ui.candidates->addItem(aux.toLocalFile()); if(ui.candidates->count()>1) { connect(ui.candidates, SIGNAL(itemActivated(QListWidgetItem*)), &chooseRoot,SLOT(accept())); ui.candidates->setMinimumSize(384,192); int a=chooseRoot.exec(); if(!a || !ui.candidates->currentItem()) { return 0; } const Path choice(ui.candidates->currentItem()->text()); CMake::setProjectRootRelative(project, folderPath.relativePath(choice)); folderPath = choice; } else { CMake::setProjectRootRelative(project, "./"); } } CMakeFolderItem* rootItem = new CMakeFolderItem(project, project->path(), QString(), 0 ); QFileSystemWatcher* w = new QFileSystemWatcher(project); w->setObjectName(project->name()+"_ProjectWatcher"); connect(w, SIGNAL(fileChanged(QString)), SLOT(dirtyFile(QString))); connect(w, SIGNAL(directoryChanged(QString)), SLOT(directoryChanged(QString))); m_watchers[project] = w; kDebug(9042) << "Added watcher for project " << project << project->name(); m_filter->add(project); KUrl cachefile=CMake::currentBuildDir(project); if( cachefile.isEmpty() ) { CMake::checkForNeedingConfigure(project); } else { cachefile.addPath("CMakeCache.txt"); w->addPath(cachefile.toLocalFile()); } Q_ASSERT(rootItem->rowCount()==0); return rootItem; } QList CMakeManager::parse(ProjectFolderItem*) { return QList< ProjectFolderItem* >(); } KJob* CMakeManager::createImportJob(ProjectFolderItem* dom) { KJob* job = new CMakeImportJob(dom, this); connect(job, SIGNAL(finished(KJob*)), SLOT(importFinished(KJob*))); return job; } QList CMakeManager::targets() const { QList ret; foreach(IProject* p, m_watchers.keys()) { ret+=p->projectItem()->targetList(); } return ret; } Path::List CMakeManager::includeDirectories(KDevelop::ProjectBaseItem *item) const { IProject* project = item->project(); // kDebug(9042) << "Querying inc dirs for " << item; while(item) { if(CompilationDataAttached* includer = dynamic_cast( item )) { QStringList dirs = includer->includeDirectories(item); //Here there's the possibility that it might not be a target. We should make sure that's not the case ProjectTargetItem* tItem = dynamic_cast(item); return CMake::resolveSystemDirs(project, processGeneratorExpression(dirs, project, tItem)); } item = item->parent(); // kDebug(9042) << "Looking for an includer: " << item; } // No includer found, so no include-directories to be returned; return Path::List(); } QHash< QString, QString > CMakeManager::defines(KDevelop::ProjectBaseItem *item ) const { CompilationDataAttached* att=0; ProjectBaseItem* it=item; // kDebug(9042) << "Querying defines for " << item << dynamic_cast(item); while(!att && item) { att = dynamic_cast( item ); it = item; item = item->parent(); // kDebug(9042) << "Looking for a folder: " << folder << item; } if( !att ) { // Not a CMake folder, so no defines to be returned; return QHash(); } CMakeFolderItem* folder = dynamic_cast(it); CMakeDefinitions defs = att->definitions(folder ? folder->formerParent() : dynamic_cast(item)); //qDebug() << "lalala" << defs << it->url(); return defs; } KDevelop::IProjectBuilder * CMakeManager::builder() const { IPlugin* i = core()->pluginController()->pluginForExtension( "org.kdevelop.IProjectBuilder", "KDevCMakeBuilder"); Q_ASSERT(i); KDevelop::IProjectBuilder* _builder = i->extension(); Q_ASSERT(_builder ); return _builder ; } bool CMakeManager::reload(KDevelop::ProjectFolderItem* folder) { kDebug(9032) << "reloading" << folder->path(); IProject* p = folder->project(); if(!p->isReady()) return false; CMakeFolderItem* fi = dynamic_cast(folder); for(ProjectBaseItem* it = folder; !fi && it->parent();) { it = it->parent(); fi = dynamic_cast(it); } Q_ASSERT(fi && "at least the root item should be a CMakeFolderItem"); KJob *job=createImportJob(fi); connect(job, SIGNAL(result(KJob*)), SLOT(importFinished(KJob*))); p->setReloadJob(job); ICore::self()->runController()->registerJob( job ); return true; } void CMakeManager::importFinished(KJob* j) { CMakeImportJob* job = qobject_cast(j); Q_ASSERT(job); *m_projectsData[job->project()] = job->projectData(); } void CMakeManager::deletedWatchedDirectory(IProject* p, const KUrl& dir) { if(p->folder().equals(dir, KUrl::CompareWithoutTrailingSlash)) { ICore::self()->projectController()->closeProject(p); } else { if(dir.fileName()=="CMakeLists.txt") { QList folders = p->foldersForUrl(dir.upUrl()); foreach(ProjectFolderItem* folder, folders) reload(folder); } else { qDeleteAll(p->itemsForUrl(dir)); } } } void CMakeManager::directoryChanged(const QString& dir) { m_fileSystemChangedBuffer << dir; m_fileSystemChangeTimer->start(); } void CMakeManager::filesystemBuffererTimeout() { Q_FOREACH(const QString& file, m_fileSystemChangedBuffer) { realDirectoryChanged(file); } m_fileSystemChangedBuffer.clear(); } void CMakeManager::realDirectoryChanged(const QString& dir) { KUrl path(dir); IProject* p=ICore::self()->projectController()->findProjectForUrl(dir); if(!p || !p->isReady()) { if(p) { m_fileSystemChangedBuffer << dir; m_fileSystemChangeTimer->start(); } return; } if(!QFile::exists(dir)) { path.adjustPath(KUrl::AddTrailingSlash); deletedWatchedDirectory(p, path); } else dirtyFile(dir); } void CMakeManager::dirtyFile(const QString & dirty) { const KUrl dirtyFile(dirty); IProject* p=ICore::self()->projectController()->findProjectForUrl(dirtyFile); kDebug() << "dirty FileSystem: " << dirty << (p ? p->isReady() : 0); if(p) { if(dirtyFile.fileName() == "CMakeLists.txt") { QList files=p->filesForUrl(dirtyFile);; Q_ASSERT(files.count()==1); CMakeFolderItem *folderItem=static_cast(files.first()->parent()); #if 0 KUrl relative=KUrl::relativeUrl(projectBaseUrl, dir); initializeProject(proj, dir); KUrl current=projectBaseUrl; QStringList subs=relative.toLocalFile().split("/"); subs.append(QString()); for(; !subs.isEmpty(); current.cd(subs.takeFirst())) { parseOnly(proj, current); } #endif reload(folderItem); } else if(QFileInfo(dirty).isDir() && p->isReady()) { QList folders=p->foldersForPath(IndexedString(dirty)); Q_ASSERT(folders.isEmpty() || folders.size()==1); if(!folders.isEmpty()) { CMakeCommitChangesJob* job = new CMakeCommitChangesJob(Path(dirtyFile), this, p); job->start(); } } } else if(dirtyFile.fileName()=="CMakeCache.txt") { //we first have to check from which project is this builddir foreach(KDevelop::IProject* pp, m_watchers.uniqueKeys()) { KUrl buildDir = CMake::currentBuildDir(pp); if(dirtyFile.upUrl().equals(buildDir, KUrl::CompareWithoutTrailingSlash)) { reload(pp->projectItem()); } } } else if(dirty.endsWith(".cmake")) { foreach(KDevelop::IProject* project, m_watchers.uniqueKeys()) { if(m_watchers[project]->files().contains(dirty)) reload(project->projectItem()); } } } QList< KDevelop::ProjectTargetItem * > CMakeManager::targets(KDevelop::ProjectFolderItem * folder) const { return folder->targetList(); } QString CMakeManager::name() const { return "CMake"; } KDevelop::ParseJob * CMakeManager::createParseJob(const IndexedString &/*url*/) { return 0; } KDevelop::ILanguage * CMakeManager::language() { return core()->languageController()->language(name()); } KDevelop::ICodeHighlighting* CMakeManager::codeHighlighting() const { return m_highlight; } ContextMenuExtension CMakeManager::contextMenuExtension( KDevelop::Context* context ) { if( context->type() != KDevelop::Context::ProjectItemContext ) return IPlugin::contextMenuExtension( context ); KDevelop::ProjectItemContext* ctx = dynamic_cast( context ); QList items = ctx->items(); if( items.isEmpty() ) return IPlugin::contextMenuExtension( context ); m_clickedItems = items; ContextMenuExtension menuExt; if(items.count()==1 && dynamic_cast(items.first())) { KAction* action = new KAction( i18n( "Jump to Target Definition" ), this ); connect( action, SIGNAL(triggered()), this, SLOT(jumpToDeclaration()) ); menuExt.addAction( ContextMenuExtension::ProjectGroup, action ); } return menuExt; } void CMakeManager::jumpToDeclaration() { DUChainAttatched* du=dynamic_cast(m_clickedItems.first()); if(du) { KTextEditor::Cursor c; KUrl url; { KDevelop::DUChainReadLocker lock; Declaration* decl = du->declaration().data(); if(!decl) return; c = decl->rangeInCurrentRevision().start.textCursor(); url = decl->url().toUrl(); } ICore::self()->documentController()->openDocument(url, c); } } // TODO: Port to Path API bool CMakeManager::moveFilesAndFolders(const QList< ProjectBaseItem* > &items, ProjectFolderItem* toFolder) { using namespace CMakeEdit; ApplyChangesWidget changesWidget; changesWidget.setCaption(DIALOG_CAPTION); changesWidget.setInformation(i18n("Move files and folders within CMakeLists as follows:")); bool cmakeSuccessful = true; CMakeFolderItem *nearestCMakeFolderItem = nearestCMakeFolder(toFolder); IProject* project=toFolder->project(); KUrl::List movedUrls; KUrl::List oldUrls; foreach(ProjectBaseItem *movedItem, items) { QList dirtyItems = cmakeListedItemsAffectedByUrlChange(project, movedItem->url()); KUrl movedItemNewUrl = toFolder->url(); movedItemNewUrl.addPath(movedItem->baseName()); if (movedItem->folder()) movedItemNewUrl.adjustPath(KUrl::AddTrailingSlash); foreach(ProjectBaseItem* dirtyItem, dirtyItems) { KUrl dirtyItemNewUrl = afterMoveUrl(dirtyItem->url(), movedItem->url(), movedItemNewUrl); if (CMakeFolderItem* folder = dynamic_cast(dirtyItem)) { cmakeSuccessful &= changesWidgetRemoveCMakeFolder(folder, &changesWidget); cmakeSuccessful &= changesWidgetAddFolder(dirtyItemNewUrl, nearestCMakeFolderItem, &changesWidget); } else if (dirtyItem->parent()->target()) { cmakeSuccessful &= changesWidgetMoveTargetFile(dirtyItem, dirtyItemNewUrl, &changesWidget); } } oldUrls += movedItem->url(); movedUrls += movedItemNewUrl; } if (changesWidget.hasDocuments() && cmakeSuccessful) cmakeSuccessful &= changesWidget.exec() && changesWidget.applyAllChanges(); if (!cmakeSuccessful) { if (KMessageBox::questionYesNo( QApplication::activeWindow(), i18n("Changes to CMakeLists failed, abort move?"), DIALOG_CAPTION ) == KMessageBox::Yes) return false; } KUrl::List::const_iterator it1=oldUrls.constBegin(), it1End=oldUrls.constEnd(); KUrl::List::const_iterator it2=movedUrls.constBegin(); Q_ASSERT(oldUrls.size()==movedUrls.size()); for(; it1!=it1End; ++it1, ++it2) { if (!KDevelop::renameUrl(project, *it1, *it2)) return false; QList renamedItems = project->itemsForUrl(*it2); bool dir = QFileInfo(it2->toLocalFile()).isDir(); foreach(ProjectBaseItem* item, renamedItems) { if(dir) emit folderRenamed(Path(*it1), item->folder()); else emit fileRenamed(Path(*it1), item->file()); } } return true; } bool CMakeManager::copyFilesAndFolders(const KDevelop::Path::List &items, KDevelop::ProjectFolderItem* toFolder) { IProject* project = toFolder->project(); foreach(const Path& path, items) { if (!KDevelop::copyUrl(project, path.toUrl(), toFolder->url())) return false; } return true; } bool CMakeManager::removeFilesAndFolders(const QList &items) { using namespace CMakeEdit; IProject* p = 0; QList urls; foreach(ProjectBaseItem* item, items) { Q_ASSERT(item->folder() || item->file()); urls += item->url(); if(!p) p = item->project(); } //First do CMakeLists changes ApplyChangesWidget changesWidget; changesWidget.setCaption(DIALOG_CAPTION); changesWidget.setInformation(i18n("Remove files and folders from CMakeLists as follows:")); bool cmakeSuccessful = changesWidgetRemoveItems(cmakeListedItemsAffectedByItemsChanged(items).toSet(), &changesWidget); if (changesWidget.hasDocuments() && cmakeSuccessful) cmakeSuccessful &= changesWidget.exec() && changesWidget.applyAllChanges(); if (!cmakeSuccessful) { if (KMessageBox::questionYesNo( QApplication::activeWindow(), i18n("Changes to CMakeLists failed, abort deletion?"), DIALOG_CAPTION ) == KMessageBox::Yes) return false; } bool ret = true; //Then delete the files/folders foreach(const QUrl& file, urls) { ret &= KDevelop::removeUrl(p, file, QDir(file.toLocalFile()).exists()); } return ret; } bool CMakeManager::removeFilesFromTargets(const QList &files) { using namespace CMakeEdit; ApplyChangesWidget changesWidget; changesWidget.setCaption(DIALOG_CAPTION); changesWidget.setInformation(i18n("Modify project targets as follows:")); if (!files.isEmpty() && changesWidgetRemoveFilesFromTargets(files, &changesWidget) && changesWidget.exec() && changesWidget.applyAllChanges()) { return true; } return false; } ProjectFolderItem* CMakeManager::addFolder(const Path& folder, ProjectFolderItem* parent) { using namespace CMakeEdit; CMakeFolderItem *cmakeParent = nearestCMakeFolder(parent); if(!cmakeParent) return 0; ApplyChangesWidget changesWidget; changesWidget.setCaption(DIALOG_CAPTION); changesWidget.setInformation(i18n("Create folder '%1':", folder.lastPathSegment())); ///FIXME: use path in changes widget changesWidgetAddFolder(folder.toUrl(), cmakeParent, &changesWidget); if(changesWidget.exec() && changesWidget.applyAllChanges()) { if(KDevelop::createFolder(folder.toUrl())) { //If saved we create the folder then the CMakeLists.txt file Path newCMakeLists(folder, "CMakeLists.txt"); KDevelop::createFile( newCMakeLists.toUrl() ); } else KMessageBox::error(0, i18n("Could not save the change."), DIALOG_CAPTION); } return 0; } KDevelop::ProjectFileItem* CMakeManager::addFile( const Path& file, KDevelop::ProjectFolderItem* parent) { KDevelop::ProjectFileItem* created = 0; if ( KDevelop::createFile(file.toUrl()) ) { QList< ProjectFileItem* > files = parent->project()->filesForPath(IndexedString(file.pathOrUrl())); if(!files.isEmpty()) created = files.first(); else created = new KDevelop::ProjectFileItem( parent->project(), file, parent ); } return created; } bool CMakeManager::addFilesToTarget(const QList< ProjectFileItem* > &_files, ProjectTargetItem* target) { using namespace CMakeEdit; const QSet headerExt = QSet() << ".h" << ".hpp" << ".hxx"; QList< ProjectFileItem* > files = _files; for (int i = files.count() - 1; i >= 0; --i) { QString fileName = files[i]->fileName(); QString fileExt = fileName.mid(fileName.lastIndexOf('.')); QList sameUrlItems = files[i]->project()->itemsForUrl(files[i]->url()); if (headerExt.contains(fileExt)) files.removeAt(i); else foreach(ProjectBaseItem* item, sameUrlItems) { if (item->parent() == target) { files.removeAt(i); break; } } } if(files.isEmpty()) return true; ApplyChangesWidget changesWidget; changesWidget.setCaption(DIALOG_CAPTION); changesWidget.setInformation(i18n("Modify target '%1' as follows:", target->baseName())); bool success = changesWidgetAddFilesToTarget(files, target, &changesWidget) && changesWidget.exec() && changesWidget.applyAllChanges(); if(!success) KMessageBox::error(0, i18n("CMakeLists changes failed."), DIALOG_CAPTION); return success; } //TODO: port to Path API bool CMakeManager::renameFileOrFolder(ProjectBaseItem *item, const Path &newPath) { using namespace CMakeEdit; ApplyChangesWidget changesWidget; changesWidget.setCaption(DIALOG_CAPTION); changesWidget.setInformation(i18n("Rename '%1' to '%2':", item->text(), newPath.lastPathSegment())); bool cmakeSuccessful = true, changedCMakeLists=false; IProject* project=item->project(); const Path oldPath=item->path(); KUrl oldUrl=oldPath.toUrl(); if (item->file()) { QList targetFiles = cmakeListedItemsAffectedByUrlChange(project, oldUrl); foreach(ProjectBaseItem* targetFile, targetFiles) ///FIXME: use path in changes widget cmakeSuccessful &= changesWidgetMoveTargetFile(targetFile, newPath.toUrl(), &changesWidget); } else if (CMakeFolderItem *folder = dynamic_cast(item)) ///FIXME: use path in changes widget cmakeSuccessful &= changesWidgetRenameFolder(folder, newPath.toUrl(), &changesWidget); item->setPath(newPath); if (changesWidget.hasDocuments() && cmakeSuccessful) { changedCMakeLists = changesWidget.exec() && changesWidget.applyAllChanges(); cmakeSuccessful &= changedCMakeLists; } if (!cmakeSuccessful) { if (KMessageBox::questionYesNo( QApplication::activeWindow(), i18n("Changes to CMakeLists failed, abort rename?"), DIALOG_CAPTION ) == KMessageBox::Yes) return false; } bool ret = KDevelop::renameUrl(project, oldUrl, newPath.toUrl()); if(!ret) { item->setPath(oldPath); } return ret; } bool CMakeManager::renameFile(ProjectFileItem *item, const Path &newPath) { return renameFileOrFolder(item, newPath); } bool CMakeManager::renameFolder(ProjectFolderItem* item, const Path &newPath) { return renameFileOrFolder(item, newPath); } QWidget* CMakeManager::specialLanguageObjectNavigationWidget(const KUrl& url, const KDevelop::SimpleCursor& position) { KDevelop::TopDUContextPointer top= TopDUContextPointer(KDevelop::DUChain::self()->chainForDocument(url)); Declaration *decl=0; if(top) { int useAt=top->findUseAt(top->transformToLocalRevision(position)); if(useAt>=0) { Use u=top->uses()[useAt]; decl=u.usedDeclaration(top->topContext()); } } CMakeNavigationWidget* doc=0; if(decl) { doc=new CMakeNavigationWidget(top, decl); } else { const IDocument* d=ICore::self()->documentController()->documentForUrl(url); const KTextEditor::Document* e=d->textDocument(); KTextEditor::Cursor start=position.textCursor(), end=position.textCursor(), step(0,1); for(QChar i=e->character(start); i.isLetter() || i=='_'; i=e->character(start-=step)) {} start+=step; for(QChar i=e->character(end); i.isLetter() || i=='_'; i=e->character(end+=step)) {} QString id=e->text(KTextEditor::Range(start, end)); ICMakeDocumentation* docu=CMake::cmakeDocumentation(); if( docu ) { KSharedPtr desc=docu->description(id, url); if(!desc.isNull()) { doc=new CMakeNavigationWidget(top, desc); } } } return doc; } QPair CMakeManager::cacheValue(KDevelop::IProject* project, const QString& id) const { QPair ret; if(project==0 && !m_projectsData.isEmpty()) { project=m_projectsData.keys().first(); } // kDebug() << "cache value " << id << project << (m_projectsData.contains(project) && m_projectsData[project].cache.contains(id)); CMakeProjectData* data = m_projectsData[project]; if(data && data->cache.contains(id)) { const CacheEntry& e=data->cache.value(id); ret.first=e.value; ret.second=e.doc; } return ret; } void CMakeManager::projectClosing(IProject* p) { delete m_projectsData.take(p); delete m_watchers.take(p); m_filter->remove(p); kDebug(9042) << "Project closed" << p; } QStringList CMakeManager::processGeneratorExpression(const QStringList& expr, IProject* project, ProjectTargetItem* target) const { QStringList ret; const CMakeProjectData* data = m_projectsData[project]; GenerationExpressionSolver exec(data->properties, data->targetAlias); if(target) exec.setTargetName(target->text()); exec.defineVariable("INSTALL_PREFIX", data->vm.value("CMAKE_INSTALL_PREFIX").join(QString())); for(QStringList::const_iterator it = expr.constBegin(), itEnd = expr.constEnd(); it!=itEnd; ++it) { QStringList val = exec.run(*it).split(';'); ret += val; } return ret; } void CMakeManager::addPending(const Path& path, CMakeFolderItem* folder) { m_pending.insert(path, folder); } CMakeFolderItem* CMakeManager::takePending(const Path& path) { return m_pending.take(path); } void CMakeManager::addWatcher(IProject* p, const QString& path) { if (QFileSystemWatcher* watcher = m_watchers.value(p)) { watcher->addPath(path); } else { kWarning() << "Could not find a watcher for project" << p << p->name() << ", path " << path; Q_ASSERT(false); } } CMakeProjectData CMakeManager::projectData(IProject* project) { Q_ASSERT(QThread::currentThread() == project->thread()); CMakeProjectData* data = m_projectsData[project]; if(!data) { data = new CMakeProjectData; m_projectsData[project] = data; } return *data; } ProjectFilterManager* CMakeManager::filterManager() const { return m_filter; }