qmakeproject.cpp 54.2 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

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

32
#include "qmakeprojectmanager.h"
33
34
#include "qmakeprojectimporter.h"
#include "qmakebuildinfo.h"
con's avatar
con committed
35
#include "qmakestep.h"
36
37
38
39
40
41
42
#include "qmakenodes.h"
#include "qmakeprojectmanagerconstants.h"
#include "qmakebuildconfiguration.h"
#include "findqmakeprofiles.h"
#include "wizards/abstractmobileapp.h"
#include "wizards/qtquickapp.h"
#include "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
using namespace QmakeProjectManager;
using namespace QmakeProjectManager::Internal;
con's avatar
con committed
69
70
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
// -----------------------------------------------------------------------
// Helpers:
// -----------------------------------------------------------------------

namespace {

79
QmakeBuildConfiguration *enableActiveQmakeBuildConfiguration(ProjectExplorer::Target *t, bool enabled)
Tobias Hunger's avatar
Tobias Hunger committed
80
81
82
{
    if (!t)
        return 0;
83
    QmakeBuildConfiguration *bc = static_cast<QmakeBuildConfiguration *>(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
void updateBoilerPlateCodeFiles(const AbstractMobileApp *app, const QString &proFile)
{
    const QList<AbstractGeneratedFileInfo> updates =
            app->fileUpdates(proFile);
    if (!updates.empty()) {
95
        const QString title = QmakeManager::tr("Update of Generated Files");
96
97
98
99
        QStringList fileNames;
        foreach (const AbstractGeneratedFileInfo &info, updates)
            fileNames.append(QDir::toNativeSeparators(info.fileInfo.fileName()));
        const QString message =
100
                QmakeManager::tr("In project<br><br>%1<br><br>The following files are either "
101
102
103
104
105
106
107
108
109
110
                               "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

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

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

public:
122
    QmakeProjectFile(const QString &filePath, QObject *parent = 0);
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

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

140
/// Watches folders for QmakePriFile nodes
141
142
143
144
145
146
147
/// use one file system watcher to watch all folders
/// such minimizing system ressouce usage

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

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

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

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

169
170
// QmakeProjectFiles: Struct for (Cached) lists of files in a project
class QmakeProjectFiles {
Tobias Hunger's avatar
Tobias Hunger committed
171
public:
con's avatar
con committed
172
    void clear();
173
    bool equals(const QmakeProjectFiles &f) const;
con's avatar
con committed
174
175
176
177
178
179

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

180
void QmakeProjectFiles::clear()
con's avatar
con committed
181
182
183
184
185
186
187
188
{
    for (int i = 0; i < FileTypeSize; ++i) {
        files[i].clear();
        generatedFiles[i].clear();
    }
    proFiles.clear();
}

189
bool QmakeProjectFiles::equals(const QmakeProjectFiles &f) const
con's avatar
con committed
190
191
192
193
194
195
196
197
198
{
    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;
}

199
inline bool operator==(const QmakeProjectFiles &f1, const QmakeProjectFiles &f2)
con's avatar
con committed
200
201
{       return f1.equals(f2); }

202
inline bool operator!=(const QmakeProjectFiles &f1, const QmakeProjectFiles &f2)
con's avatar
con committed
203
204
{       return !f1.equals(f2); }

205
QDebug operator<<(QDebug d, const  QmakeProjectFiles &f)
con's avatar
con committed
206
207
{
    QDebug nsp = d.nospace();
208
    nsp << "QmakeProjectFiles: proFiles=" <<  f.proFiles << '\n';
con's avatar
con committed
209
210
211
212
213
    for (int i = 0; i < FileTypeSize; ++i)
        nsp << "Type " << i << " files=" << f.files[i] <<  " generated=" << f.generatedFiles[i] << '\n';
    return d;
}

214
// A visitor to collect all files of a project in a QmakeProjectFiles struct
con's avatar
con committed
215
216
class ProjectFilesVisitor : public ProjectExplorer::NodesVisitor
{
217
    ProjectFilesVisitor(QmakeProjectFiles *files);
hjk's avatar
hjk committed
218

con's avatar
con committed
219
220
public:

221
    static void findProjectFiles(QmakeProFileNode *rootNode, QmakeProjectFiles *files);
con's avatar
con committed
222
223
224
225
226

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

private:
227
    QmakeProjectFiles *m_files;
con's avatar
con committed
228
229
};

230
ProjectFilesVisitor::ProjectFilesVisitor(QmakeProjectFiles *files) :
con's avatar
con committed
231
232
233
234
    m_files(files)
{
}

235
void ProjectFilesVisitor::findProjectFiles(QmakeProFileNode *rootNode, QmakeProjectFiles *files)
con's avatar
con committed
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
{
    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);
    }
}

}

268
// ----------- QmakeProjectFile
Friedemann Kleint's avatar
Friedemann Kleint committed
269
namespace Internal {
270
QmakeProjectFile::QmakeProjectFile(const QString &filePath, QObject *parent)
271
    : Core::IDocument(parent),
272
      m_mimeType(QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE))
con's avatar
con committed
273
{
274
    setFilePath(filePath);
con's avatar
con committed
275
276
}

277
bool QmakeProjectFile::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
QString QmakeProjectFile::defaultPath() const
con's avatar
con committed
284
285
286
287
{
    return QString();
}

288
QString QmakeProjectFile::suggestedFileName() const
con's avatar
con committed
289
290
291
292
{
    return QString();
}

293
QString QmakeProjectFile::mimeType() const
con's avatar
con committed
294
295
296
297
{
    return m_mimeType;
}

298
bool QmakeProjectFile::isModified() const
con's avatar
con committed
299
{
300
    return false; // we save after changing anyway
con's avatar
con committed
301
302
}

303
bool QmakeProjectFile::isSaveAsAllowed() const
con's avatar
con committed
304
305
306
307
{
    return false;
}

308
Core::IDocument::ReloadBehavior QmakeProjectFile::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 QmakeProjectFile::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 QmakeProject
con's avatar
con committed
327

328
  QmakeProject manages information about an individual Qt 4 (.pro) project file.
con's avatar
con committed
329
330
  */

331
QmakeProject::QmakeProject(QmakeManager *manager, const QString &fileName) :
con's avatar
con committed
332
    m_manager(manager),
dt's avatar
dt committed
333
    m_rootProjectNode(0),
334
335
336
    m_nodesWatcher(new Internal::QmakeNodesWatcher(this)),
    m_fileInfo(new QmakeProjectFile(fileName, this)),
    m_projectFiles(new QmakeProjectFiles),
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(QmakeProjectManager::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
QmakeProject::~QmakeProject()
con's avatar
con committed
358
{
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
    // Deleting the root node triggers a few things, make sure rootProjectNode
    // returns 0 already
367
    QmakeProFileNode *root = m_rootProjectNode;
368
369
    m_rootProjectNode = 0;
    delete root;
con's avatar
con committed
370
371
}

372
void QmakeProject::updateFileList()
con's avatar
con committed
373
{
374
    QmakeProjectFiles newFiles;
con's avatar
con committed
375
376
377
378
379
380
381
382
383
    ProjectFilesVisitor::findProjectFiles(m_rootProjectNode, &newFiles);
    if (newFiles != *m_projectFiles) {
        *m_projectFiles = newFiles;
        emit fileListChanged();
        if (debug)
            qDebug() << Q_FUNC_INFO << *m_projectFiles;
    }
}

384
bool QmakeProject::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 QmakeProFileNode(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(QmakeProjectManager::QmakeProFileNode*,bool,bool)),
            this, SIGNAL(proFileUpdated(QmakeProjectManager::QmakeProFileNode*,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 (QmakeProFileNode *node, applicationProFiles(QmakeProject::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
/// equalFileList compares two file lists ignoring
/// <configuration> without generating temporary lists

444
bool QmakeProject::equalFileList(const QStringList &a, const QStringList &b)
dt's avatar
dt committed
445
446
447
448
449
450
451
452
453
{
    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 QmakeProject::updateCodeModels()
con's avatar
con committed
467
468
{
    if (debug)
469
        qDebug() << "QmakeProject::updateCodeModel()";
con's avatar
con committed
470

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

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

478
void QmakeProject::updateCppCodeModel()
479
{
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
    FindQmakeProFiles findQmakeProFiles;
    QList<QmakeProFileNode *> proFiles = findQmakeProFiles(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
    QStringList allFiles;
512
    foreach (QmakeProFileNode *pro, proFiles) {
513
        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
        if (QmakeProFileNode *node = rootQmakeProjectNode())
547
            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
void QmakeProject::updateQmlJSCodeModel()
595
{
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
    FindQmakeProFiles findQt4ProFiles;
    QList<QmakeProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
605

606
    projectInfo.importPaths.clear();
607
608

    bool hasQmlLib = false;
609
    foreach (QmakeProFileNode *node, proFiles) {
610
        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
//  */
642
void QmakeProject::update()
con's avatar
con committed
643
{
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;
651
    enableActiveQmakeBuildConfiguration(activeTarget(), true);
652
    updateBuildSystemData();
Daniel Teske's avatar
Daniel Teske committed
653
654
    if (activeTarget())
        activeTarget()->updateDefaultDeployConfigurations();
655
656
657
658
    updateRunConfigurations();
    emit proFilesEvaluated();
}

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

665
void QmakeProject::scheduleAsyncUpdate(QmakeProFileNode *node)
dt's avatar
dt committed
666
{
dt's avatar
dt committed
667
668
    if (m_asyncUpdateState == ShuttingDown)
        return;
669

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

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

683
    enableActiveQmakeBuildConfiguration(activeTarget(), false);
684

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

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

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

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

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

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

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

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


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

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

784
void QmakeProject::decrementPendingEvaluateFutures()
dt's avatar
dt committed
785
786
787
{
    --m_pendingEvaluateFuturesCount;

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

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

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

824
bool QmakeProject::wasEvaluateCanceled()
dt's avatar
dt committed
825
826
827
828
{
    return m_cancelEvaluate;
}

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

    m_qmakeVfs->invalidateCache();

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

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

    m_asyncUpdateFutureInterface->reportStarted();

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

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

864
void QmakeProject::buildFinished(bool success)
865
866
867
868
869
{
    if (success)
        m_qmakeVfs->invalidateContents();
}

870
ProjectExplorer::IProjectManager *QmakeProject::projectManager() const
con's avatar
con committed
871
872
873
874
{
    return m_manager;
}

875
QmakeManager *QmakeProject::qmakeProjectManager() const
con's avatar
con committed
876
877
878
879
{
    return m_manager;
}

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

888
QString QmakeProject::displayName() const
con's avatar
con committed
889
{
890
    return QFileInfo(projectFilePath()).completeBaseName();
con's avatar
con committed
891
892
}

893
Core::Id QmakeProject::id() const
Tobias Hunger's avatar
Tobias Hunger committed
894
{
895
    return Core::Id(Constants::QMAKEPROJECT_ID);
Tobias Hunger's avatar
Tobias Hunger committed
896
897
}

898
Core::IDocument *QmakeProject::document() const
con's avatar
con committed
899
900
901
902
{
    return m_fileInfo;
}

903
QStringList QmakeProject::files(FilesMode fileMode) const
con's avatar
con committed
904
905
906
907
908
909
910
911
912
913
{
    QStringList files;
    for (int i = 0; i < FileTypeSize; ++i) {
        files += m_projectFiles->files[i];
        if (fileMode == AllFiles)
            files += m_projectFiles->generatedFiles[i];
    }
    return files;
}

914
915
916
// Find the folder that contains a file a certain type (recurse down)
static FolderNode *folderOf(FolderNode *in, FileType fileType, const QString &fileName)
{
917
    foreach (FileNode *fn, in->fileNodes())
918
919
        if (fn->fileType() == fileType && fn->path() == fileName)
            return in;
920
    foreach (FolderNode *folder, in->subFolderNodes())
921
922
923
924
925
        if (FolderNode *pn = folderOf(folder, fileType, fileName))
            return pn;
    return 0;
}

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

936
QString QmakeProject::generatedUiHeader(const QString &formFile) const
937
938
939
940
{
    // Look in sub-profiles as SessionManager::projectForFile returns
    // the top-level project only.
    if (m_rootProjectNode)
941
942
        if (const QmakeProFileNode *pro = proFileNodeOf(m_rootProjectNode, FormType, formFile))
            return QmakeProFileNode::uiHeaderFile(pro->uiDirectory(), formFile);
943
944
945
    return QString();
}

946
void QmakeProject::proFileParseError(const QString &errorMessage)
con's avatar
con committed
947
{
hjk's avatar
hjk committed
948
    Core::MessageManager::write(errorMessage);
con's avatar
con committed
949
950
}

951
QtSupport::ProFileReader *QmakeProject::createProFileReader(const QmakeProFileNode *qmakeProFileNode, QmakeBuildConfiguration *bc)
952
{
953
    if (!m_qmakeGlobals) {
954
        m_qmakeGlobals = new ProFileGlobals;