1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "qt4project.h"
32 #include "profilereader.h"
33 #include "qt4projectmanager.h"
35 #include "qmakestep.h"
36 #include "qt4runconfiguration.h"
38 #include "qt4projectconfigwidget.h"
39 #include "qt4projectmanagerconstants.h"
40 #include "projectloadwizard.h"
41 #include "qt4buildconfiguration.h"
42 #include "findqt4profiles.h"
43 #include "qmldumptool.h"
45 #include <coreplugin/icore.h>
46 #include <coreplugin/messagemanager.h>
47 #include <coreplugin/coreconstants.h>
48 #include <coreplugin/progressmanager/progressmanager.h>
49 #include <extensionsystem/pluginmanager.h>
50 #include <cpptools/cppmodelmanagerinterface.h>
51 #include <qmljs/qmljsmodelmanagerinterface.h>
52 #include <projectexplorer/buildenvironmentwidget.h>
53 #include <projectexplorer/customexecutablerunconfiguration.h>
54 #include <projectexplorer/projectexplorer.h>
55 #include <utils/qtcassert.h>
57 #include <QtCore/QDebug>
58 #include <QtCore/QDir>
59 #include <QtGui/QFileDialog>
60 #include <QtGui/QInputDialog>
62 using namespace Qt4ProjectManager;
63 using namespace Qt4ProjectManager::Internal;
64 using namespace ProjectExplorer;
68 namespace Qt4ProjectManager {
71 // Qt4ProjectFiles: Struct for (Cached) lists of files in a project
72 struct Qt4ProjectFiles {
74 bool equals(const Qt4ProjectFiles &f) const;
76 QStringList files[ProjectExplorer::FileTypeSize];
77 QStringList generatedFiles[ProjectExplorer::FileTypeSize];
81 void Qt4ProjectFiles::clear()
83 for (int i = 0; i < FileTypeSize; ++i) {
85 generatedFiles[i].clear();
90 bool Qt4ProjectFiles::equals(const Qt4ProjectFiles &f) const
92 for (int i = 0; i < FileTypeSize; ++i)
93 if (files[i] != f.files[i] || generatedFiles[i] != f.generatedFiles[i])
95 if (proFiles != f.proFiles)
100 inline bool operator==(const Qt4ProjectFiles &f1, const Qt4ProjectFiles &f2)
101 { return f1.equals(f2); }
103 inline bool operator!=(const Qt4ProjectFiles &f1, const Qt4ProjectFiles &f2)
104 { return !f1.equals(f2); }
106 QDebug operator<<(QDebug d, const Qt4ProjectFiles &f)
108 QDebug nsp = d.nospace();
109 nsp << "Qt4ProjectFiles: proFiles=" << f.proFiles << '\n';
110 for (int i = 0; i < FileTypeSize; ++i)
111 nsp << "Type " << i << " files=" << f.files[i] << " generated=" << f.generatedFiles[i] << '\n';
115 // A visitor to collect all files of a project in a Qt4ProjectFiles struct
116 class ProjectFilesVisitor : public ProjectExplorer::NodesVisitor
118 Q_DISABLE_COPY(ProjectFilesVisitor)
119 ProjectFilesVisitor(Qt4ProjectFiles *files);
122 static void findProjectFiles(Qt4ProFileNode *rootNode, Qt4ProjectFiles *files);
124 void visitProjectNode(ProjectNode *projectNode);
125 void visitFolderNode(FolderNode *folderNode);
128 Qt4ProjectFiles *m_files;
131 ProjectFilesVisitor::ProjectFilesVisitor(Qt4ProjectFiles *files) :
136 void ProjectFilesVisitor::findProjectFiles(Qt4ProFileNode *rootNode, Qt4ProjectFiles *files)
139 ProjectFilesVisitor visitor(files);
140 rootNode->accept(&visitor);
141 for (int i = 0; i < FileTypeSize; ++i) {
142 qSort(files->files[i]);
143 qSort(files->generatedFiles[i]);
145 qSort(files->proFiles);
148 void ProjectFilesVisitor::visitProjectNode(ProjectNode *projectNode)
150 const QString path = projectNode->path();
151 if (!m_files->proFiles.contains(path))
152 m_files->proFiles.append(path);
153 visitFolderNode(projectNode);
156 void ProjectFilesVisitor::visitFolderNode(FolderNode *folderNode)
158 foreach (FileNode *fileNode, folderNode->fileNodes()) {
159 const QString path = fileNode->path();
160 const int type = fileNode->fileType();
161 QStringList &targetList = fileNode->isGenerated() ? m_files->generatedFiles[type] : m_files->files[type];
162 if (!targetList.contains(path))
163 targetList.push_back(path);
170 // ----------- Qt4ProjectFile
171 Qt4ProjectFile::Qt4ProjectFile(Qt4Project *project, const QString &filePath, QObject *parent)
172 : Core::IFile(parent),
173 m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)),
179 bool Qt4ProjectFile::save(const QString &)
181 // This is never used
185 void Qt4ProjectFile::rename(const QString &newName)
192 QString Qt4ProjectFile::fileName() const
197 QString Qt4ProjectFile::defaultPath() const
202 QString Qt4ProjectFile::suggestedFileName() const
207 QString Qt4ProjectFile::mimeType() const
212 bool Qt4ProjectFile::isModified() const
214 return false; // we save after changing anyway
217 bool Qt4ProjectFile::isReadOnly() const
219 QFileInfo fi(m_filePath);
220 return !fi.isWritable();
223 bool Qt4ProjectFile::isSaveAsAllowed() const
228 Core::IFile::ReloadBehavior Qt4ProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
232 return BehaviorSilent;
235 void Qt4ProjectFile::reload(ReloadFlag flag, ChangeType type)
244 Qt4Project manages information about an individual Qt 4 (.pro) project file.
247 Qt4Project::Qt4Project(Qt4Manager *manager, const QString& fileName) :
249 m_rootProjectNode(0),
250 m_nodesWatcher(new Internal::Qt4NodesWatcher(this)),
251 m_targetFactory(new Qt4TargetFactory(this)),
252 m_fileInfo(new Qt4ProjectFile(this, fileName, this)),
253 m_projectFiles(new Qt4ProjectFiles),
255 m_asyncUpdateFutureInterface(0),
256 m_pendingEvaluateFuturesCount(0),
257 m_asyncUpdateState(NoState),
258 m_cancelEvaluate(false)
260 m_asyncUpdateTimer.setSingleShot(true);
261 m_asyncUpdateTimer.setInterval(3000);
262 connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate()));
264 setSupportedTargetIds(QtVersionManager::instance()->supportedTargetIds());
267 Qt4Project::~Qt4Project()
269 m_codeModelFuture.cancel();
270 m_asyncUpdateState = ShuttingDown;
271 m_manager->unregisterProject(this);
272 delete m_projectFiles;
273 m_cancelEvaluate = true;
274 delete m_rootProjectNode;
277 void Qt4Project::updateFileList()
279 Qt4ProjectFiles newFiles;
280 ProjectFilesVisitor::findProjectFiles(m_rootProjectNode, &newFiles);
281 if (newFiles != *m_projectFiles) {
282 *m_projectFiles = newFiles;
283 emit fileListChanged();
285 qDebug() << Q_FUNC_INFO << *m_projectFiles;
289 bool Qt4Project::fromMap(const QVariantMap &map)
291 if (!Project::fromMap(map))
294 // Prune targets without buildconfigurations:
295 // This can happen esp. when updating from a old version of Qt Creator
296 QList<Target *>ts(targets());
297 foreach (Target *t, ts) {
298 if (t->buildConfigurations().isEmpty()) {
299 qWarning() << "Removing" << t->id() << "since it has no buildconfigurations!";
304 // Add buildconfigurations so we can parse the pro-files.
305 if (targets().isEmpty())
308 if (targets().isEmpty()) {
309 qWarning() << "Unable to create targets!";
313 Q_ASSERT(activeTarget());
314 Q_ASSERT(activeTarget()->activeBuildConfiguration());
316 m_manager->registerProject(this);
318 m_rootProjectNode = new Qt4ProFileNode(this, m_fileInfo->fileName(), this);
319 m_rootProjectNode->registerWatcher(m_nodesWatcher);
323 // This might be incorrect, need a full update
326 createApplicationProjects();
328 foreach (Target *t, targets())
331 setSupportedTargetIds(QtVersionManager::instance()->supportedTargetIds());
333 // Setup Qt versions supported (== possible targets).
334 connect(this, SIGNAL(addedTarget(ProjectExplorer::Target*)),
335 this, SLOT(onAddedTarget(ProjectExplorer::Target*)));
337 connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>)),
338 this, SLOT(qtVersionsChanged()));
340 connect(m_nodesWatcher, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode*,bool)),
341 this, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *,bool)));
343 connect(m_nodesWatcher, SIGNAL(proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode*)),
344 this, SIGNAL(proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode*)));
346 connect(this, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
347 this, SLOT(activeTargetWasChanged()));
352 Qt4TargetFactory *Qt4Project::targetFactory() const
354 return m_targetFactory;
357 Qt4Target *Qt4Project::activeTarget() const
359 return static_cast<Qt4Target *>(Project::activeTarget());
362 void Qt4Project::onAddedTarget(ProjectExplorer::Target *t)
365 Qt4Target *qt4target = qobject_cast<Qt4Target *>(t);
367 connect(qt4target, SIGNAL(buildDirectoryInitialized()),
368 this, SIGNAL(buildDirectoryInitialized()));
369 connect(qt4target, SIGNAL(proFileEvaluateNeeded(Qt4ProjectManager::Internal::Qt4Target*)),
370 this, SLOT(proFileEvaluateNeeded(Qt4ProjectManager::Internal::Qt4Target*)));
373 void Qt4Project::proFileEvaluateNeeded(Qt4ProjectManager::Internal::Qt4Target *target)
375 if (activeTarget() == target)
376 scheduleAsyncUpdate();
379 /// equalFileList compares two file lists ignoring
380 /// <configuration> without generating temporary lists
382 bool Qt4Project::equalFileList(const QStringList &a, const QStringList &b)
384 if (abs(a.length() - b.length()) > 1)
386 QStringList::const_iterator ait = a.constBegin();
387 QStringList::const_iterator bit = b.constBegin();
388 QStringList::const_iterator aend = a.constEnd();
389 QStringList::const_iterator bend = b.constEnd();
391 while (ait != aend && bit != bend) {
392 if (*ait == QLatin1String("<configuration>"))
394 else if (*bit == QLatin1String("<configuration>"))
396 else if (*ait == *bit)
401 return (ait == aend && bit == bend);
404 void Qt4Project::updateCodeModels()
407 qDebug()<<"Qt4Project::updateCodeModel()";
409 if (!activeTarget() || !activeTarget()->activeBuildConfiguration())
412 updateCppCodeModel();
413 updateQmlJSCodeModel();
416 void Qt4Project::updateCppCodeModel()
418 Qt4BuildConfiguration *activeBC = activeTarget()->activeBuildConfiguration();
420 CppTools::CppModelManagerInterface *modelmanager =
421 ExtensionSystem::PluginManager::instance()
422 ->getObject<CppTools::CppModelManagerInterface>();
427 // Collect global headers/defines
428 QStringList predefinedIncludePaths;
429 QStringList predefinedFrameworkPaths;
430 QByteArray predefinedMacros;
432 QString qtFrameworkPath = activeBC->qtVersion()->frameworkInstallPath();
433 if (!qtFrameworkPath.isEmpty())
434 predefinedFrameworkPaths.append(qtFrameworkPath);
436 ToolChain *tc = activeBC->toolChain();
438 predefinedMacros = tc->predefinedMacros();
439 //qDebug()<<"Predefined Macros";
440 //qDebug()<<tc->predefinedMacros();
442 //qDebug()<<"System Header Paths";
443 //foreach(const HeaderPath &hp, tc->systemHeaderPaths())
444 // qDebug()<<hp.path();
446 foreach (const HeaderPath &headerPath, tc->systemHeaderPaths()) {
447 if (headerPath.kind() == HeaderPath::FrameworkHeaderPath)
448 predefinedFrameworkPaths.append(headerPath.path());
450 predefinedIncludePaths.append(headerPath.path());
454 FindQt4ProFiles findQt4ProFiles;
455 QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
456 QByteArray allDefinedMacros = predefinedMacros;
457 QStringList allIncludePaths;
458 QStringList allFrameworkPaths = predefinedFrameworkPaths;
459 QStringList allPrecompileHeaders;
461 // Collect per .pro file information
462 foreach (Qt4ProFileNode *pro, proFiles) {
463 Internal::CodeModelInfo info;
464 info.defines = predefinedMacros;
465 info.frameworkPaths = predefinedFrameworkPaths;
467 info.precompiledHeader = pro->variableValue(PrecompiledHeaderVar);
469 allPrecompileHeaders.append(info.precompiledHeader);
471 // Add custom defines
473 foreach (const QString &def, pro->variableValue(DefinesVar)) {
474 allDefinedMacros += "#define ";
475 info.defines += "#define ";
476 const int index = def.indexOf(QLatin1Char('='));
478 allDefinedMacros += def.toLatin1();
479 allDefinedMacros += " 1\n";
480 info.defines += def.toLatin1();
481 info.defines += " 1\n";
483 const QString name = def.left(index);
484 const QString value = def.mid(index + 1);
485 allDefinedMacros += name.toLatin1();
486 allDefinedMacros += ' ';
487 allDefinedMacros += value.toLocal8Bit();
488 allDefinedMacros += '\n';
489 info.defines += name.toLatin1();
491 info.defines += value.toLocal8Bit();
492 info.defines += '\n';
496 const QStringList proIncludePaths = pro->variableValue(IncludePathVar);
497 foreach (const QString &includePath, proIncludePaths) {
498 if (!allIncludePaths.contains(includePath))
499 allIncludePaths.append(includePath);
500 if (!info.includes.contains(includePath))
501 info.includes.append(includePath);
504 #if 0 // Experimental PKGCONFIG support
505 { // Pkg Config support
506 QStringList pkgConfig = pro->variableValue(PkgConfigVar);
507 if (!pkgConfig.isEmpty()) {
508 pkgConfig.prepend("--cflags-only-I");
510 process.start("pkg-config", pkgConfig);
511 process.waitForFinished();
512 QString result = process.readAllStandardOutput();
513 foreach(const QString &part, result.trimmed().split(' ', QString::SkipEmptyParts)) {
514 info.includes.append(part.mid(2)); // Chop off "-I"
520 // Add mkspec directory
521 info.includes.append(activeBC->qtVersion()->mkspecPath());
522 info.includes.append(predefinedIncludePaths);
524 // qDebug()<<"Dumping code model information";
525 // qDebug()<<"for .pro file"<< pro->path();
526 // qDebug()<<info.defines;
527 // qDebug()<<info.includes;
528 // qDebug()<<info.frameworkPaths;
532 // Add mkspec directory
533 allIncludePaths.append(activeBC->qtVersion()->mkspecPath());
535 allIncludePaths.append(predefinedIncludePaths);
538 files += m_projectFiles->files[HeaderType];
539 files += m_projectFiles->generatedFiles[HeaderType];
540 files += m_projectFiles->files[SourceType];
541 files += m_projectFiles->generatedFiles[SourceType];
543 CppTools::CppModelManagerInterface::ProjectInfo pinfo = modelmanager->projectInfo(this);
545 //qDebug()<<"Using precompiled header"<<allPrecompileHeaders;
547 bool fileList = equalFileList(pinfo.sourceFiles, files);
549 if (pinfo.defines == allDefinedMacros
550 && pinfo.includePaths == allIncludePaths
551 && pinfo.frameworkPaths == allFrameworkPaths
553 && pinfo.precompiledHeaders == allPrecompileHeaders) {
554 // Nothing to update...
556 pinfo.sourceFiles.clear();
557 if (pinfo.defines != allDefinedMacros
558 || pinfo.includePaths != allIncludePaths
559 || pinfo.frameworkPaths != allFrameworkPaths
560 || pinfo.precompiledHeaders != allPrecompileHeaders)
562 pinfo.sourceFiles.append(QLatin1String("<configuration>"));
565 //pinfo.defines = predefinedMacros;
566 pinfo.defines = allDefinedMacros;
567 pinfo.includePaths = allIncludePaths;
568 pinfo.frameworkPaths = allFrameworkPaths;
569 pinfo.sourceFiles += files;
570 pinfo.precompiledHeaders = allPrecompileHeaders;
572 modelmanager->updateProjectInfo(pinfo);
573 m_codeModelFuture = modelmanager->updateSourceFiles(pinfo.sourceFiles);
577 void Qt4Project::updateQmlJSCodeModel()
579 if (m_projectFiles->files[QMLType].isEmpty())
582 QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
586 QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(this);
588 // Not essential since the QmlJS engine parses required files on demand.
589 //projectInfo.sourceFiles = ...
591 FindQt4ProFiles findQt4ProFiles;
592 QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
594 foreach (Qt4ProFileNode *node, proFiles) {
595 projectInfo.importPaths.append(node->variableValue(QmlImportPathVar));
597 if (activeTarget() && activeTarget()->activeBuildConfiguration()) {
598 const QtVersion *qtVersion = activeTarget()->activeBuildConfiguration()->qtVersion();
599 if (qtVersion->isValid()) {
600 const QString qtVersionImportPath = qtVersion->versionInfo().value("QT_INSTALL_IMPORTS");
601 if (!qtVersionImportPath.isEmpty())
602 projectInfo.importPaths += qtVersionImportPath;
605 QmlDumpTool::pathAndEnvironment(this, &projectInfo.qmlDumpPath, &projectInfo.qmlDumpEnvironment);
606 projectInfo.importPaths.removeDuplicates();
608 modelManager->updateProjectInfo(projectInfo);
611 void Qt4Project::qtVersionsChanged()
613 setSupportedTargetIds(QtVersionManager::instance()->supportedTargetIds());
617 // Updates complete project
619 void Qt4Project::update()
622 qDebug()<<"Doing sync update";
623 m_rootProjectNode->update();
626 qDebug()<<"State is now Base";
627 m_asyncUpdateState = Base;
630 void Qt4Project::scheduleAsyncUpdate(Qt4ProFileNode *node)
632 if (m_asyncUpdateState == ShuttingDown)
636 qDebug()<<"schduleAsyncUpdate (node)";
637 Q_ASSERT(m_asyncUpdateState != NoState);
639 if (m_cancelEvaluate) {
641 qDebug()<<" Already canceling, nothing to do";
642 // A cancel is in progress
643 // That implies that a full update is going to happen afterwards
644 // So we don't need to do anything
648 if (m_asyncUpdateState == AsyncFullUpdatePending) {
651 qDebug()<<" full update pending, restarting timer";
652 m_asyncUpdateTimer.start();
653 } else if (m_asyncUpdateState == AsyncPartialUpdatePending
654 || m_asyncUpdateState == Base) {
656 qDebug()<<" adding node to async update list, setting state to AsyncPartialUpdatePending";
658 m_asyncUpdateState = AsyncPartialUpdatePending;
660 QList<Internal::Qt4ProFileNode *>::iterator it;
663 qDebug()<<"scheduleAsyncUpdate();"<<m_partialEvaluate.size()<<"nodes";
664 it = m_partialEvaluate.begin();
665 while (it != m_partialEvaluate.end()) {
669 } else if (node->isParent(*it)) { // We already have the parent in the list, nothing to do
672 } else if ((*it)->isParent(node)) { // The node is the parent of a child already in the list
673 it = m_partialEvaluate.erase(it);
680 m_partialEvaluate.append(node);
681 // and start the timer anew
682 m_asyncUpdateTimer.start();
683 } else if (m_asyncUpdateState == AsyncUpdateInProgress) {
684 // A update is in progress
685 // And this slot only gets called if a file changed on disc
686 // So we'll play it safe and schedule a complete evaluate
687 // This might trigger if due to version control a few files
688 // change a partial update gets in progress and then another
689 // batch of changes come in, which triggers a full update
690 // even if that's not really needed
692 qDebug()<<" Async update in progress, scheduling new one afterwards";
693 scheduleAsyncUpdate();
697 void Qt4Project::scheduleAsyncUpdate()
700 qDebug()<<"scheduleAsyncUpdate";
701 if (m_asyncUpdateState == ShuttingDown)
704 Q_ASSERT(m_asyncUpdateState != NoState);
705 if (m_cancelEvaluate) { // we are in progress of canceling
706 // and will start the evaluation after that
708 qDebug()<<" canceling is in progress, doing nothing";
711 if (m_asyncUpdateState == AsyncUpdateInProgress) {
713 qDebug()<<" update in progress, canceling and setting state to full update pending";
714 m_cancelEvaluate = true;
715 m_asyncUpdateState = AsyncFullUpdatePending;
716 m_rootProjectNode->emitProFileInvalidated();
721 qDebug()<<" starting timer for full update, setting state to full update pending";
722 m_partialEvaluate.clear();
723 m_rootProjectNode->emitProFileInvalidated();
724 m_asyncUpdateState = AsyncFullUpdatePending;
725 m_asyncUpdateTimer.start();
727 // Cancel running code model update
728 m_codeModelFuture.cancel();
732 void Qt4Project::incrementPendingEvaluateFutures()
734 ++m_pendingEvaluateFuturesCount;
736 qDebug()<<"incrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
738 m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(),
739 m_asyncUpdateFutureInterface->progressMaximum() + 1);
742 void Qt4Project::decrementPendingEvaluateFutures()
744 --m_pendingEvaluateFuturesCount;
747 qDebug()<<"decrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
749 m_asyncUpdateFutureInterface->setProgressValue(m_asyncUpdateFutureInterface->progressValue() + 1);
750 if (m_pendingEvaluateFuturesCount == 0) {
752 qDebug()<<" WOHOO, no pending futures, cleaning up";
755 qDebug()<<" reporting finished";
756 m_asyncUpdateFutureInterface->reportFinished();
757 delete m_asyncUpdateFutureInterface;
758 m_asyncUpdateFutureInterface = 0;
759 m_cancelEvaluate = false;
761 // TODO clear the profile cache ?
762 if (m_asyncUpdateState == AsyncFullUpdatePending || m_asyncUpdateState == AsyncPartialUpdatePending) {
764 qDebug()<<" Oh update is pending start the timer";
765 m_asyncUpdateTimer.start();
766 } else if (m_asyncUpdateState != ShuttingDown){
767 // After beeing done, we need to call:
771 qDebug()<<" Setting state to Base";
772 m_asyncUpdateState = Base;
777 bool Qt4Project::wasEvaluateCanceled()
779 return m_cancelEvaluate;
782 QString Qt4Project::defaultTopLevelBuildDirectory() const
784 return defaultTopLevelBuildDirectory(file()->fileName());
787 QString Qt4Project::defaultTopLevelBuildDirectory(const QString &profilePath)
789 if (profilePath.isEmpty())
791 QFileInfo info(profilePath);
792 return QDir(projectDirectory(profilePath) + QLatin1String("/../") + info.baseName() + QLatin1String("-build")).absolutePath();
795 void Qt4Project::asyncUpdate()
798 qDebug()<<"async update, timer expired, doing now";
799 Q_ASSERT(!m_asyncUpdateFutureInterface);
800 m_asyncUpdateFutureInterface = new QFutureInterface<void>();
802 Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
804 m_asyncUpdateFutureInterface->setProgressRange(0, 0);
805 progressManager->addTask(m_asyncUpdateFutureInterface->future(), tr("Evaluating"), Constants::PROFILE_EVALUATE);
807 qDebug()<<" adding task";
809 m_asyncUpdateFutureInterface->reportStarted();
811 if (m_asyncUpdateState == AsyncFullUpdatePending) {
813 qDebug()<<" full update, starting with root node";
814 m_rootProjectNode->asyncUpdate();
817 qDebug()<<" partial update,"<<m_partialEvaluate.size()<<"nodes to update";
818 foreach(Qt4ProFileNode *node, m_partialEvaluate)
822 m_partialEvaluate.clear();
824 qDebug()<<" Setting state to AsyncUpdateInProgress";
825 m_asyncUpdateState = AsyncUpdateInProgress;
828 ProjectExplorer::IProjectManager *Qt4Project::projectManager() const
833 Qt4Manager *Qt4Project::qt4ProjectManager() const
838 QString Qt4Project::displayName() const
840 return QFileInfo(file()->fileName()).completeBaseName();
843 QString Qt4Project::id() const
845 return QLatin1String(Constants::QT4PROJECT_ID);
848 Core::IFile *Qt4Project::file() const
853 QStringList Qt4Project::files(FilesMode fileMode) const
856 for (int i = 0; i < FileTypeSize; ++i) {
857 files += m_projectFiles->files[i];
858 if (fileMode == AllFiles)
859 files += m_projectFiles->generatedFiles[i];
864 // Find the folder that contains a file a certain type (recurse down)
865 static FolderNode *folderOf(FolderNode *in, FileType fileType, const QString &fileName)
867 foreach(FileNode *fn, in->fileNodes())
868 if (fn->fileType() == fileType && fn->path() == fileName)
870 foreach(FolderNode *folder, in->subFolderNodes())
871 if (FolderNode *pn = folderOf(folder, fileType, fileName))
876 // Find the Qt4ProFileNode that contains a file of a certain type.
877 // First recurse down to folder, then find the pro-file.
878 static Qt4ProFileNode *proFileNodeOf(Qt4ProFileNode *in, FileType fileType, const QString &fileName)
880 for (FolderNode *folder = folderOf(in, fileType, fileName); folder; folder = folder->parentFolderNode())
881 if (Qt4ProFileNode *proFile = qobject_cast<Qt4ProFileNode *>(folder))
886 QString Qt4Project::generatedUiHeader(const QString &formFile) const
888 // Look in sub-profiles as SessionManager::projectForFile returns
889 // the top-level project only.
890 if (m_rootProjectNode)
891 if (const Qt4ProFileNode *pro = proFileNodeOf(m_rootProjectNode, FormType, formFile))
892 return Qt4ProFileNode::uiHeaderFile(pro->uiDirectory(), formFile);
896 QList<ProjectExplorer::Project*> Qt4Project::dependsOn()
898 // NBS implement dependsOn
899 return QList<Project *>();
902 void Qt4Project::addDefaultBuild()
904 // TODO this could probably refactored
905 // That is the ProjectLoadWizard divided into useful bits
906 // and this code then called here, instead of that strange forwarding
907 // to a wizard, which doesn't even show up
908 ProjectLoadWizard wizard(this);
912 void Qt4Project::proFileParseError(const QString &errorMessage)
914 Core::ICore::instance()->messageManager()->printToOutputPane(errorMessage);
917 ProFileReader *Qt4Project::createProFileReader(Qt4ProFileNode *qt4ProFileNode)
919 if (!m_proFileOption) {
920 m_proFileOption = new ProFileOption;
921 m_proFileOptionRefCnt = 0;
923 if (activeTarget() &&
924 activeTarget()->activeBuildConfiguration()) {
925 QtVersion *version = activeTarget()->activeBuildConfiguration()->qtVersion();
926 if (version->isValid())
927 m_proFileOption->properties = version->versionInfo();
930 ProFileCacheManager::instance()->incRefCount();
932 ++m_proFileOptionRefCnt;
934 ProFileReader *reader = new ProFileReader(m_proFileOption);
936 reader->setOutputDir(qt4ProFileNode->buildDir());
941 void Qt4Project::destroyProFileReader(ProFileReader *reader)
944 if (!--m_proFileOptionRefCnt) {
945 QString dir = QFileInfo(m_fileInfo->fileName()).absolutePath();
946 if (!dir.endsWith(QLatin1Char('/')))
947 dir += QLatin1Char('/');
948 ProFileCacheManager::instance()->discardFiles(dir);
949 ProFileCacheManager::instance()->decRefCount();
951 delete m_proFileOption;
956 Qt4ProFileNode *Qt4Project::rootProjectNode() const
958 return m_rootProjectNode;
961 bool Qt4Project::validParse(const QString &proFilePath) const
964 if (!m_rootProjectNode)
966 const Qt4ProFileNode *node = m_rootProjectNode->findProFileFor(proFilePath);
967 return node && node->validParse();
970 BuildConfigWidget *Qt4Project::createConfigWidget()
972 return new Qt4ProjectConfigWidget(this);
975 QList<BuildConfigWidget*> Qt4Project::subConfigWidgets()
977 QList<BuildConfigWidget*> subWidgets;
978 subWidgets << new BuildEnvironmentWidget;
982 void Qt4Project::collectLeafProFiles(QList<Qt4ProFileNode *> &list, Qt4ProFileNode *node)
984 if (node->projectType() != Internal::SubDirsTemplate) {
987 foreach (ProjectNode *n, node->subProjectNodes()) {
988 Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(n);
990 collectLeafProFiles(list, qt4ProFileNode);
995 void Qt4Project::collectApplicationProFiles(QList<Qt4ProFileNode *> &list, Qt4ProFileNode *node)
997 if (node->projectType() == Internal::ApplicationTemplate
998 || node->projectType() == Internal::ScriptTemplate) {
1001 foreach (ProjectNode *n, node->subProjectNodes()) {
1002 Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(n);
1004 collectApplicationProFiles(list, qt4ProFileNode);
1008 void Qt4Project::createApplicationProjects()
1010 foreach (Target *target, targets()) {
1011 if (target->runConfigurations().count()) {
1012 // Remove all run configurations which the new project wizard created
1013 QList<RunConfiguration*> toRemove;
1014 foreach (RunConfiguration * rc, target->runConfigurations()) {
1015 CustomExecutableRunConfiguration *cerc = qobject_cast<CustomExecutableRunConfiguration *>(rc);
1016 if (cerc && !cerc->isConfigured())
1017 toRemove.append(rc);
1019 foreach (RunConfiguration *rc, toRemove)
1020 target->removeRunConfiguration(rc);
1023 // We use the list twice
1024 QList<Qt4ProFileNode *> profiles = applicationProFiles();
1026 foreach (Qt4ProFileNode *pro, profiles)
1027 paths << pro->path();
1029 foreach (RunConfiguration *rc, target->runConfigurations()) {
1030 if (Qt4RunConfiguration *qt4rc = qobject_cast<Qt4RunConfiguration *>(rc)) {
1031 if (!paths.contains(qt4rc->proFilePath())) {
1032 // A deleted .pro file? or a change template
1033 // We do remove those though
1034 target->removeRunConfiguration(rc);
1039 // Only add new runconfigurations if there are none.
1040 if (target->runConfigurations().isEmpty()) {
1041 Qt4Target *qt4Target = static_cast<Qt4Target *>(target);
1042 foreach (Qt4ProFileNode *qt4proFile, profiles) {
1043 qt4Target->addRunConfigurationForPath(qt4proFile->path());
1046 // Oh still none? Add a custom executable runconfiguration
1047 if (target->runConfigurations().isEmpty()) {
1048 target->addRunConfiguration(new ProjectExplorer::CustomExecutableRunConfiguration(target));
1053 QList<Qt4ProFileNode *> Qt4Project::leafProFiles() const
1055 QList<Qt4ProFileNode *> list;
1056 if (!rootProjectNode())
1058 collectLeafProFiles(list, rootProjectNode());
1062 QList<Qt4ProFileNode *> Qt4Project::applicationProFiles() const
1064 QList<Qt4ProFileNode *> list;
1065 if (!rootProjectNode())
1067 collectApplicationProFiles(list, rootProjectNode());
1071 bool Qt4Project::hasApplicationProFile(const QString &path) const
1076 QList<Qt4ProFileNode *> list = applicationProFiles();
1077 foreach (Qt4ProFileNode * node, list)
1078 if (node->path() == path)
1083 QStringList Qt4Project::applicationProFilePathes(const QString &prepend) const
1085 QStringList proFiles;
1086 foreach (Qt4ProFileNode *node, applicationProFiles())
1087 proFiles.append(prepend + node->path());
1091 void Qt4Project::activeTargetWasChanged()
1093 if (!activeTarget())
1096 scheduleAsyncUpdate();
1099 bool Qt4Project::hasSubNode(Qt4PriFileNode *root, const QString &path)
1101 if (root->path() == path)
1103 foreach (FolderNode *fn, root->subFolderNodes()) {
1104 if (qobject_cast<Qt4ProFileNode *>(fn)) {
1105 // we aren't interested in pro file nodes
1106 } else if (Qt4PriFileNode *qt4prifilenode = qobject_cast<Qt4PriFileNode *>(fn)) {
1107 if (hasSubNode(qt4prifilenode, path))
1114 void Qt4Project::findProFile(const QString& fileName, Qt4ProFileNode *root, QList<Qt4ProFileNode *> &list)
1116 if (hasSubNode(root, fileName))
1119 foreach (FolderNode *fn, root->subFolderNodes())
1120 if (Qt4ProFileNode *qt4proFileNode = qobject_cast<Qt4ProFileNode *>(fn))
1121 findProFile(fileName, qt4proFileNode, list);
1124 void Qt4Project::notifyChanged(const QString &name)
1126 if (files(Qt4Project::ExcludeGeneratedFiles).contains(name)) {
1127 QList<Qt4ProFileNode *> list;
1128 findProFile(name, rootProjectNode(), list);
1129 foreach(Qt4ProFileNode *node, list) {
1130 ProFileCacheManager::instance()->discardFile(name);
1136 CentralizedFolderWatcher *Qt4Project::centralizedFolderWatcher()
1138 return &m_centralizedFolderWatcher;
1142 /// Centralized Folder Watcher
1145 // All the folder have a trailing slash!
1148 bool debugCFW = false;
1151 CentralizedFolderWatcher::CentralizedFolderWatcher()
1153 m_compressTimer.setSingleShot(true);
1154 m_compressTimer.setInterval(200);
1155 connect(&m_compressTimer, SIGNAL(timeout()),
1156 this, SLOT(onTimer()));
1157 connect (&m_watcher, SIGNAL(directoryChanged(QString)),
1158 this, SLOT(folderChanged(QString)));
1161 CentralizedFolderWatcher::~CentralizedFolderWatcher()
1166 QSet<QString> CentralizedFolderWatcher::recursiveDirs(const QString &folder)
1168 QSet<QString> result;
1170 QStringList list = dir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
1171 foreach (const QString &f, list) {
1172 result.insert(folder + f + "/");
1173 result += recursiveDirs(folder + f + "/");
1178 void CentralizedFolderWatcher::watchFolders(const QList<QString> &folders, Qt4PriFileNode *node)
1181 qDebug()<<"CFW::watchFolders()"<<folders<<"for node"<<node->path();
1182 m_watcher.addPaths(folders);
1184 foreach (const QString &f, folders) {
1186 if (!folder.endsWith('/'))
1188 m_map.insert(folder, node);
1190 // Support for recursive watching
1191 // we add the recursive directories we find
1192 QSet<QString> tmp = recursiveDirs(folder);
1194 m_watcher.addPaths(tmp.toList());
1195 m_recursiveWatchedFolders += tmp;
1198 qDebug()<<"adding recursive dirs for"<< folder<<":"<<tmp;
1202 void CentralizedFolderWatcher::unwatchFolders(const QList<QString> &folders, Qt4PriFileNode *node)
1205 qDebug()<<"CFW::unwatchFolders()"<<folders<<"for node"<<node->path();
1206 foreach (const QString &f, folders) {
1208 if (!folder.endsWith('/'))
1210 m_map.remove(folder, node);
1211 if (!m_map.contains(folder)) {
1212 m_watcher.removePath(folder);
1215 // Figure out which recursive directories we can remove
1216 // TODO this might not scale. I'm pretty sure it doesn't
1217 // A scaling implementation would need to save more information
1218 // where a given directory watcher actual comes from...
1220 QStringList toRemove;
1221 foreach (const QString &rwf, m_recursiveWatchedFolders) {
1222 if (rwf.startsWith(folder)) {
1223 // So the rwf is a subdirectory of a folder we aren't watching
1224 // but maybe someone else wants us to watch
1225 bool needToWatch = false;
1226 QMultiMap<QString, Qt4PriFileNode *>::const_iterator it, end;
1227 end = m_map.constEnd();
1228 for (it = m_map.constEnd(); it != end; ++it) {
1229 if (rwf.startsWith(it.key())) {
1235 m_watcher.removePath(rwf);
1242 qDebug()<<"removing recursive dirs for"<<folder<<":"<<toRemove;
1244 foreach (const QString &tr, toRemove) {
1245 m_recursiveWatchedFolders.remove(tr);
1250 void CentralizedFolderWatcher::folderChanged(const QString &folder)
1252 m_changedFolders.insert(folder);
1253 m_compressTimer.start();
1256 void CentralizedFolderWatcher::onTimer()
1258 foreach(const QString &folder, m_changedFolders)
1259 delayedFolderChanged(folder);
1260 m_changedFolders.clear();
1263 void CentralizedFolderWatcher::delayedFolderChanged(const QString &folder)
1266 qDebug()<<"CFW::folderChanged"<<folder;
1267 // Figure out whom to inform
1269 QString dir = folder;
1271 if (!dir.endsWith('/'))
1273 QList<Qt4PriFileNode *> nodes = m_map.values(dir);
1274 foreach (Qt4PriFileNode *node, nodes) {
1275 node->folderChanged(folder);
1278 // Chop off last part, and break if there's nothing to chop off
1280 if (dir.length() < 2)
1283 // We start before the last slash
1284 int index = dir.lastIndexOf('/', dir.length() - 2);
1287 dir = dir.left(index + 1);
1291 QString folderWithSlash = folder;
1292 if (!folder.endsWith('/'))
1293 folderWithSlash.append('/');
1295 // If a subdirectory was added, watch it too
1296 QSet<QString> tmp = recursiveDirs(folderWithSlash);
1297 if (!tmp.isEmpty()) {
1299 qDebug()<<"found new recursive dirs"<<tmp;
1301 QSet<QString> alreadyAdded = m_watcher.directories().toSet();
1302 tmp.subtract(alreadyAdded);
1304 m_watcher.addPaths(tmp.toList());
1305 m_recursiveWatchedFolders += tmp;
1310 Handle special case were a subproject of the qt directory is opened, and
1311 qt was configured to be built as a shadow build -> also build in the sub-
1312 project in the correct shadow build directory.
1315 // TODO this function should be called on project first load
1316 // and it should check against all configured qt versions ?
1317 //void Qt4Project::detectQtShadowBuild(const QString &buildConfiguration) const
1319 // if (project()->activeBuildConfiguration() == buildConfiguration)
1322 // const QString currentQtDir = static_cast<Qt4Project *>(project())->qtDir(buildConfiguration);
1323 // const QString qtSourceDir = static_cast<Qt4Project *>(project())->qtVersion(buildConfiguration)->sourcePath();
1325 // // if the project is a sub-project of Qt and Qt was shadow-built then automatically
1326 // // adjust the build directory of the sub-project.
1327 // if (project()->file()->fileName().startsWith(qtSourceDir) && qtSourceDir != currentQtDir) {
1328 // project()->setValue(buildConfiguration, "useShadowBuild", true);
1329 // QString buildDir = project()->projectDirectory();
1330 // buildDir.replace(qtSourceDir, currentQtDir);
1331 // project()->setValue(buildConfiguration, "buildDirectory", buildDir);
1332 // project()->setValue(buildConfiguration, "autoShadowBuild", true);