Skip to content
Snippets Groups Projects
qt4project.cpp 40.1 KiB
Newer Older
/**************************************************************************
con's avatar
con committed
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
**
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
**
**************************************************************************/
hjk's avatar
hjk committed

con's avatar
con committed
#include "qt4project.h"
hjk's avatar
hjk committed

con's avatar
con committed
#include "profilereader.h"
Tobias Hunger's avatar
Tobias Hunger committed
#include "qt4projectmanager.h"
con's avatar
con committed
#include "makestep.h"
#include "qmakestep.h"
#include "qt4runconfiguration.h"
#include "qt4nodes.h"
#include "qt4projectconfigwidget.h"
con's avatar
con committed
#include "qt4projectmanagerconstants.h"
#include "projectloadwizard.h"
dt's avatar
dt committed
#include "qt4buildconfiguration.h"
#include "findqt4profiles.h"
con's avatar
con committed
#include <coreplugin/messagemanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <projectexplorer/buildenvironmentwidget.h>
Tobias Hunger's avatar
Tobias Hunger committed
#include <projectexplorer/customexecutablerunconfiguration.h>
#include <utils/qtcassert.h>
con's avatar
con committed

#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtGui/QFileDialog>
#include <QtGui/QInputDialog>
con's avatar
con committed

using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
using namespace ProjectExplorer;

enum { debug = 0 };

namespace Qt4ProjectManager {
namespace Internal {

// Qt4ProjectFiles: Struct for (Cached) lists of files in a project
struct Qt4ProjectFiles {
    void clear();
    bool equals(const Qt4ProjectFiles &f) const;

    QStringList files[ProjectExplorer::FileTypeSize];
    QStringList generatedFiles[ProjectExplorer::FileTypeSize];
    QStringList proFiles;
};

void Qt4ProjectFiles::clear()
{
    for (int i = 0; i < FileTypeSize; ++i) {
        files[i].clear();
        generatedFiles[i].clear();
    }
    proFiles.clear();
}

bool Qt4ProjectFiles::equals(const Qt4ProjectFiles &f) const
{
    for (int i = 0; i < FileTypeSize; ++i)
        if (files[i] != f.files[i] || generatedFiles[i] != f.generatedFiles[i])
            return false;
    if (proFiles != f.proFiles)
        return false;
    return true;
}

inline bool operator==(const Qt4ProjectFiles &f1, const Qt4ProjectFiles &f2)
{       return f1.equals(f2); }

inline bool operator!=(const Qt4ProjectFiles &f1, const Qt4ProjectFiles &f2)
{       return !f1.equals(f2); }

QDebug operator<<(QDebug d, const  Qt4ProjectFiles &f)
{
    QDebug nsp = d.nospace();
    nsp << "Qt4ProjectFiles: proFiles=" <<  f.proFiles << '\n';
    for (int i = 0; i < FileTypeSize; ++i)
        nsp << "Type " << i << " files=" << f.files[i] <<  " generated=" << f.generatedFiles[i] << '\n';
    return d;
}

// A visitor to collect all files of a project in a Qt4ProjectFiles struct
class ProjectFilesVisitor : public ProjectExplorer::NodesVisitor
{
    Q_DISABLE_COPY(ProjectFilesVisitor)
    ProjectFilesVisitor(Qt4ProjectFiles *files);
public:

    static void findProjectFiles(Qt4ProFileNode *rootNode, Qt4ProjectFiles *files);

    void visitProjectNode(ProjectNode *projectNode);
    void visitFolderNode(FolderNode *folderNode);

private:
    Qt4ProjectFiles *m_files;
};

ProjectFilesVisitor::ProjectFilesVisitor(Qt4ProjectFiles *files) :
    m_files(files)
{
}

void ProjectFilesVisitor::findProjectFiles(Qt4ProFileNode *rootNode, Qt4ProjectFiles *files)
{
    files->clear();
    ProjectFilesVisitor visitor(files);
    rootNode->accept(&visitor);
    for (int i = 0; i < FileTypeSize; ++i) {
        qSort(files->files[i]);
        qSort(files->generatedFiles[i]);
    }
    qSort(files->proFiles);
}

void ProjectFilesVisitor::visitProjectNode(ProjectNode *projectNode)
{
    const QString path = projectNode->path();
    if (!m_files->proFiles.contains(path))
        m_files->proFiles.append(path);
    visitFolderNode(projectNode);
}

void ProjectFilesVisitor::visitFolderNode(FolderNode *folderNode)
{
    foreach (FileNode *fileNode, folderNode->fileNodes()) {
        const QString path = fileNode->path();
        const int type = fileNode->fileType();
        QStringList &targetList = fileNode->isGenerated() ? m_files->generatedFiles[type] : m_files->files[type];
        if (!targetList.contains(path))
            targetList.push_back(path);
    }
}

}
}

