qt4project.cpp 54.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

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

Tobias Hunger's avatar
Tobias Hunger committed
32
#include "qt4projectmanager.h"
con's avatar
con committed
33 34 35
#include "qmakestep.h"
#include "qt4nodes.h"
#include "qt4projectmanagerconstants.h"
dt's avatar
dt committed
36
#include "qt4buildconfiguration.h"
37
#include "findqt4profiles.h"
38
#include "buildconfigurationinfo.h"
39 40 41
#include "qt4projectmanager/wizards/abstractmobileapp.h"
#include "qt4projectmanager/wizards/qtquickapp.h"
#include "qt4projectmanager/wizards/html5app.h"
42

43
#include <coreplugin/icore.h>
44
#include <coreplugin/icontext.h>
dt's avatar
dt committed
45
#include <coreplugin/progressmanager/progressmanager.h>
46
#include <coreplugin/documentmanager.h>
47
#include <cpptools/cppmodelmanagerinterface.h>
48
#include <qmljstools/qmljsmodelmanager.h>
49
#include <projectexplorer/buildmanager.h>
50 51
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
52
#include <projectexplorer/toolchain.h>
53
#include <projectexplorer/headerpath.h>
54
#include <projectexplorer/target.h>
55
#include <projectexplorer/projectexplorer.h>
56
#include <projectexplorer/projectmacroexpander.h>
57
#include <proparser/qmakevfs.h>
58
#include <qtsupport/profilereader.h>
Tobias Hunger's avatar
Tobias Hunger committed
59
#include <qtsupport/qtkitinformation.h>
con's avatar
con committed
60

61 62 63
#include <QDebug>
#include <QDir>
#include <QFileSystemWatcher>
64
#include <QMessageBox>
con's avatar
con committed
65 66 67 68 69

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

hjk's avatar
hjk committed
70
enum { debug = 0 };
con's avatar
con committed
71

Tobias Hunger's avatar
Tobias Hunger committed
72 73 74 75 76 77 78 79 80 81
// -----------------------------------------------------------------------
// Helpers:
// -----------------------------------------------------------------------

namespace {

Qt4BuildConfiguration *enableActiveQt4BuildConfiguration(ProjectExplorer::Target *t, bool enabled)
{
    if (!t)
        return 0;
82
    Qt4BuildConfiguration *bc = static_cast<Qt4BuildConfiguration *>(t->activeBuildConfiguration());
Tobias Hunger's avatar
Tobias Hunger committed
83 84 85 86 87 88
    if (!bc)
        return 0;
    bc->setEnabled(enabled);
    return bc;
}

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
void updateBoilerPlateCodeFiles(const AbstractMobileApp *app, const QString &proFile)
{
    const QList<AbstractGeneratedFileInfo> updates =
            app->fileUpdates(proFile);
    if (!updates.empty()) {
        const QString title = Qt4Manager::tr("Update of Generated Files");
        QStringList fileNames;
        foreach (const AbstractGeneratedFileInfo &info, updates)
            fileNames.append(QDir::toNativeSeparators(info.fileInfo.fileName()));
        const QString message =
                Qt4Manager::tr("In project<br><br>%1<br><br>The following files are either "
                               "outdated or have been modified:<br><br>%2<br><br>Do you want "
                               "Qt Creator to update the files? Any changes will be lost.")
                .arg(proFile, fileNames.join(QLatin1String(", ")));
        if (QMessageBox::question(0, title, message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
            QString error;
            if (!app->updateFiles(updates, error))
                QMessageBox::critical(0, title, error);
        }
    }
}
Tobias Hunger's avatar
Tobias Hunger committed
110 111
} // namespace

con's avatar
con committed
112 113 114
namespace Qt4ProjectManager {
namespace Internal {

115
class Qt4ProjectFile : public Core::IDocument
116 117 118 119
{
    Q_OBJECT

public:
Tobias Hunger's avatar
Tobias Hunger committed
120
    Qt4ProjectFile(const QString &filePath, QObject *parent = 0);
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148

    bool save(QString *errorString, const QString &fileName, bool autoSave);
    QString fileName() const;
    virtual void rename(const QString &newName);

    QString defaultPath() const;
    QString suggestedFileName() const;
    virtual QString mimeType() const;

    bool isModified() const;
    bool isSaveAsAllowed() const;

    ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
    bool reload(QString *errorString, ReloadFlag flag, ChangeType type);

private:
    const QString m_mimeType;
    QString m_filePath;
};

/// Watches folders for Qt4PriFile nodes
/// use one file system watcher to watch all folders
/// such minimizing system ressouce usage

class CentralizedFolderWatcher : public QObject
{
    Q_OBJECT
public:
149
    CentralizedFolderWatcher(Qt4Project *parent);
150
    ~CentralizedFolderWatcher();
151 152
    void watchFolders(const QList<QString> &folders, Qt4ProjectManager::Qt4PriFileNode *node);
    void unwatchFolders(const QList<QString> &folders, Qt4ProjectManager::Qt4PriFileNode *node);
153 154 155 156 157 158 159

private slots:
    void folderChanged(const QString &folder);
    void onTimer();
    void delayedFolderChanged(const QString &folder);

private:
160
    Qt4Project *m_project;
161 162
    QSet<QString> recursiveDirs(const QString &folder);
    QFileSystemWatcher m_watcher;
163
    QMultiMap<QString, Qt4ProjectManager::Qt4PriFileNode *> m_map;
164 165 166 167 168 169

