qmakeproject.cpp 53.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
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
** 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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

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

33
#include "qmakeprojectmanager.h"
34 35
#include "qmakeprojectimporter.h"
#include "qmakebuildinfo.h"
con's avatar
con committed
36
#include "qmakestep.h"
37 38 39 40
#include "qmakenodes.h"
#include "qmakeprojectmanagerconstants.h"
#include "qmakebuildconfiguration.h"
#include "findqmakeprofiles.h"
41

42
#include <utils/algorithm.h>
43
#include <coreplugin/icontext.h>
44
#include <coreplugin/icore.h>
dt's avatar
dt committed
45
#include <coreplugin/progressmanager/progressmanager.h>
46
#include <cpptools/cppmodelmanager.h>
47
#include <qmljs/qmljsmodelmanagerinterface.h>
48
#include <projectexplorer/buildmanager.h>
49 50
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
51
#include <projectexplorer/toolchain.h>
52
#include <projectexplorer/headerpath.h>
53
#include <projectexplorer/target.h>
54
#include <projectexplorer/projectexplorer.h>
55
#include <proparser/qmakevfs.h>
56
#include <qtsupport/profilereader.h>
Tobias Hunger's avatar
Tobias Hunger committed
57
#include <qtsupport/qtkitinformation.h>
58
#include <qtsupport/qtversionmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
59
#include <qtsupport/uicodemodelsupport.h>
60
#include <resourceeditor/resourcenode.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
using namespace ProjectExplorer;
70
using namespace Utils;
con's avatar
con committed
71

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

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

namespace {

80
QmakeBuildConfiguration *enableActiveQmakeBuildConfiguration(Target *t, bool enabled)
Tobias Hunger's avatar
Tobias Hunger committed
81 82 83
{
    if (!t)
        return 0;
84
    QmakeBuildConfiguration *bc = static_cast<QmakeBuildConfiguration *>(t->activeBuildConfiguration());
Tobias Hunger's avatar
Tobias Hunger committed
85 86 87 88 89 90 91 92
    if (!bc)
        return 0;
    bc->setEnabled(enabled);
    return bc;
}

} // namespace

