qt4project.cpp 53.6 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"
33
34
#include "qmakeprojectimporter.h"
#include "qmakebuildinfo.h"
con's avatar
con committed
35
36
37
#include "qmakestep.h"
#include "qt4nodes.h"
#include "qt4projectmanagerconstants.h"
dt's avatar
dt committed
38
#include "qt4buildconfiguration.h"
39
#include "findqt4profiles.h"
40
41
42
#include "qt4projectmanager/wizards/abstractmobileapp.h"
#include "qt4projectmanager/wizards/qtquickapp.h"
#include "qt4projectmanager/wizards/html5app.h"
43

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>
Tobias Hunger's avatar
Tobias Hunger committed
60
#include <qtsupport/uicodemodelsupport.h>
con's avatar
con committed
61

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

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

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

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

namespace {

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

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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
111

Tobias Hunger's avatar
Tobias Hunger committed
112
113
} // namespace

con's avatar
con committed
114
115
116
namespace Qt4ProjectManager {
namespace Internal {

117
class Qt4ProjectFile : public Core::IDocument
118
119
120
121
{
    Q_OBJECT

public:
Tobias Hunger's avatar
Tobias Hunger committed
122
    Qt4ProjectFile(const QString &filePath, QObject *parent = 0);
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

    bool save(QString *errorString, const QString &fileName, bool autoSave);

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

/// 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:
148
    CentralizedFolderWatcher(Qt4Project *parent);
149
    ~CentralizedFolderWatcher();
150
151
    void watchFolders(const QList<QString> &folders, Qt4ProjectManager::Qt4PriFileNode *node);
    void unwatchFolders(const QList<QString> &folders, Qt4ProjectManager::Qt4PriFileNode *node);
152
153
154
155
156
157
158

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

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

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

con's avatar
con committed
169
// Qt4ProjectFiles: Struct for (Cached) lists of files in a project
Tobias Hunger's avatar
Tobias Hunger committed
170
171
class Qt4ProjectFiles {
public:
con's avatar
con committed
172
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
    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
218

con's avatar
con committed
219
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
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
269
namespace Internal {
Tobias Hunger's avatar
Tobias Hunger committed
270
Qt4ProjectFile::Qt4ProjectFile(const QString &filePath, QObject *parent)
271
    : Core::IDocument(parent),
272
      m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE))
con's avatar
con committed
273
{
274
    setFilePath(filePath);
con's avatar
con committed
275
276
}

277
bool Qt4ProjectFile::save(QString *, const QString &, bool)
con's avatar
con committed
278
{
279
280
    // This is never used
    return false;
con's avatar
con committed
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
}

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

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

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

bool Qt4ProjectFile::isModified() const
{
300
    return false; // we save after changing anyway
con's avatar
con committed
301
302
303
304
305
306
307
}

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

308
Core::IDocument::ReloadBehavior Qt4ProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
con's avatar
con committed
309
{
310
311
312
313
314
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
}

315
bool Qt4ProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
316
{
317
    Q_UNUSED(errorString)
318
319
    Q_UNUSED(flag)
    Q_UNUSED(type)
320
    return true;
con's avatar
con committed
321
322
}

323
} // namespace Internal
324

con's avatar
con committed
325
/*!
326
  \class Qt4Project
con's avatar
con committed
327
328
329
330
331
332

  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
333
    m_rootProjectNode(0),
con's avatar
con committed
334
    m_nodesWatcher(new Internal::Qt4NodesWatcher(this)),
Tobias Hunger's avatar
Tobias Hunger committed
335
    m_fileInfo(new Qt4ProjectFile(fileName, this)),
336
    m_projectFiles(new Qt4ProjectFiles),
337
    m_qmakeVfs(new QMakeVfs),
338
    m_qmakeGlobals(0),
dt's avatar
dt committed
339
340
341
    m_asyncUpdateFutureInterface(0),
    m_pendingEvaluateFuturesCount(0),
    m_asyncUpdateState(NoState),
342
    m_cancelEvaluate(false),
343
344
    m_centralizedFolderWatcher(0),
    m_activeTarget(0)
con's avatar
con committed
345
{
346
    setProjectContext(Core::Context(Qt4ProjectManager::Constants::PROJECT_ID));
347
    setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX));
348

dt's avatar
dt committed
349
350
351
    m_asyncUpdateTimer.setSingleShot(true);
    m_asyncUpdateTimer.setInterval(3000);
    connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate()));
352

hjk's avatar
hjk committed
353
    connect(BuildManager::instance(), SIGNAL(buildQueueFinished(bool)),
354
            SLOT(buildFinished(bool)));
con's avatar
con committed
355
356
357
358
}

Qt4Project::~Qt4Project()
{
359
    m_codeModelFuture.cancel();
360
    m_asyncUpdateState = ShuttingDown;
dt's avatar
dt committed
361
    m_manager->unregisterProject(this);
362
    delete m_qmakeVfs;
con's avatar
con committed
363
    delete m_projectFiles;
364
    m_cancelEvaluate = true;
365
366
367
368
369
    // 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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
}

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

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

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

dt's avatar
dt committed
399
400
    m_manager->registerProject(this);

401
    m_rootProjectNode = new Qt4ProFileNode(this, m_fileInfo->filePath(), this);
dt's avatar
dt committed
402
403
    m_rootProjectNode->registerWatcher(m_nodesWatcher);

Tobias Hunger's avatar
Tobias Hunger committed
404
405
    update();
    updateFileList();
406
407
    // This might be incorrect, need a full update
    updateCodeModels();
408

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

413
    // Now we emit update once :)
Daniel Teske's avatar
Daniel Teske committed
414
    m_rootProjectNode->emitProFileUpdatedRecursive();
415

416
417
418
419
420
421
    // 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
422
423
424
    connect(this, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
            this, SLOT(activeTargetWasChanged()));

425
426
427
428
    // // Update boiler plate code for subprojects.
    QtQuickApp qtQuickApp;
    const Html5App html5App;

429
    foreach (Qt4ProFileNode *node, applicationProFiles(Qt4Project::ExactAndCumulativeParse)) {
430
431
432
433
434
435
436
437
        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
438
    return true;
con's avatar
con committed
439
440
}

dt's avatar
dt committed
441
442
443
444
445
446
447
448
449
450
451
452
453
/// 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) {
454
        if (*ait == CppTools::CppModelManagerInterface::configurationFileName())
dt's avatar
dt committed
455
            ++ait;
456
        else if (*bit == CppTools::CppModelManagerInterface::configurationFileName())
dt's avatar
dt committed
457
458
459
460
461
462
463
            ++bit;
        else if (*ait == *bit)
            ++ait, ++bit;
        else
           return false;
    }
    return (ait == aend && bit == bend);
Tobias Hunger's avatar
Tobias Hunger committed
464
465
}

466
void Qt4Project::updateCodeModels()
con's avatar
con committed
467
468
469
470
{
    if (debug)
        qDebug()<<"Qt4Project::updateCodeModel()";

471
    if (activeTarget() && !activeTarget()->activeBuildConfiguration())
Tobias Hunger's avatar
Tobias Hunger committed
472
473
        return;

474
475
476
477
478
479
    updateCppCodeModel();
    updateQmlJSCodeModel();
}

void Qt4Project::updateCppCodeModel()
{
480
481
    typedef CppTools::ProjectPart ProjectPart;
    typedef CppTools::ProjectFile ProjectFile;
482

Tobias Hunger's avatar
Tobias Hunger committed
483
    Kit *k = 0;
484
    QtSupport::BaseQtVersion *qtVersion = 0;
Tobias Hunger's avatar
Tobias Hunger committed
485
    if (ProjectExplorer::Target *target = activeTarget())
Tobias Hunger's avatar
Tobias Hunger committed
486
        k = target->kit();
Tobias Hunger's avatar
Tobias Hunger committed
487
    else
488
        k = KitManager::defaultKit();
Tobias Hunger's avatar
Tobias Hunger committed
489
    qtVersion = QtSupport::QtKitInformation::qtVersion(k);
490

491
492
    CppTools::CppModelManagerInterface *modelmanager =
        CppTools::CppModelManagerInterface::instance();
con's avatar
con committed
493

494
    if (!modelmanager)
con's avatar
con committed
495
496
        return;

497
498
    FindQt4ProFiles findQt4ProFiles;
    QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
con's avatar
con committed
499

500
    CppTools::CppModelManagerInterface::ProjectInfo pinfo = modelmanager->projectInfo(this);
501
502
503
504
505
506
507
508
    pinfo.clearProjectParts();
    ProjectPart::QtVersion qtVersionForPart = ProjectPart::NoQt;
    if (qtVersion) {
        if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
            qtVersionForPart = ProjectPart::Qt4;
        else
            qtVersionForPart = ProjectPart::Qt5;
    }
509

Tobias Hunger's avatar
Tobias Hunger committed
510
    QHash<QString, QString> uiCodeModelData;
511
512
513
    QStringList allFiles;
    foreach (Qt4ProFileNode *pro, proFiles) {
        ProjectPart::Ptr part(new ProjectPart);
514
515
516
        part->project = this;
        part->displayName = pro->displayName();
        part->projectFile = pro->path();
517
518
519
520
521

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

523
        const QStringList cxxflags = pro->variableValue(CppFlagsVar);
524
525
526
527
        part->evaluateToolchain(ToolChainKitInformation::toolChain(k),
                                cxxflags,
                                cxxflags,
                                SysRootKitInformation::sysRoot(k));
528

529
530
531
        // part->defines
        part->defines += pro->cxxDefines();

532
        // part->includePaths, part->frameworkPaths
533
534
535
        part->includePaths.append(pro->variableValue(IncludePathVar));

        if (qtVersion) {
536
537
538
539
540
541
            foreach (const HeaderPath &header, qtVersion->systemHeaderPathes(k)) {
                if (header.kind() == HeaderPath::FrameworkHeaderPath)
                    part->frameworkPaths.append(header.path());
                else
                    part->includePaths.append(header.path());
            }
542
543
            if (!qtVersion->frameworkInstallPath().isEmpty())
                part->frameworkPaths.append(qtVersion->frameworkInstallPath());
544
        }
545

546
547
        if (Qt4ProFileNode *node = rootQt4ProjectNode())
            part->includePaths.append(node->resolvedMkspecPath());
con's avatar
con committed
548

549
550
        // part->precompiledHeaders
        part->precompiledHeaders.append(pro->variableValue(PrecompiledHeaderVar));
dt's avatar
dt committed
551

552
        // part->files
553
554
555
556
557
558
559
560
        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);
        }
Tobias Hunger's avatar
Tobias Hunger committed
561
562
563
564
565
566

        // Ui Files:
        QHash<QString, QString> uiData = pro->uiFiles();
        for (QHash<QString, QString>::const_iterator i = uiData.constBegin(); i != uiData.constEnd(); ++i) {
            allFiles << i.value();
            part->files << ProjectFile(i.value(), ProjectFile::CXXHeader);
567
        }
Tobias Hunger's avatar
Tobias Hunger committed
568
        uiCodeModelData.unite(uiData);
con's avatar
con committed
569

570
        part->files.prepend(ProjectFile(CppTools::CppModelManagerInterface::configurationFileName(),
571
572
573
                                        ProjectFile::CXXSource));
        foreach (const QString &file, pro->variableValue(ObjCSourceVar)) {
            allFiles << file;
574
575
576
            // Although the enum constant is called ObjCSourceVar, it actually is ObjC++ source
            // code, as qmake does not handle C (and ObjC).
            part->files << ProjectFile(file, ProjectFile::ObjCXXSource);
577
        }
578
579
580
581
582
        foreach (const QString &file, pro->variableValue(ObjCHeaderVar)) {
            allFiles << file;
            part->files << ProjectFile(file, ProjectFile::ObjCXXHeader);
        }

583
        pinfo.appendProjectPart(part);
con's avatar
con committed
584
    }
585

586
587
    setProjectLanguage(ProjectExplorer::Constants::LANG_CXX, !allFiles.isEmpty());

Tobias Hunger's avatar
Tobias Hunger committed
588
    // Also update Ui Code Model Support:
589
    QtSupport::UiCodeModelManager::update(this, uiCodeModelData);
Tobias Hunger's avatar
Tobias Hunger committed
590

591
    m_codeModelFuture = modelmanager->updateProjectInfo(pinfo);
592
593
}

594
595
void Qt4Project::updateQmlJSCodeModel()
{
596
    QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
597
598
599
    if (!modelManager)
        return;

600
601
    QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
            QmlJSTools::defaultProjectInfoForProject(this);
602
603
604
605

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

606
    projectInfo.importPaths.clear();
607
608

    bool hasQmlLib = false;
609
610
    foreach (Qt4ProFileNode *node, proFiles) {
        projectInfo.importPaths.append(node->variableValue(QmlImportPathVar));
Fawzi Mohamed's avatar
Fawzi Mohamed committed
611
612
        projectInfo.activeResourceFiles.append(node->variableValue(ExactResourceVar));
        projectInfo.allResourceFiles.append(node->variableValue(ResourceVar));
613
614
615
616
617
618
        if (!hasQmlLib) {
            QStringList qtLibs = node->variableValue(QtVar);
            hasQmlLib = qtLibs.contains(QLatin1String("declarative")) ||
                    qtLibs.contains(QLatin1String("qml")) ||
                    qtLibs.contains(QLatin1String("quick"));
        }
619
    }
620

621
622
623
624
625
626
627
628
629
    // 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);

630
    projectInfo.importPaths.removeDuplicates();
Fawzi Mohamed's avatar
Fawzi Mohamed committed
631
632
    projectInfo.activeResourceFiles.removeDuplicates();
    projectInfo.allResourceFiles.removeDuplicates();
633

634
635
    setProjectLanguage(ProjectExplorer::Constants::LANG_QMLJS, !projectInfo.sourceFiles.isEmpty());

636
    modelManager->updateProjectInfo(projectInfo);
637
638
}

639
640
641
///*!
//  Updates complete project
//  */
con's avatar
con committed
642
643
void Qt4Project::update()
{
dt's avatar
dt committed
644
645
    if (debug)
        qDebug()<<"Doing sync update";
con's avatar
con committed
646
    m_rootProjectNode->update();
647

dt's avatar
dt committed
648
649
    if (debug)
        qDebug()<<"State is now Base";
dt's avatar
dt committed
650
    m_asyncUpdateState = Base;
Tobias Hunger's avatar
Tobias Hunger committed
651
    enableActiveQt4BuildConfiguration(activeTarget(), true);
652
    updateBuildSystemData();
653
654
655
656
657
658
    updateRunConfigurations();
    emit proFilesEvaluated();
}

void Qt4Project::updateRunConfigurations()
{
659
660
    if (activeTarget())
        activeTarget()->updateDefaultRunConfigurations();
dt's avatar
dt committed
661
662
663
664
}

void Qt4Project::scheduleAsyncUpdate(Qt4ProFileNode *node)
{
dt's avatar
dt committed
665
666
    if (m_asyncUpdateState == ShuttingDown)
        return;
667

dt's avatar
dt committed
668
    if (debug)
Daniel Teske's avatar
Daniel Teske committed
669
        qDebug()<<"schduleAsyncUpdate (node)"<<node->path();
dt's avatar
dt committed
670
671
672
    Q_ASSERT(m_asyncUpdateState != NoState);

    if (m_cancelEvaluate) {
dt's avatar
dt committed
673
674
        if (debug)
            qDebug()<<"  Already canceling, nothing to do";
dt's avatar
dt committed
675
676
677
678
679
680
        // 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
681
    enableActiveQt4BuildConfiguration(activeTarget(), false);
682

dt's avatar
dt committed
683
684
    if (m_asyncUpdateState == AsyncFullUpdatePending) {
        // Just postpone
dt's avatar
dt committed
685
686
        if (debug)
            qDebug()<<"  full update pending, restarting timer";
dt's avatar
dt committed
687
688
689
        m_asyncUpdateTimer.start();
    } else if (m_asyncUpdateState == AsyncPartialUpdatePending
               || m_asyncUpdateState == Base) {
dt's avatar
dt committed
690
691
        if (debug)
            qDebug()<<"  adding node to async update list, setting state to AsyncPartialUpdatePending";
dt's avatar
dt committed
692
693
694
        // Add the node
        m_asyncUpdateState = AsyncPartialUpdatePending;

695
        QList<Qt4ProFileNode *>::iterator it;
dt's avatar
dt committed
696
        bool add = true;
dt's avatar
dt committed
697
698
        if (debug)
            qDebug()<<"scheduleAsyncUpdate();"<<m_partialEvaluate.size()<<"nodes";
dt's avatar
dt committed
699
700
701
702
703
704
        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
705
706
                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
707
708
709
710
711
712
713
714
715
716
717
                add = false;
                break;
            } else {
                ++it;
            }
        }

        if (add)
            m_partialEvaluate.append(node);
        // and start the timer anew
        m_asyncUpdateTimer.start();
718
719
720

        // Cancel running code model update
        m_codeModelFuture.cancel();
dt's avatar
dt committed
721
722
723
724
725
726
727
728
    } 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
729
730
        if (debug)
            qDebug()<<"  Async update in progress, scheduling new one afterwards";
dt's avatar
dt committed
731
732
733
734
735
736
        scheduleAsyncUpdate();
    }
}