// ----------- Qt4ProjectFile
Qt4ProjectFile::Qt4ProjectFile(Qt4Project *project, const QString &filePath, QObject *parent)
    : Core::IFile(parent),
      m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)),
      m_project(project),
      m_filePath(filePath)
{
}

bool Qt4ProjectFile::save(const QString &)
{
    // This is never used
    return false;
con's avatar
con committed
}

dt's avatar
dt committed
void Qt4ProjectFile::rename(const QString &newName)
{
    // Can't happen
    Q_UNUSED(newName);
    Q_ASSERT(false);
}

con's avatar
con committed
QString Qt4ProjectFile::fileName() const
{
    return m_filePath;
}

QString Qt4ProjectFile::defaultPath() const
{
    return QString();
}

QString Qt4ProjectFile::suggestedFileName() const
{
    return QString();
}

QString Qt4ProjectFile::mimeType() const
{
    return m_mimeType;
}

bool Qt4ProjectFile::isModified() const
{
    return false; // we save after changing anyway
con's avatar
con committed
}

bool Qt4ProjectFile::isReadOnly() const
{
    QFileInfo fi(m_filePath);
    return !fi.isWritable();
con's avatar
con committed
}

bool Qt4ProjectFile::isSaveAsAllowed() const
{
    return false;
}

Core::IFile::ReloadBehavior Qt4ProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
con's avatar
con committed
{
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
}

void Qt4ProjectFile::reload(ReloadFlag flag, ChangeType type)
{
    Q_UNUSED(flag)
    Q_UNUSED(type)
con's avatar
con committed
}

/*!
  \class Qt4Project
con's avatar
con committed

  Qt4Project manages information about an individual Qt 4 (.pro) project file.
  */

Qt4Project::Qt4Project(Qt4Manager *manager, const QString& fileName) :
    m_manager(manager),
dt's avatar
dt committed
    m_rootProjectNode(0),
con's avatar
con committed
    m_nodesWatcher(new Internal::Qt4NodesWatcher(this)),
Tobias Hunger's avatar
Tobias Hunger committed
    m_targetFactory(new Qt4TargetFactory(this)),
con's avatar
con committed
    m_fileInfo(new Qt4ProjectFile(this, fileName, this)),
    m_projectFiles(new Qt4ProjectFiles),
    m_proFileOption(0),
    m_asyncUpdateFutureInterface(0),
    m_pendingEvaluateFuturesCount(0),
    m_asyncUpdateState(NoState),
    m_cancelEvaluate(false)
con's avatar
con committed
{
    m_asyncUpdateTimer.setSingleShot(true);
    m_asyncUpdateTimer.setInterval(3000);
    connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate()));

    setSupportedTargetIds(QtVersionManager::instance()->supportedTargetIds());
con's avatar
con committed
}

Qt4Project::~Qt4Project()
{
    m_codeModelFuture.cancel();
    m_asyncUpdateState = ShuttingDown;
    m_manager->unregisterProject(this);
con's avatar
con committed
    delete m_projectFiles;
    m_cancelEvaluate = true;
    delete m_rootProjectNode;
con's avatar
con committed
}

void Qt4Project::updateFileList()
{
    Qt4ProjectFiles newFiles;
    ProjectFilesVisitor::findProjectFiles(m_rootProjectNode, &newFiles);
    if (newFiles != *m_projectFiles) {
        *m_projectFiles = newFiles;
        emit fileListChanged();
        if (debug)
            qDebug() << Q_FUNC_INFO << *m_projectFiles;
    }
}