93
namespace QmakeProjectManager {
con's avatar
con committed
94 95
namespace Internal {

96
class QmakeProjectFile : public Core::IDocument
97 98 99 100
{
    Q_OBJECT

public:
101
    QmakeProjectFile(const QString &filePath, QObject *parent = 0);
102

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

105 106
    QString defaultPath() const override;
    QString suggestedFileName() const override;
107

108 109
    bool isModified() const override;
    bool isSaveAsAllowed() const override;
110

111 112
    ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override;
    bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override;
113 114
};

115
/// Watches folders for QmakePriFile nodes
116 117 118 119 120 121 122
/// use one file system watcher to watch all folders
/// such minimizing system ressouce usage

class CentralizedFolderWatcher : public QObject
{
    Q_OBJECT
public:
123
    CentralizedFolderWatcher(QmakeProject *parent);
124
    ~CentralizedFolderWatcher();
125 126
    void watchFolders(const QList<QString> &folders, QmakePriFileNode *node);
    void unwatchFolders(const QList<QString> &folders, QmakePriFileNode *node);
127 128 129 130 131 132 133

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

private:
134
    QmakeProject *m_project;
135 136
    QSet<QString> recursiveDirs(const QString &folder);
    QFileSystemWatcher m_watcher;
137
    QMultiMap<QString, QmakePriFileNode *> m_map;
138 139 140 141 142 143

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

144 145
// QmakeProjectFiles: Struct for (Cached) lists of files in a project
class QmakeProjectFiles {
Tobias Hunger's avatar
Tobias Hunger committed
146
public:
con's avatar
con committed
147
    void clear();
148
    bool equals(const QmakeProjectFiles &f) const;
con's avatar
con committed
149

150 151
    QStringList files[FileTypeSize];
    QStringList generatedFiles[FileTypeSize];
con's avatar
con committed
152 153 154
    QStringList proFiles;
};

155
void QmakeProjectFiles::clear()
con's avatar
con committed
156 157 158 159 160 161 162 163
{
    for (int i = 0; i < FileTypeSize; ++i) {
        files[i].clear();
        generatedFiles[i].clear();
    }
    proFiles.clear();
}

164
bool QmakeProjectFiles::equals(const QmakeProjectFiles &f) const
con's avatar
con committed
165 166 167 168 169 170 171 172 173
{
    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;
}

174
inline bool operator==(const QmakeProjectFiles &f1, const QmakeProjectFiles &f2)
con's avatar
con committed
175 176
{       return f1.equals(f2); }

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

180
QDebug operator<<(QDebug d, const  QmakeProjectFiles &f)
con's avatar
con committed
181 182
{
    QDebug nsp = d.nospace();
183
    nsp << "QmakeProjectFiles: proFiles=" <<  f.proFiles << '\n';
con's avatar
con committed
184 185 186 187 188
    for (int i = 0; i < FileTypeSize; ++i)
        nsp << "Type " << i << " files=" << f.files[i] <<  " generated=" << f.generatedFiles[i] << '\n';
    return d;
}

189
// A visitor to collect all files of a project in a QmakeProjectFiles struct
190
class ProjectFilesVisitor : public NodesVisitor
con's avatar
con committed
191
{
192
    ProjectFilesVisitor(QmakeProjectFiles *files);
hjk's avatar
hjk committed
193

con's avatar
con committed
194 195
public:

196
    static void findProjectFiles(QmakeProFileNode *rootNode, QmakeProjectFiles *files);
con's avatar
con committed
197 198 199 200 201

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

private:
202
    QmakeProjectFiles *m_files;
con's avatar
con committed
203 204
};

205
ProjectFilesVisitor::ProjectFilesVisitor(QmakeProjectFiles *files) :
con's avatar
con committed
206 207 208 209
    m_files(files)
{
}

Daniel Teske's avatar
Daniel Teske committed
210 211 212 213 214 215 216 217
namespace {
// uses std::unique, so takes a sorted list
void unique(QStringList &list)
{
    list.erase(std::unique(list.begin(), list.end()), list.end());
}
}

218
void ProjectFilesVisitor::findProjectFiles(QmakeProFileNode *rootNode, QmakeProjectFiles *files)
con's avatar
con committed
219 220 221 222 223
{
    files->clear();
    ProjectFilesVisitor visitor(files);
    rootNode->accept(&visitor);
    for (int i = 0; i < FileTypeSize; ++i) {
224
        Utils::sort(files->files[i]);
Daniel Teske's avatar
Daniel Teske committed
225
        unique(files->files[i]);
226
        Utils::sort(files->generatedFiles[i]);
Daniel Teske's avatar
Daniel Teske committed
227
        unique(files->generatedFiles[i]);
con's avatar
con committed
228
    }
229
    Utils::sort(files->proFiles);
Daniel Teske's avatar
Daniel Teske committed
230
    unique(files->proFiles);
con's avatar
con committed
231 232 233 234
}

void ProjectFilesVisitor::visitProjectNode(ProjectNode *projectNode)
{
235
    m_files->proFiles.append(projectNode->path().toString());
con's avatar
con committed
236 237 238 239 240
    visitFolderNode(projectNode);
}

void ProjectFilesVisitor::visitFolderNode(FolderNode *folderNode)
{
241
    if (dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(folderNode))
242
        m_files->files[ResourceType].push_back(folderNode->path().toString());
243

con's avatar
con committed
244 245 246
    foreach (FileNode *fileNode, folderNode->fileNodes()) {
        const int type = fileNode->fileType();
        QStringList &targetList = fileNode->isGenerated() ? m_files->generatedFiles[type] : m_files->files[type];
247
        targetList.push_back(fileNode->path().toString());
con's avatar
con committed
248 249 250 251 252
    }
}

}

253
// ----------- QmakeProjectFile
Friedemann Kleint's avatar
Friedemann Kleint committed
254
namespace Internal {
255
QmakeProjectFile::QmakeProjectFile(const QString &filePath, QObject *parent)
hjk's avatar
hjk committed
256
    : Core::IDocument(parent)
con's avatar
con committed
257
{
258
    setId("Qmake.ProFile");
hjk's avatar
hjk committed
259
    setMimeType(QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE));
260
    setFilePath(FileName::fromString(filePath));
con's avatar
con committed
261 262
}

263
bool QmakeProjectFile::save(QString *, const QString &, bool)
con's avatar
con committed
264
{
265 266
    // This is never used
    return false;
con's avatar
con committed
267 268
}

269
QString QmakeProjectFile::defaultPath() const
con's avatar
con committed
270 271 272 273
{
    return QString();
}

274
QString QmakeProjectFile::suggestedFileName() const
con's avatar
con committed
275 276 277 278
{
    return QString();
}

279
bool QmakeProjectFile::isModified() const
con's avatar
con committed
280
{
281
    return false; // we save after changing anyway
con's avatar
con committed
282 283
}

284
bool QmakeProjectFile::isSaveAsAllowed() const
con's avatar
con committed
285 286 287 288
{
    return false;
}

289
Core::IDocument::ReloadBehavior QmakeProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
con's avatar
con committed
290
{
291 292 293 294 295
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
}

296
bool QmakeProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
297
{
298
    Q_UNUSED(errorString)
299 300
    Q_UNUSED(flag)
    Q_UNUSED(type)
301
    return true;
con's avatar
con committed
302 303
}

304
} // namespace Internal
305

con's avatar
con committed
306
/*!
307
  \class QmakeProject
con's avatar
con committed
308

309
  QmakeProject manages information about an individual Qt 4 (.pro) project file.
con's avatar
con committed
310 311
  */

312
QmakeProject::QmakeProject(QmakeManager *manager, const QString &fileName) :
con's avatar
con committed
313
    m_manager(manager),
314 315
    m_fileInfo(new QmakeProjectFile(fileName, this)),
    m_projectFiles(new QmakeProjectFiles),
316
    m_qmakeVfs(new QMakeVfs)
con's avatar
con committed
317
{
318
    setId(Constants::QMAKEPROJECT_ID);
319
    setProjectContext(Core::Context(QmakeProjectManager::Constants::PROJECT_ID));
320
    setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX));