void Qt4Project::scheduleAsyncUpdate()
{
dt's avatar
dt committed
737
738
    if (debug)
        qDebug()<<"scheduleAsyncUpdate";
dt's avatar
dt committed
739
740
    if (m_asyncUpdateState == ShuttingDown)
        return;
741

dt's avatar
dt committed
742
743
744
    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
745
746
        if (debug)
            qDebug()<<"  canceling is in progress, doing nothing";
dt's avatar
dt committed
747
748
749
        return;
    }
    if (m_asyncUpdateState == AsyncUpdateInProgress) {
dt's avatar
dt committed
750
751
        if (debug)
            qDebug()<<"  update in progress, canceling and setting state to full update pending";
dt's avatar
dt committed
752
753
        m_cancelEvaluate = true;
        m_asyncUpdateState = AsyncFullUpdatePending;
Tobias Hunger's avatar
Tobias Hunger committed
754
        enableActiveQt4BuildConfiguration(activeTarget(), false);
Daniel Teske's avatar
Daniel Teske committed
755
        m_rootProjectNode->setParseInProgressRecursive(true);
dt's avatar
dt committed
756
757
758
        return;
    }

dt's avatar
dt committed
759
760
    if (debug)
        qDebug()<<"  starting timer for full update, setting state to full update pending";