bool Qt4Project::fromMap(const QVariantMap &map)
con's avatar
con committed
{
    if (!Project::fromMap(map))
        return false;
con's avatar
con committed

Tobias Hunger's avatar
Tobias Hunger committed
    // Prune targets without buildconfigurations:
    // This can happen esp. when updating from a old version of Qt Creator
    QList<Target *>ts(targets());
    foreach (Target *t, ts) {
        if (t->buildConfigurations().isEmpty()) {
            qWarning() << "Removing" << t->id() << "since it has no buildconfigurations!";
Tobias Hunger's avatar
Tobias Hunger committed
            removeTarget(t);
Tobias Hunger's avatar
Tobias Hunger committed
    }

    // Add buildconfigurations so we can parse the pro-files.
    if (targets().isEmpty())
con's avatar
con committed

    if (targets().isEmpty()) {
        qWarning() << "Unable to create targets!";
        return false;
    }

Tobias Hunger's avatar
Tobias Hunger committed
    Q_ASSERT(activeTarget());
    Q_ASSERT(activeTarget()->activeBuildConfiguration());
    m_manager->registerProject(this);

    m_rootProjectNode = new Qt4ProFileNode(this, m_fileInfo->fileName(), this);
    m_rootProjectNode->registerWatcher(m_nodesWatcher);

Tobias Hunger's avatar
Tobias Hunger committed
    update();
    updateFileList();
    // This might be incorrect, need a full update
dt's avatar
dt committed
    createApplicationProjects();
    foreach (Target *t, targets())
        onAddedTarget(t);
con's avatar
con committed

    setSupportedTargetIds(QtVersionManager::instance()->supportedTargetIds());
con's avatar
con committed

    // Setup Qt versions supported (== possible targets).
    connect(this, SIGNAL(addedTarget(ProjectExplorer::Target*)),
            this, SLOT(onAddedTarget(ProjectExplorer::Target*)));

    connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>)),
            this, SLOT(qtVersionsChanged()));
con's avatar
con committed

    connect(m_nodesWatcher, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *)),
            this, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *)));

Tobias Hunger's avatar
Tobias Hunger committed
    connect(this, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
            this, SLOT(activeTargetWasChanged()));

dt's avatar
dt committed
    return true;
con's avatar
con committed
}

Tobias Hunger's avatar
Tobias Hunger committed
Qt4TargetFactory *Qt4Project::targetFactory() const
Tobias Hunger's avatar
Tobias Hunger committed
    return m_targetFactory;
Tobias Hunger's avatar
Tobias Hunger committed
Qt4Target *Qt4Project::activeTarget() const
Tobias Hunger's avatar
Tobias Hunger committed
    return static_cast<Qt4Target *>(Project::activeTarget());
Tobias Hunger's avatar
Tobias Hunger committed
void Qt4Project::onAddedTarget(ProjectExplorer::Target *t)
{
    Q_ASSERT(t);
    Qt4Target *qt4target = qobject_cast<Qt4Target *>(t);
    Q_ASSERT(qt4target);
    connect(qt4target, SIGNAL(buildDirectoryInitialized()),
            this, SIGNAL(buildDirectoryInitialized()));
    connect(qt4target, SIGNAL(proFileEvaluateNeeded(Qt4ProjectManager::Internal::Qt4Target*)),
            this, SLOT(proFileEvaluateNeeded(Qt4ProjectManager::Internal::Qt4Target*)));
}

void Qt4Project::proFileEvaluateNeeded(Qt4ProjectManager::Internal::Qt4Target *target)
{
    if (activeTarget() == target)
        scheduleAsyncUpdate();
}

/// equalFileList compares two file lists ignoring
/// <configuration> without generating temporary lists