321
    setRequiredKitMatcher(QtSupport::QtKitInformation::qtVersionMatcher());
322

dt's avatar
dt committed
323
    m_asyncUpdateTimer.setSingleShot(true);
324
    m_asyncUpdateTimer.setInterval(3000);
dt's avatar
dt committed
325
    connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate()));
326

hjk's avatar
hjk committed
327
    connect(BuildManager::instance(), SIGNAL(buildQueueFinished(bool)),
328
            SLOT(buildFinished(bool)));
329 330 331 332

    setPreferredKitMatcher(KitMatcher([this](const Kit *kit) -> bool {
                               return matchesKit(kit);
                           }));
con's avatar
con committed
333 334
}

335
QmakeProject::~QmakeProject()
con's avatar
con committed
336
{
337
    m_codeModelFuture.cancel();
338
    m_asyncUpdateState = ShuttingDown;
dt's avatar
dt committed
339
    m_manager->unregisterProject(this);
con's avatar
con committed
340
    delete m_projectFiles;
341
    m_cancelEvaluate = true;
342 343
    // Deleting the root node triggers a few things, make sure rootProjectNode
    // returns 0 already
344
    QmakeProFileNode *root = m_rootProjectNode;
345 346
    m_rootProjectNode = 0;
    delete root;
347
    Q_ASSERT(m_qmakeGlobalsRefCnt == 0);
348
    delete m_qmakeVfs;
con's avatar
con committed
349 350
}

351
void QmakeProject::updateFileList()
con's avatar
con committed
352
{
353
    QmakeProjectFiles newFiles;
con's avatar
con committed
354 355 356 357 358 359 360 361 362
    ProjectFilesVisitor::findProjectFiles(m_rootProjectNode, &newFiles);
    if (newFiles != *m_projectFiles) {
        *m_projectFiles = newFiles;
        emit fileListChanged();
        if (debug)
            qDebug() << Q_FUNC_INFO << *m_projectFiles;
    }
}

363
Project::RestoreResult QmakeProject::fromMap(const QVariantMap &map, QString *errorMessage)
con's avatar
con committed
364
{
365 366 367
    RestoreResult result = Project::fromMap(map, errorMessage);
    if (result != RestoreResult::Ok)
        return result;
con's avatar
con committed
368

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

dt's avatar
dt committed
379 380
    m_manager->registerProject(this);

381
    m_rootProjectNode = new QmakeProFileNode(this, m_fileInfo->filePath());
382

383 384
    // On active buildconfiguration changes, reevaluate the .pro files
    m_activeTarget = activeTarget();
385 386 387 388
    if (m_activeTarget) {
        connect(m_activeTarget, &Target::activeBuildConfigurationChanged,
                this, &QmakeProject::scheduleAsyncUpdateLater);
    }
389

390 391
    connect(this, &Project::activeTargetChanged,
            this, &QmakeProject::activeTargetWasChanged);
Tobias Hunger's avatar
Tobias Hunger committed
392

393
    scheduleAsyncUpdate(QmakeProFileNode::ParseNow);
394
    return RestoreResult::Ok;
con's avatar
con committed
395 396
}