dt's avatar
dt committed
761
    m_partialEvaluate.clear();
Tobias Hunger's avatar
Tobias Hunger committed
762
    enableActiveQt4BuildConfiguration(activeTarget(), false);
Daniel Teske's avatar
Daniel Teske committed
763
    m_rootProjectNode->setParseInProgressRecursive(true);
dt's avatar
dt committed
764
765
    m_asyncUpdateState = AsyncFullUpdatePending;
    m_asyncUpdateTimer.start();
766
767
768

    // Cancel running code model update
    m_codeModelFuture.cancel();
dt's avatar
dt committed
769
770
771
772
773
774
}


void Qt4Project::incrementPendingEvaluateFutures()
{
    ++m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
775
776
    if (debug)
        qDebug()<<"incrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
777
778
779
780
781
782
783
784
785

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

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

dt's avatar
dt committed
786
787
    if (debug)
        qDebug()<<"decrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
788
789
790

    m_asyncUpdateFutureInterface->setProgressValue(m_asyncUpdateFutureInterface->progressValue() + 1);
    if (m_pendingEvaluateFuturesCount == 0) {
dt's avatar
dt committed
791
792
        if (debug)
            qDebug()<<"  WOHOO, no pending futures, cleaning up";
dt's avatar
dt committed
793
        // We are done!
dt's avatar
dt committed
794
795
        if (debug)
            qDebug()<<"  reporting finished";
796

dt's avatar
dt committed
797
798
799
800
801
802
803
        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
804
805
            if (debug)
                qDebug()<<"  Oh update is pending start the timer";
dt's avatar
dt committed
806
            m_asyncUpdateTimer.start();
807
        } else  if (m_asyncUpdateState != ShuttingDown){
Tobias Hunger's avatar
Tobias Hunger committed
808
            // After being done, we need to call:
809
            m_asyncUpdateState = Base;
Tobias Hunger's avatar
Tobias Hunger committed
810
            enableActiveQt4BuildConfiguration(activeTarget(), true);
811
            updateFileList();
812
            updateCodeModels();
813
            updateBuildSystemData();
814
815
            updateRunConfigurations();
            emit proFilesEvaluated();
dt's avatar
dt committed
816
817
            if (debug)
                qDebug()<<"  Setting state to Base";
dt's avatar
dt committed
818
819
820
821
822
823
824
825
826
827
828
        }
    }
}

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