    QSet<QString> m_recursiveWatchedFolders;
    QTimer m_compressTimer;
    QSet<QString> m_changedFolders;
};

con's avatar
con committed
170
// Qt4ProjectFiles: Struct for (Cached) lists of files in a project
Tobias Hunger's avatar
Tobias Hunger committed
171 172
class Qt4ProjectFiles {
public:
con's avatar
con committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    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
{
    ProjectFilesVisitor(Qt4ProjectFiles *files);
hjk's avatar
hjk committed
219

con's avatar
con committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
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
Friedemann Kleint's avatar
Friedemann Kleint committed
270
namespace Internal {
Tobias Hunger's avatar
Tobias Hunger committed
271
Qt4ProjectFile::Qt4ProjectFile(const QString &filePath, QObject *parent)
272
    : Core::IDocument(parent),
con's avatar
con committed
273 274 275 276 277
      m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)),
      m_filePath(filePath)
{
}

278
bool Qt4ProjectFile::save(QString *, const QString &, bool)
con's avatar
con committed
279
{
280 281
    // This is never used
    return false;
con's avatar
con committed
282 283
}

dt's avatar
dt committed
284 285 286 287 288 289 290
void Qt4ProjectFile::rename(const QString &newName)
{
    // Can't happen
    Q_UNUSED(newName);
    Q_ASSERT(false);
}

con's avatar
con committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
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
{
313
    return false; // we save after changing anyway
con's avatar
con committed
314 315 316 317 318 319 320
}

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

321
Core::IDocument::ReloadBehavior Qt4ProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
con's avatar
con committed
322
{
323 324 325 326 327
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
}

328
bool Qt4ProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
329
{
330
    Q_UNUSED(errorString)
331 332
    Q_UNUSED(flag)
    Q_UNUSED(type)
333
    return true;
con's avatar
con committed
334 335
}

336
} // namespace Internal
337

con's avatar
con committed
338
/*!
339
  \class Qt4Project
con's avatar
con committed
340 341 342 343 344 345

  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
346
    m_rootProjectNode(0),
con's avatar
con committed
347
    m_nodesWatcher(new Internal::Qt4NodesWatcher(this)),
Tobias Hunger's avatar
Tobias Hunger committed
348
    m_fileInfo(new Qt4ProjectFile(fileName, this)),
349
    m_projectFiles(new Qt4ProjectFiles),
350
    m_qmakeVfs(new QMakeVfs),
351
    m_qmakeGlobals(0),
dt's avatar
dt committed
352 353 354
    m_asyncUpdateFutureInterface(0),
    m_pendingEvaluateFuturesCount(0),
    m_asyncUpdateState(NoState),
355
    m_cancelEvaluate(false),
356 357
    m_centralizedFolderWatcher(0),
    m_activeTarget(0)
con's avatar
con committed
358
{
359
    setProjectContext(Core::Context(Qt4ProjectManager::Constants::PROJECT_ID));
360
    setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX));
361

dt's avatar
dt committed
362 363 364
    m_asyncUpdateTimer.setSingleShot(true);
    m_asyncUpdateTimer.setInterval(3000);
    connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate()));
365 366 367 368

    connect(ProjectExplorerPlugin::instance()->buildManager(),
            SIGNAL(buildQueueFinished(bool)),
            SLOT(buildFinished(bool)));
con's avatar
con committed
369 370 371 372
}

Qt4Project::~Qt4Project()
{
373
    m_codeModelFuture.cancel();
374
    m_asyncUpdateState = ShuttingDown;
dt's avatar
dt committed
375
    m_manager->unregisterProject(this);
376
    delete m_qmakeVfs;
con's avatar
con committed
377
    delete m_projectFiles;
378
    m_cancelEvaluate = true;
379 380 381 382 383
    // Deleting the root node triggers a few things, make sure rootProjectNode
    // returns 0 already
    Qt4ProFileNode *root = m_rootProjectNode;
    m_rootProjectNode = 0;
    delete root;
con's avatar
con committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397
}

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;
    }
}

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
bool Qt4Project::setupTarget(ProjectExplorer::Target *t)
{
    QList<BuildConfigurationInfo> infoList
            = Qt4BuildConfigurationFactory::availableBuildConfigurations(t->kit(), m_fileInfo->fileName());
    setupTarget(t, infoList);
    return true;
}

void Qt4Project::setupTarget(ProjectExplorer::Target *t, const QList<BuildConfigurationInfo> &infoList)
{
    // Build Configurations:
    foreach (const BuildConfigurationInfo &info, infoList) {
        QString name = info.buildConfig & QtSupport::BaseQtVersion::DebugBuild
                ? tr("Debug") : tr("Release");
        Qt4BuildConfiguration *bc
                = Qt4BuildConfiguration::setup(t, name, name,
                                               info.buildConfig, info.additionalArguments,
                                               info.directory, info.importing);
        t->addBuildConfiguration(bc);
    }

    // Deploy Configurations:
    t->updateDefaultDeployConfigurations();
    // Do not create Run Configurations: Those will be generated later anyway.
}

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

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

dt's avatar
dt committed
439 440 441 442 443
    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
444 445
    update();
    updateFileList();
446 447
    // This might be incorrect, need a full update
    updateCodeModels();
448

Tobias Hunger's avatar
Tobias Hunger committed
449
    // We have the profile nodes now, so we know the runconfigs!
450 451
    connect(m_nodesWatcher, SIGNAL(proFileUpdated(Qt4ProjectManager::Qt4ProFileNode*,bool,bool)),
            this, SIGNAL(proFileUpdated(Qt4ProjectManager::Qt4ProFileNode*,bool,bool)));
452

453
    // Now we emit update once :)
Daniel Teske's avatar
Daniel Teske committed
454
    m_rootProjectNode->emitProFileUpdatedRecursive();
455

456 457 458 459 460 461
    // On active buildconfiguration changes, reevaluate the .pro files
    m_activeTarget = activeTarget();
    if (m_activeTarget)
        connect(m_activeTarget, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
                this, SLOT(scheduleAsyncUpdate()));

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

465 466 467 468 469 470 471 472 473 474 475 476 477
    // // Update boiler plate code for subprojects.
    QtQuickApp qtQuickApp;
    const Html5App html5App;

    foreach (Qt4ProFileNode *node, applicationProFiles()) {
        const QString path = node->path();

        qtQuickApp.setComponentSet(QtQuickApp::QtQuick10Components);
        updateBoilerPlateCodeFiles(&qtQuickApp, path);
        qtQuickApp.setComponentSet(QtQuickApp::QtQuick20Components);
        updateBoilerPlateCodeFiles(&qtQuickApp, path);
        updateBoilerPlateCodeFiles(&html5App, path);
    }
dt's avatar
dt committed
478
    return true;
con's avatar
con committed
479 480
}

dt's avatar
dt committed
481 482 483 484 485 486 487 488 489 490 491 492 493
/// 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) {
494
        if (*ait == CppTools::CppModelManagerInterface::configurationFileName())
dt's avatar
dt committed
495
            ++ait;
496
        else if (*bit == CppTools::CppModelManagerInterface::configurationFileName())
dt's avatar
dt committed
497 498 499 500 501 502 503
            ++bit;
        else if (*ait == *bit)
            ++ait, ++bit;
        else
           return false;
    }
    return (ait == aend && bit == bend);
Tobias Hunger's avatar
Tobias Hunger committed
504 505
}

506
void Qt4Project::updateCodeModels()
con's avatar
con committed
507 508 509 510
{
    if (debug)
        qDebug()<<"Qt4Project::updateCodeModel()";

511
    if (activeTarget() && !activeTarget()->activeBuildConfiguration())
Tobias Hunger's avatar
Tobias Hunger committed
512 513
        return;

514 515 516 517 518 519
    updateCppCodeModel();
    updateQmlJSCodeModel();
}

void Qt4Project::updateCppCodeModel()
{
520 521
    typedef CppTools::ProjectPart ProjectPart;
    typedef CppTools::ProjectFile ProjectFile;
522

Tobias Hunger's avatar
Tobias Hunger committed
523
    Kit *k = 0;
524
    QtSupport::BaseQtVersion *qtVersion = 0;
Tobias Hunger's avatar
Tobias Hunger committed
525
    if (ProjectExplorer::Target *target = activeTarget())
Tobias Hunger's avatar
Tobias Hunger committed
526
        k = target->kit();
Tobias Hunger's avatar
Tobias Hunger committed
527
    else
Tobias Hunger's avatar
Tobias Hunger committed
528 529
        k = KitManager::instance()->defaultKit();
    qtVersion = QtSupport::QtKitInformation::qtVersion(k);
530

531 532
    CppTools::CppModelManagerInterface *modelmanager =
        CppTools::CppModelManagerInterface::instance();
con's avatar
con committed
533

534
    if (!modelmanager)
con's avatar
con committed
535 536
        return;

537 538
    FindQt4ProFiles findQt4ProFiles;
    QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
con's avatar
con committed
539

540
    CppTools::CppModelManagerInterface::ProjectInfo pinfo = modelmanager->projectInfo(this);
541 542 543 544 545 546 547 548
    pinfo.clearProjectParts();
    ProjectPart::QtVersion qtVersionForPart = ProjectPart::NoQt;
    if (qtVersion) {
        if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
            qtVersionForPart = ProjectPart::Qt4;
        else
            qtVersionForPart = ProjectPart::Qt5;
    }
549

550 551 552
    QStringList allFiles;
    foreach (Qt4ProFileNode *pro, proFiles) {
        ProjectPart::Ptr part(new ProjectPart);
553 554 555 556 557

        if (pro->variableValue(ConfigVar).contains(QLatin1String("qt")))
            part->qtVersion = qtVersionForPart;
        else
            part->qtVersion = ProjectPart::NoQt;
558

559
        const QStringList cxxflags = pro->variableValue(CppFlagsVar);
560 561 562 563
        part->evaluateToolchain(ToolChainKitInformation::toolChain(k),
                                cxxflags,
                                cxxflags,
                                SysRootKitInformation::sysRoot(k));
564

565 566 567
        // part->defines
        part->defines += pro->cxxDefines();

568
        // part->includePaths, part->frameworkPaths
569 570 571
        part->includePaths.append(pro->variableValue(IncludePathVar));

        if (qtVersion) {
572 573 574 575 576 577
            foreach (const HeaderPath &header, qtVersion->systemHeaderPathes(k)) {
                if (header.kind() == HeaderPath::FrameworkHeaderPath)
                    part->frameworkPaths.append(header.path());
                else
                    part->includePaths.append(header.path());
            }
578 579
            if (!qtVersion->frameworkInstallPath().isEmpty())
                part->frameworkPaths.append(qtVersion->frameworkInstallPath());
580
        }
581

582 583
        if (Qt4ProFileNode *node = rootQt4ProjectNode())
            part->includePaths.append(node->resolvedMkspecPath());
con's avatar
con committed
584

585 586
        // part->precompiledHeaders
        part->precompiledHeaders.append(pro->variableValue(PrecompiledHeaderVar));
dt's avatar
dt committed
587

588
        // part->files
589 590 591 592 593 594 595 596 597 598 599 600
        foreach (const QString &file, pro->variableValue(CppSourceVar)) {
            allFiles << file;
            part->files << ProjectFile(file, ProjectFile::CXXSource);
        }
        foreach (const QString &file, pro->variableValue(CppHeaderVar)) {
            allFiles << file;
            part->files << ProjectFile(file, ProjectFile::CXXHeader);
        }
        foreach (const QString &file, pro->uiFiles()) {
            allFiles << file;
            part->files << ProjectFile(file, ProjectFile::CXXHeader);
        }
con's avatar
con committed
601

602
        part->files.prepend(ProjectFile(CppTools::CppModelManagerInterface::configurationFileName(),
603 604 605 606 607
                                        ProjectFile::CXXSource));
        foreach (const QString &file, pro->variableValue(ObjCSourceVar)) {
            allFiles << file;
            part->files << ProjectFile(file, ProjectFile::ObjCSource);
        }
608 609 610 611 612
        foreach (const QString &file, pro->variableValue(ObjCHeaderVar)) {
            allFiles << file;
            part->files << ProjectFile(file, ProjectFile::ObjCXXHeader);
        }

613
        pinfo.appendProjectPart(part);
con's avatar
con committed
614
    }
615

616 617
    setProjectLanguage(ProjectExplorer::Constants::LANG_CXX, !allFiles.isEmpty());

618
    modelmanager->updateProjectInfo(pinfo);
619 620
    m_codeModelFuture = modelmanager->updateSourceFiles(allFiles,
        CppTools::CppModelManagerInterface::ForcedProgressNotification);
621 622
}

623 624
void Qt4Project::updateQmlJSCodeModel()
{
625
    QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
626 627 628
    if (!modelManager)
        return;

629 630
    QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
            QmlJSTools::defaultProjectInfoForProject(this);
631 632 633 634

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

635
    projectInfo.importPaths.clear();
636 637

    bool hasQmlLib = false;
638 639
    foreach (Qt4ProFileNode *node, proFiles) {
        projectInfo.importPaths.append(node->variableValue(QmlImportPathVar));
Fawzi Mohamed's avatar
Fawzi Mohamed committed
640 641
        projectInfo.activeResourceFiles.append(node->variableValue(ExactResourceVar));
        projectInfo.allResourceFiles.append(node->variableValue(ResourceVar));
642 643 644 645 646 647
        if (!hasQmlLib) {
            QStringList qtLibs = node->variableValue(QtVar);
            hasQmlLib = qtLibs.contains(QLatin1String("declarative")) ||
                    qtLibs.contains(QLatin1String("qml")) ||
                    qtLibs.contains(QLatin1String("quick"));
        }
648
    }
649

650 651 652 653 654 655 656 657 658
    // If the project directory has a pro/pri file that includes a qml or quick or declarative
    // library then chances of the project being a QML project is quite high.
    // This assumption fails when there are no QDeclarativeEngine/QDeclarativeView (QtQuick 1)
    // or QQmlEngine/QQuickView (QtQuick 2) instances.
    Core::Context pl(ProjectExplorer::Constants::LANG_CXX);
    if (hasQmlLib)
        pl.add(ProjectExplorer::Constants::LANG_QMLJS);
    setProjectLanguages(pl);

659
    projectInfo.importPaths.removeDuplicates();
Fawzi Mohamed's avatar
Fawzi Mohamed committed
660 661
    projectInfo.activeResourceFiles.removeDuplicates();
    projectInfo.allResourceFiles.removeDuplicates();
662

663 664
    setProjectLanguage(ProjectExplorer::Constants::LANG_QMLJS, !projectInfo.sourceFiles.isEmpty());

665
    modelManager->updateProjectInfo(projectInfo);
666 667
}

668 669 670
///*!
//  Updates complete project
//  */
con's avatar
con committed
671 672
void Qt4Project::update()
{
dt's avatar
dt committed
673 674
    if (debug)
        qDebug()<<"Doing sync update";
con's avatar
con committed
675
    m_rootProjectNode->update();
676

dt's avatar
dt committed
677 678
    if (debug)
        qDebug()<<"State is now Base";
dt's avatar
dt committed
679
    m_asyncUpdateState = Base;
Tobias Hunger's avatar
Tobias Hunger committed
680
    enableActiveQt4BuildConfiguration(activeTarget(), true);
681
    updateBuildSystemData();
682 683 684 685 686 687
    updateRunConfigurations();
    emit proFilesEvaluated();
}

void Qt4Project::updateRunConfigurations()
{
688 689
    if (activeTarget())
        activeTarget()->updateDefaultRunConfigurations();
dt's avatar
dt committed
690 691 692 693
}

void Qt4Project::scheduleAsyncUpdate(Qt4ProFileNode *node)
{
dt's avatar
dt committed
694 695
    if (m_asyncUpdateState == ShuttingDown)
        return;
696

dt's avatar
dt committed
697
    if (debug)
Daniel Teske's avatar
Daniel Teske committed
698
        qDebug()<<"schduleAsyncUpdate (node)"<<node->path();
dt's avatar
dt committed
699 700 701
    Q_ASSERT(m_asyncUpdateState != NoState);

    if (m_cancelEvaluate) {
dt's avatar
dt committed
702 703
        if (debug)
            qDebug()<<"  Already canceling, nothing to do";
dt's avatar
dt committed
704 705 706 707 708 709
        // 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;
    }

Tobias Hunger's avatar
Tobias Hunger committed
710
    enableActiveQt4BuildConfiguration(activeTarget(), false);
711

dt's avatar
dt committed
712 713
    if (m_asyncUpdateState == AsyncFullUpdatePending) {
        // Just postpone
dt's avatar
dt committed
714 715
        if (debug)
            qDebug()<<"  full update pending, restarting timer";
dt's avatar
dt committed
716 717 718
        m_asyncUpdateTimer.start();
    } else if (m_asyncUpdateState == AsyncPartialUpdatePending
               || m_asyncUpdateState == Base) {
dt's avatar
dt committed
719 720
        if (debug)
            qDebug()<<"  adding node to async update list, setting state to AsyncPartialUpdatePending";
dt's avatar
dt committed
721 722 723
        // Add the node
        m_asyncUpdateState = AsyncPartialUpdatePending;

724
        QList<Qt4ProFileNode *>::iterator it;
dt's avatar
dt committed
725
        bool add = true;
dt's avatar
dt committed
726 727
        if (debug)
            qDebug()<<"scheduleAsyncUpdate();"<<m_partialEvaluate.size()<<"nodes";
dt's avatar
dt committed
728 729 730 731 732 733
        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
Daniel Teske's avatar
Daniel Teske committed
734 735
                it = m_partialEvaluate.erase(it);
            } else if ((*it)->isParent(node)) { // The node is the parent of a child already in the list
dt's avatar
dt committed
736 737 738 739 740 741 742 743 744 745 746
                add = false;
                break;
            } else {
                ++it;
            }
        }