dt's avatar
dt committed
397 398 399
/// equalFileList compares two file lists ignoring
/// <configuration> without generating temporary lists

400
bool QmakeProject::equalFileList(const QStringList &a, const QStringList &b)
dt's avatar
dt committed
401 402 403 404 405 406 407 408 409
{
    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) {
410
        if (*ait == CppTools::CppModelManager::configurationFileName())
dt's avatar
dt committed
411
            ++ait;
412
        else if (*bit == CppTools::CppModelManager::configurationFileName())
dt's avatar
dt committed
413 414 415 416 417 418 419
            ++bit;
        else if (*ait == *bit)
            ++ait, ++bit;
        else
           return false;
    }
    return (ait == aend && bit == bend);
Tobias Hunger's avatar
Tobias Hunger committed
420 421
}

422
void QmakeProject::updateCodeModels()
con's avatar
con committed
423 424
{
    if (debug)
425
        qDebug() << "QmakeProject::updateCodeModel()";
con's avatar
con committed
426

427
    if (activeTarget() && !activeTarget()->activeBuildConfiguration())
Tobias Hunger's avatar
Tobias Hunger committed
428 429
        return;

430 431 432 433
    updateCppCodeModel();
    updateQmlJSCodeModel();
}

434
void QmakeProject::updateCppCodeModel()
435
{
436 437
    typedef CppTools::ProjectPart ProjectPart;
    typedef CppTools::ProjectFile ProjectFile;
438

Tobias Hunger's avatar
Tobias Hunger committed
439
    Kit *k = 0;
440
    if (Target *target = activeTarget())
Tobias Hunger's avatar
Tobias Hunger committed
441
        k = target->kit();
Tobias Hunger's avatar
Tobias Hunger committed
442
    else
443
        k = KitManager::defaultKit();
con's avatar
con committed
444

445
    CppTools::CppModelManager *modelmanager = CppTools::CppModelManager::instance();
446 447
    FindQmakeProFiles findQmakeProFiles;
    QList<QmakeProFileNode *> proFiles = findQmakeProFiles(rootProjectNode());
con's avatar
con committed
448

449
    CppTools::ProjectInfo pinfo(this);
450

451
    QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);
452 453 454 455 456 457 458
    ProjectPart::QtVersion qtVersionForPart = ProjectPart::NoQt;
    if (qtVersion) {
        if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
            qtVersionForPart = ProjectPart::Qt4;
        else
            qtVersionForPart = ProjectPart::Qt5;
    }
459

Tobias Hunger's avatar
Tobias Hunger committed
460
    QHash<QString, QString> uiCodeModelData;
461
    QStringList allFiles;
