qmakeproject.cpp 54.1 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 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
void updateBoilerPlateCodeFiles(const AbstractMobileApp *app, const QString &proFile)
{
92
93
    const QList<AbstractGeneratedFileInfo> updates = app->fileUpdates(proFile);
    const QString nativeProFile = QDir::toNativeSeparators(proFile);
94
    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
                               "outdated or have been modified:<br><br>%2<br><br>Do you want "
                               "Qt Creator to update the files? Any changes will be lost.")
103
                .arg(nativeProFile, fileNames.join(QLatin1String(", ")));
104
105
106
107
108
109
110
        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
    setId(Constants::QMAKEPROJECT_ID);
347
    setProjectContext(Core::Context(QmakeProjectManager::Constants::PROJECT_ID));
348
    setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX));
349

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

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

358
QmakeProject::~QmakeProject()
con's avatar
con committed
359
{
360
    m_codeModelFuture.cancel();
361
    m_asyncUpdateState = ShuttingDown;
dt's avatar
dt committed
362
    m_manager->unregisterProject(this);
363
    delete m_qmakeVfs;
con's avatar
con committed
364
    delete m_projectFiles;
365
    m_cancelEvaluate = true;
366
367
    // Deleting the root node triggers a few things, make sure rootProjectNode
    // returns 0 already
368
    QmakeProFileNode *root = m_rootProjectNode;
369
370
    m_rootProjectNode = 0;
    delete root;
con's avatar
con committed
371
372
}

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

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

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

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

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

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

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

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

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

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

430
    foreach (QmakeProFileNode *node, applicationProFiles(QmakeProject::ExactAndCumulativeParse)) {
431
432
        const QString path = node->path();

433
434
435
436
        foreach (TemplateInfo info, QtQuickApp::templateInfos()) {
            qtQuickApp.setTemplateInfo(info);
            updateBoilerPlateCodeFiles(&qtQuickApp, path);
        }
437
438
        updateBoilerPlateCodeFiles(&html5App, path);
    }
dt's avatar
dt committed
439
    return true;
con's avatar
con committed
440
441
}

dt's avatar
dt committed
442
443
444
/// equalFileList compares two file lists ignoring
/// <configuration> without generating temporary lists

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

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

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

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

479
void QmakeProject::updateCppCodeModel()
480
{
481
482
    typedef CppTools::ProjectPart ProjectPart;
    typedef CppTools::ProjectFile ProjectFile;
483

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

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

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

498
499
    FindQmakeProFiles findQmakeProFiles;
    QList<QmakeProFileNode *> proFiles = findQmakeProFiles(rootProjectNode());
con's avatar
con committed
500

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

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

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

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

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

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

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

547
        if (QmakeProFileNode *node = rootQmakeProjectNode())
548
            part->includePaths.append(node->resolvedMkspecPath());
con's avatar
con committed
549

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

553
        // part->files
554
555
556
557
558
559
560
561
        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
562
563
564
565
566
567

        // 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);
568
        }
Tobias Hunger's avatar
Tobias Hunger committed
569
        uiCodeModelData.unite(uiData);
con's avatar
con committed
570

571
        part->files.prepend(ProjectFile(CppTools::CppModelManagerInterface::configurationFileName(),
572
573
574
                                        ProjectFile::CXXSource));
        foreach (const QString &file, pro->variableValue(ObjCSourceVar)) {
            allFiles << file;
575
576
577
            // 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);
578
        }
579
580
581
582
583
        foreach (const QString &file, pro->variableValue(ObjCHeaderVar)) {
            allFiles << file;
            part->files << ProjectFile(file, ProjectFile::ObjCXXHeader);
        }

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

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

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

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

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

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

604
605
    FindQmakeProFiles findQt4ProFiles;
    QList<QmakeProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
606

607
    projectInfo.importPaths.clear();
608
609

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

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

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

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

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

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

dt's avatar
dt committed
649
650
    if (debug)
        qDebug()<<"State is now Base";
dt's avatar
dt committed
651
    m_asyncUpdateState = Base;
652
    enableActiveQmakeBuildConfiguration(activeTarget(), true);
653
    updateBuildSystemData();
Daniel Teske's avatar
Daniel Teske committed
654
655
    if (activeTarget())
        activeTarget()->updateDefaultDeployConfigurations();
656
657
658
659
    updateRunConfigurations();
    emit proFilesEvaluated();
}

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

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

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

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

684
    enableActiveQmakeBuildConfiguration(activeTarget(), false);
685

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

    m_qmakeVfs->invalidateCache();

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

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

    m_asyncUpdateFutureInterface->reportStarted();

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

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

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

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

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

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

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

894
Core::IDocument *QmakeProject::document() const
con's avatar
con committed
895
896
897
898
{
    return m_fileInfo;
}

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

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