        if (add)
            m_partialEvaluate.append(node);
        // and start the timer anew
        m_asyncUpdateTimer.start();
747 748 749

        // Cancel running code model update
        m_codeModelFuture.cancel();
dt's avatar
dt committed
750 751 752 753 754 755 756 757
    } 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
758 759
        if (debug)
            qDebug()<<"  Async update in progress, scheduling new one afterwards";
dt's avatar
dt committed
760 761 762 763 764 765
        scheduleAsyncUpdate();
    }
}

void Qt4Project::scheduleAsyncUpdate()
{
dt's avatar
dt committed
766 767
    if (debug)
        qDebug()<<"scheduleAsyncUpdate";
dt's avatar
dt committed
768 769
    if (m_asyncUpdateState == ShuttingDown)
        return;
770

dt's avatar
dt committed
771 772 773
    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
774 775
        if (debug)
            qDebug()<<"  canceling is in progress, doing nothing";
dt's avatar
dt committed
776 777 778
        return;
    }
    if (m_asyncUpdateState == AsyncUpdateInProgress) {
dt's avatar
dt committed
779 780
        if (debug)
            qDebug()<<"  update in progress, canceling and setting state to full update pending";
dt's avatar
dt committed
781 782
        m_cancelEvaluate = true;
        m_asyncUpdateState = AsyncFullUpdatePending;
Tobias Hunger's avatar
Tobias Hunger committed
783
        enableActiveQt4BuildConfiguration(activeTarget(), false);
Daniel Teske's avatar
Daniel Teske committed
784
        m_rootProjectNode->setParseInProgressRecursive(true);
dt's avatar
dt committed
785 786 787
        return;
    }