462
    foreach (QmakeProFileNode *pro, proFiles) {
463 464 465
        ProjectPart::Ptr templatePart(new ProjectPart);
        templatePart->project = this;
        templatePart->displayName = pro->displayName();
466
        templatePart->projectFile = pro->path().toString();
467
        templatePart->selectedForBuilding = pro->includedInExactParse();
468 469

        if (pro->variableValue(ConfigVar).contains(QLatin1String("qt")))
470
            templatePart->qtVersion = qtVersionForPart;
471
        else
472
            templatePart->qtVersion = ProjectPart::NoQt;
473

474
        // part->defines
475
        templatePart->projectDefines += pro->cxxDefines();
476

477 478 479 480 481 482 483 484
        foreach (const QString &inc, pro->variableValue(IncludePathVar)) {
            const auto headerPath
                = ProjectPart::HeaderPath(inc, ProjectPart::HeaderPath::IncludePath);
            if (!templatePart->headerPaths.contains(headerPath))
                templatePart->headerPaths += headerPath;
        }

        if (qtVersion) {
485
            if (!qtVersion->frameworkInstallPath().isEmpty()) {
486
                templatePart->headerPaths += ProjectPart::HeaderPath(
487 488
                            qtVersion->frameworkInstallPath(),
                            ProjectPart::HeaderPath::FrameworkPath);
489
            }
490
        }
491

492
        // part->precompiledHeaders
493 494
        templatePart->precompiledHeaders.append(pro->variableValue(PrecompiledHeaderVar));

495 496
        templatePart->updateLanguageFeatures();

497 498 499 500 501 502 503 504 505 506 507
        ProjectPart::Ptr cppPart = templatePart->copy();
        { // C++ files:
            // part->files
            foreach (const QString &file, pro->variableValue(CppSourceVar)) {
                allFiles << file;
                cppPart->files << ProjectFile(file, ProjectFile::CXXSource);
            }
            foreach (const QString &file, pro->variableValue(CppHeaderVar)) {
                allFiles << file;
                cppPart->files << ProjectFile(file, ProjectFile::CXXHeader);
            }
dt's avatar
dt committed
508

509 510 511 512 513 514 515 516
            // Ui Files:
            QHash<QString, QString> uiData = pro->uiFiles();
            for (QHash<QString, QString>::const_iterator i = uiData.constBegin(); i != uiData.constEnd(); ++i) {
                allFiles << i.value();
                cppPart->files << ProjectFile(i.value(), ProjectFile::CXXHeader);
            }
            uiCodeModelData.unite(uiData);

517
            cppPart->files.prepend(ProjectFile(CppTools::CppModelManager::configurationFileName(),
518 519 520 521 522 523 524 525 526 527
                                            ProjectFile::CXXSource));
            const QStringList cxxflags = pro->variableValue(CppFlagsVar);
            cppPart->evaluateToolchain(ToolChainKitInformation::toolChain(k),
                                       cxxflags,
                                       SysRootKitInformation::sysRoot(k));

            if (!cppPart->files.isEmpty()) {
                pinfo.appendProjectPart(cppPart);
                setProjectLanguage(ProjectExplorer::Constants::LANG_CXX, true);
            }
528
        }
Tobias Hunger's avatar
Tobias Hunger committed
529

530 531 532 533 534 535 536 537 538 539 540 541
        ProjectPart::Ptr objcppPart = templatePart->copy();
        { // ObjC++ files:
            foreach (const QString &file, pro->variableValue(ObjCSourceVar)) {
                allFiles << file;
                // Although the enum constant is called ObjCSourceVar, it actually is ObjC++ source
                // code, as qmake does not handle C (and ObjC).
                objcppPart->files << ProjectFile(file, ProjectFile::ObjCXXSource);
            }
            foreach (const QString &file, pro->variableValue(ObjCHeaderVar)) {
                allFiles << file;
                objcppPart->files << ProjectFile(file, ProjectFile::ObjCXXHeader);
            }
542

543 544 545 546
            const QStringList cxxflags = pro->variableValue(CppFlagsVar);
            objcppPart->evaluateToolchain(ToolChainKitInformation::toolChain(k),
                                          cxxflags,
                                          SysRootKitInformation::sysRoot(k));
547

548 549 550 551 552 553
            if (!objcppPart->files.isEmpty()) {
                pinfo.appendProjectPart(objcppPart);
                // TODO: there is no LANG_OBJCXX, so:
                setProjectLanguage(ProjectExplorer::Constants::LANG_CXX, true);
            }
        }
554

555 556 557 558 559
        if (!objcppPart->files.isEmpty())
            cppPart->displayName += QLatin1String(" (C++)");
        if (!cppPart->files.isEmpty())
            objcppPart->displayName += QLatin1String(" (ObjC++)");
    }
560
    pinfo.finish();
561

Tobias Hunger's avatar
Tobias Hunger committed
562
    // Also update Ui Code Model Support:
563
    QtSupport::UiCodeModelManager::update(this, uiCodeModelData);
Tobias Hunger's avatar
Tobias Hunger committed
564

565
    m_codeModelFuture = modelmanager->updateProjectInfo(pinfo);
566 567
}