void Qt4Project::asyncUpdate()
{
dt's avatar
dt committed
829
830
    if (debug)
        qDebug()<<"async update, timer expired, doing now";
831
832
833

    m_qmakeVfs->invalidateCache();

dt's avatar
dt committed
834
835
836
    Q_ASSERT(!m_asyncUpdateFutureInterface);
    m_asyncUpdateFutureInterface = new QFutureInterface<void>();

con's avatar
con committed
837
    m_asyncUpdateFutureInterface->setProgressRange(0, 0);
838
    Core::ProgressManager::addTask(m_asyncUpdateFutureInterface->future(), tr("Evaluating"),
839
                             Constants::PROFILE_EVALUATE);
dt's avatar
dt committed
840
841
    if (debug)
        qDebug()<<"  adding task";
dt's avatar
dt committed
842
843
844
845

    m_asyncUpdateFutureInterface->reportStarted();

    if (m_asyncUpdateState == AsyncFullUpdatePending) {
dt's avatar
dt committed
846
847
        if (debug)
            qDebug()<<"  full update, starting with root node";
dt's avatar
dt committed
848
849
        m_rootProjectNode->asyncUpdate();
    } else {
dt's avatar
dt committed
850
851
        if (debug)
            qDebug()<<"  partial update,"<<m_partialEvaluate.size()<<"nodes to update";
852
        foreach (Qt4ProFileNode *node, m_partialEvaluate)
dt's avatar
dt committed
853
854
855
856
            node->asyncUpdate();
    }

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

862
863
864
865
866
867
void Qt4Project::buildFinished(bool success)
{
    if (success)
        m_qmakeVfs->invalidateContents();
}

con's avatar
con committed
868
869
870
871
872
873
874
875
876
877
ProjectExplorer::IProjectManager *Qt4Project::projectManager() const
{
    return m_manager;
}

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

878
bool Qt4Project::supportsKit(Kit *k, QString *errorMessage) const
Tobias Hunger's avatar
Tobias Hunger committed
879
{
880
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(k);
881
882
    if (!version && errorMessage)
        *errorMessage = tr("No Qt version set in kit.");
Tobias Hunger's avatar
Tobias Hunger committed
883
884
885
    return version;
}

886
QString Qt4Project::displayName() const
con's avatar
con committed
887
{
888
    return QFileInfo(projectFilePath()).completeBaseName();
con's avatar
con committed
889
890
}

891
Core::Id Qt4Project::id() const
Tobias Hunger's avatar
Tobias Hunger committed
892
{
893
    return Core::Id(Constants::QT4PROJECT_ID);
Tobias Hunger's avatar
Tobias Hunger committed
894
895
}

896
Core::IDocument *Qt4Project::document() const
con's avatar
con committed
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
{
    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;
}

912
913
914
// Find the folder that contains a file a certain type (recurse down)
static FolderNode *folderOf(FolderNode *in, FileType fileType, const QString &fileName)
{
915
    foreach (FileNode *fn, in->fileNodes())
916
917
        if (fn->fileType() == fileType && fn->path() == fileName)
            return in;
918
    foreach (FolderNode *folder, in->subFolderNodes())
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
        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
944
945
void Qt4Project::proFileParseError(const QString &errorMessage)
{
hjk's avatar
hjk committed
946
    Core::MessageManager::write(errorMessage);
con's avatar
con committed
947
948
}

949
QtSupport::ProFileReader *Qt4Project::createProFileReader(const Qt4ProFileNode *qt4ProFileNode, Qt4BuildConfiguration *bc)
950
{
951
    if (!m_qmakeGlobals) {
952
        m_qmakeGlobals = new ProFileGlobals;
953
        m_qmakeGlobalsRefCnt = 0;
954

Tobias Hunger's avatar
Tobias Hunger committed
955
        Kit *k;
956
957
        Utils::Environment env = Utils::Environment::systemEnvironment();
        QStringList qmakeArgs;
Orgad Shaneh's avatar
Orgad Shaneh committed
958
        if (!bc)
959
            bc = activeTarget() ? static_cast<Qt4BuildConfiguration *>(activeTarget()->activeBuildConfiguration()) : 0;
Orgad Shaneh's avatar
Orgad Shaneh committed
960
961

        if (bc) {
Tobias Hunger's avatar
Tobias Hunger committed
962
            k = bc->target()->kit();
Orgad Shaneh's avatar
Orgad Shaneh committed
963
            env = bc->environment();
964
            if (bc->qmakeStep())
Orgad Shaneh's avatar
Orgad Shaneh committed
965
                qmakeArgs = bc->qmakeStep()->parserArguments();
966
            else
967
968
                qmakeArgs = bc->configCommandLineArguments();
        } else {
969
            k = KitManager::defaultKit();
970
        }
971

Tobias Hunger's avatar
Tobias Hunger committed
972
973
974
        QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);
        QString systemRoot = SysRootKitInformation::hasSysRoot(k)
                ? SysRootKitInformation::sysRoot(k).toString() : QString();
Tobias Hunger's avatar
Tobias Hunger committed
975

976
977
        if (qtVersion && qtVersion->isValid()) {
            m_qmakeGlobals->qmake_abslocation = QDir::cleanPath(qtVersion->qmakeCommand().toString());
978
            m_qmakeGlobals->setProperties(qtVersion->versionInfo());
979
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
980
        m_qmakeGlobals->setDirectories(m_rootProjectNode->sourceDir(), m_rootProjectNode->buildDir());
981
        m_qmakeGlobals->sysroot = systemRoot;
982
983
984

        Utils::Environment::const_iterator eit = env.constBegin(), eend = env.constEnd();
        for (; eit != eend; ++eit)
985
            m_qmakeGlobals->environment.insert(env.key(eit), env.value(eit));
986

987
        m_qmakeGlobals->setCommandLineArguments(m_rootProjectNode->buildDir(), qmakeArgs);
988

989
        QtSupport::ProFileCacheManager::instance()->incRefCount();
990
    }
991
    ++m_qmakeGlobalsRefCnt;
992

993
    QtSupport::ProFileReader *reader = new QtSupport::ProFileReader(m_qmakeGlobals, m_qmakeVfs);
994
995
996
997
998
999

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

    return reader;
}

1000
ProFileGlobals *Qt4Project::qmakeGlobals()
1001
{
1002
    return m_qmakeGlobals;
1003
1004
}

1005
void Qt4Project::destroyProFileReader(QtSupport::ProFileReader *reader)
1006
1007
{
    delete reader;
1008
    if (!--m_qmakeGlobalsRefCnt) {
1009
        QString dir = QFileInfo(m_fileInfo->filePath()).absolutePath();