bool Qt4Project::equalFileList(const QStringList &a, const QStringList &b)
{
    if (abs(a.length() - b.length()) > 1)
        return false;
    QStringList::const_iterator ait = a.constBegin();
    QStringList::const_iterator bit = b.constBegin();
    QStringList::const_iterator aend = a.constEnd();
    QStringList::const_iterator bend = b.constEnd();

    while (ait != aend && bit != bend) {
        if (*ait == QLatin1String("<configuration>"))
            ++ait;
        else if (*bit == QLatin1String("<configuration>"))
            ++bit;
        else if (*ait == *bit)
            ++ait, ++bit;
        else
           return false;
    }
    return (ait == aend && bit == bend);
void Qt4Project::updateCodeModels()
con's avatar
con committed
{
    if (debug)
        qDebug()<<"Qt4Project::updateCodeModel()";

Tobias Hunger's avatar
Tobias Hunger committed
    if (!activeTarget() || !activeTarget()->activeBuildConfiguration())
        return;

    updateCppCodeModel();
    updateQmlJSCodeModel();
}

void Qt4Project::updateCppCodeModel()
{
Tobias Hunger's avatar
Tobias Hunger committed
    Qt4BuildConfiguration *activeBC = activeTarget()->activeBuildConfiguration();
con's avatar
con committed
    CppTools::CppModelManagerInterface *modelmanager =
        ExtensionSystem::PluginManager::instance()
            ->getObject<CppTools::CppModelManagerInterface>();
con's avatar
con committed

con's avatar
con committed
        return;

    // Collect global headers/defines
    QStringList predefinedIncludePaths;
    QStringList predefinedFrameworkPaths;
    QByteArray predefinedMacros;
con's avatar
con committed

dt's avatar
dt committed
    ToolChain *tc = activeBC->toolChain();
    if (tc) {
        predefinedMacros = tc->predefinedMacros();
Tobias Hunger's avatar
Tobias Hunger committed
        //qDebug()<<"Predefined Macros";
        //qDebug()<<tc->predefinedMacros();
        //qDebug()<<"";
        //qDebug()<<"System Header Paths";
        //foreach(const HeaderPath &hp, tc->systemHeaderPaths())
        //    qDebug()<<hp.path();
        foreach (const HeaderPath &headerPath, tc->systemHeaderPaths()) {
            if (headerPath.kind() == HeaderPath::FrameworkHeaderPath)
                predefinedFrameworkPaths.append(headerPath.path());
            else
                predefinedIncludePaths.append(headerPath.path());
        }
con's avatar
con committed
    }

Roberto Raggi's avatar
Roberto Raggi committed
    FindQt4ProFiles findQt4ProFiles;
    QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
    QByteArray allDefinedMacros = predefinedMacros;
    QStringList allIncludePaths;
Roberto Raggi's avatar
Roberto Raggi committed
    QStringList allFrameworkPaths = predefinedFrameworkPaths;
    QStringList allPrecompileHeaders;
Roberto Raggi's avatar
Roberto Raggi committed

    // Collect per .pro file information
con's avatar
con committed
    foreach (Qt4ProFileNode *pro, proFiles) {
        Internal::CodeModelInfo info;
        info.defines = predefinedMacros;
        info.frameworkPaths = predefinedFrameworkPaths;

        info.precompiledHeader = pro->variableValue(PrecompiledHeaderVar);

        allPrecompileHeaders.append(info.precompiledHeader);

        foreach (const QString &def, pro->variableValue(DefinesVar)) {
            allDefinedMacros += "#define ";
            info.defines += "#define ";
con's avatar
con committed
            const int index = def.indexOf(QLatin1Char('='));
            if (index == -1) {
                allDefinedMacros += def.toLatin1();
                allDefinedMacros += " 1\n";
                info.defines += def.toLatin1();
                info.defines += " 1\n";
con's avatar
con committed
            } else {
                const QString name = def.left(index);
                const QString value = def.mid(index + 1);
                allDefinedMacros += name.toLatin1();
                allDefinedMacros += ' ';
                allDefinedMacros += value.toLocal8Bit();
                allDefinedMacros += '\n';
                info.defines += name.toLatin1();
                info.defines += ' ';
                info.defines += value.toLocal8Bit();
                info.defines += '\n';
con's avatar
con committed
            }
        }

        const QStringList proIncludePaths = pro->variableValue(IncludePathVar);
        foreach (const QString &includePath, proIncludePaths) {
            if (!allIncludePaths.contains(includePath))
                allIncludePaths.append(includePath);
            if (!info.includes.contains(includePath))
                info.includes.append(includePath);
#if 0 // Experimental PKGCONFIG support
        { // Pkg Config support
            QStringList pkgConfig = pro->variableValue(PkgConfigVar);
            if (!pkgConfig.isEmpty()) {
                pkgConfig.prepend("--cflags-only-I");
                QProcess process;
                process.start("pkg-config", pkgConfig);
                process.waitForFinished();
                QString result = process.readAllStandardOutput();
                foreach(const QString &part, result.trimmed().split(' ', QString::SkipEmptyParts)) {
                    info.includes.append(part.mid(2)); // Chop off "-I"
dt's avatar
dt committed
        info.includes.append(activeBC->qtVersion()->mkspecPath());
        info.includes.append(predefinedIncludePaths);
//        qDebug()<<"Dumping code model information";
//        qDebug()<<"for .pro file"<< pro->path();
//        qDebug()<<info.defines;
//        qDebug()<<info.includes;
//        qDebug()<<info.frameworkPaths;
//        qDebug()<<"\n";
con's avatar
con committed
    }

dt's avatar
dt committed
    // Add mkspec directory
dt's avatar
dt committed
    allIncludePaths.append(activeBC->qtVersion()->mkspecPath());
    allIncludePaths.append(predefinedIncludePaths);
con's avatar
con committed
    QStringList files;
    files += m_projectFiles->files[HeaderType];
    files += m_projectFiles->generatedFiles[HeaderType];
    files += m_projectFiles->files[SourceType];
    files += m_projectFiles->generatedFiles[SourceType];

    CppTools::CppModelManagerInterface::ProjectInfo pinfo = modelmanager->projectInfo(this);
con's avatar
con committed

    //qDebug()<<"Using precompiled header"<<allPrecompileHeaders;

    bool fileList = equalFileList(pinfo.sourceFiles, files);

    if (pinfo.defines == allDefinedMacros
        && pinfo.includePaths == allIncludePaths
        && pinfo.frameworkPaths == allFrameworkPaths
        && fileList
        && pinfo.precompiledHeaders == allPrecompileHeaders) {
        // Nothing to update...
con's avatar
con committed
    } else {
        pinfo.sourceFiles.clear();
        if (pinfo.defines != allDefinedMacros
            || pinfo.includePaths != allIncludePaths
            || pinfo.frameworkPaths != allFrameworkPaths
            || pinfo.precompiledHeaders != allPrecompileHeaders)
        {
            pinfo.sourceFiles.append(QLatin1String("<configuration>"));
        }

        //pinfo.defines = predefinedMacros;
        pinfo.defines = allDefinedMacros;
        pinfo.includePaths = allIncludePaths;
        pinfo.frameworkPaths = allFrameworkPaths;
        pinfo.sourceFiles += files;
        pinfo.precompiledHeaders = allPrecompileHeaders;

        modelmanager->updateProjectInfo(pinfo);
dt's avatar
dt committed
        m_codeModelFuture = modelmanager->updateSourceFiles(pinfo.sourceFiles);
con's avatar
con committed
    }
void Qt4Project::updateQmlJSCodeModel()
{
    QObject *modelManager =
            ExtensionSystem::PluginManager::instance()->getObjectByName(QmlJS::MODELMANAGERINTERFACE_OBJECTNAME);
    QmlJS::ModelManagerInterface::ProjectInfo projectInfo;
    bool success = QMetaObject::invokeMethod(
                modelManager, "projectInfo", Qt::DirectConnection,
                Q_RETURN_ARG(QmlJS::ModelManagerInterface::ProjectInfo, projectInfo),
                Q_ARG(ProjectExplorer::Project *, this));
    QTC_ASSERT(success, return);

    // Not essential since the QmlJS engine parses required files on demand.
    //projectInfo.sourceFiles = ...

    FindQt4ProFiles findQt4ProFiles;
    QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());

    foreach (Qt4ProFileNode *node, proFiles) {
        projectInfo.importPaths.append(node->variableValue(QmlImportPathVar));
    }
    projectInfo.importPaths.removeDuplicates();

    success = QMetaObject::invokeMethod(
                modelManager, "updateProjectInfo", Qt::DirectConnection,
                Q_ARG(QmlJS::ModelManagerInterface::ProjectInfo, projectInfo));
    QTC_ASSERT(success, return);
Tobias Hunger's avatar
Tobias Hunger committed
void Qt4Project::qtVersionsChanged()
{
    setSupportedTargetIds(QtVersionManager::instance()->supportedTargetIds());
}

///*!
//  Updates complete project
//  */
con's avatar
con committed
void Qt4Project::update()
{
dt's avatar
dt committed
    if (debug)
        qDebug()<<"Doing sync update";
con's avatar
con committed
    m_rootProjectNode->update();
dt's avatar
dt committed
    if (debug)
        qDebug()<<"State is now Base";
    m_asyncUpdateState = Base;
}

void Qt4Project::scheduleAsyncUpdate(Qt4ProFileNode *node)
{
dt's avatar
dt committed
    if (debug)
        qDebug()<<"schduleAsyncUpdate (node)";
    Q_ASSERT(m_asyncUpdateState != NoState);

    if (m_cancelEvaluate) {
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  Already canceling, nothing to do";
        // A cancel is in progress
        // That implies that a full update is going to happen afterwards
        // So we don't need to do anything
        return;
    }

    if (m_asyncUpdateState == AsyncFullUpdatePending) {
        // Just postpone
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  full update pending, restarting timer";
        m_asyncUpdateTimer.start();
    } else if (m_asyncUpdateState == AsyncPartialUpdatePending
               || m_asyncUpdateState == Base) {
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  adding node to async update list, setting state to AsyncPartialUpdatePending";
        // Add the node
        m_asyncUpdateState = AsyncPartialUpdatePending;

        QList<Internal::Qt4ProFileNode *>::iterator it;
        bool add = true;
dt's avatar
dt committed
        if (debug)
            qDebug()<<"scheduleAsyncUpdate();"<<m_partialEvaluate.size()<<"nodes";
        it = m_partialEvaluate.begin();
        while (it != m_partialEvaluate.end()) {
            if (*it == node) {
                add = false;
                break;
            } else if (node->isParent(*it)) { // We already have the parent in the list, nothing to do
                add = false;
                break;
            } else if ((*it)->isParent(node)) { // The node is the parent of a child already in the list
                it = m_partialEvaluate.erase(it);
            } else {
                ++it;
            }
        }

        if (add)
            m_partialEvaluate.append(node);
        // and start the timer anew
        m_asyncUpdateTimer.start();
    } else if (m_asyncUpdateState == AsyncUpdateInProgress) {
        // A update is in progress
        // And this slot only gets called if a file changed on disc
        // So we'll play it safe and schedule a complete evaluate
        // This might trigger if due to version control a few files
        // change a partial update gets in progress and then another
        // batch of changes come in, which triggers a full update
        // even if that's not really needed
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  Async update in progress, scheduling new one afterwards";
        scheduleAsyncUpdate();
    }
}

void Qt4Project::scheduleAsyncUpdate()
{
dt's avatar
dt committed
    if (debug)
        qDebug()<<"scheduleAsyncUpdate";
    Q_ASSERT(m_asyncUpdateState != NoState);
    if (m_cancelEvaluate) { // we are in progress of canceling
                            // and will start the evaluation after that
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  canceling is in progress, doing nothing";
        return;
    }
    if (m_asyncUpdateState == AsyncUpdateInProgress) {
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  update in progress, canceling and setting state to full update pending";
        m_cancelEvaluate = true;
        m_asyncUpdateState = AsyncFullUpdatePending;
        return;
    }

dt's avatar
dt committed
    if (debug)
        qDebug()<<"  starting timer for full update, setting state to full update pending";
    m_partialEvaluate.clear();
    m_asyncUpdateState = AsyncFullUpdatePending;
    m_asyncUpdateTimer.start();

    // Cancel running code model update
    m_codeModelFuture.cancel();
}


void Qt4Project::incrementPendingEvaluateFutures()
{
    ++m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
    if (debug)
        qDebug()<<"incrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;

    m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(),
                                                  m_asyncUpdateFutureInterface->progressMaximum() + 1);
}

void Qt4Project::decrementPendingEvaluateFutures()
{
    --m_pendingEvaluateFuturesCount;

dt's avatar
dt committed
    if (debug)
        qDebug()<<"decrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;

    m_asyncUpdateFutureInterface->setProgressValue(m_asyncUpdateFutureInterface->progressValue() + 1);
    if (m_pendingEvaluateFuturesCount == 0) {
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  WOHOO, no pending futures, cleaning up";
        // We are done!
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  reporting finished";
        m_asyncUpdateFutureInterface->reportFinished();
        delete m_asyncUpdateFutureInterface;
        m_asyncUpdateFutureInterface = 0;
        m_cancelEvaluate = false;

        // TODO clear the profile cache ?
        if (m_asyncUpdateState == AsyncFullUpdatePending || m_asyncUpdateState == AsyncPartialUpdatePending) {
dt's avatar
dt committed
            if (debug)
                qDebug()<<"  Oh update is pending start the timer";
            m_asyncUpdateTimer.start();
        } else  if (m_asyncUpdateState != ShuttingDown){
            // After beeing done, we need to call:
            updateFileList();
dt's avatar
dt committed
            if (debug)
                qDebug()<<"  Setting state to Base";
            m_asyncUpdateState = Base;
        }
    }
}

bool Qt4Project::wasEvaluateCanceled()
{
    return m_cancelEvaluate;
}

QString Qt4Project::defaultTopLevelBuildDirectory() const
{
    return defaultTopLevelBuildDirectory(file()->fileName());
}

QString Qt4Project::defaultTopLevelBuildDirectory(const QString &profilePath)
{
    if (profilePath.isEmpty())
        return QString();
    QFileInfo info(profilePath);
    return QDir(projectDirectory(profilePath) + QLatin1String("/../") + info.baseName() + QLatin1String("-build")).absolutePath();
void Qt4Project::asyncUpdate()
{
dt's avatar
dt committed
    if (debug)
        qDebug()<<"async update, timer expired, doing now";
    Q_ASSERT(!m_asyncUpdateFutureInterface);
    m_asyncUpdateFutureInterface = new QFutureInterface<void>();

    Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();

    m_asyncUpdateFutureInterface->setProgressRange(0, 0);
    progressManager->addTask(m_asyncUpdateFutureInterface->future(), tr("Evaluating"), Constants::PROFILE_EVALUATE);
dt's avatar
dt committed
    if (debug)
        qDebug()<<"  adding task";

    m_asyncUpdateFutureInterface->reportStarted();

    if (m_asyncUpdateState == AsyncFullUpdatePending) {
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  full update, starting with root node";
        m_rootProjectNode->asyncUpdate();
    } else {
dt's avatar
dt committed
        if (debug)
            qDebug()<<"  partial update,"<<m_partialEvaluate.size()<<"nodes to update";
        foreach(Qt4ProFileNode *node, m_partialEvaluate)
            node->asyncUpdate();
    }

    m_partialEvaluate.clear();
dt's avatar
dt committed
    if (debug)
        qDebug()<<"  Setting state to AsyncUpdateInProgress";
    m_asyncUpdateState = AsyncUpdateInProgress;
con's avatar
con committed
}

ProjectExplorer::IProjectManager *Qt4Project::projectManager() const
{
    return m_manager;
}

Qt4Manager *Qt4Project::qt4ProjectManager() const
{
    return m_manager;
}

QString Qt4Project::displayName() const
con's avatar
con committed
{
    return QFileInfo(file()->fileName()).completeBaseName();
}

QString Qt4Project::id() const
{
Tobias Hunger's avatar
Tobias Hunger committed
    return QLatin1String(Constants::QT4PROJECT_ID);
con's avatar
con committed
Core::IFile *Qt4Project::file() const
{
    return m_fileInfo;
}

QStringList Qt4Project::files(FilesMode fileMode) const
{
    QStringList files;
    for (int i = 0; i < FileTypeSize; ++i) {
        files += m_projectFiles->files[i];
        if (fileMode == AllFiles)
            files += m_projectFiles->generatedFiles[i];
    }
    return files;
}

// Find the folder that contains a file a certain type (recurse down)
static FolderNode *folderOf(FolderNode *in, FileType fileType, const QString &fileName)
{
    foreach(FileNode *fn, in->fileNodes())
        if (fn->fileType() == fileType && fn->path() == fileName)
            return in;
    foreach(FolderNode *folder, in->subFolderNodes())
        if (FolderNode *pn = folderOf(folder, fileType, fileName))
            return pn;
    return 0;
}

// Find the Qt4ProFileNode that contains a file of a certain type.
// First recurse down to folder, then find the pro-file.
static Qt4ProFileNode *proFileNodeOf(Qt4ProFileNode *in, FileType fileType, const QString &fileName)
{
    for (FolderNode *folder =  folderOf(in, fileType, fileName); folder; folder = folder->parentFolderNode())
        if (Qt4ProFileNode *proFile = qobject_cast<Qt4ProFileNode *>(folder))
            return proFile;
    return 0;
}

QString Qt4Project::generatedUiHeader(const QString &formFile) const
{
    // Look in sub-profiles as SessionManager::projectForFile returns
    // the top-level project only.
    if (m_rootProjectNode)
        if (const Qt4ProFileNode *pro = proFileNodeOf(m_rootProjectNode, FormType, formFile))
            return Qt4ProFileNode::uiHeaderFile(pro->uiDirectory(), formFile);
    return QString();
}

con's avatar
con committed
QList<ProjectExplorer::Project*> Qt4Project::dependsOn()
{
    // NBS implement dependsOn
    return QList<Project *>();
}

void Qt4Project::addDefaultBuild()
{
    // TODO this could probably refactored
    // That is the ProjectLoadWizard divided into useful bits
    // and this code then called here, instead of that strange forwarding
    // to a wizard, which doesn't even show up
    ProjectLoadWizard wizard(this);
    wizard.execDialog();
con's avatar
con committed
}

void Qt4Project::proFileParseError(const QString &errorMessage)
{
    Core::ICore::instance()->messageManager()->printToOutputPane(errorMessage);
con's avatar
con committed
}

ProFileReader *Qt4Project::createProFileReader(Qt4ProFileNode *qt4ProFileNode)
{
    if (!m_proFileOption) {
        m_proFileOption = new ProFileOption;
        m_proFileOptionRefCnt = 0;

Tobias Hunger's avatar
Tobias Hunger committed
        if (activeTarget() &&
            activeTarget()->activeBuildConfiguration()) {
            QtVersion *version = activeTarget()->activeBuildConfiguration()->qtVersion();
            if (version->isValid())
                m_proFileOption->properties = version->versionInfo();
        }
        ProFileCacheManager::instance()->incRefCount();
    }
    ++m_proFileOptionRefCnt;

    ProFileReader *reader = new ProFileReader(m_proFileOption);

    reader->setOutputDir(qt4ProFileNode->buildDir());

    return reader;
}

void Qt4Project::destroyProFileReader(ProFileReader *reader)
{
    delete reader;
    if (!--m_proFileOptionRefCnt) {
        QString dir = QFileInfo(m_fileInfo->fileName()).absolutePath();
        if (!dir.endsWith(QLatin1Char('/')))
            dir += QLatin1Char('/');
        ProFileCacheManager::instance()->discardFiles(dir);
        ProFileCacheManager::instance()->decRefCount();
con's avatar
con committed
Qt4ProFileNode *Qt4Project::rootProjectNode() const
{
    return m_rootProjectNode;
}

dt's avatar
dt committed
BuildConfigWidget *Qt4Project::createConfigWidget()
con's avatar
con committed
{
    return new Qt4ProjectConfigWidget(this);
con's avatar
con committed
}

dt's avatar
dt committed
QList<BuildConfigWidget*> Qt4Project::subConfigWidgets()
con's avatar
con committed
{
dt's avatar
dt committed
    QList<BuildConfigWidget*> subWidgets;
    subWidgets << new BuildEnvironmentWidget;
con's avatar
con committed
    return subWidgets;
}

void Qt4Project::collectLeafProFiles(QList<Qt4ProFileNode *> &list, Qt4ProFileNode *node)
{
    if (node->projectType() != Internal::SubDirsTemplate) {
        list.append(node);
    }
    foreach (ProjectNode *n, node->subProjectNodes()) {
        Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(n);
        if (qt4ProFileNode)
            collectLeafProFiles(list, qt4ProFileNode);
    }
}


con's avatar
con committed
void Qt4Project::collectApplicationProFiles(QList<Qt4ProFileNode *> &list, Qt4ProFileNode *node)
{
    if (node->projectType() == Internal::ApplicationTemplate
        || node->projectType() == Internal::ScriptTemplate) {
        list.append(node);
    }
    foreach (ProjectNode *n, node->subProjectNodes()) {
        Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(n);
        if (qt4ProFileNode)
            collectApplicationProFiles(list, qt4ProFileNode);
    }
}

dt's avatar
dt committed
void Qt4Project::createApplicationProjects()
con's avatar
con committed
{
dt's avatar
dt committed
    foreach (Target *target, targets()) {
        if (target->runConfigurations().count()) {
            // Remove all run configurations which the new project wizard created
            QList<RunConfiguration*> toRemove;
            foreach (RunConfiguration * rc, target->runConfigurations()) {
                CustomExecutableRunConfiguration *cerc = qobject_cast<CustomExecutableRunConfiguration *>(rc);
                if (cerc && !cerc->isConfigured())
                    toRemove.append(rc);
dt's avatar
dt committed
            foreach (RunConfiguration *rc, toRemove)
                target->removeRunConfiguration(rc);
con's avatar
con committed
        }

        // We use the list twice
        QList<Qt4ProFileNode *> profiles = applicationProFiles();
        QStringList paths;
        foreach (Qt4ProFileNode *pro, profiles)
            paths << pro->path();

        foreach (RunConfiguration *rc, target->runConfigurations()) {
            if (Qt4RunConfiguration *qt4rc = qobject_cast<Qt4RunConfiguration *>(rc)) {