568
void QmakeProject::updateQmlJSCodeModel()
569
{
570
    QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
571 572 573
    if (!modelManager)
        return;

574
    QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
575
            modelManager->defaultProjectInfoForProject(this);
576

577 578
    FindQmakeProFiles findQt4ProFiles;
    QList<QmakeProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
579

580
    projectInfo.importPaths.clear();
581 582

    bool hasQmlLib = false;
583
    foreach (QmakeProFileNode *node, proFiles) {
584
        foreach (const QString &path, node->variableValue(QmlImportPathVar))
585
            projectInfo.importPaths.maybeInsert(FileName::fromString(path),
586
                                                QmlJS::Dialect::Qml);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
587 588
        projectInfo.activeResourceFiles.append(node->variableValue(ExactResourceVar));
        projectInfo.allResourceFiles.append(node->variableValue(ResourceVar));
589 590 591 592 593 594
        if (!hasQmlLib) {
            QStringList qtLibs = node->variableValue(QtVar);
            hasQmlLib = qtLibs.contains(QLatin1String("declarative")) ||
                    qtLibs.contains(QLatin1String("qml")) ||
                    qtLibs.contains(QLatin1String("quick"));
        }
595
    }
596

597 598 599 600 601
    // 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.
    if (hasQmlLib)
602
        addProjectLanguage(ProjectExplorer::Constants::LANG_QMLJS);
603

Fawzi Mohamed's avatar
Fawzi Mohamed committed
604 605
    projectInfo.activeResourceFiles.removeDuplicates();
    projectInfo.allResourceFiles.removeDuplicates();
606

607
    modelManager->updateProjectInfo(projectInfo, this);
608 609
}

610
void QmakeProject::updateRunConfigurations()
611
{
612 613
    if (activeTarget())
        activeTarget()->updateDefaultRunConfigurations();
dt's avatar
dt committed
614 615
}

616
void QmakeProject::scheduleAsyncUpdate(QmakeProFileNode *node, QmakeProFileNode::AsyncUpdateDelay delay)
dt's avatar
dt committed
617
{
dt's avatar
dt committed
618 619
    if (m_asyncUpdateState == ShuttingDown)
        return;
620

dt's avatar
dt committed
621
    if (debug)
Daniel Teske's avatar
Daniel Teske committed
622
        qDebug()<<"schduleAsyncUpdate (node)"<<node->path();
dt's avatar
dt committed
623 624

    if (m_cancelEvaluate) {
dt's avatar
dt committed
625 626
        if (debug)
            qDebug()<<"  Already canceling, nothing to do";
dt's avatar
dt committed
627 628 629 630 631 632
        // 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;
    }

633
    enableActiveQmakeBuildConfiguration(activeTarget(), false);
634

dt's avatar
dt committed
635 636
    if (m_asyncUpdateState == AsyncFullUpdatePending) {
        // Just postpone
dt's avatar
dt committed
637 638
        if (debug)
            qDebug()<<"  full update pending, restarting timer";
639
        startAsyncTimer(delay);
dt's avatar
dt committed
640 641
    } else if (m_asyncUpdateState == AsyncPartialUpdatePending
               || m_asyncUpdateState == Base) {
dt's avatar
dt committed
642 643
        if (debug)
            qDebug()<<"  adding node to async update list, setting state to AsyncPartialUpdatePending";
dt's avatar
dt committed
644 645 646
        // Add the node
        m_asyncUpdateState = AsyncPartialUpdatePending;

647
        QList<QmakeProFileNode *>::iterator it;
dt's avatar
dt committed
648
        bool add = true;
dt's avatar
dt committed
649 650
        if (debug)
            qDebug()<<"scheduleAsyncUpdate();"<<m_partialEvaluate.size()<<"nodes";
dt's avatar
dt committed
651 652 653 654 655 656
        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
657 658
                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
659 660 661 662 663 664 665 666 667
                add = false;
                break;
            } else {
                ++it;
            }
        }

        if (add)
            m_partialEvaluate.append(node);
668 669 670

        // Cancel running code model update
        m_codeModelFuture.cancel();
671 672 673

        startAsyncTimer(delay);

dt's avatar
dt committed
674 675 676 677 678 679 680 681
    } 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
682 683
        if (debug)
            qDebug()<<"  Async update in progress, scheduling new one afterwards";
684
        scheduleAsyncUpdate(delay);
dt's avatar
dt committed
685 686 687
    }
}