dt's avatar
dt committed
788 789
    if (debug)
        qDebug()<<"  starting timer for full update, setting state to full update pending";
dt's avatar
dt committed
790
    m_partialEvaluate.clear();
Tobias Hunger's avatar
Tobias Hunger committed
791
    enableActiveQt4BuildConfiguration(activeTarget(), false);
Daniel Teske's avatar
Daniel Teske committed
792
    m_rootProjectNode->setParseInProgressRecursive(true);
dt's avatar
dt committed
793 794
    m_asyncUpdateState = AsyncFullUpdatePending;
    m_asyncUpdateTimer.start();
795 796 797

    // Cancel running code model update
    m_codeModelFuture.cancel();
dt's avatar
dt committed
798 799 800 801 802 803
}


void Qt4Project::incrementPendingEvaluateFutures()
{
    ++m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
804 805
    if (debug)
        qDebug()<<"incrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
806 807 808 809 810 811 812 813 814

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

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

dt's avatar
dt committed
815 816
    if (debug)
        qDebug()<<"decrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
817 818 819

    m_asyncUpdateFutureInterface->setProgressValue(m_asyncUpdateFutureInterface->progressValue() + 1);
    if (m_pendingEvaluateFuturesCount == 0) {
dt's avatar
dt committed
820 821
        if (debug)
            qDebug()<<"  WOHOO, no pending futures, cleaning up";
dt's avatar
dt committed
822
        // We are done!
dt's avatar
dt committed
823 824
        if (debug)
            qDebug()<<"  reporting finished";
825

dt's avatar
dt committed
826 827 828 829 830 831 832
        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
833 834
            if (debug)
                qDebug()<<"  Oh update is pending start the timer";
dt's avatar
dt committed
835
            m_asyncUpdateTimer.start();
836
        } else  if (m_asyncUpdateState != ShuttingDown){
Tobias Hunger's avatar
Tobias Hunger committed
837
            // After being done, we need to call:
838
            m_asyncUpdateState = Base;
Tobias Hunger's avatar
Tobias Hunger committed
839
            enableActiveQt4BuildConfiguration(activeTarget(), true);
840
            updateFileList();
841
            updateCodeModels();
842
            updateBuildSystemData();
843 844
            updateRunConfigurations();
            emit proFilesEvaluated();
dt's avatar
dt committed
845 846
            if (debug)
                qDebug()<<"  Setting state to Base";
dt's avatar
dt committed
847 848 849 850 851 852 853 854 855 856 857
        }
    }
}

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

void Qt4Project::asyncUpdate()
{
dt's avatar
dt committed
858 859
    if (debug)
        qDebug()<<"async update, timer expired, doing now";
860 861 862

    m_qmakeVfs->invalidateCache();

dt's avatar
dt committed
863 864 865
    Q_ASSERT(!m_asyncUpdateFutureInterface);
    m_asyncUpdateFutureInterface = new QFutureInterface<void>();

hjk's avatar
hjk committed
866
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
dt's avatar
dt committed
867

con's avatar
con committed
868
    m_asyncUpdateFutureInterface->setProgressRange(0, 0);
869 870
    progressManager->addTask(m_asyncUpdateFutureInterface->future(), tr("Evaluating"),
                             QLatin1String(Constants::PROFILE_EVALUATE));
dt's avatar
dt committed
871 872
    if (debug)
        qDebug()<<"  adding task";
dt's avatar
dt committed
873 874 875 876

    m_asyncUpdateFutureInterface->reportStarted();

    if (m_asyncUpdateState == AsyncFullUpdatePending) {
dt's avatar
dt committed
877 878
        if (debug)
            qDebug()<<"  full update, starting with root node";
dt's avatar
dt committed
879 880
        m_rootProjectNode->asyncUpdate();
    } else {
dt's avatar
dt committed
881 882
        if (debug)
            qDebug()<<"  partial update,"<<m_partialEvaluate.size()<<"nodes to update";
883
        foreach (Qt4ProFileNode *node, m_partialEvaluate)
dt's avatar
dt committed
884 885 886 887
            node->asyncUpdate();
    }

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

893 894 895 896 897 898
void Qt4Project::buildFinished(bool success)
{
    if (success)
        m_qmakeVfs->invalidateContents();
}

con's avatar
con committed
899 900 901 902 903 904 905 906 907 908
ProjectExplorer::IProjectManager *Qt4Project::projectManager() const
{
    return m_manager;
}

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

909
bool Qt4Project::supportsKit(Kit *k, QString *errorMessage) const
Tobias Hunger's avatar
Tobias Hunger committed
910
{
911
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(k);
912 913
    if (!version && errorMessage)
        *errorMessage = tr("No Qt version set in kit.");
Tobias Hunger's avatar
Tobias Hunger committed
914 915 916
    return version;
}

917
QString Qt4Project::displayName() const
con's avatar
con committed
918
{
919
    return QFileInfo(document()->fileName()).completeBaseName();
con's avatar
con committed
920 921
}

922
Core::Id Qt4Project::id() const
Tobias Hunger's avatar
Tobias Hunger committed
923
{
924
    return Core::Id(Constants::QT4PROJECT_ID);
Tobias Hunger's avatar
Tobias Hunger committed
925 926
}

927
Core::IDocument *Qt4Project::document() const
con's avatar
con committed
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
{
    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;
}

943 944 945
// Find the folder that contains a file a certain type (recurse down)
static FolderNode *folderOf(FolderNode *in, FileType fileType, const QString &fileName)
{
946
    foreach (FileNode *fn, in->fileNodes())
947 948
        if (fn->fileType() == fileType && fn->path() == fileName)
            return in;
949
    foreach (FolderNode *folder, in->subFolderNodes())
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
        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
975 976
void Qt4Project::proFileParseError(const QString &errorMessage)
{
977
    Core::ICore::messageManager()->printToOutputPane(errorMessage, Core::MessageManager::NoModeSwitch);
con's avatar
con committed
978 979
}

980
QtSupport::ProFileReader *Qt4Project::createProFileReader(const Qt4ProFileNode *qt4ProFileNode, Qt4BuildConfiguration *bc)
981
{
982
    if (!m_qmakeGlobals) {
983
        m_qmakeGlobals = new ProFileGlobals;
984
        m_qmakeGlobalsRefCnt = 0;
985

Tobias Hunger's avatar
Tobias Hunger committed
986
        Kit *k;
987 988
        Utils::Environment env = Utils::Environment::systemEnvironment();
        QStringList qmakeArgs;
Orgad Shaneh's avatar
Orgad Shaneh committed
989
        if (!bc)
990
            bc = activeTarget() ? static_cast<Qt4BuildConfiguration *>(activeTarget()->activeBuildConfiguration()) : 0;
Orgad Shaneh's avatar
Orgad Shaneh committed
991 992

        if (bc) {
Tobias Hunger's avatar
Tobias Hunger committed
993
            k = bc->target()->kit();
Orgad Shaneh's avatar
Orgad Shaneh committed
994
            env = bc->environment();
995
            if (bc->qmakeStep())
Orgad Shaneh's avatar
Orgad Shaneh committed
996
                qmakeArgs = bc->qmakeStep()->parserArguments();