688
void QmakeProject::scheduleAsyncUpdate(QmakeProFileNode::AsyncUpdateDelay delay)
dt's avatar
dt committed
689
{
dt's avatar
dt committed
690 691
    if (debug)
        qDebug()<<"scheduleAsyncUpdate";
dt's avatar
dt committed
692 693
    if (m_asyncUpdateState == ShuttingDown)
        return;
694

dt's avatar
dt committed
695 696
    if (m_cancelEvaluate) { // we are in progress of canceling
                            // and will start the evaluation after that
dt's avatar
dt committed
697 698
        if (debug)
            qDebug()<<"  canceling is in progress, doing nothing";
dt's avatar
dt committed
699 700 701
        return;
    }
    if (m_asyncUpdateState == AsyncUpdateInProgress) {
dt's avatar
dt committed
702 703
        if (debug)
            qDebug()<<"  update in progress, canceling and setting state to full update pending";
dt's avatar
dt committed
704 705
        m_cancelEvaluate = true;
        m_asyncUpdateState = AsyncFullUpdatePending;
706
        enableActiveQmakeBuildConfiguration(activeTarget(), false);
Daniel Teske's avatar
Daniel Teske committed
707
        m_rootProjectNode->setParseInProgressRecursive(true);
dt's avatar
dt committed
708 709 710
        return;
    }

dt's avatar
dt committed
711 712
    if (debug)
        qDebug()<<"  starting timer for full update, setting state to full update pending";
dt's avatar
dt committed
713
    m_partialEvaluate.clear();
714
    enableActiveQmakeBuildConfiguration(activeTarget(), false);
Daniel Teske's avatar
Daniel Teske committed
715
    m_rootProjectNode->setParseInProgressRecursive(true);
dt's avatar
dt committed
716
    m_asyncUpdateState = AsyncFullUpdatePending;
717 718 719

    // Cancel running code model update
    m_codeModelFuture.cancel();
720
    startAsyncTimer(delay);
dt's avatar
dt committed
721 722
}

723 724 725 726 727 728
void QmakeProject::startAsyncTimer(QmakeProFileNode::AsyncUpdateDelay delay)
{
    m_asyncUpdateTimer.stop();
    m_asyncUpdateTimer.setInterval(qMin(m_asyncUpdateTimer.interval(), delay == QmakeProFileNode::ParseLater ? 3000 : 0));
    m_asyncUpdateTimer.start();
}
dt's avatar
dt committed
729

730
void QmakeProject::incrementPendingEvaluateFutures()
dt's avatar
dt committed
731 732
{
    ++m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
733 734
    if (debug)
        qDebug()<<"incrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
735 736 737 738 739

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

740
void QmakeProject::decrementPendingEvaluateFutures()
dt's avatar
dt committed
741 742 743
{
    --m_pendingEvaluateFuturesCount;

dt's avatar
dt committed
744 745
    if (debug)
        qDebug()<<"decrementPendingEvaluateFutures to"<<m_pendingEvaluateFuturesCount;
dt's avatar
dt committed
746 747 748

    m_asyncUpdateFutureInterface->setProgressValue(m_asyncUpdateFutureInterface->progressValue() + 1);
    if (m_pendingEvaluateFuturesCount == 0) {
dt's avatar
dt committed
749 750
        if (debug)
            qDebug()<<"  WOHOO, no pending futures, cleaning up";
dt's avatar
dt committed
751
        // We are done!
dt's avatar
dt committed
752 753
        if (debug)
            qDebug()<<"  reporting finished";
754

dt's avatar
dt committed
755 756 757 758 759 760 761
        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
762 763
            if (debug)
                qDebug()<<"  Oh update is pending start the timer";
764
            startAsyncTimer(QmakeProFileNode::ParseLater);
765
        } else  if (m_asyncUpdateState != ShuttingDown){
Tobias Hunger's avatar
Tobias Hunger committed
766
            // After being done, we need to call:
767
            m_asyncUpdateState = Base;
768
            enableActiveQmakeBuildConfiguration(activeTarget(), true);
769
            updateFileList();
770
            updateCodeModels();
771
            updateBuildSystemData();
772 773
            if (activeTarget())
                activeTarget()->updateDefaultDeployConfigurations();
774 775
            updateRunConfigurations();
            emit proFilesEvaluated();
dt's avatar
dt committed
776 777
            if (debug)
                qDebug()<<"  Setting state to Base";
dt's avatar
dt committed
778 779 780 781
        }
    }
}

782
bool QmakeProject::wasEvaluateCanceled()
dt's avatar
dt committed
783 784 785 786
{
    return m_cancelEvaluate;
}

787
void QmakeProject::asyncUpdate()
dt's avatar
dt committed
788
{